From d25f6e5b1fcca8d14d624abcf6a9bba258c89292 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 24 Nov 2017 12:25:35 +0100 Subject: [PATCH 01/48] Remove rand.Seed() from loop --- examples/games/life/life.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/games/life/life.go b/examples/games/life/life.go index 192792c..a86151d 100644 --- a/examples/games/life/life.go +++ b/examples/games/life/life.go @@ -30,18 +30,24 @@ type Game struct { } func main() { + rand.Seed(time.Now().UnixNano()) + game := Game{} game.Init() + raylib.InitWindow(game.ScreenWidth, game.ScreenHeight, "Conway's Game of Life") raylib.SetTargetFPS(20) + for !raylib.WindowShouldClose() { if game.Playing { game.Update() } + game.Input() game.Draw() } + raylib.CloseWindow() } @@ -61,7 +67,6 @@ func (g *Game) Init() { g.Cells[x][y] = &Cell{} g.Cells[x][y].Position = raylib.NewVector2((float32(x) * squareSize), (float32(y)*squareSize)+1) g.Cells[x][y].Size = raylib.NewVector2(squareSize-1, squareSize-1) - rand.Seed(time.Now().UnixNano()) if rand.Float64() < 0.1 { g.Cells[x][y].Alive = true } From 5b59c0b7e97d81e23d3bb45d3a313598dfbd38cf Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 27 Nov 2017 05:32:39 +0100 Subject: [PATCH 02/48] rREM - raylib Resource EMbedder --- Makefile | 2 +- examples/others/resources/data.h | 8 + examples/others/resources/data.rres | Bin 0 -> 370176 bytes examples/others/resources/main.go | 95 ++++++ raylib/audio.go | 5 +- raylib/rres.go | 323 ++++++++++---------- rres/README.md | 3 + rres/cmd/rrem/README.md | 25 ++ rres/cmd/rrem/main.go | 453 ++++++++++++++++++++++++++++ rres/rres.go | 163 ++++++++++ 10 files changed, 911 insertions(+), 166 deletions(-) create mode 100644 examples/others/resources/data.h create mode 100644 examples/others/resources/data.rres create mode 100644 examples/others/resources/main.go create mode 100644 rres/README.md create mode 100644 rres/cmd/rrem/README.md create mode 100644 rres/cmd/rrem/main.go create mode 100644 rres/rres.go diff --git a/Makefile b/Makefile index 92ee2ea..964f4d9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PACKAGES= raylib raygui raymath easings physics +PACKAGES= raylib raygui raymath easings physics rres GO?= go diff --git a/examples/others/resources/data.h b/examples/others/resources/data.h new file mode 100644 index 0000000..da00b6e --- /dev/null +++ b/examples/others/resources/data.h @@ -0,0 +1,8 @@ +#define NUM_RESOURCES 6 + +#define RES_coin.wav 0x00000000 // Embedded as WAVE +#define RES_raylib_logo.gif 0x00000001 // Embedded as IMAGE +#define RES_raylib_logo.jpg 0x00000002 // Embedded as IMAGE +#define RES_raylib_logo.png 0x00000003 // Embedded as IMAGE +#define RES_raylib_logo.tga 0x00000004 // Embedded as IMAGE +#define RES_tanatana.ogg 0x00000005 // Embedded as VORBIS diff --git a/examples/others/resources/data.rres b/examples/others/resources/data.rres new file mode 100644 index 0000000000000000000000000000000000000000..b0d85192d0c370eb550f65919d50df77780f0bad GIT binary patch literal 370176 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
m=lLa*l_Ly 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
+ *
+ * Copyright © 2014      Jonas Ådahl
+ * Copyright © 2015      Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_pointer; +struct wl_region; +struct wl_surface; +struct zwp_confined_pointer_v1; +struct zwp_locked_pointer_v1; +struct zwp_pointer_constraints_v1; + +/** + * @page page_iface_zwp_pointer_constraints_v1 zwp_pointer_constraints_v1 + * @section page_iface_zwp_pointer_constraints_v1_desc Description + * + * The global interface exposing pointer constraining functionality. It + * exposes two requests: lock_pointer for locking the pointer to its + * position, and confine_pointer for locking the pointer to a region. + * + * The lock_pointer and confine_pointer requests create the objects + * wp_locked_pointer and wp_confined_pointer respectively, and the client can + * use these objects to interact with the lock. + * + * For any surface, only one lock or confinement may be active across all + * wl_pointer objects of the same seat. If a lock or confinement is requested + * when another lock or confinement is active or requested on the same surface + * and with any of the wl_pointer objects of the same seat, an + * 'already_constrained' error will be raised. + * @section page_iface_zwp_pointer_constraints_v1_api API + * See @ref iface_zwp_pointer_constraints_v1. + */ +/** + * @defgroup iface_zwp_pointer_constraints_v1 The zwp_pointer_constraints_v1 interface + * + * The global interface exposing pointer constraining functionality. It + * exposes two requests: lock_pointer for locking the pointer to its + * position, and confine_pointer for locking the pointer to a region. + * + * The lock_pointer and confine_pointer requests create the objects + * wp_locked_pointer and wp_confined_pointer respectively, and the client can + * use these objects to interact with the lock. + * + * For any surface, only one lock or confinement may be active across all + * wl_pointer objects of the same seat. If a lock or confinement is requested + * when another lock or confinement is active or requested on the same surface + * and with any of the wl_pointer objects of the same seat, an + * 'already_constrained' error will be raised. + */ +extern const struct wl_interface zwp_pointer_constraints_v1_interface; +/** + * @page page_iface_zwp_locked_pointer_v1 zwp_locked_pointer_v1 + * @section page_iface_zwp_locked_pointer_v1_desc Description + * + * The wp_locked_pointer interface represents a locked pointer state. + * + * While the lock of this object is active, the wl_pointer objects of the + * associated seat will not emit any wl_pointer.motion events. + * + * This object will send the event 'locked' when the lock is activated. + * Whenever the lock is activated, it is guaranteed that the locked surface + * will already have received pointer focus and that the pointer will be + * within the region passed to the request creating this object. + * + * To unlock the pointer, send the destroy request. This will also destroy + * the wp_locked_pointer object. + * + * If the compositor decides to unlock the pointer the unlocked event is + * sent. See wp_locked_pointer.unlock for details. + * + * When unlocking, the compositor may warp the cursor position to the set + * cursor position hint. If it does, it will not result in any relative + * motion events emitted via wp_relative_pointer. + * + * If the surface the lock was requested on is destroyed and the lock is not + * yet activated, the wp_locked_pointer object is now defunct and must be + * destroyed. + * @section page_iface_zwp_locked_pointer_v1_api API + * See @ref iface_zwp_locked_pointer_v1. + */ +/** + * @defgroup iface_zwp_locked_pointer_v1 The zwp_locked_pointer_v1 interface + * + * The wp_locked_pointer interface represents a locked pointer state. + * + * While the lock of this object is active, the wl_pointer objects of the + * associated seat will not emit any wl_pointer.motion events. + * + * This object will send the event 'locked' when the lock is activated. + * Whenever the lock is activated, it is guaranteed that the locked surface + * will already have received pointer focus and that the pointer will be + * within the region passed to the request creating this object. + * + * To unlock the pointer, send the destroy request. This will also destroy + * the wp_locked_pointer object. + * + * If the compositor decides to unlock the pointer the unlocked event is + * sent. See wp_locked_pointer.unlock for details. + * + * When unlocking, the compositor may warp the cursor position to the set + * cursor position hint. If it does, it will not result in any relative + * motion events emitted via wp_relative_pointer. + * + * If the surface the lock was requested on is destroyed and the lock is not + * yet activated, the wp_locked_pointer object is now defunct and must be + * destroyed. + */ +extern const struct wl_interface zwp_locked_pointer_v1_interface; +/** + * @page page_iface_zwp_confined_pointer_v1 zwp_confined_pointer_v1 + * @section page_iface_zwp_confined_pointer_v1_desc Description + * + * The wp_confined_pointer interface represents a confined pointer state. + * + * This object will send the event 'confined' when the confinement is + * activated. Whenever the confinement is activated, it is guaranteed that + * the surface the pointer is confined to will already have received pointer + * focus and that the pointer will be within the region passed to the request + * creating this object. It is up to the compositor to decide whether this + * requires some user interaction and if the pointer will warp to within the + * passed region if outside. + * + * To unconfine the pointer, send the destroy request. This will also destroy + * the wp_confined_pointer object. + * + * If the compositor decides to unconfine the pointer the unconfined event is + * sent. The wp_confined_pointer object is at this point defunct and should + * be destroyed. + * @section page_iface_zwp_confined_pointer_v1_api API + * See @ref iface_zwp_confined_pointer_v1. + */ +/** + * @defgroup iface_zwp_confined_pointer_v1 The zwp_confined_pointer_v1 interface + * + * The wp_confined_pointer interface represents a confined pointer state. + * + * This object will send the event 'confined' when the confinement is + * activated. Whenever the confinement is activated, it is guaranteed that + * the surface the pointer is confined to will already have received pointer + * focus and that the pointer will be within the region passed to the request + * creating this object. It is up to the compositor to decide whether this + * requires some user interaction and if the pointer will warp to within the + * passed region if outside. + * + * To unconfine the pointer, send the destroy request. This will also destroy + * the wp_confined_pointer object. + * + * If the compositor decides to unconfine the pointer the unconfined event is + * sent. The wp_confined_pointer object is at this point defunct and should + * be destroyed. + */ +extern const struct wl_interface zwp_confined_pointer_v1_interface; + +#ifndef ZWP_POINTER_CONSTRAINTS_V1_ERROR_ENUM +#define ZWP_POINTER_CONSTRAINTS_V1_ERROR_ENUM +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * wp_pointer_constraints error values + * + * These errors can be emitted in response to wp_pointer_constraints + * requests. + */ +enum zwp_pointer_constraints_v1_error { + /** + * pointer constraint already requested on that surface + */ + ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED = 1, +}; +#endif /* ZWP_POINTER_CONSTRAINTS_V1_ERROR_ENUM */ + +#ifndef ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ENUM +#define ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ENUM +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * the pointer constraint may reactivate + * + * A persistent pointer constraint may again reactivate once it has + * been deactivated. See the corresponding deactivation event + * (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for + * details. + */ +enum zwp_pointer_constraints_v1_lifetime { + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT = 1, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT = 2, +}; +#endif /* ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ENUM */ + +#define ZWP_POINTER_CONSTRAINTS_V1_DESTROY 0 +#define ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER 1 +#define ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER 2 + + +/** + * @ingroup iface_zwp_pointer_constraints_v1 + */ +#define ZWP_POINTER_CONSTRAINTS_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_pointer_constraints_v1 + */ +#define ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_pointer_constraints_v1 + */ +#define ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER_SINCE_VERSION 1 + +/** @ingroup iface_zwp_pointer_constraints_v1 */ +static inline void +zwp_pointer_constraints_v1_set_user_data(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_pointer_constraints_v1, user_data); +} + +/** @ingroup iface_zwp_pointer_constraints_v1 */ +static inline void * +zwp_pointer_constraints_v1_get_user_data(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_pointer_constraints_v1); +} + +static inline uint32_t +zwp_pointer_constraints_v1_get_version(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_pointer_constraints_v1); +} + +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * + * Used by the client to notify the server that it will no longer use this + * pointer constraints object. + */ +static inline void +zwp_pointer_constraints_v1_destroy(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_pointer_constraints_v1, + ZWP_POINTER_CONSTRAINTS_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) zwp_pointer_constraints_v1); +} + +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * + * The lock_pointer request lets the client request to disable movements of + * the virtual pointer (i.e. the cursor), effectively locking the pointer + * to a position. This request may not take effect immediately; in the + * future, when the compositor deems implementation-specific constraints + * are satisfied, the pointer lock will be activated and the compositor + * sends a locked event. + * + * The protocol provides no guarantee that the constraints are ever + * satisfied, and does not require the compositor to send an error if the + * constraints cannot ever be satisfied. It is thus possible to request a + * lock that will never activate. + * + * There may not be another pointer constraint of any kind requested or + * active on the surface for any of the wl_pointer objects of the seat of + * the passed pointer when requesting a lock. If there is, an error will be + * raised. See general pointer lock documentation for more details. + * + * The intersection of the region passed with this request and the input + * region of the surface is used to determine where the pointer must be + * in order for the lock to activate. It is up to the compositor whether to + * warp the pointer or require some kind of user interaction for the lock + * to activate. If the region is null the surface input region is used. + * + * A surface may receive pointer focus without the lock being activated. + * + * The request creates a new object wp_locked_pointer which is used to + * interact with the lock as well as receive updates about its state. See + * the the description of wp_locked_pointer for further information. + * + * Note that while a pointer is locked, the wl_pointer objects of the + * corresponding seat will not emit any wl_pointer.motion events, but + * relative motion events will still be emitted via wp_relative_pointer + * objects of the same seat. wl_pointer.axis and wl_pointer.button events + * are unaffected. + */ +static inline struct zwp_locked_pointer_v1 * +zwp_pointer_constraints_v1_lock_pointer(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, struct wl_surface *surface, struct wl_pointer *pointer, struct wl_region *region, uint32_t lifetime) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_constructor((struct wl_proxy *) zwp_pointer_constraints_v1, + ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER, &zwp_locked_pointer_v1_interface, NULL, surface, pointer, region, lifetime); + + return (struct zwp_locked_pointer_v1 *) id; +} + +/** + * @ingroup iface_zwp_pointer_constraints_v1 + * + * The confine_pointer request lets the client request to confine the + * pointer cursor to a given region. This request may not take effect + * immediately; in the future, when the compositor deems implementation- + * specific constraints are satisfied, the pointer confinement will be + * activated and the compositor sends a confined event. + * + * The intersection of the region passed with this request and the input + * region of the surface is used to determine where the pointer must be + * in order for the confinement to activate. It is up to the compositor + * whether to warp the pointer or require some kind of user interaction for + * the confinement to activate. If the region is null the surface input + * region is used. + * + * The request will create a new object wp_confined_pointer which is used + * to interact with the confinement as well as receive updates about its + * state. See the the description of wp_confined_pointer for further + * information. + */ +static inline struct zwp_confined_pointer_v1 * +zwp_pointer_constraints_v1_confine_pointer(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, struct wl_surface *surface, struct wl_pointer *pointer, struct wl_region *region, uint32_t lifetime) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_constructor((struct wl_proxy *) zwp_pointer_constraints_v1, + ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER, &zwp_confined_pointer_v1_interface, NULL, surface, pointer, region, lifetime); + + return (struct zwp_confined_pointer_v1 *) id; +} + +/** + * @ingroup iface_zwp_locked_pointer_v1 + * @struct zwp_locked_pointer_v1_listener + */ +struct zwp_locked_pointer_v1_listener { + /** + * lock activation event + * + * Notification that the pointer lock of the seat's pointer is + * activated. + */ + void (*locked)(void *data, + struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1); + /** + * lock deactivation event + * + * Notification that the pointer lock of the seat's pointer is no + * longer active. If this is a oneshot pointer lock (see + * wp_pointer_constraints.lifetime) this object is now defunct and + * should be destroyed. If this is a persistent pointer lock (see + * wp_pointer_constraints.lifetime) this pointer lock may again + * reactivate in the future. + */ + void (*unlocked)(void *data, + struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1); +}; + +/** + * @ingroup zwp_locked_pointer_v1_iface + */ +static inline int +zwp_locked_pointer_v1_add_listener(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, + const struct zwp_locked_pointer_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_locked_pointer_v1, + (void (**)(void)) listener, data); +} + +#define ZWP_LOCKED_POINTER_V1_DESTROY 0 +#define ZWP_LOCKED_POINTER_V1_SET_CURSOR_POSITION_HINT 1 +#define ZWP_LOCKED_POINTER_V1_SET_REGION 2 + +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_LOCKED_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_UNLOCKED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_SET_CURSOR_POSITION_HINT_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_locked_pointer_v1 + */ +#define ZWP_LOCKED_POINTER_V1_SET_REGION_SINCE_VERSION 1 + +/** @ingroup iface_zwp_locked_pointer_v1 */ +static inline void +zwp_locked_pointer_v1_set_user_data(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_locked_pointer_v1, user_data); +} + +/** @ingroup iface_zwp_locked_pointer_v1 */ +static inline void * +zwp_locked_pointer_v1_get_user_data(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_locked_pointer_v1); +} + +static inline uint32_t +zwp_locked_pointer_v1_get_version(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_locked_pointer_v1); +} + +/** + * @ingroup iface_zwp_locked_pointer_v1 + * + * Destroy the locked pointer object. If applicable, the compositor will + * unlock the pointer. + */ +static inline void +zwp_locked_pointer_v1_destroy(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_locked_pointer_v1, + ZWP_LOCKED_POINTER_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) zwp_locked_pointer_v1); +} + +/** + * @ingroup iface_zwp_locked_pointer_v1 + * + * Set the cursor position hint relative to the top left corner of the + * surface. + * + * If the client is drawing its own cursor, it should update the position + * hint to the position of its own cursor. A compositor may use this + * information to warp the pointer upon unlock in order to avoid pointer + * jumps. + * + * The cursor position hint is double buffered. The new hint will only take + * effect when the associated surface gets it pending state applied. See + * wl_surface.commit for details. + */ +static inline void +zwp_locked_pointer_v1_set_cursor_position_hint(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_locked_pointer_v1, + ZWP_LOCKED_POINTER_V1_SET_CURSOR_POSITION_HINT, surface_x, surface_y); +} + +/** + * @ingroup iface_zwp_locked_pointer_v1 + * + * Set a new region used to lock the pointer. + * + * The new lock region is double-buffered. The new lock region will + * only take effect when the associated surface gets its pending state + * applied. See wl_surface.commit for details. + * + * For details about the lock region, see wp_locked_pointer. + */ +static inline void +zwp_locked_pointer_v1_set_region(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, struct wl_region *region) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_locked_pointer_v1, + ZWP_LOCKED_POINTER_V1_SET_REGION, region); +} + +/** + * @ingroup iface_zwp_confined_pointer_v1 + * @struct zwp_confined_pointer_v1_listener + */ +struct zwp_confined_pointer_v1_listener { + /** + * pointer confined + * + * Notification that the pointer confinement of the seat's + * pointer is activated. + */ + void (*confined)(void *data, + struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1); + /** + * pointer unconfined + * + * Notification that the pointer confinement of the seat's + * pointer is no longer active. If this is a oneshot pointer + * confinement (see wp_pointer_constraints.lifetime) this object is + * now defunct and should be destroyed. If this is a persistent + * pointer confinement (see wp_pointer_constraints.lifetime) this + * pointer confinement may again reactivate in the future. + */ + void (*unconfined)(void *data, + struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1); +}; + +/** + * @ingroup zwp_confined_pointer_v1_iface + */ +static inline int +zwp_confined_pointer_v1_add_listener(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1, + const struct zwp_confined_pointer_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_confined_pointer_v1, + (void (**)(void)) listener, data); +} + +#define ZWP_CONFINED_POINTER_V1_DESTROY 0 +#define ZWP_CONFINED_POINTER_V1_SET_REGION 1 + +/** + * @ingroup iface_zwp_confined_pointer_v1 + */ +#define ZWP_CONFINED_POINTER_V1_CONFINED_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_confined_pointer_v1 + */ +#define ZWP_CONFINED_POINTER_V1_UNCONFINED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_confined_pointer_v1 + */ +#define ZWP_CONFINED_POINTER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_confined_pointer_v1 + */ +#define ZWP_CONFINED_POINTER_V1_SET_REGION_SINCE_VERSION 1 + +/** @ingroup iface_zwp_confined_pointer_v1 */ +static inline void +zwp_confined_pointer_v1_set_user_data(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_confined_pointer_v1, user_data); +} + +/** @ingroup iface_zwp_confined_pointer_v1 */ +static inline void * +zwp_confined_pointer_v1_get_user_data(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_confined_pointer_v1); +} + +static inline uint32_t +zwp_confined_pointer_v1_get_version(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_confined_pointer_v1); +} + +/** + * @ingroup iface_zwp_confined_pointer_v1 + * + * Destroy the confined pointer object. If applicable, the compositor will + * unconfine the pointer. + */ +static inline void +zwp_confined_pointer_v1_destroy(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_confined_pointer_v1, + ZWP_CONFINED_POINTER_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) zwp_confined_pointer_v1); +} + +/** + * @ingroup iface_zwp_confined_pointer_v1 + * + * Set a new region used to confine the pointer. + * + * The new confine region is double-buffered. The new confine region will + * only take effect when the associated surface gets its pending state + * applied. See wl_surface.commit for details. + * + * If the confinement is active when the new confinement region is applied + * and the pointer ends up outside of newly applied region, the pointer may + * warped to a position within the new confinement region. If warped, a + * wl_pointer.motion event will be emitted, but no + * wp_relative_pointer.relative_motion event. + * + * The compositor may also, instead of using the new region, unconfine the + * pointer. + * + * For details about the confine region, see wp_confined_pointer. + */ +static inline void +zwp_confined_pointer_v1_set_region(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1, struct wl_region *region) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_confined_pointer_v1, + ZWP_CONFINED_POINTER_V1_SET_REGION, region); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c new file mode 100644 index 0000000..3483ca8 --- /dev/null +++ b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c @@ -0,0 +1,69 @@ +/* Generated by wayland-scanner 1.12.0 */ + +/* + * Copyright © 2014 Jonas Ådahl + * Copyright © 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +extern const struct wl_interface wl_pointer_interface; +extern const struct wl_interface zwp_relative_pointer_v1_interface; + +static const struct wl_interface *types[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &zwp_relative_pointer_v1_interface, + &wl_pointer_interface, +}; + +static const struct wl_message zwp_relative_pointer_manager_v1_requests[] = { + { "destroy", "", types + 0 }, + { "get_relative_pointer", "no", types + 6 }, +}; + +WL_EXPORT const struct wl_interface zwp_relative_pointer_manager_v1_interface = { + "zwp_relative_pointer_manager_v1", 1, + 2, zwp_relative_pointer_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwp_relative_pointer_v1_requests[] = { + { "destroy", "", types + 0 }, +}; + +static const struct wl_message zwp_relative_pointer_v1_events[] = { + { "relative_motion", "uuffff", types + 0 }, +}; + +WL_EXPORT const struct wl_interface zwp_relative_pointer_v1_interface = { + "zwp_relative_pointer_v1", 1, + 1, zwp_relative_pointer_v1_requests, + 1, zwp_relative_pointer_v1_events, +}; + diff --git a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h new file mode 100644 index 0000000..4c3a2ef --- /dev/null +++ b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h @@ -0,0 +1,295 @@ +/* Generated by wayland-scanner 1.12.0 */ + +#ifndef RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_relative_pointer_unstable_v1 The relative_pointer_unstable_v1 protocol + * protocol for relative pointer motion events + * + * @section page_desc_relative_pointer_unstable_v1 Description + * + * This protocol specifies a set of interfaces used for making clients able to + * receive relative pointer events not obstructed by barriers (such as the + * monitor edge or other pointer barriers). + * + * To start receiving relative pointer events, a client must first bind the + * global interface "wp_relative_pointer_manager" which, if a compositor + * supports relative pointer motion events, is exposed by the registry. After + * having created the relative pointer manager proxy object, the client uses + * it to create the actual relative pointer object using the + * "get_relative_pointer" request given a wl_pointer. The relative pointer + * motion events will then, when applicable, be transmitted via the proxy of + * the newly created relative pointer object. See the documentation of the + * relative pointer interface for more details. + * + * Warning! The protocol described in this file is experimental and backward + * incompatible changes may be made. Backward compatible changes may be added + * together with the corresponding interface version bump. Backward + * incompatible changes are done by bumping the version number in the protocol + * and interface names and resetting the interface version. Once the protocol + * is to be declared stable, the 'z' prefix and the version number in the + * protocol and interface names are removed and the interface version number is + * reset. + * + * @section page_ifaces_relative_pointer_unstable_v1 Interfaces + * - @subpage page_iface_zwp_relative_pointer_manager_v1 - get relative pointer objects + * - @subpage page_iface_zwp_relative_pointer_v1 - relative pointer object + * @section page_copyright_relative_pointer_unstable_v1 Copyright + *
+ *
+ * Copyright © 2014      Jonas Ådahl
+ * Copyright © 2015      Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_pointer; +struct zwp_relative_pointer_manager_v1; +struct zwp_relative_pointer_v1; + +/** + * @page page_iface_zwp_relative_pointer_manager_v1 zwp_relative_pointer_manager_v1 + * @section page_iface_zwp_relative_pointer_manager_v1_desc Description + * + * A global interface used for getting the relative pointer object for a + * given pointer. + * @section page_iface_zwp_relative_pointer_manager_v1_api API + * See @ref iface_zwp_relative_pointer_manager_v1. + */ +/** + * @defgroup iface_zwp_relative_pointer_manager_v1 The zwp_relative_pointer_manager_v1 interface + * + * A global interface used for getting the relative pointer object for a + * given pointer. + */ +extern const struct wl_interface zwp_relative_pointer_manager_v1_interface; +/** + * @page page_iface_zwp_relative_pointer_v1 zwp_relative_pointer_v1 + * @section page_iface_zwp_relative_pointer_v1_desc Description + * + * A wp_relative_pointer object is an extension to the wl_pointer interface + * used for emitting relative pointer events. It shares the same focus as + * wl_pointer objects of the same seat and will only emit events when it has + * focus. + * @section page_iface_zwp_relative_pointer_v1_api API + * See @ref iface_zwp_relative_pointer_v1. + */ +/** + * @defgroup iface_zwp_relative_pointer_v1 The zwp_relative_pointer_v1 interface + * + * A wp_relative_pointer object is an extension to the wl_pointer interface + * used for emitting relative pointer events. It shares the same focus as + * wl_pointer objects of the same seat and will only emit events when it has + * focus. + */ +extern const struct wl_interface zwp_relative_pointer_v1_interface; + +#define ZWP_RELATIVE_POINTER_MANAGER_V1_DESTROY 0 +#define ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER 1 + + +/** + * @ingroup iface_zwp_relative_pointer_manager_v1 + */ +#define ZWP_RELATIVE_POINTER_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_relative_pointer_manager_v1 + */ +#define ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER_SINCE_VERSION 1 + +/** @ingroup iface_zwp_relative_pointer_manager_v1 */ +static inline void +zwp_relative_pointer_manager_v1_set_user_data(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_relative_pointer_manager_v1, user_data); +} + +/** @ingroup iface_zwp_relative_pointer_manager_v1 */ +static inline void * +zwp_relative_pointer_manager_v1_get_user_data(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_relative_pointer_manager_v1); +} + +static inline uint32_t +zwp_relative_pointer_manager_v1_get_version(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_relative_pointer_manager_v1); +} + +/** + * @ingroup iface_zwp_relative_pointer_manager_v1 + * + * Used by the client to notify the server that it will no longer use this + * relative pointer manager object. + */ +static inline void +zwp_relative_pointer_manager_v1_destroy(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_relative_pointer_manager_v1, + ZWP_RELATIVE_POINTER_MANAGER_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) zwp_relative_pointer_manager_v1); +} + +/** + * @ingroup iface_zwp_relative_pointer_manager_v1 + * + * Create a relative pointer interface given a wl_pointer object. See the + * wp_relative_pointer interface for more details. + */ +static inline struct zwp_relative_pointer_v1 * +zwp_relative_pointer_manager_v1_get_relative_pointer(struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1, struct wl_pointer *pointer) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_constructor((struct wl_proxy *) zwp_relative_pointer_manager_v1, + ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER, &zwp_relative_pointer_v1_interface, NULL, pointer); + + return (struct zwp_relative_pointer_v1 *) id; +} + +/** + * @ingroup iface_zwp_relative_pointer_v1 + * @struct zwp_relative_pointer_v1_listener + */ +struct zwp_relative_pointer_v1_listener { + /** + * relative pointer motion + * + * Relative x/y pointer motion from the pointer of the seat + * associated with this object. + * + * A relative motion is in the same dimension as regular wl_pointer + * motion events, except they do not represent an absolute + * position. For example, moving a pointer from (x, y) to (x', y') + * would have the equivalent relative motion (x' - x, y' - y). If a + * pointer motion caused the absolute pointer position to be + * clipped by for example the edge of the monitor, the relative + * motion is unaffected by the clipping and will represent the + * unclipped motion. + * + * This event also contains non-accelerated motion deltas. The + * non-accelerated delta is, when applicable, the regular pointer + * motion delta as it was before having applied motion acceleration + * and other transformations such as normalization. + * + * Note that the non-accelerated delta does not represent 'raw' + * events as they were read from some device. Pointer motion + * acceleration is device- and configuration-specific and + * non-accelerated deltas and accelerated deltas may have the same + * value on some devices. + * + * Relative motions are not coupled to wl_pointer.motion events, + * and can be sent in combination with such events, but also + * independently. There may also be scenarios where + * wl_pointer.motion is sent, but there is no relative motion. The + * order of an absolute and relative motion event originating from + * the same physical motion is not guaranteed. + * + * If the client needs button events or focus state, it can receive + * them from a wl_pointer object of the same seat that the + * wp_relative_pointer object is associated with. + * @param utime_hi high 32 bits of a 64 bit timestamp with microsecond granularity + * @param utime_lo low 32 bits of a 64 bit timestamp with microsecond granularity + * @param dx the x component of the motion vector + * @param dy the y component of the motion vector + * @param dx_unaccel the x component of the unaccelerated motion vector + * @param dy_unaccel the y component of the unaccelerated motion vector + */ + void (*relative_motion)(void *data, + struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, + uint32_t utime_hi, + uint32_t utime_lo, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t dx_unaccel, + wl_fixed_t dy_unaccel); +}; + +/** + * @ingroup zwp_relative_pointer_v1_iface + */ +static inline int +zwp_relative_pointer_v1_add_listener(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, + const struct zwp_relative_pointer_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_relative_pointer_v1, + (void (**)(void)) listener, data); +} + +#define ZWP_RELATIVE_POINTER_V1_DESTROY 0 + +/** + * @ingroup iface_zwp_relative_pointer_v1 + */ +#define ZWP_RELATIVE_POINTER_V1_RELATIVE_MOTION_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_relative_pointer_v1 + */ +#define ZWP_RELATIVE_POINTER_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_zwp_relative_pointer_v1 */ +static inline void +zwp_relative_pointer_v1_set_user_data(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_relative_pointer_v1, user_data); +} + +/** @ingroup iface_zwp_relative_pointer_v1 */ +static inline void * +zwp_relative_pointer_v1_get_user_data(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_relative_pointer_v1); +} + +static inline uint32_t +zwp_relative_pointer_v1_get_version(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_relative_pointer_v1); +} + +/** + * @ingroup iface_zwp_relative_pointer_v1 + */ +static inline void +zwp_relative_pointer_v1_destroy(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_relative_pointer_v1, + ZWP_RELATIVE_POINTER_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) zwp_relative_pointer_v1); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/raylib/platform_desktop.go b/raylib/platform_desktop.go index 3994cd8..0528477 100644 --- a/raylib/platform_desktop.go +++ b/raylib/platform_desktop.go @@ -1,4 +1,4 @@ -// +build !android,!arm +// +build !android,!arm,!wayland package raylib diff --git a/raylib/platform_wayland.go b/raylib/platform_wayland.go new file mode 100644 index 0000000..0582daf --- /dev/null +++ b/raylib/platform_wayland.go @@ -0,0 +1,93 @@ +// +build wayland + +package raylib + +/* +#include "raylib.h" +#include +*/ +import "C" + +import ( + "os" + "unsafe" +) + +// InitWindow - Initialize Window and OpenGL Graphics +func InitWindow(width int32, height int32, t interface{}) { + cwidth := (C.int)(width) + cheight := (C.int)(height) + + title, ok := t.(string) + if ok { + ctitle := C.CString(title) + cptitle := unsafe.Pointer(ctitle) + defer C.free(cptitle) + C.InitWindow(cwidth, cheight, cptitle) + } +} + +// SetCallbackFunc - Sets callback function +func SetCallbackFunc(func(unsafe.Pointer)) { + return +} + +// ShowCursor - Shows cursor +func ShowCursor() { + return +} + +// HideCursor - Hides cursor +func HideCursor() { + return +} + +// IsCursorHidden - Returns true if cursor is not visible +func IsCursorHidden() bool { + return false +} + +// EnableCursor - Enables cursor +func EnableCursor() { + C.EnableCursor() +} + +// DisableCursor - Disables cursor +func DisableCursor() { + C.DisableCursor() +} + +// IsFileDropped - Check if a file have been dropped into window +func IsFileDropped() bool { + ret := C.IsFileDropped() + v := bool(int(ret) == 1) + return v +} + +// GetDroppedFiles - Retrieve dropped files into window +func GetDroppedFiles(count *int32) []string { + ccount := (*C.int)(unsafe.Pointer(count)) + ret := C.GetDroppedFiles(ccount) + + tmpslice := (*[1 << 24]*C.char)(unsafe.Pointer(ret))[:*count:*count] + gostrings := make([]string, *count) + for i, s := range tmpslice { + gostrings[i] = C.GoString(s) + } + + return gostrings +} + +// ClearDroppedFiles - Clear dropped files paths buffer +func ClearDroppedFiles() { + C.ClearDroppedFiles() +} + +// OpenAsset - Open asset +func OpenAsset(name string) (Asset, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + return f, nil +} From 9a64523d95bf47bb09bf3de44c96217130952b79 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 27 Nov 2017 19:31:46 +0100 Subject: [PATCH 09/48] Update GLFW sources --- raylib/external/glfw/deps/glad.c | 151 +- raylib/external/glfw/deps/glad/glad.h | 894 +- raylib/external/glfw/deps/linmath.h | 2 +- raylib/external/glfw/deps/nuklear.h | 23590 ++++++++++++++++ raylib/external/glfw/deps/nuklear_glfw_gl2.h | 372 + raylib/external/glfw/deps/stb_image_write.h | 1048 + raylib/external/glfw/deps/tinycthread.c | 2 +- raylib/external/glfw/deps/tinycthread.h | 4 +- raylib/external/glfw/deps/vs2008/stdint.h | 247 + .../external/glfw/deps/vulkan/vk_platform.h | 14 +- raylib/external/glfw/deps/vulkan/vulkan.h | 956 +- raylib/external/glfw/include/GLFW/glfw3.h | 1579 +- .../external/glfw/include/GLFW/glfw3native.h | 130 +- raylib/external/glfw/src/cocoa_init.m | 286 +- raylib/external/glfw/src/cocoa_joystick.h | 28 +- raylib/external/glfw/src/cocoa_joystick.m | 679 +- raylib/external/glfw/src/cocoa_monitor.m | 272 +- raylib/external/glfw/src/cocoa_platform.h | 54 +- raylib/external/glfw/src/cocoa_time.c | 8 +- raylib/external/glfw/src/cocoa_window.m | 562 +- raylib/external/glfw/src/context.c | 33 +- raylib/external/glfw/src/egl_context.c | 162 +- raylib/external/glfw/src/egl_context.h | 84 +- raylib/external/glfw/src/glx_context.c | 81 +- raylib/external/glfw/src/glx_context.h | 21 +- raylib/external/glfw/src/init.c | 300 +- raylib/external/glfw/src/input.c | 693 +- raylib/external/glfw/src/internal.h | 559 +- raylib/external/glfw/src/linux_joystick.c | 454 +- raylib/external/glfw/src/linux_joystick.h | 44 +- raylib/external/glfw/src/mappings.h | 310 + raylib/external/glfw/src/mir_init.c | 270 +- raylib/external/glfw/src/mir_monitor.c | 132 +- raylib/external/glfw/src/mir_platform.h | 51 +- raylib/external/glfw/src/mir_window.c | 460 +- raylib/external/glfw/src/monitor.c | 122 +- raylib/external/glfw/src/nsgl_context.h | 8 +- raylib/external/glfw/src/nsgl_context.m | 117 +- .../glfw/src/{posix_tls.c => null_init.c} | 44 +- raylib/external/glfw/src/null_joystick.c | 42 + raylib/external/glfw/src/null_joystick.h | 31 + .../glfw/src/{win32_tls.c => null_monitor.c} | 59 +- raylib/external/glfw/src/null_platform.h | 62 + raylib/external/glfw/src/null_window.c | 316 + raylib/external/glfw/src/osmesa_context.c | 370 + raylib/external/glfw/src/osmesa_context.h | 94 + raylib/external/glfw/src/posix_thread.c | 103 + .../glfw/src/{posix_tls.h => posix_thread.h} | 24 +- raylib/external/glfw/src/posix_time.c | 16 +- raylib/external/glfw/src/posix_time.h | 14 +- raylib/external/glfw/src/vulkan.c | 122 +- raylib/external/glfw/src/wgl_context.c | 177 +- raylib/external/glfw/src/wgl_context.h | 39 +- raylib/external/glfw/src/win32_init.c | 456 +- raylib/external/glfw/src/win32_joystick.c | 598 +- raylib/external/glfw/src/win32_joystick.h | 18 +- raylib/external/glfw/src/win32_monitor.c | 335 +- raylib/external/glfw/src/win32_platform.h | 173 +- raylib/external/glfw/src/win32_thread.c | 97 + raylib/external/glfw/src/win32_time.c | 18 +- raylib/external/glfw/src/win32_window.c | 669 +- raylib/external/glfw/src/window.c | 287 +- raylib/external/glfw/src/wl_init.c | 507 +- raylib/external/glfw/src/wl_monitor.c | 140 +- raylib/external/glfw/src/wl_platform.h | 111 +- raylib/external/glfw/src/wl_window.c | 189 +- raylib/external/glfw/src/x11_init.c | 401 +- raylib/external/glfw/src/x11_monitor.c | 269 +- raylib/external/glfw/src/x11_platform.h | 194 +- raylib/external/glfw/src/x11_window.c | 1161 +- raylib/external/glfw/src/xkb_unicode.c | 61 +- raylib/external/glfw/src/xkb_unicode.h | 7 +- 72 files changed, 37076 insertions(+), 4907 deletions(-) create mode 100644 raylib/external/glfw/deps/nuklear.h create mode 100644 raylib/external/glfw/deps/nuklear_glfw_gl2.h create mode 100644 raylib/external/glfw/deps/stb_image_write.h create mode 100644 raylib/external/glfw/deps/vs2008/stdint.h create mode 100644 raylib/external/glfw/src/mappings.h rename raylib/external/glfw/src/{posix_tls.c => null_init.c} (57%) create mode 100644 raylib/external/glfw/src/null_joystick.c create mode 100644 raylib/external/glfw/src/null_joystick.h rename raylib/external/glfw/src/{win32_tls.c => null_monitor.c} (56%) create mode 100644 raylib/external/glfw/src/null_platform.h create mode 100644 raylib/external/glfw/src/null_window.c create mode 100644 raylib/external/glfw/src/osmesa_context.c create mode 100644 raylib/external/glfw/src/osmesa_context.h create mode 100644 raylib/external/glfw/src/posix_thread.c rename raylib/external/glfw/src/{posix_tls.h => posix_thread.h} (74%) create mode 100644 raylib/external/glfw/src/win32_thread.c diff --git a/raylib/external/glfw/deps/glad.c b/raylib/external/glfw/deps/glad.c index 7886e60..10b0a00 100644 --- a/raylib/external/glfw/deps/glad.c +++ b/raylib/external/glfw/deps/glad.c @@ -1,3 +1,25 @@ +/* + + OpenGL loader generated by glad 0.1.12a0 on Fri Sep 23 13:36:15 2016. + + Language/Generator: C/C++ + Specification: gl + APIs: gl=3.2 + Profile: compatibility + Extensions: + GL_ARB_multisample, + GL_ARB_robustness, + GL_KHR_debug + Loader: False + Local files: False + Omit khrplatform: False + + Commandline: + --profile="compatibility" --api="gl=3.2" --generator="c" --spec="gl" --no-loader --extensions="GL_ARB_multisample,GL_ARB_robustness,GL_KHR_debug" + Online: + http://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&api=gl%3D3.2&extensions=GL_ARB_multisample&extensions=GL_ARB_robustness&extensions=GL_KHR_debug +*/ + #include #include #include @@ -5,14 +27,59 @@ struct gladGLversionStruct GLVersion; +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) +#define _GLAD_IS_SOME_NEW_VERSION 1 +#endif + +static int max_loaded_major; +static int max_loaded_minor; + +static const char *exts = NULL; +static int num_exts_i = 0; +static const char **exts_i = NULL; + +static int get_exts(void) { +#ifdef _GLAD_IS_SOME_NEW_VERSION + if(max_loaded_major < 3) { +#endif + exts = (const char *)glGetString(GL_EXTENSIONS); +#ifdef _GLAD_IS_SOME_NEW_VERSION + } else { + int index; + + num_exts_i = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i); + if (num_exts_i > 0) { + exts_i = (const char **)realloc((void *)exts_i, num_exts_i * sizeof *exts_i); + } + + if (exts_i == NULL) { + return 0; + } + + for(index = 0; index < num_exts_i; index++) { + exts_i[index] = (const char*)glGetStringi(GL_EXTENSIONS, index); + } + } +#endif + return 1; +} + +static void free_exts(void) { + if (exts_i != NULL) { + free((char **)exts_i); + exts_i = NULL; + } +} + static int has_ext(const char *ext) { -#if defined(GL_VERSION_3_0) || defined(GL_ES_VERSION_3_0) - if(GLVersion.major < 3 || glGetStringi == NULL) { +#ifdef _GLAD_IS_SOME_NEW_VERSION + if(max_loaded_major < 3) { #endif const char *extensions; const char *loc; const char *terminator; - extensions = (const char*) glGetString(GL_EXTENSIONS); + extensions = exts; if(extensions == NULL || ext == NULL) { return 0; } @@ -30,13 +97,14 @@ static int has_ext(const char *ext) { } extensions = terminator; } -#if defined(GL_VERSION_3_0) || defined(GL_ES_VERSION_3_0) +#ifdef _GLAD_IS_SOME_NEW_VERSION } else { - GLint num_exts, index; + int index; - glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts); - for(index = 0; index < num_exts; index++) { - if(strcmp((const char*) glGetStringi(GL_EXTENSIONS, index), ext) == 0) { + for(index = 0; index < num_exts_i; index++) { + const char *e = exts_i[index]; + + if(strcmp(e, ext) == 0) { return 1; } } @@ -316,7 +384,7 @@ PFNGLRASTERPOS3FPROC glad_glRasterPos3f; PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D; PFNGLTEXCOORD3FPROC glad_glTexCoord3f; PFNGLDELETESYNCPROC glad_glDeleteSync; -PFNGLTEXCOORD3DPROC glad_glTexCoord3d; +PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D; PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample; PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements; @@ -363,7 +431,7 @@ PFNGLVIEWPORTPROC glad_glViewport; PFNGLUNIFORM1UIVPROC glad_glUniform1uiv; PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings; PFNGLINDEXDVPROC glad_glIndexdv; -PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D; +PFNGLTEXCOORD3DPROC glad_glTexCoord3d; PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv; PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i; PFNGLCLEARDEPTHPROC glad_glClearDepth; @@ -722,9 +790,9 @@ PFNGLCOLORPOINTERPROC glad_glColorPointer; PFNGLFRONTFACEPROC glad_glFrontFace; PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v; PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv; +int GLAD_GL_KHR_debug; int GLAD_GL_ARB_robustness; int GLAD_GL_ARB_multisample; -int GLAD_GL_EXT_separate_specular_color; PFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB; PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB; PFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB; @@ -746,6 +814,27 @@ PFNGLGETNCONVOLUTIONFILTERARBPROC glad_glGetnConvolutionFilterARB; PFNGLGETNSEPARABLEFILTERARBPROC glad_glGetnSeparableFilterARB; PFNGLGETNHISTOGRAMARBPROC glad_glGetnHistogramARB; PFNGLGETNMINMAXARBPROC glad_glGetnMinmaxARB; +PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl; +PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert; +PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback; +PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog; +PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup; +PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup; +PFNGLOBJECTLABELPROC glad_glObjectLabel; +PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel; +PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel; +PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel; +PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR; +PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR; +PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR; +PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR; +PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR; +PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR; +PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR; +PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR; +PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR; +PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR; +PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR; static void load_GL_VERSION_1_0(GLADloadproc load) { if(!GLAD_GL_VERSION_1_0) return; glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); @@ -1475,10 +1564,38 @@ static void load_GL_ARB_robustness(GLADloadproc load) { glad_glGetnHistogramARB = (PFNGLGETNHISTOGRAMARBPROC)load("glGetnHistogramARB"); glad_glGetnMinmaxARB = (PFNGLGETNMINMAXARBPROC)load("glGetnMinmaxARB"); } -static void find_extensionsGL(void) { - GLAD_GL_EXT_separate_specular_color = has_ext("GL_EXT_separate_specular_color"); +static void load_GL_KHR_debug(GLADloadproc load) { + if(!GLAD_GL_KHR_debug) return; + glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)load("glDebugMessageControl"); + glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)load("glDebugMessageInsert"); + glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)load("glDebugMessageCallback"); + glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)load("glGetDebugMessageLog"); + glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)load("glPushDebugGroup"); + glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)load("glPopDebugGroup"); + glad_glObjectLabel = (PFNGLOBJECTLABELPROC)load("glObjectLabel"); + glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)load("glGetObjectLabel"); + glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)load("glObjectPtrLabel"); + glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load("glGetObjectPtrLabel"); + glad_glGetPointerv = (PFNGLGETPOINTERVPROC)load("glGetPointerv"); + glad_glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC)load("glDebugMessageControlKHR"); + glad_glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC)load("glDebugMessageInsertKHR"); + glad_glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC)load("glDebugMessageCallbackKHR"); + glad_glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC)load("glGetDebugMessageLogKHR"); + glad_glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC)load("glPushDebugGroupKHR"); + glad_glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC)load("glPopDebugGroupKHR"); + glad_glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC)load("glObjectLabelKHR"); + glad_glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC)load("glGetObjectLabelKHR"); + glad_glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC)load("glObjectPtrLabelKHR"); + glad_glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC)load("glGetObjectPtrLabelKHR"); + glad_glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC)load("glGetPointervKHR"); +} +static int find_extensionsGL(void) { + if (!get_exts()) return 0; GLAD_GL_ARB_multisample = has_ext("GL_ARB_multisample"); GLAD_GL_ARB_robustness = has_ext("GL_ARB_robustness"); + GLAD_GL_KHR_debug = has_ext("GL_KHR_debug"); + free_exts(); + return 1; } static void find_coreGL(void) { @@ -1516,6 +1633,7 @@ static void find_coreGL(void) { #endif GLVersion.major = major; GLVersion.minor = minor; + max_loaded_major = major; max_loaded_minor = minor; GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; @@ -1527,6 +1645,10 @@ static void find_coreGL(void) { GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3; + if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 2)) { + max_loaded_major = 3; + max_loaded_minor = 2; + } } int gladLoadGLLoader(GLADloadproc load) { @@ -1547,9 +1669,10 @@ int gladLoadGLLoader(GLADloadproc load) { load_GL_VERSION_3_1(load); load_GL_VERSION_3_2(load); - find_extensionsGL(); + if (!find_extensionsGL()) return 0; load_GL_ARB_multisample(load); load_GL_ARB_robustness(load); + load_GL_KHR_debug(load); return GLVersion.major != 0 || GLVersion.minor != 0; } diff --git a/raylib/external/glfw/deps/glad/glad.h b/raylib/external/glfw/deps/glad/glad.h index ef96591..7d81e98 100644 --- a/raylib/external/glfw/deps/glad/glad.h +++ b/raylib/external/glfw/deps/glad/glad.h @@ -1,3 +1,25 @@ +/* + + OpenGL loader generated by glad 0.1.12a0 on Fri Sep 23 13:36:15 2016. + + Language/Generator: C/C++ + Specification: gl + APIs: gl=3.2 + Profile: compatibility + Extensions: + GL_ARB_multisample, + GL_ARB_robustness, + GL_KHR_debug + Loader: False + Local files: False + Omit khrplatform: False + + Commandline: + --profile="compatibility" --api="gl=3.2" --generator="c" --spec="gl" --no-loader --extensions="GL_ARB_multisample,GL_ARB_robustness,GL_KHR_debug" + Online: + http://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&api=gl%3D3.2&extensions=GL_ARB_multisample&extensions=GL_ARB_robustness&extensions=GL_KHR_debug +*/ + #ifndef __glad_h_ #define __glad_h_ @@ -30,8 +52,6 @@ struct gladGLversionStruct { int minor; }; -extern struct gladGLversionStruct GLVersion; - typedef void* (* GLADloadproc)(const char *name); #ifndef GLAPI @@ -59,6 +79,8 @@ typedef void* (* GLADloadproc)(const char *name); # define GLAPI extern # endif #endif + +GLAPI struct gladGLversionStruct GLVersion; GLAPI int gladLoadGLLoader(GLADloadproc); #include @@ -1403,19 +1425,19 @@ GLAPI PFNGLSCISSORPROC glad_glScissor; typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); GLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf; #define glTexParameterf glad_glTexParameterf -typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat* params); +typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params); GLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; #define glTexParameterfv glad_glTexParameterfv typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); GLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri; #define glTexParameteri glad_glTexParameteri -typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint* params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params); GLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; #define glTexParameteriv glad_glTexParameteriv -typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void* pixels); +typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXIMAGE1DPROC glad_glTexImage1D; #define glTexImage1D glad_glTexImage1D -typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels); +typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D; #define glTexImage2D glad_glTexImage2D typedef void (APIENTRYP PFNGLDRAWBUFFERPROC)(GLenum buf); @@ -1478,40 +1500,40 @@ GLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei; typedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum src); GLAPI PFNGLREADBUFFERPROC glad_glReadBuffer; #define glReadBuffer glad_glReadBuffer -typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); +typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); GLAPI PFNGLREADPIXELSPROC glad_glReadPixels; #define glReadPixels glad_glReadPixels -typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean* data); +typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data); GLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv; #define glGetBooleanv glad_glGetBooleanv -typedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble* data); +typedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble *data); GLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev; #define glGetDoublev glad_glGetDoublev typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(); GLAPI PFNGLGETERRORPROC glad_glGetError; #define glGetError glad_glGetError -typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat* data); +typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data); GLAPI PFNGLGETFLOATVPROC glad_glGetFloatv; #define glGetFloatv glad_glGetFloatv -typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint* data); +typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data); GLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv; #define glGetIntegerv glad_glGetIntegerv -typedef const GLubyte* (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name); +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name); GLAPI PFNGLGETSTRINGPROC glad_glGetString; #define glGetString glad_glGetString -typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void* pixels); +typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void *pixels); GLAPI PFNGLGETTEXIMAGEPROC glad_glGetTexImage; #define glGetTexImage glad_glGetTexImage -typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat* params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params); GLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; #define glGetTexParameterfv glad_glGetTexParameterfv -typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; #define glGetTexParameteriv glad_glGetTexParameteriv -typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat* params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat *params); GLAPI PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv; #define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv -typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint *params); GLAPI PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv; #define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap); @@ -1532,7 +1554,7 @@ GLAPI PFNGLENDLISTPROC glad_glEndList; typedef void (APIENTRYP PFNGLCALLLISTPROC)(GLuint list); GLAPI PFNGLCALLLISTPROC glad_glCallList; #define glCallList glad_glCallList -typedef void (APIENTRYP PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void* lists); +typedef void (APIENTRYP PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void *lists); GLAPI PFNGLCALLLISTSPROC glad_glCallLists; #define glCallLists glad_glCallLists typedef void (APIENTRYP PFNGLDELETELISTSPROC)(GLuint list, GLsizei range); @@ -1547,109 +1569,109 @@ GLAPI PFNGLLISTBASEPROC glad_glListBase; typedef void (APIENTRYP PFNGLBEGINPROC)(GLenum mode); GLAPI PFNGLBEGINPROC glad_glBegin; #define glBegin glad_glBegin -typedef void (APIENTRYP PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte* bitmap); +typedef void (APIENTRYP PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap); GLAPI PFNGLBITMAPPROC glad_glBitmap; #define glBitmap glad_glBitmap typedef void (APIENTRYP PFNGLCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); GLAPI PFNGLCOLOR3BPROC glad_glColor3b; #define glColor3b glad_glColor3b -typedef void (APIENTRYP PFNGLCOLOR3BVPROC)(const GLbyte* v); +typedef void (APIENTRYP PFNGLCOLOR3BVPROC)(const GLbyte *v); GLAPI PFNGLCOLOR3BVPROC glad_glColor3bv; #define glColor3bv glad_glColor3bv typedef void (APIENTRYP PFNGLCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); GLAPI PFNGLCOLOR3DPROC glad_glColor3d; #define glColor3d glad_glColor3d -typedef void (APIENTRYP PFNGLCOLOR3DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLCOLOR3DVPROC)(const GLdouble *v); GLAPI PFNGLCOLOR3DVPROC glad_glColor3dv; #define glColor3dv glad_glColor3dv typedef void (APIENTRYP PFNGLCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); GLAPI PFNGLCOLOR3FPROC glad_glColor3f; #define glColor3f glad_glColor3f -typedef void (APIENTRYP PFNGLCOLOR3FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLCOLOR3FVPROC)(const GLfloat *v); GLAPI PFNGLCOLOR3FVPROC glad_glColor3fv; #define glColor3fv glad_glColor3fv typedef void (APIENTRYP PFNGLCOLOR3IPROC)(GLint red, GLint green, GLint blue); GLAPI PFNGLCOLOR3IPROC glad_glColor3i; #define glColor3i glad_glColor3i -typedef void (APIENTRYP PFNGLCOLOR3IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLCOLOR3IVPROC)(const GLint *v); GLAPI PFNGLCOLOR3IVPROC glad_glColor3iv; #define glColor3iv glad_glColor3iv typedef void (APIENTRYP PFNGLCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); GLAPI PFNGLCOLOR3SPROC glad_glColor3s; #define glColor3s glad_glColor3s -typedef void (APIENTRYP PFNGLCOLOR3SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLCOLOR3SVPROC)(const GLshort *v); GLAPI PFNGLCOLOR3SVPROC glad_glColor3sv; #define glColor3sv glad_glColor3sv typedef void (APIENTRYP PFNGLCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); GLAPI PFNGLCOLOR3UBPROC glad_glColor3ub; #define glColor3ub glad_glColor3ub -typedef void (APIENTRYP PFNGLCOLOR3UBVPROC)(const GLubyte* v); +typedef void (APIENTRYP PFNGLCOLOR3UBVPROC)(const GLubyte *v); GLAPI PFNGLCOLOR3UBVPROC glad_glColor3ubv; #define glColor3ubv glad_glColor3ubv typedef void (APIENTRYP PFNGLCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); GLAPI PFNGLCOLOR3UIPROC glad_glColor3ui; #define glColor3ui glad_glColor3ui -typedef void (APIENTRYP PFNGLCOLOR3UIVPROC)(const GLuint* v); +typedef void (APIENTRYP PFNGLCOLOR3UIVPROC)(const GLuint *v); GLAPI PFNGLCOLOR3UIVPROC glad_glColor3uiv; #define glColor3uiv glad_glColor3uiv typedef void (APIENTRYP PFNGLCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); GLAPI PFNGLCOLOR3USPROC glad_glColor3us; #define glColor3us glad_glColor3us -typedef void (APIENTRYP PFNGLCOLOR3USVPROC)(const GLushort* v); +typedef void (APIENTRYP PFNGLCOLOR3USVPROC)(const GLushort *v); GLAPI PFNGLCOLOR3USVPROC glad_glColor3usv; #define glColor3usv glad_glColor3usv typedef void (APIENTRYP PFNGLCOLOR4BPROC)(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); GLAPI PFNGLCOLOR4BPROC glad_glColor4b; #define glColor4b glad_glColor4b -typedef void (APIENTRYP PFNGLCOLOR4BVPROC)(const GLbyte* v); +typedef void (APIENTRYP PFNGLCOLOR4BVPROC)(const GLbyte *v); GLAPI PFNGLCOLOR4BVPROC glad_glColor4bv; #define glColor4bv glad_glColor4bv typedef void (APIENTRYP PFNGLCOLOR4DPROC)(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); GLAPI PFNGLCOLOR4DPROC glad_glColor4d; #define glColor4d glad_glColor4d -typedef void (APIENTRYP PFNGLCOLOR4DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLCOLOR4DVPROC)(const GLdouble *v); GLAPI PFNGLCOLOR4DVPROC glad_glColor4dv; #define glColor4dv glad_glColor4dv typedef void (APIENTRYP PFNGLCOLOR4FPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); GLAPI PFNGLCOLOR4FPROC glad_glColor4f; #define glColor4f glad_glColor4f -typedef void (APIENTRYP PFNGLCOLOR4FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLCOLOR4FVPROC)(const GLfloat *v); GLAPI PFNGLCOLOR4FVPROC glad_glColor4fv; #define glColor4fv glad_glColor4fv typedef void (APIENTRYP PFNGLCOLOR4IPROC)(GLint red, GLint green, GLint blue, GLint alpha); GLAPI PFNGLCOLOR4IPROC glad_glColor4i; #define glColor4i glad_glColor4i -typedef void (APIENTRYP PFNGLCOLOR4IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLCOLOR4IVPROC)(const GLint *v); GLAPI PFNGLCOLOR4IVPROC glad_glColor4iv; #define glColor4iv glad_glColor4iv typedef void (APIENTRYP PFNGLCOLOR4SPROC)(GLshort red, GLshort green, GLshort blue, GLshort alpha); GLAPI PFNGLCOLOR4SPROC glad_glColor4s; #define glColor4s glad_glColor4s -typedef void (APIENTRYP PFNGLCOLOR4SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLCOLOR4SVPROC)(const GLshort *v); GLAPI PFNGLCOLOR4SVPROC glad_glColor4sv; #define glColor4sv glad_glColor4sv typedef void (APIENTRYP PFNGLCOLOR4UBPROC)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); GLAPI PFNGLCOLOR4UBPROC glad_glColor4ub; #define glColor4ub glad_glColor4ub -typedef void (APIENTRYP PFNGLCOLOR4UBVPROC)(const GLubyte* v); +typedef void (APIENTRYP PFNGLCOLOR4UBVPROC)(const GLubyte *v); GLAPI PFNGLCOLOR4UBVPROC glad_glColor4ubv; #define glColor4ubv glad_glColor4ubv typedef void (APIENTRYP PFNGLCOLOR4UIPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha); GLAPI PFNGLCOLOR4UIPROC glad_glColor4ui; #define glColor4ui glad_glColor4ui -typedef void (APIENTRYP PFNGLCOLOR4UIVPROC)(const GLuint* v); +typedef void (APIENTRYP PFNGLCOLOR4UIVPROC)(const GLuint *v); GLAPI PFNGLCOLOR4UIVPROC glad_glColor4uiv; #define glColor4uiv glad_glColor4uiv typedef void (APIENTRYP PFNGLCOLOR4USPROC)(GLushort red, GLushort green, GLushort blue, GLushort alpha); GLAPI PFNGLCOLOR4USPROC glad_glColor4us; #define glColor4us glad_glColor4us -typedef void (APIENTRYP PFNGLCOLOR4USVPROC)(const GLushort* v); +typedef void (APIENTRYP PFNGLCOLOR4USVPROC)(const GLushort *v); GLAPI PFNGLCOLOR4USVPROC glad_glColor4usv; #define glColor4usv glad_glColor4usv typedef void (APIENTRYP PFNGLEDGEFLAGPROC)(GLboolean flag); GLAPI PFNGLEDGEFLAGPROC glad_glEdgeFlag; #define glEdgeFlag glad_glEdgeFlag -typedef void (APIENTRYP PFNGLEDGEFLAGVPROC)(const GLboolean* flag); +typedef void (APIENTRYP PFNGLEDGEFLAGVPROC)(const GLboolean *flag); GLAPI PFNGLEDGEFLAGVPROC glad_glEdgeFlagv; #define glEdgeFlagv glad_glEdgeFlagv typedef void (APIENTRYP PFNGLENDPROC)(); @@ -1658,322 +1680,322 @@ GLAPI PFNGLENDPROC glad_glEnd; typedef void (APIENTRYP PFNGLINDEXDPROC)(GLdouble c); GLAPI PFNGLINDEXDPROC glad_glIndexd; #define glIndexd glad_glIndexd -typedef void (APIENTRYP PFNGLINDEXDVPROC)(const GLdouble* c); +typedef void (APIENTRYP PFNGLINDEXDVPROC)(const GLdouble *c); GLAPI PFNGLINDEXDVPROC glad_glIndexdv; #define glIndexdv glad_glIndexdv typedef void (APIENTRYP PFNGLINDEXFPROC)(GLfloat c); GLAPI PFNGLINDEXFPROC glad_glIndexf; #define glIndexf glad_glIndexf -typedef void (APIENTRYP PFNGLINDEXFVPROC)(const GLfloat* c); +typedef void (APIENTRYP PFNGLINDEXFVPROC)(const GLfloat *c); GLAPI PFNGLINDEXFVPROC glad_glIndexfv; #define glIndexfv glad_glIndexfv typedef void (APIENTRYP PFNGLINDEXIPROC)(GLint c); GLAPI PFNGLINDEXIPROC glad_glIndexi; #define glIndexi glad_glIndexi -typedef void (APIENTRYP PFNGLINDEXIVPROC)(const GLint* c); +typedef void (APIENTRYP PFNGLINDEXIVPROC)(const GLint *c); GLAPI PFNGLINDEXIVPROC glad_glIndexiv; #define glIndexiv glad_glIndexiv typedef void (APIENTRYP PFNGLINDEXSPROC)(GLshort c); GLAPI PFNGLINDEXSPROC glad_glIndexs; #define glIndexs glad_glIndexs -typedef void (APIENTRYP PFNGLINDEXSVPROC)(const GLshort* c); +typedef void (APIENTRYP PFNGLINDEXSVPROC)(const GLshort *c); GLAPI PFNGLINDEXSVPROC glad_glIndexsv; #define glIndexsv glad_glIndexsv typedef void (APIENTRYP PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz); GLAPI PFNGLNORMAL3BPROC glad_glNormal3b; #define glNormal3b glad_glNormal3b -typedef void (APIENTRYP PFNGLNORMAL3BVPROC)(const GLbyte* v); +typedef void (APIENTRYP PFNGLNORMAL3BVPROC)(const GLbyte *v); GLAPI PFNGLNORMAL3BVPROC glad_glNormal3bv; #define glNormal3bv glad_glNormal3bv typedef void (APIENTRYP PFNGLNORMAL3DPROC)(GLdouble nx, GLdouble ny, GLdouble nz); GLAPI PFNGLNORMAL3DPROC glad_glNormal3d; #define glNormal3d glad_glNormal3d -typedef void (APIENTRYP PFNGLNORMAL3DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLNORMAL3DVPROC)(const GLdouble *v); GLAPI PFNGLNORMAL3DVPROC glad_glNormal3dv; #define glNormal3dv glad_glNormal3dv typedef void (APIENTRYP PFNGLNORMAL3FPROC)(GLfloat nx, GLfloat ny, GLfloat nz); GLAPI PFNGLNORMAL3FPROC glad_glNormal3f; #define glNormal3f glad_glNormal3f -typedef void (APIENTRYP PFNGLNORMAL3FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLNORMAL3FVPROC)(const GLfloat *v); GLAPI PFNGLNORMAL3FVPROC glad_glNormal3fv; #define glNormal3fv glad_glNormal3fv typedef void (APIENTRYP PFNGLNORMAL3IPROC)(GLint nx, GLint ny, GLint nz); GLAPI PFNGLNORMAL3IPROC glad_glNormal3i; #define glNormal3i glad_glNormal3i -typedef void (APIENTRYP PFNGLNORMAL3IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLNORMAL3IVPROC)(const GLint *v); GLAPI PFNGLNORMAL3IVPROC glad_glNormal3iv; #define glNormal3iv glad_glNormal3iv typedef void (APIENTRYP PFNGLNORMAL3SPROC)(GLshort nx, GLshort ny, GLshort nz); GLAPI PFNGLNORMAL3SPROC glad_glNormal3s; #define glNormal3s glad_glNormal3s -typedef void (APIENTRYP PFNGLNORMAL3SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLNORMAL3SVPROC)(const GLshort *v); GLAPI PFNGLNORMAL3SVPROC glad_glNormal3sv; #define glNormal3sv glad_glNormal3sv typedef void (APIENTRYP PFNGLRASTERPOS2DPROC)(GLdouble x, GLdouble y); GLAPI PFNGLRASTERPOS2DPROC glad_glRasterPos2d; #define glRasterPos2d glad_glRasterPos2d -typedef void (APIENTRYP PFNGLRASTERPOS2DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLRASTERPOS2DVPROC)(const GLdouble *v); GLAPI PFNGLRASTERPOS2DVPROC glad_glRasterPos2dv; #define glRasterPos2dv glad_glRasterPos2dv typedef void (APIENTRYP PFNGLRASTERPOS2FPROC)(GLfloat x, GLfloat y); GLAPI PFNGLRASTERPOS2FPROC glad_glRasterPos2f; #define glRasterPos2f glad_glRasterPos2f -typedef void (APIENTRYP PFNGLRASTERPOS2FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLRASTERPOS2FVPROC)(const GLfloat *v); GLAPI PFNGLRASTERPOS2FVPROC glad_glRasterPos2fv; #define glRasterPos2fv glad_glRasterPos2fv typedef void (APIENTRYP PFNGLRASTERPOS2IPROC)(GLint x, GLint y); GLAPI PFNGLRASTERPOS2IPROC glad_glRasterPos2i; #define glRasterPos2i glad_glRasterPos2i -typedef void (APIENTRYP PFNGLRASTERPOS2IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLRASTERPOS2IVPROC)(const GLint *v); GLAPI PFNGLRASTERPOS2IVPROC glad_glRasterPos2iv; #define glRasterPos2iv glad_glRasterPos2iv typedef void (APIENTRYP PFNGLRASTERPOS2SPROC)(GLshort x, GLshort y); GLAPI PFNGLRASTERPOS2SPROC glad_glRasterPos2s; #define glRasterPos2s glad_glRasterPos2s -typedef void (APIENTRYP PFNGLRASTERPOS2SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLRASTERPOS2SVPROC)(const GLshort *v); GLAPI PFNGLRASTERPOS2SVPROC glad_glRasterPos2sv; #define glRasterPos2sv glad_glRasterPos2sv typedef void (APIENTRYP PFNGLRASTERPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); GLAPI PFNGLRASTERPOS3DPROC glad_glRasterPos3d; #define glRasterPos3d glad_glRasterPos3d -typedef void (APIENTRYP PFNGLRASTERPOS3DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLRASTERPOS3DVPROC)(const GLdouble *v); GLAPI PFNGLRASTERPOS3DVPROC glad_glRasterPos3dv; #define glRasterPos3dv glad_glRasterPos3dv typedef void (APIENTRYP PFNGLRASTERPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); GLAPI PFNGLRASTERPOS3FPROC glad_glRasterPos3f; #define glRasterPos3f glad_glRasterPos3f -typedef void (APIENTRYP PFNGLRASTERPOS3FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLRASTERPOS3FVPROC)(const GLfloat *v); GLAPI PFNGLRASTERPOS3FVPROC glad_glRasterPos3fv; #define glRasterPos3fv glad_glRasterPos3fv typedef void (APIENTRYP PFNGLRASTERPOS3IPROC)(GLint x, GLint y, GLint z); GLAPI PFNGLRASTERPOS3IPROC glad_glRasterPos3i; #define glRasterPos3i glad_glRasterPos3i -typedef void (APIENTRYP PFNGLRASTERPOS3IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLRASTERPOS3IVPROC)(const GLint *v); GLAPI PFNGLRASTERPOS3IVPROC glad_glRasterPos3iv; #define glRasterPos3iv glad_glRasterPos3iv typedef void (APIENTRYP PFNGLRASTERPOS3SPROC)(GLshort x, GLshort y, GLshort z); GLAPI PFNGLRASTERPOS3SPROC glad_glRasterPos3s; #define glRasterPos3s glad_glRasterPos3s -typedef void (APIENTRYP PFNGLRASTERPOS3SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLRASTERPOS3SVPROC)(const GLshort *v); GLAPI PFNGLRASTERPOS3SVPROC glad_glRasterPos3sv; #define glRasterPos3sv glad_glRasterPos3sv typedef void (APIENTRYP PFNGLRASTERPOS4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI PFNGLRASTERPOS4DPROC glad_glRasterPos4d; #define glRasterPos4d glad_glRasterPos4d -typedef void (APIENTRYP PFNGLRASTERPOS4DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLRASTERPOS4DVPROC)(const GLdouble *v); GLAPI PFNGLRASTERPOS4DVPROC glad_glRasterPos4dv; #define glRasterPos4dv glad_glRasterPos4dv typedef void (APIENTRYP PFNGLRASTERPOS4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); GLAPI PFNGLRASTERPOS4FPROC glad_glRasterPos4f; #define glRasterPos4f glad_glRasterPos4f -typedef void (APIENTRYP PFNGLRASTERPOS4FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLRASTERPOS4FVPROC)(const GLfloat *v); GLAPI PFNGLRASTERPOS4FVPROC glad_glRasterPos4fv; #define glRasterPos4fv glad_glRasterPos4fv typedef void (APIENTRYP PFNGLRASTERPOS4IPROC)(GLint x, GLint y, GLint z, GLint w); GLAPI PFNGLRASTERPOS4IPROC glad_glRasterPos4i; #define glRasterPos4i glad_glRasterPos4i -typedef void (APIENTRYP PFNGLRASTERPOS4IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLRASTERPOS4IVPROC)(const GLint *v); GLAPI PFNGLRASTERPOS4IVPROC glad_glRasterPos4iv; #define glRasterPos4iv glad_glRasterPos4iv typedef void (APIENTRYP PFNGLRASTERPOS4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); GLAPI PFNGLRASTERPOS4SPROC glad_glRasterPos4s; #define glRasterPos4s glad_glRasterPos4s -typedef void (APIENTRYP PFNGLRASTERPOS4SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLRASTERPOS4SVPROC)(const GLshort *v); GLAPI PFNGLRASTERPOS4SVPROC glad_glRasterPos4sv; #define glRasterPos4sv glad_glRasterPos4sv typedef void (APIENTRYP PFNGLRECTDPROC)(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); GLAPI PFNGLRECTDPROC glad_glRectd; #define glRectd glad_glRectd -typedef void (APIENTRYP PFNGLRECTDVPROC)(const GLdouble* v1, const GLdouble* v2); +typedef void (APIENTRYP PFNGLRECTDVPROC)(const GLdouble *v1, const GLdouble *v2); GLAPI PFNGLRECTDVPROC glad_glRectdv; #define glRectdv glad_glRectdv typedef void (APIENTRYP PFNGLRECTFPROC)(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); GLAPI PFNGLRECTFPROC glad_glRectf; #define glRectf glad_glRectf -typedef void (APIENTRYP PFNGLRECTFVPROC)(const GLfloat* v1, const GLfloat* v2); +typedef void (APIENTRYP PFNGLRECTFVPROC)(const GLfloat *v1, const GLfloat *v2); GLAPI PFNGLRECTFVPROC glad_glRectfv; #define glRectfv glad_glRectfv typedef void (APIENTRYP PFNGLRECTIPROC)(GLint x1, GLint y1, GLint x2, GLint y2); GLAPI PFNGLRECTIPROC glad_glRecti; #define glRecti glad_glRecti -typedef void (APIENTRYP PFNGLRECTIVPROC)(const GLint* v1, const GLint* v2); +typedef void (APIENTRYP PFNGLRECTIVPROC)(const GLint *v1, const GLint *v2); GLAPI PFNGLRECTIVPROC glad_glRectiv; #define glRectiv glad_glRectiv typedef void (APIENTRYP PFNGLRECTSPROC)(GLshort x1, GLshort y1, GLshort x2, GLshort y2); GLAPI PFNGLRECTSPROC glad_glRects; #define glRects glad_glRects -typedef void (APIENTRYP PFNGLRECTSVPROC)(const GLshort* v1, const GLshort* v2); +typedef void (APIENTRYP PFNGLRECTSVPROC)(const GLshort *v1, const GLshort *v2); GLAPI PFNGLRECTSVPROC glad_glRectsv; #define glRectsv glad_glRectsv typedef void (APIENTRYP PFNGLTEXCOORD1DPROC)(GLdouble s); GLAPI PFNGLTEXCOORD1DPROC glad_glTexCoord1d; #define glTexCoord1d glad_glTexCoord1d -typedef void (APIENTRYP PFNGLTEXCOORD1DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLTEXCOORD1DVPROC)(const GLdouble *v); GLAPI PFNGLTEXCOORD1DVPROC glad_glTexCoord1dv; #define glTexCoord1dv glad_glTexCoord1dv typedef void (APIENTRYP PFNGLTEXCOORD1FPROC)(GLfloat s); GLAPI PFNGLTEXCOORD1FPROC glad_glTexCoord1f; #define glTexCoord1f glad_glTexCoord1f -typedef void (APIENTRYP PFNGLTEXCOORD1FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLTEXCOORD1FVPROC)(const GLfloat *v); GLAPI PFNGLTEXCOORD1FVPROC glad_glTexCoord1fv; #define glTexCoord1fv glad_glTexCoord1fv typedef void (APIENTRYP PFNGLTEXCOORD1IPROC)(GLint s); GLAPI PFNGLTEXCOORD1IPROC glad_glTexCoord1i; #define glTexCoord1i glad_glTexCoord1i -typedef void (APIENTRYP PFNGLTEXCOORD1IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLTEXCOORD1IVPROC)(const GLint *v); GLAPI PFNGLTEXCOORD1IVPROC glad_glTexCoord1iv; #define glTexCoord1iv glad_glTexCoord1iv typedef void (APIENTRYP PFNGLTEXCOORD1SPROC)(GLshort s); GLAPI PFNGLTEXCOORD1SPROC glad_glTexCoord1s; #define glTexCoord1s glad_glTexCoord1s -typedef void (APIENTRYP PFNGLTEXCOORD1SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLTEXCOORD1SVPROC)(const GLshort *v); GLAPI PFNGLTEXCOORD1SVPROC glad_glTexCoord1sv; #define glTexCoord1sv glad_glTexCoord1sv typedef void (APIENTRYP PFNGLTEXCOORD2DPROC)(GLdouble s, GLdouble t); GLAPI PFNGLTEXCOORD2DPROC glad_glTexCoord2d; #define glTexCoord2d glad_glTexCoord2d -typedef void (APIENTRYP PFNGLTEXCOORD2DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLTEXCOORD2DVPROC)(const GLdouble *v); GLAPI PFNGLTEXCOORD2DVPROC glad_glTexCoord2dv; #define glTexCoord2dv glad_glTexCoord2dv typedef void (APIENTRYP PFNGLTEXCOORD2FPROC)(GLfloat s, GLfloat t); GLAPI PFNGLTEXCOORD2FPROC glad_glTexCoord2f; #define glTexCoord2f glad_glTexCoord2f -typedef void (APIENTRYP PFNGLTEXCOORD2FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLTEXCOORD2FVPROC)(const GLfloat *v); GLAPI PFNGLTEXCOORD2FVPROC glad_glTexCoord2fv; #define glTexCoord2fv glad_glTexCoord2fv typedef void (APIENTRYP PFNGLTEXCOORD2IPROC)(GLint s, GLint t); GLAPI PFNGLTEXCOORD2IPROC glad_glTexCoord2i; #define glTexCoord2i glad_glTexCoord2i -typedef void (APIENTRYP PFNGLTEXCOORD2IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLTEXCOORD2IVPROC)(const GLint *v); GLAPI PFNGLTEXCOORD2IVPROC glad_glTexCoord2iv; #define glTexCoord2iv glad_glTexCoord2iv typedef void (APIENTRYP PFNGLTEXCOORD2SPROC)(GLshort s, GLshort t); GLAPI PFNGLTEXCOORD2SPROC glad_glTexCoord2s; #define glTexCoord2s glad_glTexCoord2s -typedef void (APIENTRYP PFNGLTEXCOORD2SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLTEXCOORD2SVPROC)(const GLshort *v); GLAPI PFNGLTEXCOORD2SVPROC glad_glTexCoord2sv; #define glTexCoord2sv glad_glTexCoord2sv typedef void (APIENTRYP PFNGLTEXCOORD3DPROC)(GLdouble s, GLdouble t, GLdouble r); GLAPI PFNGLTEXCOORD3DPROC glad_glTexCoord3d; #define glTexCoord3d glad_glTexCoord3d -typedef void (APIENTRYP PFNGLTEXCOORD3DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLTEXCOORD3DVPROC)(const GLdouble *v); GLAPI PFNGLTEXCOORD3DVPROC glad_glTexCoord3dv; #define glTexCoord3dv glad_glTexCoord3dv typedef void (APIENTRYP PFNGLTEXCOORD3FPROC)(GLfloat s, GLfloat t, GLfloat r); GLAPI PFNGLTEXCOORD3FPROC glad_glTexCoord3f; #define glTexCoord3f glad_glTexCoord3f -typedef void (APIENTRYP PFNGLTEXCOORD3FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLTEXCOORD3FVPROC)(const GLfloat *v); GLAPI PFNGLTEXCOORD3FVPROC glad_glTexCoord3fv; #define glTexCoord3fv glad_glTexCoord3fv typedef void (APIENTRYP PFNGLTEXCOORD3IPROC)(GLint s, GLint t, GLint r); GLAPI PFNGLTEXCOORD3IPROC glad_glTexCoord3i; #define glTexCoord3i glad_glTexCoord3i -typedef void (APIENTRYP PFNGLTEXCOORD3IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLTEXCOORD3IVPROC)(const GLint *v); GLAPI PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv; #define glTexCoord3iv glad_glTexCoord3iv typedef void (APIENTRYP PFNGLTEXCOORD3SPROC)(GLshort s, GLshort t, GLshort r); GLAPI PFNGLTEXCOORD3SPROC glad_glTexCoord3s; #define glTexCoord3s glad_glTexCoord3s -typedef void (APIENTRYP PFNGLTEXCOORD3SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLTEXCOORD3SVPROC)(const GLshort *v); GLAPI PFNGLTEXCOORD3SVPROC glad_glTexCoord3sv; #define glTexCoord3sv glad_glTexCoord3sv typedef void (APIENTRYP PFNGLTEXCOORD4DPROC)(GLdouble s, GLdouble t, GLdouble r, GLdouble q); GLAPI PFNGLTEXCOORD4DPROC glad_glTexCoord4d; #define glTexCoord4d glad_glTexCoord4d -typedef void (APIENTRYP PFNGLTEXCOORD4DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLTEXCOORD4DVPROC)(const GLdouble *v); GLAPI PFNGLTEXCOORD4DVPROC glad_glTexCoord4dv; #define glTexCoord4dv glad_glTexCoord4dv typedef void (APIENTRYP PFNGLTEXCOORD4FPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat q); GLAPI PFNGLTEXCOORD4FPROC glad_glTexCoord4f; #define glTexCoord4f glad_glTexCoord4f -typedef void (APIENTRYP PFNGLTEXCOORD4FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLTEXCOORD4FVPROC)(const GLfloat *v); GLAPI PFNGLTEXCOORD4FVPROC glad_glTexCoord4fv; #define glTexCoord4fv glad_glTexCoord4fv typedef void (APIENTRYP PFNGLTEXCOORD4IPROC)(GLint s, GLint t, GLint r, GLint q); GLAPI PFNGLTEXCOORD4IPROC glad_glTexCoord4i; #define glTexCoord4i glad_glTexCoord4i -typedef void (APIENTRYP PFNGLTEXCOORD4IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLTEXCOORD4IVPROC)(const GLint *v); GLAPI PFNGLTEXCOORD4IVPROC glad_glTexCoord4iv; #define glTexCoord4iv glad_glTexCoord4iv typedef void (APIENTRYP PFNGLTEXCOORD4SPROC)(GLshort s, GLshort t, GLshort r, GLshort q); GLAPI PFNGLTEXCOORD4SPROC glad_glTexCoord4s; #define glTexCoord4s glad_glTexCoord4s -typedef void (APIENTRYP PFNGLTEXCOORD4SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLTEXCOORD4SVPROC)(const GLshort *v); GLAPI PFNGLTEXCOORD4SVPROC glad_glTexCoord4sv; #define glTexCoord4sv glad_glTexCoord4sv typedef void (APIENTRYP PFNGLVERTEX2DPROC)(GLdouble x, GLdouble y); GLAPI PFNGLVERTEX2DPROC glad_glVertex2d; #define glVertex2d glad_glVertex2d -typedef void (APIENTRYP PFNGLVERTEX2DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLVERTEX2DVPROC)(const GLdouble *v); GLAPI PFNGLVERTEX2DVPROC glad_glVertex2dv; #define glVertex2dv glad_glVertex2dv typedef void (APIENTRYP PFNGLVERTEX2FPROC)(GLfloat x, GLfloat y); GLAPI PFNGLVERTEX2FPROC glad_glVertex2f; #define glVertex2f glad_glVertex2f -typedef void (APIENTRYP PFNGLVERTEX2FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLVERTEX2FVPROC)(const GLfloat *v); GLAPI PFNGLVERTEX2FVPROC glad_glVertex2fv; #define glVertex2fv glad_glVertex2fv typedef void (APIENTRYP PFNGLVERTEX2IPROC)(GLint x, GLint y); GLAPI PFNGLVERTEX2IPROC glad_glVertex2i; #define glVertex2i glad_glVertex2i -typedef void (APIENTRYP PFNGLVERTEX2IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLVERTEX2IVPROC)(const GLint *v); GLAPI PFNGLVERTEX2IVPROC glad_glVertex2iv; #define glVertex2iv glad_glVertex2iv typedef void (APIENTRYP PFNGLVERTEX2SPROC)(GLshort x, GLshort y); GLAPI PFNGLVERTEX2SPROC glad_glVertex2s; #define glVertex2s glad_glVertex2s -typedef void (APIENTRYP PFNGLVERTEX2SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLVERTEX2SVPROC)(const GLshort *v); GLAPI PFNGLVERTEX2SVPROC glad_glVertex2sv; #define glVertex2sv glad_glVertex2sv typedef void (APIENTRYP PFNGLVERTEX3DPROC)(GLdouble x, GLdouble y, GLdouble z); GLAPI PFNGLVERTEX3DPROC glad_glVertex3d; #define glVertex3d glad_glVertex3d -typedef void (APIENTRYP PFNGLVERTEX3DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLVERTEX3DVPROC)(const GLdouble *v); GLAPI PFNGLVERTEX3DVPROC glad_glVertex3dv; #define glVertex3dv glad_glVertex3dv typedef void (APIENTRYP PFNGLVERTEX3FPROC)(GLfloat x, GLfloat y, GLfloat z); GLAPI PFNGLVERTEX3FPROC glad_glVertex3f; #define glVertex3f glad_glVertex3f -typedef void (APIENTRYP PFNGLVERTEX3FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLVERTEX3FVPROC)(const GLfloat *v); GLAPI PFNGLVERTEX3FVPROC glad_glVertex3fv; #define glVertex3fv glad_glVertex3fv typedef void (APIENTRYP PFNGLVERTEX3IPROC)(GLint x, GLint y, GLint z); GLAPI PFNGLVERTEX3IPROC glad_glVertex3i; #define glVertex3i glad_glVertex3i -typedef void (APIENTRYP PFNGLVERTEX3IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLVERTEX3IVPROC)(const GLint *v); GLAPI PFNGLVERTEX3IVPROC glad_glVertex3iv; #define glVertex3iv glad_glVertex3iv typedef void (APIENTRYP PFNGLVERTEX3SPROC)(GLshort x, GLshort y, GLshort z); GLAPI PFNGLVERTEX3SPROC glad_glVertex3s; #define glVertex3s glad_glVertex3s -typedef void (APIENTRYP PFNGLVERTEX3SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLVERTEX3SVPROC)(const GLshort *v); GLAPI PFNGLVERTEX3SVPROC glad_glVertex3sv; #define glVertex3sv glad_glVertex3sv typedef void (APIENTRYP PFNGLVERTEX4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI PFNGLVERTEX4DPROC glad_glVertex4d; #define glVertex4d glad_glVertex4d -typedef void (APIENTRYP PFNGLVERTEX4DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLVERTEX4DVPROC)(const GLdouble *v); GLAPI PFNGLVERTEX4DVPROC glad_glVertex4dv; #define glVertex4dv glad_glVertex4dv typedef void (APIENTRYP PFNGLVERTEX4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); GLAPI PFNGLVERTEX4FPROC glad_glVertex4f; #define glVertex4f glad_glVertex4f -typedef void (APIENTRYP PFNGLVERTEX4FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLVERTEX4FVPROC)(const GLfloat *v); GLAPI PFNGLVERTEX4FVPROC glad_glVertex4fv; #define glVertex4fv glad_glVertex4fv typedef void (APIENTRYP PFNGLVERTEX4IPROC)(GLint x, GLint y, GLint z, GLint w); GLAPI PFNGLVERTEX4IPROC glad_glVertex4i; #define glVertex4i glad_glVertex4i -typedef void (APIENTRYP PFNGLVERTEX4IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLVERTEX4IVPROC)(const GLint *v); GLAPI PFNGLVERTEX4IVPROC glad_glVertex4iv; #define glVertex4iv glad_glVertex4iv typedef void (APIENTRYP PFNGLVERTEX4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); GLAPI PFNGLVERTEX4SPROC glad_glVertex4s; #define glVertex4s glad_glVertex4s -typedef void (APIENTRYP PFNGLVERTEX4SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLVERTEX4SVPROC)(const GLshort *v); GLAPI PFNGLVERTEX4SVPROC glad_glVertex4sv; #define glVertex4sv glad_glVertex4sv -typedef void (APIENTRYP PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble* equation); +typedef void (APIENTRYP PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble *equation); GLAPI PFNGLCLIPPLANEPROC glad_glClipPlane; #define glClipPlane glad_glClipPlane typedef void (APIENTRYP PFNGLCOLORMATERIALPROC)(GLenum face, GLenum mode); @@ -1982,37 +2004,37 @@ GLAPI PFNGLCOLORMATERIALPROC glad_glColorMaterial; typedef void (APIENTRYP PFNGLFOGFPROC)(GLenum pname, GLfloat param); GLAPI PFNGLFOGFPROC glad_glFogf; #define glFogf glad_glFogf -typedef void (APIENTRYP PFNGLFOGFVPROC)(GLenum pname, const GLfloat* params); +typedef void (APIENTRYP PFNGLFOGFVPROC)(GLenum pname, const GLfloat *params); GLAPI PFNGLFOGFVPROC glad_glFogfv; #define glFogfv glad_glFogfv typedef void (APIENTRYP PFNGLFOGIPROC)(GLenum pname, GLint param); GLAPI PFNGLFOGIPROC glad_glFogi; #define glFogi glad_glFogi -typedef void (APIENTRYP PFNGLFOGIVPROC)(GLenum pname, const GLint* params); +typedef void (APIENTRYP PFNGLFOGIVPROC)(GLenum pname, const GLint *params); GLAPI PFNGLFOGIVPROC glad_glFogiv; #define glFogiv glad_glFogiv typedef void (APIENTRYP PFNGLLIGHTFPROC)(GLenum light, GLenum pname, GLfloat param); GLAPI PFNGLLIGHTFPROC glad_glLightf; #define glLightf glad_glLightf -typedef void (APIENTRYP PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat* params); +typedef void (APIENTRYP PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat *params); GLAPI PFNGLLIGHTFVPROC glad_glLightfv; #define glLightfv glad_glLightfv typedef void (APIENTRYP PFNGLLIGHTIPROC)(GLenum light, GLenum pname, GLint param); GLAPI PFNGLLIGHTIPROC glad_glLighti; #define glLighti glad_glLighti -typedef void (APIENTRYP PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint* params); +typedef void (APIENTRYP PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint *params); GLAPI PFNGLLIGHTIVPROC glad_glLightiv; #define glLightiv glad_glLightiv typedef void (APIENTRYP PFNGLLIGHTMODELFPROC)(GLenum pname, GLfloat param); GLAPI PFNGLLIGHTMODELFPROC glad_glLightModelf; #define glLightModelf glad_glLightModelf -typedef void (APIENTRYP PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat* params); +typedef void (APIENTRYP PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat *params); GLAPI PFNGLLIGHTMODELFVPROC glad_glLightModelfv; #define glLightModelfv glad_glLightModelfv typedef void (APIENTRYP PFNGLLIGHTMODELIPROC)(GLenum pname, GLint param); GLAPI PFNGLLIGHTMODELIPROC glad_glLightModeli; #define glLightModeli glad_glLightModeli -typedef void (APIENTRYP PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint* params); +typedef void (APIENTRYP PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint *params); GLAPI PFNGLLIGHTMODELIVPROC glad_glLightModeliv; #define glLightModeliv glad_glLightModeliv typedef void (APIENTRYP PFNGLLINESTIPPLEPROC)(GLint factor, GLushort pattern); @@ -2021,16 +2043,16 @@ GLAPI PFNGLLINESTIPPLEPROC glad_glLineStipple; typedef void (APIENTRYP PFNGLMATERIALFPROC)(GLenum face, GLenum pname, GLfloat param); GLAPI PFNGLMATERIALFPROC glad_glMaterialf; #define glMaterialf glad_glMaterialf -typedef void (APIENTRYP PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat* params); +typedef void (APIENTRYP PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat *params); GLAPI PFNGLMATERIALFVPROC glad_glMaterialfv; #define glMaterialfv glad_glMaterialfv typedef void (APIENTRYP PFNGLMATERIALIPROC)(GLenum face, GLenum pname, GLint param); GLAPI PFNGLMATERIALIPROC glad_glMateriali; #define glMateriali glad_glMateriali -typedef void (APIENTRYP PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint* params); +typedef void (APIENTRYP PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint *params); GLAPI PFNGLMATERIALIVPROC glad_glMaterialiv; #define glMaterialiv glad_glMaterialiv -typedef void (APIENTRYP PFNGLPOLYGONSTIPPLEPROC)(const GLubyte* mask); +typedef void (APIENTRYP PFNGLPOLYGONSTIPPLEPROC)(const GLubyte *mask); GLAPI PFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple; #define glPolygonStipple glad_glPolygonStipple typedef void (APIENTRYP PFNGLSHADEMODELPROC)(GLenum mode); @@ -2039,37 +2061,37 @@ GLAPI PFNGLSHADEMODELPROC glad_glShadeModel; typedef void (APIENTRYP PFNGLTEXENVFPROC)(GLenum target, GLenum pname, GLfloat param); GLAPI PFNGLTEXENVFPROC glad_glTexEnvf; #define glTexEnvf glad_glTexEnvf -typedef void (APIENTRYP PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat* params); +typedef void (APIENTRYP PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat *params); GLAPI PFNGLTEXENVFVPROC glad_glTexEnvfv; #define glTexEnvfv glad_glTexEnvfv typedef void (APIENTRYP PFNGLTEXENVIPROC)(GLenum target, GLenum pname, GLint param); GLAPI PFNGLTEXENVIPROC glad_glTexEnvi; #define glTexEnvi glad_glTexEnvi -typedef void (APIENTRYP PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint* params); +typedef void (APIENTRYP PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint *params); GLAPI PFNGLTEXENVIVPROC glad_glTexEnviv; #define glTexEnviv glad_glTexEnviv typedef void (APIENTRYP PFNGLTEXGENDPROC)(GLenum coord, GLenum pname, GLdouble param); GLAPI PFNGLTEXGENDPROC glad_glTexGend; #define glTexGend glad_glTexGend -typedef void (APIENTRYP PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble* params); +typedef void (APIENTRYP PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble *params); GLAPI PFNGLTEXGENDVPROC glad_glTexGendv; #define glTexGendv glad_glTexGendv typedef void (APIENTRYP PFNGLTEXGENFPROC)(GLenum coord, GLenum pname, GLfloat param); GLAPI PFNGLTEXGENFPROC glad_glTexGenf; #define glTexGenf glad_glTexGenf -typedef void (APIENTRYP PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat* params); +typedef void (APIENTRYP PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat *params); GLAPI PFNGLTEXGENFVPROC glad_glTexGenfv; #define glTexGenfv glad_glTexGenfv typedef void (APIENTRYP PFNGLTEXGENIPROC)(GLenum coord, GLenum pname, GLint param); GLAPI PFNGLTEXGENIPROC glad_glTexGeni; #define glTexGeni glad_glTexGeni -typedef void (APIENTRYP PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint* params); +typedef void (APIENTRYP PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint *params); GLAPI PFNGLTEXGENIVPROC glad_glTexGeniv; #define glTexGeniv glad_glTexGeniv -typedef void (APIENTRYP PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat* buffer); +typedef void (APIENTRYP PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat *buffer); GLAPI PFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer; #define glFeedbackBuffer glad_glFeedbackBuffer -typedef void (APIENTRYP PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint* buffer); +typedef void (APIENTRYP PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint *buffer); GLAPI PFNGLSELECTBUFFERPROC glad_glSelectBuffer; #define glSelectBuffer glad_glSelectBuffer typedef GLint (APIENTRYP PFNGLRENDERMODEPROC)(GLenum mode); @@ -2108,16 +2130,16 @@ GLAPI PFNGLPOPATTRIBPROC glad_glPopAttrib; typedef void (APIENTRYP PFNGLPUSHATTRIBPROC)(GLbitfield mask); GLAPI PFNGLPUSHATTRIBPROC glad_glPushAttrib; #define glPushAttrib glad_glPushAttrib -typedef void (APIENTRYP PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble* points); +typedef void (APIENTRYP PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); GLAPI PFNGLMAP1DPROC glad_glMap1d; #define glMap1d glad_glMap1d -typedef void (APIENTRYP PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat* points); +typedef void (APIENTRYP PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); GLAPI PFNGLMAP1FPROC glad_glMap1f; #define glMap1f glad_glMap1f -typedef void (APIENTRYP PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble* points); +typedef void (APIENTRYP PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); GLAPI PFNGLMAP2DPROC glad_glMap2d; #define glMap2d glad_glMap2d -typedef void (APIENTRYP PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat* points); +typedef void (APIENTRYP PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); GLAPI PFNGLMAP2FPROC glad_glMap2f; #define glMap2f glad_glMap2f typedef void (APIENTRYP PFNGLMAPGRID1DPROC)(GLint un, GLdouble u1, GLdouble u2); @@ -2135,25 +2157,25 @@ GLAPI PFNGLMAPGRID2FPROC glad_glMapGrid2f; typedef void (APIENTRYP PFNGLEVALCOORD1DPROC)(GLdouble u); GLAPI PFNGLEVALCOORD1DPROC glad_glEvalCoord1d; #define glEvalCoord1d glad_glEvalCoord1d -typedef void (APIENTRYP PFNGLEVALCOORD1DVPROC)(const GLdouble* u); +typedef void (APIENTRYP PFNGLEVALCOORD1DVPROC)(const GLdouble *u); GLAPI PFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv; #define glEvalCoord1dv glad_glEvalCoord1dv typedef void (APIENTRYP PFNGLEVALCOORD1FPROC)(GLfloat u); GLAPI PFNGLEVALCOORD1FPROC glad_glEvalCoord1f; #define glEvalCoord1f glad_glEvalCoord1f -typedef void (APIENTRYP PFNGLEVALCOORD1FVPROC)(const GLfloat* u); +typedef void (APIENTRYP PFNGLEVALCOORD1FVPROC)(const GLfloat *u); GLAPI PFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv; #define glEvalCoord1fv glad_glEvalCoord1fv typedef void (APIENTRYP PFNGLEVALCOORD2DPROC)(GLdouble u, GLdouble v); GLAPI PFNGLEVALCOORD2DPROC glad_glEvalCoord2d; #define glEvalCoord2d glad_glEvalCoord2d -typedef void (APIENTRYP PFNGLEVALCOORD2DVPROC)(const GLdouble* u); +typedef void (APIENTRYP PFNGLEVALCOORD2DVPROC)(const GLdouble *u); GLAPI PFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv; #define glEvalCoord2dv glad_glEvalCoord2dv typedef void (APIENTRYP PFNGLEVALCOORD2FPROC)(GLfloat u, GLfloat v); GLAPI PFNGLEVALCOORD2FPROC glad_glEvalCoord2f; #define glEvalCoord2f glad_glEvalCoord2f -typedef void (APIENTRYP PFNGLEVALCOORD2FVPROC)(const GLfloat* u); +typedef void (APIENTRYP PFNGLEVALCOORD2FVPROC)(const GLfloat *u); GLAPI PFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv; #define glEvalCoord2fv glad_glEvalCoord2fv typedef void (APIENTRYP PFNGLEVALMESH1PROC)(GLenum mode, GLint i1, GLint i2); @@ -2180,70 +2202,70 @@ GLAPI PFNGLPIXELTRANSFERFPROC glad_glPixelTransferf; typedef void (APIENTRYP PFNGLPIXELTRANSFERIPROC)(GLenum pname, GLint param); GLAPI PFNGLPIXELTRANSFERIPROC glad_glPixelTransferi; #define glPixelTransferi glad_glPixelTransferi -typedef void (APIENTRYP PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat* values); +typedef void (APIENTRYP PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat *values); GLAPI PFNGLPIXELMAPFVPROC glad_glPixelMapfv; #define glPixelMapfv glad_glPixelMapfv -typedef void (APIENTRYP PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint* values); +typedef void (APIENTRYP PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint *values); GLAPI PFNGLPIXELMAPUIVPROC glad_glPixelMapuiv; #define glPixelMapuiv glad_glPixelMapuiv -typedef void (APIENTRYP PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort* values); +typedef void (APIENTRYP PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort *values); GLAPI PFNGLPIXELMAPUSVPROC glad_glPixelMapusv; #define glPixelMapusv glad_glPixelMapusv typedef void (APIENTRYP PFNGLCOPYPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); GLAPI PFNGLCOPYPIXELSPROC glad_glCopyPixels; #define glCopyPixels glad_glCopyPixels -typedef void (APIENTRYP PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels); +typedef void (APIENTRYP PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLDRAWPIXELSPROC glad_glDrawPixels; #define glDrawPixels glad_glDrawPixels -typedef void (APIENTRYP PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble* equation); +typedef void (APIENTRYP PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble *equation); GLAPI PFNGLGETCLIPPLANEPROC glad_glGetClipPlane; #define glGetClipPlane glad_glGetClipPlane -typedef void (APIENTRYP PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat* params); +typedef void (APIENTRYP PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat *params); GLAPI PFNGLGETLIGHTFVPROC glad_glGetLightfv; #define glGetLightfv glad_glGetLightfv -typedef void (APIENTRYP PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint *params); GLAPI PFNGLGETLIGHTIVPROC glad_glGetLightiv; #define glGetLightiv glad_glGetLightiv -typedef void (APIENTRYP PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble* v); +typedef void (APIENTRYP PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble *v); GLAPI PFNGLGETMAPDVPROC glad_glGetMapdv; #define glGetMapdv glad_glGetMapdv -typedef void (APIENTRYP PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat* v); +typedef void (APIENTRYP PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat *v); GLAPI PFNGLGETMAPFVPROC glad_glGetMapfv; #define glGetMapfv glad_glGetMapfv -typedef void (APIENTRYP PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint* v); +typedef void (APIENTRYP PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint *v); GLAPI PFNGLGETMAPIVPROC glad_glGetMapiv; #define glGetMapiv glad_glGetMapiv -typedef void (APIENTRYP PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat* params); +typedef void (APIENTRYP PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat *params); GLAPI PFNGLGETMATERIALFVPROC glad_glGetMaterialfv; #define glGetMaterialfv glad_glGetMaterialfv -typedef void (APIENTRYP PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint *params); GLAPI PFNGLGETMATERIALIVPROC glad_glGetMaterialiv; #define glGetMaterialiv glad_glGetMaterialiv -typedef void (APIENTRYP PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat* values); +typedef void (APIENTRYP PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat *values); GLAPI PFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv; #define glGetPixelMapfv glad_glGetPixelMapfv -typedef void (APIENTRYP PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint* values); +typedef void (APIENTRYP PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint *values); GLAPI PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv; #define glGetPixelMapuiv glad_glGetPixelMapuiv -typedef void (APIENTRYP PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort* values); +typedef void (APIENTRYP PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort *values); GLAPI PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv; #define glGetPixelMapusv glad_glGetPixelMapusv -typedef void (APIENTRYP PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte* mask); +typedef void (APIENTRYP PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte *mask); GLAPI PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple; #define glGetPolygonStipple glad_glGetPolygonStipple -typedef void (APIENTRYP PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat* params); +typedef void (APIENTRYP PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat *params); GLAPI PFNGLGETTEXENVFVPROC glad_glGetTexEnvfv; #define glGetTexEnvfv glad_glGetTexEnvfv -typedef void (APIENTRYP PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETTEXENVIVPROC glad_glGetTexEnviv; #define glGetTexEnviv glad_glGetTexEnviv -typedef void (APIENTRYP PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble* params); +typedef void (APIENTRYP PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble *params); GLAPI PFNGLGETTEXGENDVPROC glad_glGetTexGendv; #define glGetTexGendv glad_glGetTexGendv -typedef void (APIENTRYP PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat* params); +typedef void (APIENTRYP PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat *params); GLAPI PFNGLGETTEXGENFVPROC glad_glGetTexGenfv; #define glGetTexGenfv glad_glGetTexGenfv -typedef void (APIENTRYP PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint *params); GLAPI PFNGLGETTEXGENIVPROC glad_glGetTexGeniv; #define glGetTexGeniv glad_glGetTexGeniv typedef GLboolean (APIENTRYP PFNGLISLISTPROC)(GLuint list); @@ -2255,19 +2277,19 @@ GLAPI PFNGLFRUSTUMPROC glad_glFrustum; typedef void (APIENTRYP PFNGLLOADIDENTITYPROC)(); GLAPI PFNGLLOADIDENTITYPROC glad_glLoadIdentity; #define glLoadIdentity glad_glLoadIdentity -typedef void (APIENTRYP PFNGLLOADMATRIXFPROC)(const GLfloat* m); +typedef void (APIENTRYP PFNGLLOADMATRIXFPROC)(const GLfloat *m); GLAPI PFNGLLOADMATRIXFPROC glad_glLoadMatrixf; #define glLoadMatrixf glad_glLoadMatrixf -typedef void (APIENTRYP PFNGLLOADMATRIXDPROC)(const GLdouble* m); +typedef void (APIENTRYP PFNGLLOADMATRIXDPROC)(const GLdouble *m); GLAPI PFNGLLOADMATRIXDPROC glad_glLoadMatrixd; #define glLoadMatrixd glad_glLoadMatrixd typedef void (APIENTRYP PFNGLMATRIXMODEPROC)(GLenum mode); GLAPI PFNGLMATRIXMODEPROC glad_glMatrixMode; #define glMatrixMode glad_glMatrixMode -typedef void (APIENTRYP PFNGLMULTMATRIXFPROC)(const GLfloat* m); +typedef void (APIENTRYP PFNGLMULTMATRIXFPROC)(const GLfloat *m); GLAPI PFNGLMULTMATRIXFPROC glad_glMultMatrixf; #define glMultMatrixf glad_glMultMatrixf -typedef void (APIENTRYP PFNGLMULTMATRIXDPROC)(const GLdouble* m); +typedef void (APIENTRYP PFNGLMULTMATRIXDPROC)(const GLdouble *m); GLAPI PFNGLMULTMATRIXDPROC glad_glMultMatrixd; #define glMultMatrixd glad_glMultMatrixd typedef void (APIENTRYP PFNGLORTHOPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); @@ -2304,10 +2326,10 @@ GLAPI int GLAD_GL_VERSION_1_1; typedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); GLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays; #define glDrawArrays glad_glDrawArrays -typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void* indices); +typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices); GLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements; #define glDrawElements glad_glDrawElements -typedef void (APIENTRYP PFNGLGETPOINTERVPROC)(GLenum pname, void** params); +typedef void (APIENTRYP PFNGLGETPOINTERVPROC)(GLenum pname, void **params); GLAPI PFNGLGETPOINTERVPROC glad_glGetPointerv; #define glGetPointerv glad_glGetPointerv typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); @@ -2325,19 +2347,19 @@ GLAPI PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D; typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; #define glCopyTexSubImage2D glad_glCopyTexSubImage2D -typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D; #define glTexSubImage1D glad_glTexSubImage1D -typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; #define glTexSubImage2D glad_glTexSubImage2D typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); GLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture; #define glBindTexture glad_glBindTexture -typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint* textures); +typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures); GLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures; #define glDeleteTextures glad_glDeleteTextures -typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint* textures); +typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures); GLAPI PFNGLGENTEXTURESPROC glad_glGenTextures; #define glGenTextures glad_glGenTextures typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture); @@ -2346,43 +2368,43 @@ GLAPI PFNGLISTEXTUREPROC glad_glIsTexture; typedef void (APIENTRYP PFNGLARRAYELEMENTPROC)(GLint i); GLAPI PFNGLARRAYELEMENTPROC glad_glArrayElement; #define glArrayElement glad_glArrayElement -typedef void (APIENTRYP PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLCOLORPOINTERPROC glad_glColorPointer; #define glColorPointer glad_glColorPointer typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEPROC)(GLenum array); GLAPI PFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState; #define glDisableClientState glad_glDisableClientState -typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void *pointer); GLAPI PFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer; #define glEdgeFlagPointer glad_glEdgeFlagPointer typedef void (APIENTRYP PFNGLENABLECLIENTSTATEPROC)(GLenum array); GLAPI PFNGLENABLECLIENTSTATEPROC glad_glEnableClientState; #define glEnableClientState glad_glEnableClientState -typedef void (APIENTRYP PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLINDEXPOINTERPROC glad_glIndexPointer; #define glIndexPointer glad_glIndexPointer -typedef void (APIENTRYP PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void *pointer); GLAPI PFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays; #define glInterleavedArrays glad_glInterleavedArrays -typedef void (APIENTRYP PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLNORMALPOINTERPROC glad_glNormalPointer; #define glNormalPointer glad_glNormalPointer -typedef void (APIENTRYP PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer; #define glTexCoordPointer glad_glTexCoordPointer -typedef void (APIENTRYP PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLVERTEXPOINTERPROC glad_glVertexPointer; #define glVertexPointer glad_glVertexPointer -typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint* textures, GLboolean* residences); +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint *textures, GLboolean *residences); GLAPI PFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident; #define glAreTexturesResident glad_glAreTexturesResident -typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint* textures, const GLfloat* priorities); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint *textures, const GLfloat *priorities); GLAPI PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures; #define glPrioritizeTextures glad_glPrioritizeTextures typedef void (APIENTRYP PFNGLINDEXUBPROC)(GLubyte c); GLAPI PFNGLINDEXUBPROC glad_glIndexub; #define glIndexub glad_glIndexub -typedef void (APIENTRYP PFNGLINDEXUBVPROC)(const GLubyte* c); +typedef void (APIENTRYP PFNGLINDEXUBVPROC)(const GLubyte *c); GLAPI PFNGLINDEXUBVPROC glad_glIndexubv; #define glIndexubv glad_glIndexubv typedef void (APIENTRYP PFNGLPOPCLIENTATTRIBPROC)(); @@ -2395,13 +2417,13 @@ GLAPI PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib; #ifndef GL_VERSION_1_2 #define GL_VERSION_1_2 1 GLAPI int GLAD_GL_VERSION_1_2; -typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); GLAPI PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; #define glDrawRangeElements glad_glDrawRangeElements -typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXIMAGE3DPROC glad_glTexImage3D; #define glTexImage3D glad_glTexImage3D -typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D; #define glTexSubImage3D glad_glTexSubImage3D typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); @@ -2417,25 +2439,25 @@ GLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture; typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); GLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; #define glSampleCoverage glad_glSampleCoverage -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D; #define glCompressedTexImage3D glad_glCompressedTexImage3D -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; #define glCompressedTexImage2D glad_glCompressedTexImage2D -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void* data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D; #define glCompressedTexImage1D glad_glCompressedTexImage1D -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D; #define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; #define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D; #define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D -typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void* img); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void *img); GLAPI PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage; #define glGetCompressedTexImage glad_glGetCompressedTexImage typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC)(GLenum texture); @@ -2444,109 +2466,109 @@ GLAPI PFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture; typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC)(GLenum target, GLdouble s); GLAPI PFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d; #define glMultiTexCoord1d glad_glMultiTexCoord1d -typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble *v); GLAPI PFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv; #define glMultiTexCoord1dv glad_glMultiTexCoord1dv typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC)(GLenum target, GLfloat s); GLAPI PFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f; #define glMultiTexCoord1f glad_glMultiTexCoord1f -typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat *v); GLAPI PFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv; #define glMultiTexCoord1fv glad_glMultiTexCoord1fv typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC)(GLenum target, GLint s); GLAPI PFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i; #define glMultiTexCoord1i glad_glMultiTexCoord1i -typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint *v); GLAPI PFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv; #define glMultiTexCoord1iv glad_glMultiTexCoord1iv typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC)(GLenum target, GLshort s); GLAPI PFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s; #define glMultiTexCoord1s glad_glMultiTexCoord1s -typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort *v); GLAPI PFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv; #define glMultiTexCoord1sv glad_glMultiTexCoord1sv typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC)(GLenum target, GLdouble s, GLdouble t); GLAPI PFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d; #define glMultiTexCoord2d glad_glMultiTexCoord2d -typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble *v); GLAPI PFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv; #define glMultiTexCoord2dv glad_glMultiTexCoord2dv typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t); GLAPI PFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f; #define glMultiTexCoord2f glad_glMultiTexCoord2f -typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat *v); GLAPI PFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv; #define glMultiTexCoord2fv glad_glMultiTexCoord2fv typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC)(GLenum target, GLint s, GLint t); GLAPI PFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i; #define glMultiTexCoord2i glad_glMultiTexCoord2i -typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint *v); GLAPI PFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv; #define glMultiTexCoord2iv glad_glMultiTexCoord2iv typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC)(GLenum target, GLshort s, GLshort t); GLAPI PFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s; #define glMultiTexCoord2s glad_glMultiTexCoord2s -typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort *v); GLAPI PFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv; #define glMultiTexCoord2sv glad_glMultiTexCoord2sv typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); GLAPI PFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d; #define glMultiTexCoord3d glad_glMultiTexCoord3d -typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble *v); GLAPI PFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv; #define glMultiTexCoord3dv glad_glMultiTexCoord3dv typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); GLAPI PFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f; #define glMultiTexCoord3f glad_glMultiTexCoord3f -typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat *v); GLAPI PFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv; #define glMultiTexCoord3fv glad_glMultiTexCoord3fv typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC)(GLenum target, GLint s, GLint t, GLint r); GLAPI PFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i; #define glMultiTexCoord3i glad_glMultiTexCoord3i -typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint *v); GLAPI PFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv; #define glMultiTexCoord3iv glad_glMultiTexCoord3iv typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC)(GLenum target, GLshort s, GLshort t, GLshort r); GLAPI PFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s; #define glMultiTexCoord3s glad_glMultiTexCoord3s -typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort *v); GLAPI PFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv; #define glMultiTexCoord3sv glad_glMultiTexCoord3sv typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); GLAPI PFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d; #define glMultiTexCoord4d glad_glMultiTexCoord4d -typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble *v); GLAPI PFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv; #define glMultiTexCoord4dv glad_glMultiTexCoord4dv typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); GLAPI PFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f; #define glMultiTexCoord4f glad_glMultiTexCoord4f -typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat *v); GLAPI PFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv; #define glMultiTexCoord4fv glad_glMultiTexCoord4fv typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); GLAPI PFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i; #define glMultiTexCoord4i glad_glMultiTexCoord4i -typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint *v); GLAPI PFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv; #define glMultiTexCoord4iv glad_glMultiTexCoord4iv typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); GLAPI PFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s; #define glMultiTexCoord4s glad_glMultiTexCoord4s -typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort* v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort *v); GLAPI PFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv; #define glMultiTexCoord4sv glad_glMultiTexCoord4sv -typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat* m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat *m); GLAPI PFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf; #define glLoadTransposeMatrixf glad_glLoadTransposeMatrixf -typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble* m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble *m); GLAPI PFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd; #define glLoadTransposeMatrixd glad_glLoadTransposeMatrixd -typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat* m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat *m); GLAPI PFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf; #define glMultTransposeMatrixf glad_glMultTransposeMatrixf -typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble* m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble *m); GLAPI PFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd; #define glMultTransposeMatrixd glad_glMultTransposeMatrixd #endif @@ -2556,136 +2578,136 @@ GLAPI int GLAD_GL_VERSION_1_4; typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); GLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; #define glBlendFuncSeparate glad_glBlendFuncSeparate -typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); GLAPI PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays; #define glMultiDrawArrays glad_glMultiDrawArrays -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei* count, GLenum type, const void** indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); GLAPI PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements; #define glMultiDrawElements glad_glMultiDrawElements typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); GLAPI PFNGLPOINTPARAMETERFPROC glad_glPointParameterf; #define glPointParameterf glad_glPointParameterf -typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat* params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat *params); GLAPI PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv; #define glPointParameterfv glad_glPointParameterfv typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); GLAPI PFNGLPOINTPARAMETERIPROC glad_glPointParameteri; #define glPointParameteri glad_glPointParameteri -typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint* params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint *params); GLAPI PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv; #define glPointParameteriv glad_glPointParameteriv typedef void (APIENTRYP PFNGLFOGCOORDFPROC)(GLfloat coord); GLAPI PFNGLFOGCOORDFPROC glad_glFogCoordf; #define glFogCoordf glad_glFogCoordf -typedef void (APIENTRYP PFNGLFOGCOORDFVPROC)(const GLfloat* coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVPROC)(const GLfloat *coord); GLAPI PFNGLFOGCOORDFVPROC glad_glFogCoordfv; #define glFogCoordfv glad_glFogCoordfv typedef void (APIENTRYP PFNGLFOGCOORDDPROC)(GLdouble coord); GLAPI PFNGLFOGCOORDDPROC glad_glFogCoordd; #define glFogCoordd glad_glFogCoordd -typedef void (APIENTRYP PFNGLFOGCOORDDVPROC)(const GLdouble* coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVPROC)(const GLdouble *coord); GLAPI PFNGLFOGCOORDDVPROC glad_glFogCoorddv; #define glFogCoorddv glad_glFogCoorddv -typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer; #define glFogCoordPointer glad_glFogCoordPointer typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); GLAPI PFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b; #define glSecondaryColor3b glad_glSecondaryColor3b -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte* v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte *v); GLAPI PFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv; #define glSecondaryColor3bv glad_glSecondaryColor3bv typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); GLAPI PFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d; #define glSecondaryColor3d glad_glSecondaryColor3d -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble *v); GLAPI PFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv; #define glSecondaryColor3dv glad_glSecondaryColor3dv typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); GLAPI PFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f; #define glSecondaryColor3f glad_glSecondaryColor3f -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat *v); GLAPI PFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv; #define glSecondaryColor3fv glad_glSecondaryColor3fv typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC)(GLint red, GLint green, GLint blue); GLAPI PFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i; #define glSecondaryColor3i glad_glSecondaryColor3i -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC)(const GLint *v); GLAPI PFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv; #define glSecondaryColor3iv glad_glSecondaryColor3iv typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); GLAPI PFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s; #define glSecondaryColor3s glad_glSecondaryColor3s -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC)(const GLshort *v); GLAPI PFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv; #define glSecondaryColor3sv glad_glSecondaryColor3sv typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); GLAPI PFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub; #define glSecondaryColor3ub glad_glSecondaryColor3ub -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte* v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte *v); GLAPI PFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv; #define glSecondaryColor3ubv glad_glSecondaryColor3ubv typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); GLAPI PFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui; #define glSecondaryColor3ui glad_glSecondaryColor3ui -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint* v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint *v); GLAPI PFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv; #define glSecondaryColor3uiv glad_glSecondaryColor3uiv typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); GLAPI PFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us; #define glSecondaryColor3us glad_glSecondaryColor3us -typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC)(const GLushort* v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC)(const GLushort *v); GLAPI PFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv; #define glSecondaryColor3usv glad_glSecondaryColor3usv -typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer; #define glSecondaryColorPointer glad_glSecondaryColorPointer typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC)(GLdouble x, GLdouble y); GLAPI PFNGLWINDOWPOS2DPROC glad_glWindowPos2d; #define glWindowPos2d glad_glWindowPos2d -typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC)(const GLdouble *v); GLAPI PFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv; #define glWindowPos2dv glad_glWindowPos2dv typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC)(GLfloat x, GLfloat y); GLAPI PFNGLWINDOWPOS2FPROC glad_glWindowPos2f; #define glWindowPos2f glad_glWindowPos2f -typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC)(const GLfloat *v); GLAPI PFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv; #define glWindowPos2fv glad_glWindowPos2fv typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC)(GLint x, GLint y); GLAPI PFNGLWINDOWPOS2IPROC glad_glWindowPos2i; #define glWindowPos2i glad_glWindowPos2i -typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC)(const GLint *v); GLAPI PFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv; #define glWindowPos2iv glad_glWindowPos2iv typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC)(GLshort x, GLshort y); GLAPI PFNGLWINDOWPOS2SPROC glad_glWindowPos2s; #define glWindowPos2s glad_glWindowPos2s -typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC)(const GLshort *v); GLAPI PFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv; #define glWindowPos2sv glad_glWindowPos2sv typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); GLAPI PFNGLWINDOWPOS3DPROC glad_glWindowPos3d; #define glWindowPos3d glad_glWindowPos3d -typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC)(const GLdouble* v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC)(const GLdouble *v); GLAPI PFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv; #define glWindowPos3dv glad_glWindowPos3dv typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); GLAPI PFNGLWINDOWPOS3FPROC glad_glWindowPos3f; #define glWindowPos3f glad_glWindowPos3f -typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC)(const GLfloat* v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC)(const GLfloat *v); GLAPI PFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv; #define glWindowPos3fv glad_glWindowPos3fv typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC)(GLint x, GLint y, GLint z); GLAPI PFNGLWINDOWPOS3IPROC glad_glWindowPos3i; #define glWindowPos3i glad_glWindowPos3i -typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC)(const GLint* v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC)(const GLint *v); GLAPI PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv; #define glWindowPos3iv glad_glWindowPos3iv typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC)(GLshort x, GLshort y, GLshort z); GLAPI PFNGLWINDOWPOS3SPROC glad_glWindowPos3s; #define glWindowPos3s glad_glWindowPos3s -typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC)(const GLshort* v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC)(const GLshort *v); GLAPI PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv; #define glWindowPos3sv glad_glWindowPos3sv typedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); @@ -2698,10 +2720,10 @@ GLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation; #ifndef GL_VERSION_1_5 #define GL_VERSION_1_5 1 GLAPI int GLAD_GL_VERSION_1_5; -typedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei n, GLuint* ids); +typedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei n, GLuint *ids); GLAPI PFNGLGENQUERIESPROC glad_glGenQueries; #define glGenQueries glad_glGenQueries -typedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint* ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint *ids); GLAPI PFNGLDELETEQUERIESPROC glad_glDeleteQueries; #define glDeleteQueries glad_glDeleteQueries typedef GLboolean (APIENTRYP PFNGLISQUERYPROC)(GLuint id); @@ -2713,46 +2735,46 @@ GLAPI PFNGLBEGINQUERYPROC glad_glBeginQuery; typedef void (APIENTRYP PFNGLENDQUERYPROC)(GLenum target); GLAPI PFNGLENDQUERYPROC glad_glEndQuery; #define glEndQuery glad_glEndQuery -typedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETQUERYIVPROC glad_glGetQueryiv; #define glGetQueryiv glad_glGetQueryiv -typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint *params); GLAPI PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv; #define glGetQueryObjectiv glad_glGetQueryObjectiv -typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint* params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint *params); GLAPI PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv; #define glGetQueryObjectuiv glad_glGetQueryObjectuiv typedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); GLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer; #define glBindBuffer glad_glBindBuffer -typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint* buffers); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers); GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; #define glDeleteBuffers glad_glDeleteBuffers -typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint* buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers); GLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers; #define glGenBuffers glad_glGenBuffers typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer); GLAPI PFNGLISBUFFERPROC glad_glIsBuffer; #define glIsBuffer glad_glIsBuffer -typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void* data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage); GLAPI PFNGLBUFFERDATAPROC glad_glBufferData; #define glBufferData glad_glBufferData -typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void* data); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data); GLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; #define glBufferSubData glad_glBufferSubData -typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void* data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void *data); GLAPI PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData; #define glGetBufferSubData glad_glGetBufferSubData -typedef void* (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); +typedef void * (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); GLAPI PFNGLMAPBUFFERPROC glad_glMapBuffer; #define glMapBuffer glad_glMapBuffer typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum target); GLAPI PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer; #define glUnmapBuffer glad_glUnmapBuffer -typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; #define glGetBufferParameteriv glad_glGetBufferParameteriv -typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void** params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void **params); GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; #define glGetBufferPointerv glad_glGetBufferPointerv #endif @@ -2762,7 +2784,7 @@ GLAPI int GLAD_GL_VERSION_2_0; typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); GLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; #define glBlendEquationSeparate glad_glBlendEquationSeparate -typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum* bufs); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum *bufs); GLAPI PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; #define glDrawBuffers glad_glDrawBuffers typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); @@ -2777,7 +2799,7 @@ GLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; typedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); GLAPI PFNGLATTACHSHADERPROC glad_glAttachShader; #define glAttachShader glad_glAttachShader -typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar* name); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name); GLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; #define glBindAttribLocation glad_glBindAttribLocation typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader); @@ -2804,52 +2826,52 @@ GLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); GLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; #define glEnableVertexAttribArray glad_glEnableVertexAttribArray -typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type, GLchar* name); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); GLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; #define glGetActiveAttrib glad_glGetActiveAttrib -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLint* size, GLenum* type, GLchar* name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); GLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; #define glGetActiveUniform glad_glGetActiveUniform -typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); GLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; #define glGetAttachedShaders glad_glGetAttachedShaders -typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar* name); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; #define glGetAttribLocation glad_glGetAttribLocation -typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params); GLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; #define glGetProgramiv glad_glGetProgramiv -typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); GLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; #define glGetProgramInfoLog glad_glGetProgramInfoLog -typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params); GLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv; #define glGetShaderiv glad_glGetShaderiv -typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); GLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; #define glGetShaderInfoLog glad_glGetShaderInfoLog -typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* source); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); GLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; #define glGetShaderSource glad_glGetShaderSource -typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar* name); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; #define glGetUniformLocation glad_glGetUniformLocation -typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat* params); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params); GLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; #define glGetUniformfv glad_glGetUniformfv -typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint* params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params); GLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; #define glGetUniformiv glad_glGetUniformiv -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble* params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble *params); GLAPI PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv; #define glGetVertexAttribdv glad_glGetVertexAttribdv -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat* params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params); GLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; #define glGetVertexAttribfv glad_glGetVertexAttribfv -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params); GLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; #define glGetVertexAttribiv glad_glGetVertexAttribiv -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void** pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer); GLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; #define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program); @@ -2861,7 +2883,7 @@ GLAPI PFNGLISSHADERPROC glad_glIsShader; typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program); GLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram; #define glLinkProgram glad_glLinkProgram -typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); GLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource; #define glShaderSource glad_glShaderSource typedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program); @@ -2891,37 +2913,37 @@ GLAPI PFNGLUNIFORM3IPROC glad_glUniform3i; typedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); GLAPI PFNGLUNIFORM4IPROC glad_glUniform4i; #define glUniform4i glad_glUniform4i -typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv; #define glUniform1fv glad_glUniform1fv -typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv; #define glUniform2fv glad_glUniform2fv -typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv; #define glUniform3fv glad_glUniform3fv -typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv; #define glUniform4fv glad_glUniform4fv -typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint* value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv; #define glUniform1iv glad_glUniform1iv -typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint* value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv; #define glUniform2iv glad_glUniform2iv -typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint* value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv; #define glUniform3iv glad_glUniform3iv -typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint* value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv; #define glUniform4iv glad_glUniform4iv -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; #define glUniformMatrix2fv glad_glUniformMatrix2fv -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; #define glUniformMatrix3fv glad_glUniformMatrix3fv -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; #define glUniformMatrix4fv glad_glUniformMatrix4fv typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program); @@ -2930,134 +2952,134 @@ GLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); GLAPI PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d; #define glVertexAttrib1d glad_glVertexAttrib1d -typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv; #define glVertexAttrib1dv glad_glVertexAttrib1dv typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); GLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; #define glVertexAttrib1f glad_glVertexAttrib1f -typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; #define glVertexAttrib1fv glad_glVertexAttrib1fv typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); GLAPI PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s; #define glVertexAttrib1s glad_glVertexAttrib1s -typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv; #define glVertexAttrib1sv glad_glVertexAttrib1sv typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); GLAPI PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d; #define glVertexAttrib2d glad_glVertexAttrib2d -typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv; #define glVertexAttrib2dv glad_glVertexAttrib2dv typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); GLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; #define glVertexAttrib2f glad_glVertexAttrib2f -typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; #define glVertexAttrib2fv glad_glVertexAttrib2fv typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); GLAPI PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s; #define glVertexAttrib2s glad_glVertexAttrib2s -typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv; #define glVertexAttrib2sv glad_glVertexAttrib2sv typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); GLAPI PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d; #define glVertexAttrib3d glad_glVertexAttrib3d -typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv; #define glVertexAttrib3dv glad_glVertexAttrib3dv typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); GLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; #define glVertexAttrib3f glad_glVertexAttrib3f -typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; #define glVertexAttrib3fv glad_glVertexAttrib3fv typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); GLAPI PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s; #define glVertexAttrib3s glad_glVertexAttrib3s -typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv; #define glVertexAttrib3sv glad_glVertexAttrib3sv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte *v); GLAPI PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv; #define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv; #define glVertexAttrib4Niv glad_glVertexAttrib4Niv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv; #define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); GLAPI PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub; #define glVertexAttrib4Nub glad_glVertexAttrib4Nub -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte *v); GLAPI PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv; #define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv; #define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort *v); GLAPI PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv; #define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte *v); GLAPI PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv; #define glVertexAttrib4bv glad_glVertexAttrib4bv typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d; #define glVertexAttrib4d glad_glVertexAttrib4d -typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv; #define glVertexAttrib4dv glad_glVertexAttrib4dv typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); GLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; #define glVertexAttrib4f glad_glVertexAttrib4f -typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; #define glVertexAttrib4fv glad_glVertexAttrib4fv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv; #define glVertexAttrib4iv glad_glVertexAttrib4iv typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); GLAPI PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s; #define glVertexAttrib4s glad_glVertexAttrib4s -typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv; #define glVertexAttrib4sv glad_glVertexAttrib4sv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte *v); GLAPI PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv; #define glVertexAttrib4ubv glad_glVertexAttrib4ubv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv; #define glVertexAttrib4uiv glad_glVertexAttrib4uiv -typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort *v); GLAPI PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv; #define glVertexAttrib4usv glad_glVertexAttrib4usv -typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); GLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; #define glVertexAttribPointer glad_glVertexAttribPointer #endif #ifndef GL_VERSION_2_1 #define GL_VERSION_2_1 1 GLAPI int GLAD_GL_VERSION_2_1; -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv; #define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv; #define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv -typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv; #define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv; #define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv -typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv; #define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv -typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv; #define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv #endif @@ -3067,10 +3089,10 @@ GLAPI int GLAD_GL_VERSION_3_0; typedef void (APIENTRYP PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); GLAPI PFNGLCOLORMASKIPROC glad_glColorMaski; #define glColorMaski glad_glColorMaski -typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean* data); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean *data); GLAPI PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v; #define glGetBooleani_v glad_glGetBooleani_v -typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint* data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint *data); GLAPI PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v; #define glGetIntegeri_v glad_glGetIntegeri_v typedef void (APIENTRYP PFNGLENABLEIPROC)(GLenum target, GLuint index); @@ -3094,10 +3116,10 @@ GLAPI PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange; typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); GLAPI PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase; #define glBindBufferBase glad_glBindBufferBase -typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar** varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); GLAPI PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings; #define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings -typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLsizei* size, GLenum* type, GLchar* name); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); GLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying; #define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying typedef void (APIENTRYP PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); @@ -3109,13 +3131,13 @@ GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender; typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)(); GLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender; #define glEndConditionalRender glad_glEndConditionalRender -typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer; #define glVertexAttribIPointer glad_glVertexAttribIPointer -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint *params); GLAPI PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv; #define glGetVertexAttribIiv glad_glGetVertexAttribIiv -typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint* params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint *params); GLAPI PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv; #define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); @@ -3142,49 +3164,49 @@ GLAPI PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui; typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); GLAPI PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui; #define glVertexAttribI4ui glad_glVertexAttribI4ui -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv; #define glVertexAttribI1iv glad_glVertexAttribI1iv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv; #define glVertexAttribI2iv glad_glVertexAttribI2iv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv; #define glVertexAttribI3iv glad_glVertexAttribI3iv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv; #define glVertexAttribI4iv glad_glVertexAttribI4iv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv; #define glVertexAttribI1uiv glad_glVertexAttribI1uiv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv; #define glVertexAttribI2uiv glad_glVertexAttribI2uiv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv; #define glVertexAttribI3uiv glad_glVertexAttribI3uiv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv; #define glVertexAttribI4uiv glad_glVertexAttribI4uiv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte *v); GLAPI PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv; #define glVertexAttribI4bv glad_glVertexAttribI4bv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv; #define glVertexAttribI4sv glad_glVertexAttribI4sv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte *v); GLAPI PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv; #define glVertexAttribI4ubv glad_glVertexAttribI4ubv -typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort* v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort *v); GLAPI PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv; #define glVertexAttribI4usv glad_glVertexAttribI4usv -typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint* params); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint *params); GLAPI PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv; #define glGetUniformuiv glad_glGetUniformuiv -typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar* name); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar *name); GLAPI PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation; #define glBindFragDataLocation glad_glBindFragDataLocation -typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar* name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation; #define glGetFragDataLocation glad_glGetFragDataLocation typedef void (APIENTRYP PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); @@ -3199,43 +3221,43 @@ GLAPI PFNGLUNIFORM3UIPROC glad_glUniform3ui; typedef void (APIENTRYP PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); GLAPI PFNGLUNIFORM4UIPROC glad_glUniform4ui; #define glUniform4ui glad_glUniform4ui -typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint* value); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM1UIVPROC glad_glUniform1uiv; #define glUniform1uiv glad_glUniform1uiv -typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint* value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM2UIVPROC glad_glUniform2uiv; #define glUniform2uiv glad_glUniform2uiv -typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint* value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM3UIVPROC glad_glUniform3uiv; #define glUniform3uiv glad_glUniform3uiv -typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint* value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM4UIVPROC glad_glUniform4uiv; #define glUniform4uiv glad_glUniform4uiv -typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint* params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint *params); GLAPI PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv; #define glTexParameterIiv glad_glTexParameterIiv -typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint* params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint *params); GLAPI PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv; #define glTexParameterIuiv glad_glTexParameterIuiv -typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv; #define glGetTexParameterIiv glad_glGetTexParameterIiv -typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint* params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint *params); GLAPI PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv; #define glGetTexParameterIuiv glad_glGetTexParameterIuiv -typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint* value); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint *value); GLAPI PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv; #define glClearBufferiv glad_glClearBufferiv -typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint* value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint *value); GLAPI PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv; #define glClearBufferuiv glad_glClearBufferuiv -typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat* value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat *value); GLAPI PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv; #define glClearBufferfv glad_glClearBufferfv typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); GLAPI PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi; #define glClearBufferfi glad_glClearBufferfi -typedef const GLubyte* (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); GLAPI PFNGLGETSTRINGIPROC glad_glGetStringi; #define glGetStringi glad_glGetStringi typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); @@ -3244,16 +3266,16 @@ GLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); GLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; #define glBindRenderbuffer glad_glBindRenderbuffer -typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint* renderbuffers); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers); GLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; #define glDeleteRenderbuffers glad_glDeleteRenderbuffers -typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint* renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers); GLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; #define glGenRenderbuffers glad_glGenRenderbuffers typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); GLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; #define glRenderbufferStorage glad_glRenderbufferStorage -typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; #define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); @@ -3262,10 +3284,10 @@ GLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); GLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; #define glBindFramebuffer glad_glBindFramebuffer -typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint* framebuffers); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers); GLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; #define glDeleteFramebuffers glad_glDeleteFramebuffers -typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint* framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers); GLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; #define glGenFramebuffers glad_glGenFramebuffers typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); @@ -3283,7 +3305,7 @@ GLAPI PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D; typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); GLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; #define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer -typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params); GLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; #define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target); @@ -3298,7 +3320,7 @@ GLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisam typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); GLAPI PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer; #define glFramebufferTextureLayer glad_glFramebufferTextureLayer -typedef void* (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void * (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); GLAPI PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange; #define glMapBufferRange glad_glMapBufferRange typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); @@ -3307,10 +3329,10 @@ GLAPI PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange; typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array); GLAPI PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray; #define glBindVertexArray glad_glBindVertexArray -typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint* arrays); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint *arrays); GLAPI PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays; #define glDeleteVertexArrays glad_glDeleteVertexArrays -typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint* arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays); GLAPI PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays; #define glGenVertexArrays glad_glGenVertexArrays typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC)(GLuint array); @@ -3323,7 +3345,7 @@ GLAPI int GLAD_GL_VERSION_3_1; typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); GLAPI PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced; #define glDrawArraysInstanced glad_glDrawArraysInstanced -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); GLAPI PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced; #define glDrawElementsInstanced glad_glDrawElementsInstanced typedef void (APIENTRYP PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); @@ -3335,22 +3357,22 @@ GLAPI PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex; typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); GLAPI PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData; #define glCopyBufferSubData glad_glCopyBufferSubData -typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar** uniformNames, GLuint* uniformIndices); +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); GLAPI PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices; #define glGetUniformIndices glad_glGetUniformIndices -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); GLAPI PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv; #define glGetActiveUniformsiv glad_glGetActiveUniformsiv -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); GLAPI PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName; #define glGetActiveUniformName glad_glGetActiveUniformName -typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar* uniformBlockName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar *uniformBlockName); GLAPI PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex; #define glGetUniformBlockIndex glad_glGetUniformBlockIndex -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); GLAPI PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv; #define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv -typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); GLAPI PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName; #define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); @@ -3360,16 +3382,16 @@ GLAPI PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding; #ifndef GL_VERSION_3_2 #define GL_VERSION_3_2 1 GLAPI int GLAD_GL_VERSION_3_2; -typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); GLAPI PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex; #define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex -typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); GLAPI PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex; #define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); GLAPI PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex; #define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei* count, GLenum type, const void** indices, GLsizei drawcount, const GLint* basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); GLAPI PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex; #define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC)(GLenum mode); @@ -3390,16 +3412,16 @@ GLAPI PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync; typedef void (APIENTRYP PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); GLAPI PFNGLWAITSYNCPROC glad_glWaitSync; #define glWaitSync glad_glWaitSync -typedef void (APIENTRYP PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64* data); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 *data); GLAPI PFNGLGETINTEGER64VPROC glad_glGetInteger64v; #define glGetInteger64v glad_glGetInteger64v -typedef void (APIENTRYP PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); GLAPI PFNGLGETSYNCIVPROC glad_glGetSynciv; #define glGetSynciv glad_glGetSynciv -typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64* data); +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 *data); GLAPI PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v; #define glGetInteger64i_v glad_glGetInteger64i_v -typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64* params); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 *params); GLAPI PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v; #define glGetBufferParameteri64v glad_glGetBufferParameteri64v typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); @@ -3411,16 +3433,13 @@ GLAPI PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample; typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); GLAPI PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample; #define glTexImage3DMultisample glad_glTexImage3DMultisample -typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat* val); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat *val); GLAPI PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv; #define glGetMultisamplefv glad_glGetMultisamplefv typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); GLAPI PFNGLSAMPLEMASKIPROC glad_glSampleMaski; #define glSampleMaski glad_glSampleMaski #endif -#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 -#define GL_SINGLE_COLOR_EXT 0x81F9 -#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA #define GL_MULTISAMPLE_ARB 0x809D #define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E #define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F @@ -3437,10 +3456,84 @@ GLAPI PFNGLSAMPLEMASKIPROC glad_glSampleMaski; #define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 #define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 #define GL_NO_RESET_NOTIFICATION_ARB 0x8261 -#ifndef GL_EXT_separate_specular_color -#define GL_EXT_separate_specular_color 1 -GLAPI int GLAD_GL_EXT_separate_specular_color; -#endif +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245 +#define GL_DEBUG_SOURCE_API_KHR 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A +#define GL_DEBUG_SOURCE_OTHER_KHR 0x824B +#define GL_DEBUG_TYPE_ERROR_KHR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250 +#define GL_DEBUG_TYPE_OTHER_KHR 0x8251 +#define GL_DEBUG_TYPE_MARKER_KHR 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D +#define GL_BUFFER_KHR 0x82E0 +#define GL_SHADER_KHR 0x82E1 +#define GL_PROGRAM_KHR 0x82E2 +#define GL_VERTEX_ARRAY_KHR 0x8074 +#define GL_QUERY_KHR 0x82E3 +#define GL_PROGRAM_PIPELINE_KHR 0x82E4 +#define GL_SAMPLER_KHR 0x82E6 +#define GL_MAX_LABEL_LENGTH_KHR 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147 +#define GL_DEBUG_SEVERITY_LOW_KHR 0x9148 +#define GL_DEBUG_OUTPUT_KHR 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002 +#define GL_STACK_OVERFLOW_KHR 0x0503 +#define GL_STACK_UNDERFLOW_KHR 0x0504 +#define GL_DISPLAY_LIST 0x82E7 #ifndef GL_ARB_multisample #define GL_ARB_multisample 1 GLAPI int GLAD_GL_ARB_multisample; @@ -3454,64 +3547,131 @@ GLAPI int GLAD_GL_ARB_robustness; typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC)(); GLAPI PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB; #define glGetGraphicsResetStatusARB glad_glGetGraphicsResetStatusARB -typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* img); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); GLAPI PFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB; #define glGetnTexImageARB glad_glGetnTexImageARB -typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void* data); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); GLAPI PFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB; #define glReadnPixelsARB glad_glReadnPixelsARB -typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint lod, GLsizei bufSize, void* img); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint lod, GLsizei bufSize, void *img); GLAPI PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_glGetnCompressedTexImageARB; #define glGetnCompressedTexImageARB glad_glGetnCompressedTexImageARB -typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat* params); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params); GLAPI PFNGLGETNUNIFORMFVARBPROC glad_glGetnUniformfvARB; #define glGetnUniformfvARB glad_glGetnUniformfvARB -typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint* params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint *params); GLAPI PFNGLGETNUNIFORMIVARBPROC glad_glGetnUniformivARB; #define glGetnUniformivARB glad_glGetnUniformivARB -typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint* params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint *params); GLAPI PFNGLGETNUNIFORMUIVARBPROC glad_glGetnUniformuivARB; #define glGetnUniformuivARB glad_glGetnUniformuivARB -typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble* params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble *params); GLAPI PFNGLGETNUNIFORMDVARBPROC glad_glGetnUniformdvARB; #define glGetnUniformdvARB glad_glGetnUniformdvARB -typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble* v); +typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); GLAPI PFNGLGETNMAPDVARBPROC glad_glGetnMapdvARB; #define glGetnMapdvARB glad_glGetnMapdvARB -typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat* v); +typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); GLAPI PFNGLGETNMAPFVARBPROC glad_glGetnMapfvARB; #define glGetnMapfvARB glad_glGetnMapfvARB -typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint* v); +typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint *v); GLAPI PFNGLGETNMAPIVARBPROC glad_glGetnMapivARB; #define glGetnMapivARB glad_glGetnMapivARB -typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC)(GLenum map, GLsizei bufSize, GLfloat* values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC)(GLenum map, GLsizei bufSize, GLfloat *values); GLAPI PFNGLGETNPIXELMAPFVARBPROC glad_glGetnPixelMapfvARB; #define glGetnPixelMapfvARB glad_glGetnPixelMapfvARB -typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC)(GLenum map, GLsizei bufSize, GLuint* values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC)(GLenum map, GLsizei bufSize, GLuint *values); GLAPI PFNGLGETNPIXELMAPUIVARBPROC glad_glGetnPixelMapuivARB; #define glGetnPixelMapuivARB glad_glGetnPixelMapuivARB -typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC)(GLenum map, GLsizei bufSize, GLushort* values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC)(GLenum map, GLsizei bufSize, GLushort *values); GLAPI PFNGLGETNPIXELMAPUSVARBPROC glad_glGetnPixelMapusvARB; #define glGetnPixelMapusvARB glad_glGetnPixelMapusvARB -typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC)(GLsizei bufSize, GLubyte* pattern); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC)(GLsizei bufSize, GLubyte *pattern); GLAPI PFNGLGETNPOLYGONSTIPPLEARBPROC glad_glGetnPolygonStippleARB; #define glGetnPolygonStippleARB glad_glGetnPolygonStippleARB -typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void* table); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); GLAPI PFNGLGETNCOLORTABLEARBPROC glad_glGetnColorTableARB; #define glGetnColorTableARB glad_glGetnColorTableARB -typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void* image); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); GLAPI PFNGLGETNCONVOLUTIONFILTERARBPROC glad_glGetnConvolutionFilterARB; #define glGetnConvolutionFilterARB glad_glGetnConvolutionFilterARB -typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void* row, GLsizei columnBufSize, void* column, void* span); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); GLAPI PFNGLGETNSEPARABLEFILTERARBPROC glad_glGetnSeparableFilterARB; #define glGetnSeparableFilterARB glad_glGetnSeparableFilterARB -typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void* values); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); GLAPI PFNGLGETNHISTOGRAMARBPROC glad_glGetnHistogramARB; #define glGetnHistogramARB glad_glGetnHistogramARB -typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void* values); +typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); GLAPI PFNGLGETNMINMAXARBPROC glad_glGetnMinmaxARB; #define glGetnMinmaxARB glad_glGetnMinmaxARB #endif +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +GLAPI int GLAD_GL_KHR_debug; +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl; +#define glDebugMessageControl glad_glDebugMessageControl +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert; +#define glDebugMessageInsert glad_glDebugMessageInsert +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void *userParam); +GLAPI PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback; +#define glDebugMessageCallback glad_glDebugMessageCallback +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog; +#define glGetDebugMessageLog glad_glGetDebugMessageLog +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup; +#define glPushDebugGroup glad_glPushDebugGroup +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(); +GLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup; +#define glPopDebugGroup glad_glPopDebugGroup +typedef void (APIENTRYP PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTLABELPROC glad_glObjectLabel; +#define glObjectLabel glad_glObjectLabel +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel; +#define glGetObjectLabel glad_glGetObjectLabel +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC)(const void *ptr, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel; +#define glObjectPtrLabel glad_glObjectPtrLabel +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel; +#define glGetObjectPtrLabel glad_glGetObjectPtrLabel +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR; +#define glDebugMessageControlKHR glad_glDebugMessageControlKHR +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR; +#define glDebugMessageInsertKHR glad_glDebugMessageInsertKHR +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR callback, const void *userParam); +GLAPI PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR; +#define glDebugMessageCallbackKHR glad_glDebugMessageCallbackKHR +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR; +#define glGetDebugMessageLogKHR glad_glGetDebugMessageLogKHR +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR; +#define glPushDebugGroupKHR glad_glPushDebugGroupKHR +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)(); +GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR; +#define glPopDebugGroupKHR glad_glPopDebugGroupKHR +typedef void (APIENTRYP PFNGLOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR; +#define glObjectLabelKHR glad_glObjectLabelKHR +typedef void (APIENTRYP PFNGLGETOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR; +#define glGetObjectLabelKHR glad_glGetObjectLabelKHR +typedef void (APIENTRYP PFNGLOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR; +#define glObjectPtrLabelKHR glad_glObjectPtrLabelKHR +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR; +#define glGetObjectPtrLabelKHR glad_glGetObjectPtrLabelKHR +typedef void (APIENTRYP PFNGLGETPOINTERVKHRPROC)(GLenum pname, void **params); +GLAPI PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR; +#define glGetPointervKHR glad_glGetPointervKHR +#endif #ifdef __cplusplus } diff --git a/raylib/external/glfw/deps/linmath.h b/raylib/external/glfw/deps/linmath.h index 5732a76..9c2e2a0 100644 --- a/raylib/external/glfw/deps/linmath.h +++ b/raylib/external/glfw/deps/linmath.h @@ -192,7 +192,7 @@ static inline void mat4x4_rotate(mat4x4 R, mat4x4 M, float x, float y, float z, vec3 u = {x, y, z}; if(vec3_len(u) > 1e-4) { - mat4x4 T, C, S; + mat4x4 T, C, S = {{0}}; vec3_norm(u, u); mat4x4_from_vec3_mul_outer(T, u, u); diff --git a/raylib/external/glfw/deps/nuklear.h b/raylib/external/glfw/deps/nuklear.h new file mode 100644 index 0000000..333acee --- /dev/null +++ b/raylib/external/glfw/deps/nuklear.h @@ -0,0 +1,23590 @@ +/* + Nuklear - 1.40.0 - public domain + no warrenty implied; use at your own risk. + authored from 2015-2017 by Micha Mettke + +ABOUT: + This is a minimal state graphical user interface single header toolkit + written in ANSI C and licensed under public domain. + It was designed as a simple embeddable user interface for application and does + not have any dependencies, a default renderbackend or OS window and input handling + but instead provides a very modular library approach by using simple input state + for input and draw commands describing primitive shapes as output. + So instead of providing a layered library that tries to abstract over a number + of platform and render backends it only focuses on the actual UI. + +VALUES: + - Graphical user interface toolkit + - Single header library + - Written in C89 (a.k.a. ANSI C or ISO C90) + - Small codebase (~17kLOC) + - Focus on portability, efficiency and simplicity + - No dependencies (not even the standard library if not wanted) + - Fully skinnable and customizable + - Low memory footprint with total memory control if needed or wanted + - UTF-8 support + - No global or hidden state + - Customizable library modules (you can compile and use only what you need) + - Optional font baker and vertex buffer output + +USAGE: + This library is self contained in one single header file and can be used either + in header only mode or in implementation mode. The header only mode is used + by default when included and allows including this header in other headers + and does not contain the actual implementation. + + The implementation mode requires to define the preprocessor macro + NK_IMPLEMENTATION in *one* .c/.cpp file before #includeing this file, e.g.: + + #define NK_IMPLEMENTATION + #include "nuklear.h" + + Also optionally define the symbols listed in the section "OPTIONAL DEFINES" + below in header and implementation mode if you want to use additional functionality + or need more control over the library. + IMPORTANT: Every time you include "nuklear.h" you have to define the same flags. + This is very important not doing it either leads to compiler errors + or even worse stack corruptions. + +FEATURES: + - Absolutely no platform dependend code + - Memory management control ranging from/to + - Ease of use by allocating everything from standard library + - Control every byte of memory inside the library + - Font handling control ranging from/to + - Use your own font implementation for everything + - Use this libraries internal font baking and handling API + - Drawing output control ranging from/to + - Simple shapes for more high level APIs which already have drawing capabilities + - Hardware accessible anti-aliased vertex buffer output + - Customizable colors and properties ranging from/to + - Simple changes to color by filling a simple color table + - Complete control with ability to use skinning to decorate widgets + - Bendable UI library with widget ranging from/to + - Basic widgets like buttons, checkboxes, slider, ... + - Advanced widget like abstract comboboxes, contextual menus,... + - Compile time configuration to only compile what you need + - Subset which can be used if you do not want to link or use the standard library + - Can be easily modified to only update on user input instead of frame updates + +OPTIONAL DEFINES: + NK_PRIVATE + If defined declares all functions as static, so they can only be accessed + inside the file that contains the implementation + + NK_INCLUDE_FIXED_TYPES + If defined it will include header for fixed sized types + otherwise nuklear tries to select the correct type. If that fails it will + throw a compiler error and you have to select the correct types yourself. + If used needs to be defined for implementation and header + + NK_INCLUDE_DEFAULT_ALLOCATOR + if defined it will include header and provide additional functions + to use this library without caring for memory allocation control and therefore + ease memory management. + Adds the standard library with malloc and free so don't define if you + don't want to link to the standard library + If used needs to be defined for implementation and header + + NK_INCLUDE_STANDARD_IO + if defined it will include header and provide + additional functions depending on file loading. + Adds the standard library with fopen, fclose,... so don't define this + if you don't want to link to the standard library + If used needs to be defined for implementation and header + + NK_INCLUDE_STANDARD_VARARGS + if defined it will include header and provide + additional functions depending on variable arguments + Adds the standard library with va_list and so don't define this if + you don't want to link to the standard library + If used needs to be defined for implementation and header + + NK_INCLUDE_VERTEX_BUFFER_OUTPUT + Defining this adds a vertex draw command list backend to this + library, which allows you to convert queue commands into vertex draw commands. + This is mainly if you need a hardware accessible format for OpenGL, DirectX, + Vulkan, Metal,... + If used needs to be defined for implementation and header + + NK_INCLUDE_FONT_BAKING + Defining this adds `stb_truetype` and `stb_rect_pack` implementation + to this library and provides font baking and rendering. + If you already have font handling or do not want to use this font handler + you don't have to define it. + If used needs to be defined for implementation and header + + NK_INCLUDE_DEFAULT_FONT + Defining this adds the default font: ProggyClean.ttf into this library + which can be loaded into a font atlas and allows using this library without + having a truetype font + Enabling this adds ~12kb to global stack memory + If used needs to be defined for implementation and header + + NK_INCLUDE_COMMAND_USERDATA + Defining this adds a userdata pointer into each command. Can be useful for + example if you want to provide custom shaders depending on the used widget. + Can be combined with the style structures. + If used needs to be defined for implementation and header + + NK_BUTTON_TRIGGER_ON_RELEASE + Different platforms require button clicks occuring either on buttons being + pressed (up to down) or released (down to up). + By default this library will react on buttons being pressed, but if you + define this it will only trigger if a button is released. + If used it is only required to be defined for the implementation part + + NK_ZERO_COMMAND_MEMORY + Defining this will zero out memory for each drawing command added to a + drawing queue (inside nk_command_buffer_push). Zeroing command memory + is very useful for fast checking (using memcmp) if command buffers are + equal and avoid drawing frames when nothing on screen has changed since + previous frame. + + NK_ASSERT + If you don't define this, nuklear will use with assert(). + Adds the standard library so define to nothing of not wanted + If used needs to be defined for implementation and header + + NK_BUFFER_DEFAULT_INITIAL_SIZE + Initial buffer size allocated by all buffers while using the default allocator + functions included by defining NK_INCLUDE_DEFAULT_ALLOCATOR. If you don't + want to allocate the default 4k memory then redefine it. + If used needs to be defined for implementation and header + + NK_MAX_NUMBER_BUFFER + Maximum buffer size for the conversion buffer between float and string + Under normal circumstances this should be more than sufficient. + If used needs to be defined for implementation and header + + NK_INPUT_MAX + Defines the max number of bytes which can be added as text input in one frame. + Under normal circumstances this should be more than sufficient. + If used it is only required to be defined for the implementation part + + NK_MEMSET + You can define this to 'memset' or your own memset implementation + replacement. If not nuklear will use its own version. + If used it is only required to be defined for the implementation part + + NK_MEMCPY + You can define this to 'memcpy' or your own memcpy implementation + replacement. If not nuklear will use its own version. + If used it is only required to be defined for the implementation part + + NK_SQRT + You can define this to 'sqrt' or your own sqrt implementation + replacement. If not nuklear will use its own slow and not highly + accurate version. + If used it is only required to be defined for the implementation part + + NK_SIN + You can define this to 'sinf' or your own sine implementation + replacement. If not nuklear will use its own approximation implementation. + If used it is only required to be defined for the implementation part + + NK_COS + You can define this to 'cosf' or your own cosine implementation + replacement. If not nuklear will use its own approximation implementation. + If used it is only required to be defined for the implementation part + + NK_STRTOD + You can define this to `strtod` or your own string to double conversion + implementation replacement. If not defined nuklear will use its own + imprecise and possibly unsafe version (does not handle nan or infinity!). + If used it is only required to be defined for the implementation part + + NK_DTOA + You can define this to `dtoa` or your own double to string conversion + implementation replacement. If not defined nuklear will use its own + imprecise and possibly unsafe version (does not handle nan or infinity!). + If used it is only required to be defined for the implementation part + + NK_VSNPRINTF + If you define `NK_INCLUDE_STANDARD_VARARGS` as well as `NK_INCLUDE_STANDARD_IO` + and want to be safe define this to `vsnprintf` on compilers supporting + later versions of C or C++. By default nuklear will check for your stdlib version + in C as well as compiler version in C++. if `vsnprintf` is available + it will define it to `vsnprintf` directly. If not defined and if you have + older versions of C or C++ it will be defined to `vsprintf` which is unsafe. + If used it is only required to be defined for the implementation part + + NK_BYTE + NK_INT16 + NK_UINT16 + NK_INT32 + NK_UINT32 + NK_SIZE_TYPE + NK_POINTER_TYPE + If you compile without NK_USE_FIXED_TYPE then a number of standard types + will be selected and compile time validated. If they are incorrect you can + define the correct types by overloading these type defines. + +CREDITS: + Developed by Micha Mettke and every direct or indirect contributor. + + Embeds stb_texedit, stb_truetype and stb_rectpack by Sean Barret (public domain) + Embeds ProggyClean.ttf font by Tristan Grimmer (MIT license). + + Big thank you to Omar Cornut (ocornut@github) for his imgui library and + giving me the inspiration for this library, Casey Muratori for handmade hero + and his original immediate mode graphical user interface idea and Sean + Barret for his amazing single header libraries which restored my faith + in libraries and brought me to create some of my own. + +LICENSE: + This software is dual-licensed to the public domain and under the following + license: you are granted a perpetual, irrevocable license to copy, modify, + publish and distribute this file as you see fit. +*/ +#ifndef NK_NUKLEAR_H_ +#define NK_NUKLEAR_H_ + +#ifdef __cplusplus +extern "C" { +#endif +/* + * ============================================================== + * + * CONSTANTS + * + * =============================================================== + */ +#define NK_UNDEFINED (-1.0f) +#define NK_UTF_INVALID 0xFFFD /* internal invalid utf8 rune */ +#define NK_UTF_SIZE 4 /* describes the number of bytes a glyph consists of*/ +#ifndef NK_INPUT_MAX +#define NK_INPUT_MAX 16 +#endif +#ifndef NK_MAX_NUMBER_BUFFER +#define NK_MAX_NUMBER_BUFFER 64 +#endif +#ifndef NK_SCROLLBAR_HIDING_TIMEOUT +#define NK_SCROLLBAR_HIDING_TIMEOUT 4.0f +#endif +/* + * ============================================================== + * + * HELPER + * + * =============================================================== + */ +#ifndef NK_API + #ifdef NK_PRIVATE + #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199409L)) + #define NK_API static inline + #elif defined(__cplusplus) + #define NK_API static inline + #else + #define NK_API static + #endif + #else + #define NK_API extern + #endif +#endif + +#define NK_INTERN static +#define NK_STORAGE static +#define NK_GLOBAL static + +#define NK_FLAG(x) (1 << (x)) +#define NK_STRINGIFY(x) #x +#define NK_MACRO_STRINGIFY(x) NK_STRINGIFY(x) +#define NK_STRING_JOIN_IMMEDIATE(arg1, arg2) arg1 ## arg2 +#define NK_STRING_JOIN_DELAY(arg1, arg2) NK_STRING_JOIN_IMMEDIATE(arg1, arg2) +#define NK_STRING_JOIN(arg1, arg2) NK_STRING_JOIN_DELAY(arg1, arg2) + +#ifdef _MSC_VER +#define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__COUNTER__) +#else +#define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__LINE__) +#endif + +#ifndef NK_STATIC_ASSERT +#define NK_STATIC_ASSERT(exp) typedef char NK_UNIQUE_NAME(_dummy_array)[(exp)?1:-1] +#endif + +#ifndef NK_FILE_LINE +#ifdef _MSC_VER +#define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__COUNTER__) +#else +#define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__LINE__) +#endif +#endif + +#define NK_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define NK_MAX(a,b) ((a) < (b) ? (b) : (a)) +#define NK_CLAMP(i,v,x) (NK_MAX(NK_MIN(v,x), i)) +/* + * =============================================================== + * + * BASIC + * + * =============================================================== + */ +#ifdef NK_INCLUDE_FIXED_TYPES + #include + #define NK_INT8 int8_t + #define NK_UINT8 uint8_t + #define NK_INT16 int16_t + #define NK_UINT16 uint16_t + #define NK_INT32 int32_t + #define NK_UINT32 uint32_t + #define NK_SIZE_TYPE uintptr_t + #define NK_POINTER_TYPE uintptr_t +#else + #ifndef NK_INT8 + #define NK_INT8 char + #endif + #ifndef NK_UINT8 + #define NK_UINT8 unsigned char + #endif + #ifndef NK_INT16 + #define NK_INT16 signed short + #endif + #ifndef NK_UINT16 + #define NK_UINT16 unsigned short + #endif + #ifndef NK_INT32 + #if defined(_MSC_VER) + #define NK_INT32 __int32 + #else + #define NK_INT32 signed int + #endif + #endif + #ifndef NK_UINT32 + #if defined(_MSC_VER) + #define NK_UINT32 unsigned __int32 + #else + #define NK_UINT32 unsigned int + #endif + #endif + #ifndef NK_SIZE_TYPE + #if defined(_WIN64) && defined(_MSC_VER) + #define NK_SIZE_TYPE unsigned __int64 + #elif (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER) + #define NK_SIZE_TYPE unsigned __int32 + #elif defined(__GNUC__) || defined(__clang__) + #if defined(__x86_64__) || defined(__ppc64__) + #define NK_SIZE_TYPE unsigned long + #else + #define NK_SIZE_TYPE unsigned int + #endif + #else + #define NK_SIZE_TYPE unsigned long + #endif + #endif + #ifndef NK_POINTER_TYPE + #if defined(_WIN64) && defined(_MSC_VER) + #define NK_POINTER_TYPE unsigned __int64 + #elif (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER) + #define NK_POINTER_TYPE unsigned __int32 + #elif defined(__GNUC__) || defined(__clang__) + #if defined(__x86_64__) || defined(__ppc64__) + #define NK_POINTER_TYPE unsigned long + #else + #define NK_POINTER_TYPE unsigned int + #endif + #else + #define NK_POINTER_TYPE unsigned long + #endif + #endif +#endif + +typedef NK_INT8 nk_char; +typedef NK_UINT8 nk_uchar; +typedef NK_UINT8 nk_byte; +typedef NK_INT16 nk_short; +typedef NK_UINT16 nk_ushort; +typedef NK_INT32 nk_int; +typedef NK_UINT32 nk_uint; +typedef NK_SIZE_TYPE nk_size; +typedef NK_POINTER_TYPE nk_ptr; + +typedef nk_uint nk_hash; +typedef nk_uint nk_flags; +typedef nk_uint nk_rune; + +/* Make sure correct type size: + * This will fire with a negative subscript error if the type sizes + * are set incorrectly by the compiler, and compile out if not */ +NK_STATIC_ASSERT(sizeof(nk_short) == 2); +NK_STATIC_ASSERT(sizeof(nk_ushort) == 2); +NK_STATIC_ASSERT(sizeof(nk_uint) == 4); +NK_STATIC_ASSERT(sizeof(nk_int) == 4); +NK_STATIC_ASSERT(sizeof(nk_byte) == 1); +NK_STATIC_ASSERT(sizeof(nk_flags) >= 4); +NK_STATIC_ASSERT(sizeof(nk_rune) >= 4); +NK_STATIC_ASSERT(sizeof(nk_size) >= sizeof(void*)); +NK_STATIC_ASSERT(sizeof(nk_ptr) >= sizeof(void*)); + +/* ============================================================================ + * + * API + * + * =========================================================================== */ +struct nk_buffer; +struct nk_allocator; +struct nk_command_buffer; +struct nk_draw_command; +struct nk_convert_config; +struct nk_style_item; +struct nk_text_edit; +struct nk_draw_list; +struct nk_user_font; +struct nk_panel; +struct nk_context; +struct nk_draw_vertex_layout_element; +struct nk_style_button; +struct nk_style_toggle; +struct nk_style_selectable; +struct nk_style_slide; +struct nk_style_progress; +struct nk_style_scrollbar; +struct nk_style_edit; +struct nk_style_property; +struct nk_style_chart; +struct nk_style_combo; +struct nk_style_tab; +struct nk_style_window_header; +struct nk_style_window; + +enum {nk_false, nk_true}; +struct nk_color {nk_byte r,g,b,a;}; +struct nk_colorf {float r,g,b,a;}; +struct nk_vec2 {float x,y;}; +struct nk_vec2i {short x, y;}; +struct nk_rect {float x,y,w,h;}; +struct nk_recti {short x,y,w,h;}; +typedef char nk_glyph[NK_UTF_SIZE]; +typedef union {void *ptr; int id;} nk_handle; +struct nk_image {nk_handle handle;unsigned short w,h;unsigned short region[4];}; +struct nk_cursor {struct nk_image img; struct nk_vec2 size, offset;}; +struct nk_scroll {nk_uint x, y;}; + +enum nk_heading {NK_UP, NK_RIGHT, NK_DOWN, NK_LEFT}; +enum nk_button_behavior {NK_BUTTON_DEFAULT, NK_BUTTON_REPEATER}; +enum nk_modify {NK_FIXED = nk_false, NK_MODIFIABLE = nk_true}; +enum nk_orientation {NK_VERTICAL, NK_HORIZONTAL}; +enum nk_collapse_states {NK_MINIMIZED = nk_false, NK_MAXIMIZED = nk_true}; +enum nk_show_states {NK_HIDDEN = nk_false, NK_SHOWN = nk_true}; +enum nk_chart_type {NK_CHART_LINES, NK_CHART_COLUMN, NK_CHART_MAX}; +enum nk_chart_event {NK_CHART_HOVERING = 0x01, NK_CHART_CLICKED = 0x02}; +enum nk_color_format {NK_RGB, NK_RGBA}; +enum nk_popup_type {NK_POPUP_STATIC, NK_POPUP_DYNAMIC}; +enum nk_layout_format {NK_DYNAMIC, NK_STATIC}; +enum nk_tree_type {NK_TREE_NODE, NK_TREE_TAB}; + +typedef void*(*nk_plugin_alloc)(nk_handle, void *old, nk_size); +typedef void (*nk_plugin_free)(nk_handle, void *old); +typedef int(*nk_plugin_filter)(const struct nk_text_edit*, nk_rune unicode); +typedef void(*nk_plugin_paste)(nk_handle, struct nk_text_edit*); +typedef void(*nk_plugin_copy)(nk_handle, const char*, int len); + +struct nk_allocator { + nk_handle userdata; + nk_plugin_alloc alloc; + nk_plugin_free free; +}; +enum nk_symbol_type { + NK_SYMBOL_NONE, + NK_SYMBOL_X, + NK_SYMBOL_UNDERSCORE, + NK_SYMBOL_CIRCLE_SOLID, + NK_SYMBOL_CIRCLE_OUTLINE, + NK_SYMBOL_RECT_SOLID, + NK_SYMBOL_RECT_OUTLINE, + NK_SYMBOL_TRIANGLE_UP, + NK_SYMBOL_TRIANGLE_DOWN, + NK_SYMBOL_TRIANGLE_LEFT, + NK_SYMBOL_TRIANGLE_RIGHT, + NK_SYMBOL_PLUS, + NK_SYMBOL_MINUS, + NK_SYMBOL_MAX +}; +/* ============================================================================= + * + * CONTEXT + * + * =============================================================================*/ +/* Contexts are the main entry point and the majestro of nuklear and contain all required state. + * They are used for window, memory, input, style, stack, commands and time management and need + * to be passed into all nuklear GUI specific functions. + * + * Usage + * ------------------- + * To use a context it first has to be initialized which can be achieved by calling + * one of either `nk_init_default`, `nk_init_fixed`, `nk_init`, `nk_init_custom`. + * Each takes in a font handle and a specific way of handling memory. Memory control + * hereby ranges from standard library to just specifing a fixed sized block of memory + * which nuklear has to manage itself from. + * + * struct nk_context ctx; + * nk_init_xxx(&ctx, ...); + * while (1) { + * [...] + * nk_clear(&ctx); + * } + * nk_free(&ctx); + * + * Reference + * ------------------- + * nk_init_default - Initializes context with standard library memory alloction (malloc,free) + * nk_init_fixed - Initializes context from single fixed size memory block + * nk_init - Initializes context with memory allocator callbacks for alloc and free + * nk_init_custom - Initializes context from two buffers. One for draw commands the other for window/panel/table allocations + * nk_clear - Called at the end of the frame to reset and prepare the context for the next frame + * nk_free - Shutdown and free all memory allocated inside the context + * nk_set_user_data - Utility function to pass user data to draw command + */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +/* nk_init_default - Initializes a `nk_context` struct with a default standard library allocator. + * Should be used if you don't want to be bothered with memory management in nuklear. + * Parameters: + * @ctx must point to an either stack or heap allocated `nk_context` struct + * @font must point to a previously initialized font handle for more info look at font documentation + * Return values: + * true(1) on success + * false(0) on failure */ +NK_API int nk_init_default(struct nk_context*, const struct nk_user_font*); +#endif +/* nk_init_fixed - Initializes a `nk_context` struct from a single fixed size memory block + * Should be used if you want complete control over nuklears memory management. + * Especially recommended for system with little memory or systems with virtual memory. + * For the later case you can just allocate for example 16MB of virtual memory + * and only the required amount of memory will actually be commited. + * IMPORTANT: make sure the passed memory block is aligned correctly for `nk_draw_commands` + * Parameters: + * @ctx must point to an either stack or heap allocated `nk_context` struct + * @memory must point to a previously allocated memory block + * @size must contain the total size of @memory + * @font must point to a previously initialized font handle for more info look at font documentation + * Return values: + * true(1) on success + * false(0) on failure */ +NK_API int nk_init_fixed(struct nk_context*, void *memory, nk_size size, const struct nk_user_font*); +/* nk_init - Initializes a `nk_context` struct with memory allocation callbacks for nuklear to allocate + * memory from. Used internally for `nk_init_default` and provides a kitchen sink allocation + * interface to nuklear. Can be useful for cases like monitoring memory consumption. + * Parameters: + * @ctx must point to an either stack or heap allocated `nk_context` struct + * @alloc must point to a previously allocated memory allocator + * @font must point to a previously initialized font handle for more info look at font documentation + * Return values: + * true(1) on success + * false(0) on failure */ +NK_API int nk_init(struct nk_context*, struct nk_allocator*, const struct nk_user_font*); +/* nk_init_custom - Initializes a `nk_context` struct from two different either fixed or growing + * buffers. The first buffer is for allocating draw commands while the second buffer is + * used for allocating windows, panels and state tables. + * Parameters: + * @ctx must point to an either stack or heap allocated `nk_context` struct + * @cmds must point to a previously initialized memory buffer either fixed or dynamic to store draw commands into + * @pool must point to a previously initialized memory buffer either fixed or dynamic to store windows, panels and tables + * @font must point to a previously initialized font handle for more info look at font documentation + * Return values: + * true(1) on success + * false(0) on failure */ +NK_API int nk_init_custom(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font*); +/* nk_clear - Resets the context state at the end of the frame. This includes mostly + * garbage collector tasks like removing windows or table not called and therefore + * used anymore. + * Parameters: + * @ctx must point to a previously initialized `nk_context` struct */ +NK_API void nk_clear(struct nk_context*); +/* nk_free - Frees all memory allocated by nuklear. Not needed if context was + * initialized with `nk_init_fixed`. + * Parameters: + * @ctx must point to a previously initialized `nk_context` struct */ +NK_API void nk_free(struct nk_context*); +#ifdef NK_INCLUDE_COMMAND_USERDATA +/* nk_set_user_data - Sets the currently passed userdata passed down into each draw command. + * Parameters: + * @ctx must point to a previously initialized `nk_context` struct + * @data handle with either pointer or index to be passed into every draw commands */ +NK_API void nk_set_user_data(struct nk_context*, nk_handle handle); +#endif +/* ============================================================================= + * + * INPUT + * + * =============================================================================*/ +/* The input API is responsible for holding the current input state composed of + * mouse, key and text input states. + * It is worth noting that no direct os or window handling is done in nuklear. + * Instead all input state has to be provided by platform specific code. This in one hand + * expects more work from the user and complicates usage but on the other hand + * provides simple abstraction over a big number of platforms, libraries and other + * already provided functionality. + * + * Usage + * ------------------- + * Input state needs to be provided to nuklear by first calling `nk_input_begin` + * which resets internal state like delta mouse position and button transistions. + * After `nk_input_begin` all current input state needs to be provided. This includes + * mouse motion, button and key pressed and released, text input and scrolling. + * Both event- or state-based input handling are supported by this API + * and should work without problems. Finally after all input state has been + * mirrored `nk_input_end` needs to be called to finish input process. + * + * struct nk_context ctx; + * nk_init_xxx(&ctx, ...); + * while (1) { + * Event evt; + * nk_input_begin(&ctx); + * while (GetEvent(&evt)) { + * if (evt.type == MOUSE_MOVE) + * nk_input_motion(&ctx, evt.motion.x, evt.motion.y); + * else if (evt.type == ...) { + * ... + * } + * } + * nk_input_end(&ctx); + * [...] + * nk_clear(&ctx); + * } + * nk_free(&ctx); + * + * Reference + * ------------------- + * nk_input_begin - Begins the input mirroring process. Needs to be called before all other `nk_input_xxx` calls + * nk_input_motion - Mirrors mouse cursor position + * nk_input_key - Mirrors key state with either pressed or released + * nk_input_button - Mirrors mouse button state with either pressed or released + * nk_input_scroll - Mirrors mouse scroll values + * nk_input_char - Adds a single ASCII text character into an internal text buffer + * nk_input_glyph - Adds a single multi-byte UTF-8 character into an internal text buffer + * nk_input_unicode - Adds a single unicode rune into an internal text buffer + * nk_input_end - Ends the input mirroring process by calculating state changes. Don't call any `nk_input_xxx` function referenced above after this call + */ +enum nk_keys { + NK_KEY_NONE, + NK_KEY_SHIFT, + NK_KEY_CTRL, + NK_KEY_DEL, + NK_KEY_ENTER, + NK_KEY_TAB, + NK_KEY_BACKSPACE, + NK_KEY_COPY, + NK_KEY_CUT, + NK_KEY_PASTE, + NK_KEY_UP, + NK_KEY_DOWN, + NK_KEY_LEFT, + NK_KEY_RIGHT, + /* Shortcuts: text field */ + NK_KEY_TEXT_INSERT_MODE, + NK_KEY_TEXT_REPLACE_MODE, + NK_KEY_TEXT_RESET_MODE, + NK_KEY_TEXT_LINE_START, + NK_KEY_TEXT_LINE_END, + NK_KEY_TEXT_START, + NK_KEY_TEXT_END, + NK_KEY_TEXT_UNDO, + NK_KEY_TEXT_REDO, + NK_KEY_TEXT_SELECT_ALL, + NK_KEY_TEXT_WORD_LEFT, + NK_KEY_TEXT_WORD_RIGHT, + /* Shortcuts: scrollbar */ + NK_KEY_SCROLL_START, + NK_KEY_SCROLL_END, + NK_KEY_SCROLL_DOWN, + NK_KEY_SCROLL_UP, + NK_KEY_MAX +}; +enum nk_buttons { + NK_BUTTON_LEFT, + NK_BUTTON_MIDDLE, + NK_BUTTON_RIGHT, + NK_BUTTON_DOUBLE, + NK_BUTTON_MAX +}; +/* nk_input_begin - Begins the input mirroring process by resetting text, scroll + * mouse previous mouse position and movement as well as key state transistions, + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct */ +NK_API void nk_input_begin(struct nk_context*); +/* nk_input_motion - Mirros current mouse position to nuklear + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @x must constain an integer describing the current mouse cursor x-position + * @y must constain an integer describing the current mouse cursor y-position */ +NK_API void nk_input_motion(struct nk_context*, int x, int y); +/* nk_input_key - Mirros state of a specific key to nuklear + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @key must be any value specified in enum `nk_keys` that needs to be mirrored + * @down must be 0 for key is up and 1 for key is down */ +NK_API void nk_input_key(struct nk_context*, enum nk_keys, int down); +/* nk_input_button - Mirros the state of a specific mouse button to nuklear + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @nk_buttons must be any value specified in enum `nk_buttons` that needs to be mirrored + * @x must constain an integer describing mouse cursor x-position on click up/down + * @y must constain an integer describing mouse cursor y-position on click up/down + * @down must be 0 for key is up and 1 for key is down */ +NK_API void nk_input_button(struct nk_context*, enum nk_buttons, int x, int y, int down); +/* nk_input_char - Copies a single ASCII character into an internal text buffer + * This is basically a helper function to quickly push ASCII characters into + * nuklear. Note that you can only push up to NK_INPUT_MAX bytes into + * struct `nk_input` between `nk_input_begin` and `nk_input_end`. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @c must be a single ASCII character preferable one that can be printed */ +NK_API void nk_input_scroll(struct nk_context*, struct nk_vec2 val); +/* nk_input_char - Copies a single ASCII character into an internal text buffer + * This is basically a helper function to quickly push ASCII characters into + * nuklear. Note that you can only push up to NK_INPUT_MAX bytes into + * struct `nk_input` between `nk_input_begin` and `nk_input_end`. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @c must be a single ASCII character preferable one that can be printed */ +NK_API void nk_input_char(struct nk_context*, char); +/* nk_input_unicode - Converts a encoded unicode rune into UTF-8 and copies the result + * into an internal text buffer. + * Note that you can only push up to NK_INPUT_MAX bytes into + * struct `nk_input` between `nk_input_begin` and `nk_input_end`. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @glyph UTF-32 uncode codepoint */ +NK_API void nk_input_glyph(struct nk_context*, const nk_glyph); +/* nk_input_unicode - Converts a unicode rune into UTF-8 and copies the result + * into an internal text buffer. + * Note that you can only push up to NK_INPUT_MAX bytes into + * struct `nk_input` between `nk_input_begin` and `nk_input_end`. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @glyph UTF-32 uncode codepoint */ +NK_API void nk_input_unicode(struct nk_context*, nk_rune); +/* nk_input_end - End the input mirroring process by resetting mouse grabbing + * state to ensure the mouse cursor is not grabbed indefinitely. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct */ +NK_API void nk_input_end(struct nk_context*); +/* ============================================================================= + * + * DRAWING + * + * =============================================================================*/ +/* This library was designed to be render backend agnostic so it does + * not draw anything to screen directly. Instead all drawn shapes, widgets + * are made of, are buffered into memory and make up a command queue. + * Each frame therefore fills the command buffer with draw commands + * that then need to be executed by the user and his own render backend. + * After that the command buffer needs to be cleared and a new frame can be + * started. It is probably important to note that the command buffer is the main + * drawing API and the optional vertex buffer API only takes this format and + * converts it into a hardware accessible format. + * + * Usage + * ------------------- + * To draw all draw commands accumulated over a frame you need your own render + * backend able to draw a number of 2D primitives. This includes at least + * filled and stroked rectangles, circles, text, lines, triangles and scissors. + * As soon as this criterion is met you can iterate over each draw command + * and execute each draw command in a interpreter like fashion: + * + * const struct nk_command *cmd = 0; + * nk_foreach(cmd, &ctx) { + * switch (cmd->type) { + * case NK_COMMAND_LINE: + * your_draw_line_function(...) + * break; + * case NK_COMMAND_RECT + * your_draw_rect_function(...) + * break; + * case ...: + * [...] + * } + * + * In program flow context draw commands need to be executed after input has been + * gathered and the complete UI with windows and their contained widgets have + * been executed and before calling `nk_clear` which frees all previously + * allocated draw commands. + * + * struct nk_context ctx; + * nk_init_xxx(&ctx, ...); + * while (1) { + * Event evt; + * nk_input_begin(&ctx); + * while (GetEvent(&evt)) { + * if (evt.type == MOUSE_MOVE) + * nk_input_motion(&ctx, evt.motion.x, evt.motion.y); + * else if (evt.type == [...]) { + * [...] + * } + * } + * nk_input_end(&ctx); + * + * [...] + * + * const struct nk_command *cmd = 0; + * nk_foreach(cmd, &ctx) { + * switch (cmd->type) { + * case NK_COMMAND_LINE: + * your_draw_line_function(...) + * break; + * case NK_COMMAND_RECT + * your_draw_rect_function(...) + * break; + * case ...: + * [...] + * } + * nk_clear(&ctx); + * } + * nk_free(&ctx); + * + * You probably noticed that you have to draw all of the UI each frame which is + * quite wasteful. While the actual UI updating loop is quite fast rendering + * without actually needing it is not. So there are multiple things you could do. + * + * First is only update on input. This of course is only an option if your + * application only depends on the UI and does not require any outside calculations. + * If you actually only update on input make sure to update the UI two times each + * frame and call `nk_clear` directly after the first pass and only draw in + * the second pass. In addition it is recommended to also add additional timers + * to make sure the UI is not drawn more than a fixed number of frames per second. + * + * struct nk_context ctx; + * nk_init_xxx(&ctx, ...); + * while (1) { + * [...wait for input ] + * + * [...do two UI passes ...] + * do_ui(...) + * nk_clear(&ctx); + * do_ui(...) + * + * const struct nk_command *cmd = 0; + * nk_foreach(cmd, &ctx) { + * switch (cmd->type) { + * case NK_COMMAND_LINE: + * your_draw_line_function(...) + * break; + * case NK_COMMAND_RECT + * your_draw_rect_function(...) + * break; + * case ...: + * [...] + * } + * nk_clear(&ctx); + * } + * nk_free(&ctx); + * + * The second probably more applicable trick is to only draw if anything changed. + * It is not really useful for applications with continous draw loop but + * quite useful for desktop applications. To actually get nuklear to only + * draw on changes you first have to define `NK_ZERO_COMMAND_MEMORY` and + * allocate a memory buffer that will store each unique drawing output. + * After each frame you compare the draw command memory inside the library + * with your allocated buffer by memcmp. If memcmp detects differences + * you have to copy the command buffer into the allocated buffer + * and then draw like usual (this example uses fixed memory but you could + * use dynamically allocated memory). + * + * [... other defines ...] + * #define NK_ZERO_COMMAND_MEMORY + * #include "nuklear.h" + * + * struct nk_context ctx; + * void *last = calloc(1,64*1024); + * void *buf = calloc(1,64*1024); + * nk_init_fixed(&ctx, buf, 64*1024); + * while (1) { + * [...input...] + * [...ui...] + * + * void *cmds = nk_buffer_memory(&ctx.memory); + * if (memcmp(cmds, last, ctx.memory.allocated)) { + * memcpy(last,cmds,ctx.memory.allocated); + * const struct nk_command *cmd = 0; + * nk_foreach(cmd, &ctx) { + * switch (cmd->type) { + * case NK_COMMAND_LINE: + * your_draw_line_function(...) + * break; + * case NK_COMMAND_RECT + * your_draw_rect_function(...) + * break; + * case ...: + * [...] + * } + * } + * } + * nk_clear(&ctx); + * } + * nk_free(&ctx); + * + * Finally while using draw commands makes sense for higher abstracted platforms like + * X11 and Win32 or drawing libraries it is often desirable to use graphics + * hardware directly. Therefore it is possible to just define + * `NK_INCLUDE_VERTEX_BUFFER_OUTPUT` which includes optional vertex output. + * To access the vertex output you first have to convert all draw commands into + * vertexes by calling `nk_convert` which takes in your prefered vertex format. + * After successfully converting all draw commands just iterate over and execute all + * vertex draw commands: + * + * struct nk_convert_config cfg = {}; + * static const struct nk_draw_vertex_layout_element vertex_layout[] = { + * {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, pos)}, + * {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, uv)}, + * {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct your_vertex, col)}, + * {NK_VERTEX_LAYOUT_END} + * }; + * cfg.shape_AA = NK_ANTI_ALIASING_ON; + * cfg.line_AA = NK_ANTI_ALIASING_ON; + * cfg.vertex_layout = vertex_layout; + * cfg.vertex_size = sizeof(struct your_vertex); + * cfg.vertex_alignment = NK_ALIGNOF(struct your_vertex); + * cfg.circle_segment_count = 22; + * cfg.curve_segment_count = 22; + * cfg.arc_segment_count = 22; + * cfg.global_alpha = 1.0f; + * cfg.null = dev->null; + * + * struct nk_buffer cmds, verts, idx; + * nk_buffer_init_default(&cmds); + * nk_buffer_init_default(&verts); + * nk_buffer_init_default(&idx); + * nk_convert(&ctx, &cmds, &verts, &idx, &cfg); + * nk_draw_foreach(cmd, &ctx, &cmds) { + * if (!cmd->elem_count) continue; + * [...] + * } + * nk_buffer_free(&cms); + * nk_buffer_free(&verts); + * nk_buffer_free(&idx); + * + * Reference + * ------------------- + * nk__begin - Returns the first draw command in the context draw command list to be drawn + * nk__next - Increments the draw command iterator to the next command inside the context draw command list + * nk_foreach - Iteratates over each draw command inside the context draw command list + * + * nk_convert - Converts from the abstract draw commands list into a hardware accessable vertex format + * nk__draw_begin - Returns the first vertex command in the context vertex draw list to be executed + * nk__draw_next - Increments the vertex command iterator to the next command inside the context vertex command list + * nk__draw_end - Returns the end of the vertex draw list + * nk_draw_foreach - Iterates over each vertex draw command inside the vertex draw list + */ +enum nk_anti_aliasing {NK_ANTI_ALIASING_OFF, NK_ANTI_ALIASING_ON}; +enum nk_convert_result { + NK_CONVERT_SUCCESS = 0, + NK_CONVERT_INVALID_PARAM = 1, + NK_CONVERT_COMMAND_BUFFER_FULL = NK_FLAG(1), + NK_CONVERT_VERTEX_BUFFER_FULL = NK_FLAG(2), + NK_CONVERT_ELEMENT_BUFFER_FULL = NK_FLAG(3) +}; +struct nk_draw_null_texture { + nk_handle texture; /* texture handle to a texture with a white pixel */ + struct nk_vec2 uv; /* coordinates to a white pixel in the texture */ +}; +struct nk_convert_config { + float global_alpha; /* global alpha value */ + enum nk_anti_aliasing line_AA; /* line anti-aliasing flag can be turned off if you are tight on memory */ + enum nk_anti_aliasing shape_AA; /* shape anti-aliasing flag can be turned off if you are tight on memory */ + unsigned circle_segment_count; /* number of segments used for circles: default to 22 */ + unsigned arc_segment_count; /* number of segments used for arcs: default to 22 */ + unsigned curve_segment_count; /* number of segments used for curves: default to 22 */ + struct nk_draw_null_texture null; /* handle to texture with a white pixel for shape drawing */ + const struct nk_draw_vertex_layout_element *vertex_layout; /* describes the vertex output format and packing */ + nk_size vertex_size; /* sizeof one vertex for vertex packing */ + nk_size vertex_alignment; /* vertex alignment: Can be optained by NK_ALIGNOF */ +}; +/* nk__begin - Returns a draw command list iterator to iterate all draw + * commands accumulated over one frame. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct at the end of a frame + * Return values: + * draw command pointer pointing to the first command inside the draw command list */ +NK_API const struct nk_command* nk__begin(struct nk_context*); +/* nk__next - Returns a draw command list iterator to iterate all draw + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct at the end of a frame + * @cmd must point to an previously a draw command either returned by `nk__begin` or `nk__next` + * Return values: + * draw command pointer pointing to the next command inside the draw command list */ +NK_API const struct nk_command* nk__next(struct nk_context*, const struct nk_command*); +/* nk_foreach - Iterates over each draw command inside the context draw command list + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct at the end of a frame + * @cmd pointer initialized to NULL */ +#define nk_foreach(c, ctx) for((c) = nk__begin(ctx); (c) != 0; (c) = nk__next(ctx,c)) +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +/* nk_convert - converts all internal draw command into vertex draw commands and fills + * three buffers with vertexes, vertex draw commands and vertex indicies. The vertex format + * as well as some other configuration values have to be configurated by filling out a + * `nk_convert_config` struct. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct at the end of a frame + * @cmds must point to a previously initialized buffer to hold converted vertex draw commands + * @vertices must point to a previously initialized buffer to hold all produced verticies + * @elements must point to a previously initialized buffer to hold all procudes vertex indicies + * @config must point to a filled out `nk_config` struct to configure the conversion process + * Returns: + * returns NK_CONVERT_SUCCESS on success and a enum nk_convert_result error values if not */ +NK_API nk_flags nk_convert(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*); +/* nk__draw_begin - Returns a draw vertex command buffer iterator to iterate each the vertex draw command buffer + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct at the end of a frame + * @buf must point to an previously by `nk_convert` filled out vertex draw command buffer + * Return values: + * vertex draw command pointer pointing to the first command inside the vertex draw command buffer */ +NK_API const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*); +/* nk__draw_end - Returns the vertex draw command at the end of the vertex draw command buffer + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct at the end of a frame + * @buf must point to an previously by `nk_convert` filled out vertex draw command buffer + * Return values: + * vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer */ +NK_API const struct nk_draw_command* nk__draw_end(const struct nk_context*, const struct nk_buffer*); +/* nk__draw_next - Increments the the vertex draw command buffer iterator + * Parameters: + * @cmd must point to an previously either by `nk__draw_begin` or `nk__draw_next` returned vertex draw command + * @buf must point to an previously by `nk_convert` filled out vertex draw command buffer + * @ctx must point to an previously initialized `nk_context` struct at the end of a frame + * Return values: + * vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer */ +NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*); +/* nk_draw_foreach - Iterates over each vertex draw command inside a vertex draw command buffer + * Parameters: + * @cmd nk_draw_command pointer set to NULL + * @buf must point to an previously by `nk_convert` filled out vertex draw command buffer + * @ctx must point to an previously initialized `nk_context` struct at the end of a frame */ +#define nk_draw_foreach(cmd,ctx, b) for((cmd)=nk__draw_begin(ctx, b); (cmd)!=0; (cmd)=nk__draw_next(cmd, b, ctx)) +#endif +/* ============================================================================= + * + * WINDOW + * + * ============================================================================= + * Windows are the main persistent state used inside nuklear and are life time + * controlled by simply "retouching" (i.e. calling) each window each frame. + * All widgets inside nuklear can only be added inside function pair `nk_begin_xxx` + * and `nk_end`. Calling any widgets outside these two functions will result in an + * assert in debug or no state change in release mode. + * + * Each window holds frame persistent state like position, size, flags, state tables, + * and some garbage collected internal persistent widget state. Each window + * is linked into a window stack list which determines the drawing and overlapping + * order. The topmost window thereby is the currently active window. + * + * To change window position inside the stack occurs either automatically by + * user input by being clicked on or programatically by calling `nk_window_focus`. + * Windows by default are visible unless explicitly being defined with flag + * `NK_WINDOW_HIDDEN`, the user clicked the close button on windows with flag + * `NK_WINDOW_CLOSABLE` or if a window was explicitly hidden by calling + * `nk_window_show`. To explicitly close and destroy a window call `nk_window_close`. + * + * Usage + * ------------------- + * To create and keep a window you have to call one of the two `nk_begin_xxx` + * functions to start window declarations and `nk_end` at the end. Furthermore it + * is recommended to check the return value of `nk_begin_xxx` and only process + * widgets inside the window if the value is not 0. Either way you have to call + * `nk_end` at the end of window declarations. Furthmore do not attempt to + * nest `nk_begin_xxx` calls which will hopefully result in an assert or if not + * in a segmation fault. + * + * if (nk_begin_xxx(...) { + * [... widgets ...] + * } + * nk_end(ctx); + * + * In the grand concept window and widget declarations need to occur after input + * handling and before drawing to screen. Not doing so can result in higher + * latency or at worst invalid behavior. Furthermore make sure that `nk_clear` + * is called at the end of the frame. While nuklears default platform backends + * already call `nk_clear` for you if you write your own backend not calling + * `nk_clear` can cause asserts or even worse undefined behavior. + * + * struct nk_context ctx; + * nk_init_xxx(&ctx, ...); + * while (1) { + * Event evt; + * nk_input_begin(&ctx); + * while (GetEvent(&evt)) { + * if (evt.type == MOUSE_MOVE) + * nk_input_motion(&ctx, evt.motion.x, evt.motion.y); + * else if (evt.type == [...]) { + * nk_input_xxx(...); + * } + * } + * nk_input_end(&ctx); + * + * if (nk_begin_xxx(...) { + * [...] + * } + * nk_end(ctx); + * + * const struct nk_command *cmd = 0; + * nk_foreach(cmd, &ctx) { + * case NK_COMMAND_LINE: + * your_draw_line_function(...) + * break; + * case NK_COMMAND_RECT + * your_draw_rect_function(...) + * break; + * case ...: + * [...] + * } + * nk_clear(&ctx); + * } + * nk_free(&ctx); + * + * Reference + * ------------------- + * nk_begin - starts a new window; needs to be called every frame for every window (unless hidden) or otherwise the window gets removed + * nk_begin_titled - extended window start with seperated title and identifier to allow multiple windows with same name but not title + * nk_end - needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup + * + * nk_window_find - finds and returns the window with give name + * nk_window_get_bounds - returns a rectangle with screen position and size of the currently processed window. + * nk_window_get_position - returns the position of the currently processed window + * nk_window_get_size - returns the size with width and height of the currently processed window + * nk_window_get_width - returns the width of the currently processed window + * nk_window_get_height - returns the height of the currently processed window + * nk_window_get_panel - returns the underlying panel which contains all processing state of the currnet window + * nk_window_get_content_region - returns the position and size of the currently visible and non-clipped space inside the currently processed window + * nk_window_get_content_region_min - returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window + * nk_window_get_content_region_max - returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window + * nk_window_get_content_region_size - returns the size of the currently visible and non-clipped space inside the currently processed window + * nk_window_get_canvas - returns the draw command buffer. Can be used to draw custom widgets + * + * nk_window_has_focus - returns if the currently processed window is currently active + * nk_window_is_collapsed - returns if the window with given name is currently minimized/collapsed + * nk_window_is_closed - returns if the currently processed window was closed + * nk_window_is_hidden - returns if the currently processed window was hidden + * nk_window_is_active - same as nk_window_has_focus for some reason + * nk_window_is_hovered - returns if the currently processed window is currently being hovered by mouse + * nk_window_is_any_hovered - return if any wndow currently hovered + * nk_item_is_any_active - returns if any window or widgets is currently hovered or active + * + * nk_window_set_bounds - updates position and size of the currently processed window + * nk_window_set_position - updates position of the currently process window + * nk_window_set_size - updates the size of the currently processed window + * nk_window_set_focus - set the currently processed window as active window + * + * nk_window_close - closes the window with given window name which deletes the window at the end of the frame + * nk_window_collapse - collapses the window with given window name + * nk_window_collapse_if - collapses the window with given window name if the given condition was met + * nk_window_show - hides a visible or reshows a hidden window + * nk_window_show_if - hides/shows a window depending on condition + */ +enum nk_panel_flags { + NK_WINDOW_BORDER = NK_FLAG(0), /* Draws a border around the window to visually separate window from the background */ + NK_WINDOW_MOVABLE = NK_FLAG(1), /* The movable flag indicates that a window can be moved by user input or by dragging the window header */ + NK_WINDOW_SCALABLE = NK_FLAG(2), /* The scalable flag indicates that a window can be scaled by user input by dragging a scaler icon at the button of the window */ + NK_WINDOW_CLOSABLE = NK_FLAG(3), /* adds a closable icon into the header */ + NK_WINDOW_MINIMIZABLE = NK_FLAG(4), /* adds a minimize icon into the header */ + NK_WINDOW_NO_SCROLLBAR = NK_FLAG(5), /* Removes the scrollbar from the window */ + NK_WINDOW_TITLE = NK_FLAG(6), /* Forces a header at the top at the window showing the title */ + NK_WINDOW_SCROLL_AUTO_HIDE = NK_FLAG(7), /* Automatically hides the window scrollbar if no user interaction: also requires delta time in `nk_context` to be set each frame */ + NK_WINDOW_BACKGROUND = NK_FLAG(8), /* Always keep window in the background */ + NK_WINDOW_SCALE_LEFT = NK_FLAG(9), /* Puts window scaler in the left-ottom corner instead right-bottom*/ + NK_WINDOW_NO_INPUT = NK_FLAG(10) /* Prevents window of scaling, moving or getting focus */ +}; +/* nk_begin - starts a new window; needs to be called every frame for every window (unless hidden) or otherwise the window gets removed + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @title window title and identifier. Needs to be persitent over frames to identify the window + * @bounds initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame + * @flags window flags defined in `enum nk_panel_flags` with a number of different window behaviors + * Return values: + * returns 1 if the window can be filled up with widgets from this point until `nk_end or 0 otherwise for example if minimized `*/ +NK_API int nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags); +/* nk_begin_titled - extended window start with seperated title and identifier to allow multiple windows with same name but not title + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name window identifier. Needs to be persitent over frames to identify the window + * @title window title displayed inside header if flag `NK_WINDOW_TITLE` or either `NK_WINDOW_CLOSABLE` or `NK_WINDOW_MINIMIZED` was set + * @bounds initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame + * @flags window flags defined in `enum nk_panel_flags` with a number of different window behaviors + * Return values: + * returns 1 if the window can be filled up with widgets from this point until `nk_end or 0 otherwise `*/ +NK_API int nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags); +/* nk_end - needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup. + * All widget calls after this functions will result in asserts or no state changes + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct */ +NK_API void nk_end(struct nk_context *ctx); +/* nk_window_find - finds and returns the window with give name + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name window identifier + * Return values: + * returns a `nk_window` struct pointing to the idified window or 0 if no window with given name was found */ +NK_API struct nk_window *nk_window_find(struct nk_context *ctx, const char *name); +/* nk_window_get_bounds - returns a rectangle with screen position and size of the currently processed window. + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns a `nk_rect` struct with window upper left position and size */ +NK_API struct nk_rect nk_window_get_bounds(const struct nk_context *ctx); +/* nk_window_get_position - returns the position of the currently processed window. + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns a `nk_vec2` struct with window upper left position */ +NK_API struct nk_vec2 nk_window_get_position(const struct nk_context *ctx); +/* nk_window_get_size - returns the size with width and height of the currently processed window. + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns a `nk_vec2` struct with window size */ +NK_API struct nk_vec2 nk_window_get_size(const struct nk_context*); +/* nk_window_get_width - returns the width of the currently processed window. + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns the window width */ +NK_API float nk_window_get_width(const struct nk_context*); +/* nk_window_get_height - returns the height of the currently processed window. + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns the window height */ +NK_API float nk_window_get_height(const struct nk_context*); +/* nk_window_get_panel - returns the underlying panel which contains all processing state of the currnet window. + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns a pointer to window internal `nk_panel` state. DO NOT keep this pointer around it is only valid until `nk_end` */ +NK_API struct nk_panel* nk_window_get_panel(struct nk_context*); +/* nk_window_get_content_region - returns the position and size of the currently visible and non-clipped space inside the currently processed window. + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns `nk_rect` struct with screen position and size (no scrollbar offset) of the visible space inside the current window */ +NK_API struct nk_rect nk_window_get_content_region(struct nk_context*); +/* nk_window_get_content_region_min - returns the upper left position of the currently visible and non-clipped space inside the currently processed window. + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns `nk_vec2` struct with upper left screen position (no scrollbar offset) of the visible space inside the current window */ +NK_API struct nk_vec2 nk_window_get_content_region_min(struct nk_context*); +/* nk_window_get_content_region_max - returns the lower right screen position of the currently visible and non-clipped space inside the currently processed window. + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns `nk_vec2` struct with lower right screen position (no scrollbar offset) of the visible space inside the current window */ +NK_API struct nk_vec2 nk_window_get_content_region_max(struct nk_context*); +/* nk_window_get_content_region_size - returns the size of the currently visible and non-clipped space inside the currently processed window + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns `nk_vec2` struct with size the visible space inside the current window */ +NK_API struct nk_vec2 nk_window_get_content_region_size(struct nk_context*); +/* nk_window_get_canvas - returns the draw command buffer. Can be used to draw custom widgets + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns a pointer to window internal `nk_command_buffer` struct used as drawing canvas. Can be used to do custom drawing */ +NK_API struct nk_command_buffer* nk_window_get_canvas(struct nk_context*); +/* nk_window_has_focus - returns if the currently processed window is currently active + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns 0 if current window is not active or 1 if it is */ +NK_API int nk_window_has_focus(const struct nk_context*); +/* nk_window_is_collapsed - returns if the window with given name is currently minimized/collapsed + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of window you want to check is collapsed + * Return values: + * returns 1 if current window is minimized and 0 if window not found or is not minimized */ +NK_API int nk_window_is_collapsed(struct nk_context *ctx, const char *name); +/* nk_window_is_closed - returns if the window with given name was closed by calling `nk_close` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of window you want to check is closed + * Return values: + * returns 1 if current window was closed or 0 window not found or not closed */ +NK_API int nk_window_is_closed(struct nk_context*, const char*); +/* nk_window_is_hidden - returns if the window with given name is hidden + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of window you want to check is hidden + * Return values: + * returns 1 if current window is hidden or 0 window not found or visible */ +NK_API int nk_window_is_hidden(struct nk_context*, const char*); +/* nk_window_is_active - same as nk_window_has_focus for some reason + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of window you want to check is hidden + * Return values: + * returns 1 if current window is active or 0 window not found or not active */ +NK_API int nk_window_is_active(struct nk_context*, const char*); +/* nk_window_is_hovered - return if the current window is being hovered + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns 1 if current window is hovered or 0 otherwise */ +NK_API int nk_window_is_hovered(struct nk_context*); +/* nk_window_is_any_hovered - returns if the any window is being hovered + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns 1 if any window is hovered or 0 otherwise */ +NK_API int nk_window_is_any_hovered(struct nk_context*); +/* nk_item_is_any_active - returns if the any window is being hovered or any widget is currently active. + * Can be used to decide if input should be processed by UI or your specific input handling. + * Example could be UI and 3D camera to move inside a 3D space. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * Return values: + * returns 1 if any window is hovered or any item is active or 0 otherwise */ +NK_API int nk_item_is_any_active(struct nk_context*); +/* nk_window_set_bounds - updates position and size of the currently processed window + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @bounds points to a `nk_rect` struct with the new position and size of currently active window */ +NK_API void nk_window_set_bounds(struct nk_context*, struct nk_rect bounds); +/* nk_window_set_position - updates position of the currently processed window + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @pos points to a `nk_vec2` struct with the new position of currently active window */ +NK_API void nk_window_set_position(struct nk_context*, struct nk_vec2 pos); +/* nk_window_set_size - updates size of the currently processed window + * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @bounds points to a `nk_vec2` struct with the new size of currently active window */ +NK_API void nk_window_set_size(struct nk_context*, struct nk_vec2); +/* nk_window_set_focus - sets the window with given name as active + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of the window to be set active */ +NK_API void nk_window_set_focus(struct nk_context*, const char *name); +/* nk_window_close - closed a window and marks it for being freed at the end of the frame + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of the window to be closed */ +NK_API void nk_window_close(struct nk_context *ctx, const char *name); +/* nk_window_collapse - updates collapse state of a window with given name + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of the window to be either collapse or maximize */ +NK_API void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state); +/* nk_window_collapse - updates collapse state of a window with given name if given condition is met + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of the window to be either collapse or maximize + * @state the window should be put into + * @condition that has to be true to actually commit the collsage state change */ +NK_API void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond); +/* nk_window_show - updates visibility state of a window with given name + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of the window to be either collapse or maximize + * @state with either visible or hidden to modify the window with */ +NK_API void nk_window_show(struct nk_context*, const char *name, enum nk_show_states); +/* nk_window_show_if - updates visibility state of a window with given name if a given condition is met + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @name of the window to be either collapse or maximize + * @state with either visible or hidden to modify the window with + * @condition that has to be true to actually commit the visible state change */ +NK_API void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond); +/* ============================================================================= + * + * LAYOUT + * + * ============================================================================= */ +/* Layouting in general describes placing widget inside a window with position and size. + * While in this particular implementation there are five different APIs for layouting + * each with different trade offs between control and ease of use. + * + * All layouting methodes in this library are based around the concept of a row. + * A row has a height the window content grows by and a number of columns and each + * layouting method specifies how each widget is placed inside the row. + * After a row has been allocated by calling a layouting functions and then + * filled with widgets will advance an internal pointer over the allocated row. + * + * To acually define a layout you just call the appropriate layouting function + * and each subsequnetial widget call will place the widget as specified. Important + * here is that if you define more widgets then columns defined inside the layout + * functions it will allocate the next row without you having to make another layouting + * call. + * + * Biggest limitation with using all these APIs outside the `nk_layout_space_xxx` API + * is that you have to define the row height for each. However the row height + * often depends on the height of the font. + * + * To fix that internally nuklear uses a minimum row height that is set to the + * height plus padding of currently active font and overwrites the row height + * value if zero. + * + * If you manually want to change the minimum row height then + * use nk_layout_set_min_row_height, and use nk_layout_reset_min_row_height to + * reset it back to be derived from font height. + * + * Also if you change the font in nuklear it will automatically change the minimum + * row height for you and. This means if you change the font but still want + * a minimum row height smaller than the font you have to repush your value. + * + * For actually more advanced UI I would even recommend using the `nk_layout_space_xxx` + * layouting method in combination with a cassowary constraint solver (there are + * some versions on github with permissive license model) to take over all control over widget + * layouting yourself. However for quick and dirty layouting using all the other layouting + * functions should be fine. + * + * Usage + * ------------------- + * 1.) nk_layout_row_dynamic + * The easiest layouting function is `nk_layout_row_dynamic`. It provides each + * widgets with same horizontal space inside the row and dynamically grows + * if the owning window grows in width. So the number of columns dictates + * the size of each widget dynamically by formula: + * + * widget_width = (window_width - padding - spacing) * (1/colum_count) + * + * Just like all other layouting APIs if you define more widget than columns this + * library will allocate a new row and keep all layouting parameters previously + * defined. + * + * if (nk_begin_xxx(...) { + * // first row with height: 30 composed of two widgets + * nk_layout_row_dynamic(&ctx, 30, 2); + * nk_widget(...); + * nk_widget(...); + * + * // second row with same parameter as defined above + * nk_widget(...); + * nk_widget(...); + * + * // third row uses 0 for height which will use auto layouting + * nk_layout_row_dynamic(&ctx, 0, 2); + * nk_widget(...); + * nk_widget(...); + * } + * nk_end(...); + * + * 2.) nk_layout_row_static + * Another easy layouting function is `nk_layout_row_static`. It provides each + * widget with same horizontal pixel width inside the row and does not grow + * if the owning window scales smaller or bigger. + * + * if (nk_begin_xxx(...) { + * // first row with height: 30 composed of two widgets with width: 80 + * nk_layout_row_static(&ctx, 30, 80, 2); + * nk_widget(...); + * nk_widget(...); + * + * // second row with same parameter as defined above + * nk_widget(...); + * nk_widget(...); + * + * // third row uses 0 for height which will use auto layouting + * nk_layout_row_static(&ctx, 0, 80, 2); + * nk_widget(...); + * nk_widget(...); + * } + * nk_end(...); + * + * 3.) nk_layout_row_xxx + * A little bit more advanced layouting API are functions `nk_layout_row_begin`, + * `nk_layout_row_push` and `nk_layout_row_end`. They allow to directly + * specify each column pixel or window ratio in a row. It supports either + * directly setting per column pixel width or widget window ratio but not + * both. Furthermore it is a immediate mode API so each value is directly + * pushed before calling a widget. Therefore the layout is not automatically + * repeating like the last two layouting functions. + * + * if (nk_begin_xxx(...) { + * // first row with height: 25 composed of two widgets with width 60 and 40 + * nk_layout_row_begin(ctx, NK_STATIC, 25, 2); + * nk_layout_row_push(ctx, 60); + * nk_widget(...); + * nk_layout_row_push(ctx, 40); + * nk_widget(...); + * nk_layout_row_end(ctx); + * + * // second row with height: 25 composed of two widgets with window ratio 0.25 and 0.75 + * nk_layout_row_begin(ctx, NK_DYNAMIC, 25, 2); + * nk_layout_row_push(ctx, 0.25f); + * nk_widget(...); + * nk_layout_row_push(ctx, 0.75f); + * nk_widget(...); + * nk_layout_row_end(ctx); + * + * // third row with auto generated height: composed of two widgets with window ratio 0.25 and 0.75 + * nk_layout_row_begin(ctx, NK_DYNAMIC, 0, 2); + * nk_layout_row_push(ctx, 0.25f); + * nk_widget(...); + * nk_layout_row_push(ctx, 0.75f); + * nk_widget(...); + * nk_layout_row_end(ctx); + * } + * nk_end(...); + * + * 4.) nk_layout_row + * The array counterpart to API nk_layout_row_xxx is the single nk_layout_row + * functions. Instead of pushing either pixel or window ratio for every widget + * it allows to define it by array. The trade of for less control is that + * `nk_layout_row` is automatically repeating. Otherwise the behavior is the + * same. + * + * if (nk_begin_xxx(...) { + * // two rows with height: 30 composed of two widgets with width 60 and 40 + * const float size[] = {60,40}; + * nk_layout_row(ctx, NK_STATIC, 30, 2, ratio); + * nk_widget(...); + * nk_widget(...); + * nk_widget(...); + * nk_widget(...); + * + * // two rows with height: 30 composed of two widgets with window ratio 0.25 and 0.75 + * const float ratio[] = {0.25, 0.75}; + * nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); + * nk_widget(...); + * nk_widget(...); + * nk_widget(...); + * nk_widget(...); + * + * // two rows with auto generated height composed of two widgets with window ratio 0.25 and 0.75 + * const float ratio[] = {0.25, 0.75}; + * nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); + * nk_widget(...); + * nk_widget(...); + * nk_widget(...); + * nk_widget(...); + * } + * nk_end(...); + * + * 5.) nk_layout_row_template_xxx + * The most complex and second most flexible API is a simplified flexbox version without + * line wrapping and weights for dynamic widgets. It is an immediate mode API but + * unlike `nk_layout_row_xxx` it has auto repeat behavior and needs to be called + * before calling the templated widgets. + * The row template layout has three different per widget size specifier. The first + * one is the static widget size specifier with fixed widget pixel width. They do + * not grow if the row grows and will always stay the same. The second size + * specifier is nk_layout_row_template_push_variable which defines a + * minumum widget size but it also can grow if more space is available not taken + * by other widgets. Finally there are dynamic widgets which are completly flexible + * and unlike variable widgets can even shrink to zero if not enough space + * is provided. + * + * if (nk_begin_xxx(...) { + * // two rows with height: 30 composed of three widgets + * nk_layout_row_template_begin(ctx, 30); + * nk_layout_row_template_push_dynamic(ctx); + * nk_layout_row_template_push_variable(ctx, 80); + * nk_layout_row_template_push_static(ctx, 80); + * nk_layout_row_template_end(ctx); + * + * nk_widget(...); // dynamic widget can go to zero if not enough space + * nk_widget(...); // variable widget with min 80 pixel but can grow bigger if enough space + * nk_widget(...); // static widget with fixed 80 pixel width + * + * // second row same layout + * nk_widget(...); + * nk_widget(...); + * nk_widget(...); + * } + * nk_end(...); + * + * 6.) nk_layout_space_xxx + * Finally the most flexible API directly allows you to place widgets inside the + * window. The space layout API is an immediate mode API which does not support + * row auto repeat and directly sets position and size of a widget. Position + * and size hereby can be either specified as ratio of alloated space or + * allocated space local position and pixel size. Since this API is quite + * powerfull there are a number of utility functions to get the available space + * and convert between local allocated space and screen space. + * + * if (nk_begin_xxx(...) { + * // static row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) + * nk_layout_space_begin(ctx, NK_STATIC, 500, INT_MAX); + * nk_layout_space_push(ctx, nk_rect(0,0,150,200)); + * nk_widget(...); + * nk_layout_space_push(ctx, nk_rect(200,200,100,200)); + * nk_widget(...); + * nk_layout_space_end(ctx); + * + * // dynamic row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) + * nk_layout_space_begin(ctx, NK_DYNAMIC, 500, INT_MAX); + * nk_layout_space_push(ctx, nk_rect(0.5,0.5,0.1,0.1)); + * nk_widget(...); + * nk_layout_space_push(ctx, nk_rect(0.7,0.6,0.1,0.1)); + * nk_widget(...); + * } + * nk_end(...); + * + * Reference + * ------------------- + * nk_layout_set_min_row_height - set the currently used minimum row height to a specified value + * nk_layout_reset_min_row_height - resets the currently used minimum row height to font height + * + * nk_layout_widget_bounds - calculates current width a static layout row can fit inside a window + * nk_layout_ratio_from_pixel - utility functions to calculate window ratio from pixel size + * + * nk_layout_row_dynamic - current layout is divided into n same sized gowing columns + * nk_layout_row_static - current layout is divided into n same fixed sized columns + * nk_layout_row_begin - starts a new row with given height and number of columns + * nk_layout_row_push - pushes another column with given size or window ratio + * nk_layout_row_end - finished previously started row + * nk_layout_row - specifies row columns in array as either window ratio or size + * + * nk_layout_row_template_begin - begins the row template declaration + * nk_layout_row_template_push_dynamic - adds a dynamic column that dynamically grows and can go to zero if not enough space + * nk_layout_row_template_push_variable - adds a variable column that dynamically grows but does not shrink below specified pixel width + * nk_layout_row_template_push_static - adds a static column that does not grow and will always have the same size + * nk_layout_row_template_end - marks the end of the row template + * + * nk_layout_space_begin - begins a new layouting space that allows to specify each widgets position and size + * nk_layout_space_push - pushes position and size of the next widget in own coordiante space either as pixel or ratio + * nk_layout_space_end - marks the end of the layouting space + * + * nk_layout_space_bounds - callable after nk_layout_space_begin and returns total space allocated + * nk_layout_space_to_screen - convertes vector from nk_layout_space coordiant space into screen space + * nk_layout_space_to_local - convertes vector from screem space into nk_layout_space coordinates + * nk_layout_space_rect_to_screen - convertes rectangle from nk_layout_space coordiant space into screen space + * nk_layout_space_rect_to_local - convertes rectangle from screem space into nk_layout_space coordinates + */ +/* nk_layout_set_min_row_height - sets the currently used minimum row height. + * IMPORTANT: The passed height needs to include both your prefered row height + * as well as padding. No internal padding is added. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` + * @height new minimum row height to be used for auto generating the row height */ +NK_API void nk_layout_set_min_row_height(struct nk_context*, float height); +/* nk_layout_reset_min_row_height - Reset the currently used minimum row height + * back to font height + text padding + additional padding (style_window.min_row_height_padding) + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` */ +NK_API void nk_layout_reset_min_row_height(struct nk_context*); +/* nk_layout_widget_bounds - returns the width of the next row allocate by one of the layouting functions + * Parameters: + * @ctx must point to an previously initialized `nk_context` */ +NK_API struct nk_rect nk_layout_widget_bounds(struct nk_context*); +/* nk_layout_ratio_from_pixel - utility functions to calculate window ratio from pixel size + * Parameters: + * @ctx must point to an previously initialized `nk_context` + * @pixel_width to convert to window ratio */ +NK_API float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width); +/* nk_layout_row_dynamic - Sets current row layout to share horizontal space + * between @cols number of widgets evenly. Once called all subsequent widget + * calls greater than @cols will allocate a new row with same layout. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` + * @row_height holds height of each widget in row or zero for auto layouting + * @cols number of widget inside row */ +NK_API void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols); +/* nk_layout_row_static - Sets current row layout to fill @cols number of widgets + * in row with same @item_width horizontal size. Once called all subsequent widget + * calls greater than @cols will allocate a new row with same layout. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` + * @height holds row height to allocate from panel for widget height + * @item_width holds width of each widget in row + * @cols number of widget inside row */ +NK_API void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols); +/* nk_layout_row_begin - Starts a new dynamic or fixed row with given height and columns. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` + * @fmt either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns + * @row_height holds height of each widget in row or zero for auto layouting + * @cols number of widget inside row */ +NK_API void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols); +/* nk_layout_row_push - Specifies either window ratio or width of a single column + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_begin` + * @value either a window ratio or fixed width depending on @fmt in previous `nk_layout_row_begin` call */ +NK_API void nk_layout_row_push(struct nk_context*, float value); +/* nk_layout_row_end - finished previously started row + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_begin` */ +NK_API void nk_layout_row_end(struct nk_context*); +/* nk_layout_row - specifies row columns in array as either window ratio or size + * Parameters: + * @ctx must point to an previously initialized `nk_context` + * @fmt either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns + * @row_height holds height of each widget in row or zero for auto layouting + * @cols number of widget inside row */ +NK_API void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio); +/* nk_layout_row_template_begin - Begins the row template declaration + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @row_height holds height of each widget in row or zero for auto layouting */ +NK_API void nk_layout_row_template_begin(struct nk_context*, float row_height); +/* nk_layout_row_template_push_dynamic - adds a dynamic column that dynamically grows and can go to zero if not enough space + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` */ +NK_API void nk_layout_row_template_push_dynamic(struct nk_context*); +/* nk_layout_row_template_push_variable - adds a variable column that dynamically grows but does not shrink below specified pixel width + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` + * @min_width holds the minimum pixel width the next column must be */ +NK_API void nk_layout_row_template_push_variable(struct nk_context*, float min_width); +/* nk_layout_row_template_push_static - adds a static column that does not grow and will always have the same size + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` + * @width holds the absolulte pixel width value the next column must be */ +NK_API void nk_layout_row_template_push_static(struct nk_context*, float width); +/* nk_layout_row_template_end - marks the end of the row template + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` */ +NK_API void nk_layout_row_template_end(struct nk_context*); +/* nk_layout_space_begin - begins a new layouting space that allows to specify each widgets position and size. + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct + * @fmt either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns + * @row_height holds height of each widget in row or zero for auto layouting + * @widget_count number of widgets inside row */ +NK_API void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count); +/* nk_layout_space_push - pushes position and size of the next widget in own coordiante space either as pixel or ratio + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` + * @bounds position and size in laoyut space local coordinates */ +NK_API void nk_layout_space_push(struct nk_context*, struct nk_rect); +/* nk_layout_space_end - marks the end of the layout space + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` */ +NK_API void nk_layout_space_end(struct nk_context*); +/* nk_layout_space_bounds - returns total space allocated for `nk_layout_space` + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` */ +NK_API struct nk_rect nk_layout_space_bounds(struct nk_context*); +/* nk_layout_space_to_screen - convertes vector from nk_layout_space coordiant space into screen space + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` + * @vec position to convert from layout space into screen coordinate space */ +NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2); +/* nk_layout_space_to_screen - convertes vector from layout space into screen space + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` + * @vec position to convert from screen space into layout coordinate space */ +NK_API struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2); +/* nk_layout_space_rect_to_screen - convertes rectangle from screen space into layout space + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` + * @bounds rectangle to convert from layout space into screen space */ +NK_API struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect); +/* nk_layout_space_rect_to_local - convertes rectangle from layout space into screen space + * Parameters: + * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` + * @bounds rectangle to convert from screen space into layout space */ +NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect); +/* ============================================================================= + * + * GROUP + * + * ============================================================================= */ +NK_API int nk_group_begin(struct nk_context*, const char *title, nk_flags); +NK_API int nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char*, nk_flags); +NK_API int nk_group_scrolled_begin(struct nk_context*, struct nk_scroll*, const char *title, nk_flags); +NK_API void nk_group_scrolled_end(struct nk_context*); +NK_API void nk_group_end(struct nk_context*); +/* ============================================================================= + * + * LIST VIEW + * + * ============================================================================= */ +struct nk_list_view { +/* public: */ + int begin, end, count; +/* private: */ + int total_height; + struct nk_context *ctx; + nk_uint *scroll_pointer; + nk_uint scroll_value; +}; +NK_API int nk_list_view_begin(struct nk_context*, struct nk_list_view *out, const char *id, nk_flags, int row_height, int row_count); +NK_API void nk_list_view_end(struct nk_list_view*); +/* ============================================================================= + * + * TREE + * + * ============================================================================= */ +#define nk_tree_push(ctx, type, title, state) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +#define nk_tree_push_id(ctx, type, title, state, id) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +NK_API int nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +#define nk_tree_image_push(ctx, type, img, title, state) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +#define nk_tree_image_push_id(ctx, type, img, title, state, id) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +NK_API int nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +NK_API void nk_tree_pop(struct nk_context*); +NK_API int nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state); +NK_API int nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state); +NK_API void nk_tree_state_pop(struct nk_context*); +/* ============================================================================= + * + * WIDGET + * + * ============================================================================= */ +enum nk_widget_layout_states { + NK_WIDGET_INVALID, /* The widget cannot be seen and is completely out of view */ + NK_WIDGET_VALID, /* The widget is completely inside the window and can be updated and drawn */ + NK_WIDGET_ROM /* The widget is partially visible and cannot be updated */ +}; +enum nk_widget_states { + NK_WIDGET_STATE_MODIFIED = NK_FLAG(1), + NK_WIDGET_STATE_INACTIVE = NK_FLAG(2), /* widget is neither active nor hovered */ + NK_WIDGET_STATE_ENTERED = NK_FLAG(3), /* widget has been hovered on the current frame */ + NK_WIDGET_STATE_HOVER = NK_FLAG(4), /* widget is being hovered */ + NK_WIDGET_STATE_ACTIVED = NK_FLAG(5),/* widget is currently activated */ + NK_WIDGET_STATE_LEFT = NK_FLAG(6), /* widget is from this frame on not hovered anymore */ + NK_WIDGET_STATE_HOVERED = NK_WIDGET_STATE_HOVER|NK_WIDGET_STATE_MODIFIED, /* widget is being hovered */ + NK_WIDGET_STATE_ACTIVE = NK_WIDGET_STATE_ACTIVED|NK_WIDGET_STATE_MODIFIED /* widget is currently activated */ +}; +NK_API enum nk_widget_layout_states nk_widget(struct nk_rect*, const struct nk_context*); +NK_API enum nk_widget_layout_states nk_widget_fitting(struct nk_rect*, struct nk_context*, struct nk_vec2); +NK_API struct nk_rect nk_widget_bounds(struct nk_context*); +NK_API struct nk_vec2 nk_widget_position(struct nk_context*); +NK_API struct nk_vec2 nk_widget_size(struct nk_context*); +NK_API float nk_widget_width(struct nk_context*); +NK_API float nk_widget_height(struct nk_context*); +NK_API int nk_widget_is_hovered(struct nk_context*); +NK_API int nk_widget_is_mouse_clicked(struct nk_context*, enum nk_buttons); +NK_API int nk_widget_has_mouse_click_down(struct nk_context*, enum nk_buttons, int down); +NK_API void nk_spacing(struct nk_context*, int cols); +/* ============================================================================= + * + * TEXT + * + * ============================================================================= */ +enum nk_text_align { + NK_TEXT_ALIGN_LEFT = 0x01, + NK_TEXT_ALIGN_CENTERED = 0x02, + NK_TEXT_ALIGN_RIGHT = 0x04, + NK_TEXT_ALIGN_TOP = 0x08, + NK_TEXT_ALIGN_MIDDLE = 0x10, + NK_TEXT_ALIGN_BOTTOM = 0x20 +}; +enum nk_text_alignment { + NK_TEXT_LEFT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_LEFT, + NK_TEXT_CENTERED = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_CENTERED, + NK_TEXT_RIGHT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_RIGHT +}; +NK_API void nk_text(struct nk_context*, const char*, int, nk_flags); +NK_API void nk_text_colored(struct nk_context*, const char*, int, nk_flags, struct nk_color); +NK_API void nk_text_wrap(struct nk_context*, const char*, int); +NK_API void nk_text_wrap_colored(struct nk_context*, const char*, int, struct nk_color); +NK_API void nk_label(struct nk_context*, const char*, nk_flags align); +NK_API void nk_label_colored(struct nk_context*, const char*, nk_flags align, struct nk_color); +NK_API void nk_label_wrap(struct nk_context*, const char*); +NK_API void nk_label_colored_wrap(struct nk_context*, const char*, struct nk_color); +NK_API void nk_image(struct nk_context*, struct nk_image); +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_API void nk_labelf(struct nk_context*, nk_flags, const char*, ...); +NK_API void nk_labelf_colored(struct nk_context*, nk_flags align, struct nk_color, const char*,...); +NK_API void nk_labelf_wrap(struct nk_context*, const char*,...); +NK_API void nk_labelf_colored_wrap(struct nk_context*, struct nk_color, const char*,...); +NK_API void nk_value_bool(struct nk_context*, const char *prefix, int); +NK_API void nk_value_int(struct nk_context*, const char *prefix, int); +NK_API void nk_value_uint(struct nk_context*, const char *prefix, unsigned int); +NK_API void nk_value_float(struct nk_context*, const char *prefix, float); +NK_API void nk_value_color_byte(struct nk_context*, const char *prefix, struct nk_color); +NK_API void nk_value_color_float(struct nk_context*, const char *prefix, struct nk_color); +NK_API void nk_value_color_hex(struct nk_context*, const char *prefix, struct nk_color); +#endif +/* ============================================================================= + * + * BUTTON + * + * ============================================================================= */ +NK_API int nk_button_text(struct nk_context*, const char *title, int len); +NK_API int nk_button_label(struct nk_context*, const char *title); +NK_API int nk_button_color(struct nk_context*, struct nk_color); +NK_API int nk_button_symbol(struct nk_context*, enum nk_symbol_type); +NK_API int nk_button_image(struct nk_context*, struct nk_image img); +NK_API int nk_button_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags text_alignment); +NK_API int nk_button_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API int nk_button_image_label(struct nk_context*, struct nk_image img, const char*, nk_flags text_alignment); +NK_API int nk_button_image_text(struct nk_context*, struct nk_image img, const char*, int, nk_flags alignment); +NK_API int nk_button_text_styled(struct nk_context*, const struct nk_style_button*, const char *title, int len); +NK_API int nk_button_label_styled(struct nk_context*, const struct nk_style_button*, const char *title); +NK_API int nk_button_symbol_styled(struct nk_context*, const struct nk_style_button*, enum nk_symbol_type); +NK_API int nk_button_image_styled(struct nk_context*, const struct nk_style_button*, struct nk_image img); +NK_API int nk_button_symbol_text_styled(struct nk_context*,const struct nk_style_button*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API int nk_button_symbol_label_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol, const char *title, nk_flags align); +NK_API int nk_button_image_label_styled(struct nk_context*,const struct nk_style_button*, struct nk_image img, const char*, nk_flags text_alignment); +NK_API int nk_button_image_text_styled(struct nk_context*,const struct nk_style_button*, struct nk_image img, const char*, int, nk_flags alignment); +NK_API void nk_button_set_behavior(struct nk_context*, enum nk_button_behavior); +NK_API int nk_button_push_behavior(struct nk_context*, enum nk_button_behavior); +NK_API int nk_button_pop_behavior(struct nk_context*); +/* ============================================================================= + * + * CHECKBOX + * + * ============================================================================= */ +NK_API int nk_check_label(struct nk_context*, const char*, int active); +NK_API int nk_check_text(struct nk_context*, const char*, int,int active); +NK_API unsigned nk_check_flags_label(struct nk_context*, const char*, unsigned int flags, unsigned int value); +NK_API unsigned nk_check_flags_text(struct nk_context*, const char*, int, unsigned int flags, unsigned int value); +NK_API int nk_checkbox_label(struct nk_context*, const char*, int *active); +NK_API int nk_checkbox_text(struct nk_context*, const char*, int, int *active); +NK_API int nk_checkbox_flags_label(struct nk_context*, const char*, unsigned int *flags, unsigned int value); +NK_API int nk_checkbox_flags_text(struct nk_context*, const char*, int, unsigned int *flags, unsigned int value); +/* ============================================================================= + * + * RADIO BUTTON + * + * ============================================================================= */ +NK_API int nk_radio_label(struct nk_context*, const char*, int *active); +NK_API int nk_radio_text(struct nk_context*, const char*, int, int *active); +NK_API int nk_option_label(struct nk_context*, const char*, int active); +NK_API int nk_option_text(struct nk_context*, const char*, int, int active); +/* ============================================================================= + * + * SELECTABLE + * + * ============================================================================= */ +NK_API int nk_selectable_label(struct nk_context*, const char*, nk_flags align, int *value); +NK_API int nk_selectable_text(struct nk_context*, const char*, int, nk_flags align, int *value); +NK_API int nk_selectable_image_label(struct nk_context*,struct nk_image, const char*, nk_flags align, int *value); +NK_API int nk_selectable_image_text(struct nk_context*,struct nk_image, const char*, int, nk_flags align, int *value); +NK_API int nk_select_label(struct nk_context*, const char*, nk_flags align, int value); +NK_API int nk_select_text(struct nk_context*, const char*, int, nk_flags align, int value); +NK_API int nk_select_image_label(struct nk_context*, struct nk_image,const char*, nk_flags align, int value); +NK_API int nk_select_image_text(struct nk_context*, struct nk_image,const char*, int, nk_flags align, int value); +/* ============================================================================= + * + * SLIDER + * + * ============================================================================= */ +NK_API float nk_slide_float(struct nk_context*, float min, float val, float max, float step); +NK_API int nk_slide_int(struct nk_context*, int min, int val, int max, int step); +NK_API int nk_slider_float(struct nk_context*, float min, float *val, float max, float step); +NK_API int nk_slider_int(struct nk_context*, int min, int *val, int max, int step); +/* ============================================================================= + * + * PROGRESSBAR + * + * ============================================================================= */ +NK_API int nk_progress(struct nk_context*, nk_size *cur, nk_size max, int modifyable); +NK_API nk_size nk_prog(struct nk_context*, nk_size cur, nk_size max, int modifyable); + +/* ============================================================================= + * + * COLOR PICKER + * + * ============================================================================= */ +NK_API struct nk_color nk_color_picker(struct nk_context*, struct nk_color, enum nk_color_format); +NK_API int nk_color_pick(struct nk_context*, struct nk_color*, enum nk_color_format); +/* ============================================================================= + * + * PROPERTIES + * + * ============================================================================= */ +NK_API void nk_property_int(struct nk_context*, const char *name, int min, int *val, int max, int step, float inc_per_pixel); +NK_API void nk_property_float(struct nk_context*, const char *name, float min, float *val, float max, float step, float inc_per_pixel); +NK_API void nk_property_double(struct nk_context*, const char *name, double min, double *val, double max, double step, float inc_per_pixel); +NK_API int nk_propertyi(struct nk_context*, const char *name, int min, int val, int max, int step, float inc_per_pixel); +NK_API float nk_propertyf(struct nk_context*, const char *name, float min, float val, float max, float step, float inc_per_pixel); +NK_API double nk_propertyd(struct nk_context*, const char *name, double min, double val, double max, double step, float inc_per_pixel); +/* ============================================================================= + * + * TEXT EDIT + * + * ============================================================================= */ +enum nk_edit_flags { + NK_EDIT_DEFAULT = 0, + NK_EDIT_READ_ONLY = NK_FLAG(0), + NK_EDIT_AUTO_SELECT = NK_FLAG(1), + NK_EDIT_SIG_ENTER = NK_FLAG(2), + NK_EDIT_ALLOW_TAB = NK_FLAG(3), + NK_EDIT_NO_CURSOR = NK_FLAG(4), + NK_EDIT_SELECTABLE = NK_FLAG(5), + NK_EDIT_CLIPBOARD = NK_FLAG(6), + NK_EDIT_CTRL_ENTER_NEWLINE = NK_FLAG(7), + NK_EDIT_NO_HORIZONTAL_SCROLL = NK_FLAG(8), + NK_EDIT_ALWAYS_INSERT_MODE = NK_FLAG(9), + NK_EDIT_MULTILINE = NK_FLAG(10), + NK_EDIT_GOTO_END_ON_ACTIVATE = NK_FLAG(11) +}; +enum nk_edit_types { + NK_EDIT_SIMPLE = NK_EDIT_ALWAYS_INSERT_MODE, + NK_EDIT_FIELD = NK_EDIT_SIMPLE|NK_EDIT_SELECTABLE|NK_EDIT_CLIPBOARD, + NK_EDIT_BOX = NK_EDIT_ALWAYS_INSERT_MODE| NK_EDIT_SELECTABLE| NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB|NK_EDIT_CLIPBOARD, + NK_EDIT_EDITOR = NK_EDIT_SELECTABLE|NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB| NK_EDIT_CLIPBOARD +}; +enum nk_edit_events { + NK_EDIT_ACTIVE = NK_FLAG(0), /* edit widget is currently being modified */ + NK_EDIT_INACTIVE = NK_FLAG(1), /* edit widget is not active and is not being modified */ + NK_EDIT_ACTIVATED = NK_FLAG(2), /* edit widget went from state inactive to state active */ + NK_EDIT_DEACTIVATED = NK_FLAG(3), /* edit widget went from state active to state inactive */ + NK_EDIT_COMMITED = NK_FLAG(4) /* edit widget has received an enter and lost focus */ +}; +NK_API nk_flags nk_edit_string(struct nk_context*, nk_flags, char *buffer, int *len, int max, nk_plugin_filter); +NK_API nk_flags nk_edit_string_zero_terminated(struct nk_context*, nk_flags, char *buffer, int max, nk_plugin_filter); +NK_API nk_flags nk_edit_buffer(struct nk_context*, nk_flags, struct nk_text_edit*, nk_plugin_filter); +NK_API void nk_edit_focus(struct nk_context*, nk_flags flags); +NK_API void nk_edit_unfocus(struct nk_context*); +/* ============================================================================= + * + * CHART + * + * ============================================================================= */ +NK_API int nk_chart_begin(struct nk_context*, enum nk_chart_type, int num, float min, float max); +NK_API int nk_chart_begin_colored(struct nk_context*, enum nk_chart_type, struct nk_color, struct nk_color active, int num, float min, float max); +NK_API void nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type, int count, float min_value, float max_value); +NK_API void nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type, struct nk_color, struct nk_color active, int count, float min_value, float max_value); +NK_API nk_flags nk_chart_push(struct nk_context*, float); +NK_API nk_flags nk_chart_push_slot(struct nk_context*, float, int); +NK_API void nk_chart_end(struct nk_context*); +NK_API void nk_plot(struct nk_context*, enum nk_chart_type, const float *values, int count, int offset); +NK_API void nk_plot_function(struct nk_context*, enum nk_chart_type, void *userdata, float(*value_getter)(void* user, int index), int count, int offset); +/* ============================================================================= + * + * POPUP + * + * ============================================================================= */ +NK_API int nk_popup_begin(struct nk_context*, enum nk_popup_type, const char*, nk_flags, struct nk_rect bounds); +NK_API void nk_popup_close(struct nk_context*); +NK_API void nk_popup_end(struct nk_context*); +/* ============================================================================= + * + * COMBOBOX + * + * ============================================================================= */ +NK_API int nk_combo(struct nk_context*, const char **items, int count, int selected, int item_height, struct nk_vec2 size); +NK_API int nk_combo_separator(struct nk_context*, const char *items_separated_by_separator, int separator, int selected, int count, int item_height, struct nk_vec2 size); +NK_API int nk_combo_string(struct nk_context*, const char *items_separated_by_zeros, int selected, int count, int item_height, struct nk_vec2 size); +NK_API int nk_combo_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void *userdata, int selected, int count, int item_height, struct nk_vec2 size); +NK_API void nk_combobox(struct nk_context*, const char **items, int count, int *selected, int item_height, struct nk_vec2 size); +NK_API void nk_combobox_string(struct nk_context*, const char *items_separated_by_zeros, int *selected, int count, int item_height, struct nk_vec2 size); +NK_API void nk_combobox_separator(struct nk_context*, const char *items_separated_by_separator, int separator,int *selected, int count, int item_height, struct nk_vec2 size); +NK_API void nk_combobox_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void*, int *selected, int count, int item_height, struct nk_vec2 size); +/* ============================================================================= + * + * ABSTRACT COMBOBOX + * + * ============================================================================= */ +NK_API int nk_combo_begin_text(struct nk_context*, const char *selected, int, struct nk_vec2 size); +NK_API int nk_combo_begin_label(struct nk_context*, const char *selected, struct nk_vec2 size); +NK_API int nk_combo_begin_color(struct nk_context*, struct nk_color color, struct nk_vec2 size); +NK_API int nk_combo_begin_symbol(struct nk_context*, enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_combo_begin_symbol_label(struct nk_context*, const char *selected, enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_combo_begin_symbol_text(struct nk_context*, const char *selected, int, enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_combo_begin_image(struct nk_context*, struct nk_image img, struct nk_vec2 size); +NK_API int nk_combo_begin_image_label(struct nk_context*, const char *selected, struct nk_image, struct nk_vec2 size); +NK_API int nk_combo_begin_image_text(struct nk_context*, const char *selected, int, struct nk_image, struct nk_vec2 size); +NK_API int nk_combo_item_label(struct nk_context*, const char*, nk_flags alignment); +NK_API int nk_combo_item_text(struct nk_context*, const char*,int, nk_flags alignment); +NK_API int nk_combo_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API int nk_combo_item_image_text(struct nk_context*, struct nk_image, const char*, int,nk_flags alignment); +NK_API int nk_combo_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API int nk_combo_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API void nk_combo_close(struct nk_context*); +NK_API void nk_combo_end(struct nk_context*); +/* ============================================================================= + * + * CONTEXTUAL + * + * ============================================================================= */ +NK_API int nk_contextual_begin(struct nk_context*, nk_flags, struct nk_vec2, struct nk_rect trigger_bounds); +NK_API int nk_contextual_item_text(struct nk_context*, const char*, int,nk_flags align); +NK_API int nk_contextual_item_label(struct nk_context*, const char*, nk_flags align); +NK_API int nk_contextual_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API int nk_contextual_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); +NK_API int nk_contextual_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API int nk_contextual_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API void nk_contextual_close(struct nk_context*); +NK_API void nk_contextual_end(struct nk_context*); +/* ============================================================================= + * + * TOOLTIP + * + * ============================================================================= */ +NK_API void nk_tooltip(struct nk_context*, const char*); +NK_API int nk_tooltip_begin(struct nk_context*, float width); +NK_API void nk_tooltip_end(struct nk_context*); +/* ============================================================================= + * + * MENU + * + * ============================================================================= */ +NK_API void nk_menubar_begin(struct nk_context*); +NK_API void nk_menubar_end(struct nk_context*); +NK_API int nk_menu_begin_text(struct nk_context*, const char* title, int title_len, nk_flags align, struct nk_vec2 size); +NK_API int nk_menu_begin_label(struct nk_context*, const char*, nk_flags align, struct nk_vec2 size); +NK_API int nk_menu_begin_image(struct nk_context*, const char*, struct nk_image, struct nk_vec2 size); +NK_API int nk_menu_begin_image_text(struct nk_context*, const char*, int,nk_flags align,struct nk_image, struct nk_vec2 size); +NK_API int nk_menu_begin_image_label(struct nk_context*, const char*, nk_flags align,struct nk_image, struct nk_vec2 size); +NK_API int nk_menu_begin_symbol(struct nk_context*, const char*, enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_menu_begin_symbol_text(struct nk_context*, const char*, int,nk_flags align,enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_menu_begin_symbol_label(struct nk_context*, const char*, nk_flags align,enum nk_symbol_type, struct nk_vec2 size); +NK_API int nk_menu_item_text(struct nk_context*, const char*, int,nk_flags align); +NK_API int nk_menu_item_label(struct nk_context*, const char*, nk_flags alignment); +NK_API int nk_menu_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); +NK_API int nk_menu_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); +NK_API int nk_menu_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); +NK_API int nk_menu_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); +NK_API void nk_menu_close(struct nk_context*); +NK_API void nk_menu_end(struct nk_context*); +/* ============================================================================= + * + * STYLE + * + * ============================================================================= */ +enum nk_style_colors { + NK_COLOR_TEXT, + NK_COLOR_WINDOW, + NK_COLOR_HEADER, + NK_COLOR_BORDER, + NK_COLOR_BUTTON, + NK_COLOR_BUTTON_HOVER, + NK_COLOR_BUTTON_ACTIVE, + NK_COLOR_TOGGLE, + NK_COLOR_TOGGLE_HOVER, + NK_COLOR_TOGGLE_CURSOR, + NK_COLOR_SELECT, + NK_COLOR_SELECT_ACTIVE, + NK_COLOR_SLIDER, + NK_COLOR_SLIDER_CURSOR, + NK_COLOR_SLIDER_CURSOR_HOVER, + NK_COLOR_SLIDER_CURSOR_ACTIVE, + NK_COLOR_PROPERTY, + NK_COLOR_EDIT, + NK_COLOR_EDIT_CURSOR, + NK_COLOR_COMBO, + NK_COLOR_CHART, + NK_COLOR_CHART_COLOR, + NK_COLOR_CHART_COLOR_HIGHLIGHT, + NK_COLOR_SCROLLBAR, + NK_COLOR_SCROLLBAR_CURSOR, + NK_COLOR_SCROLLBAR_CURSOR_HOVER, + NK_COLOR_SCROLLBAR_CURSOR_ACTIVE, + NK_COLOR_TAB_HEADER, + NK_COLOR_COUNT +}; +enum nk_style_cursor { + NK_CURSOR_ARROW, + NK_CURSOR_TEXT, + NK_CURSOR_MOVE, + NK_CURSOR_RESIZE_VERTICAL, + NK_CURSOR_RESIZE_HORIZONTAL, + NK_CURSOR_RESIZE_TOP_LEFT_DOWN_RIGHT, + NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT, + NK_CURSOR_COUNT +}; +NK_API void nk_style_default(struct nk_context*); +NK_API void nk_style_from_table(struct nk_context*, const struct nk_color*); +NK_API void nk_style_load_cursor(struct nk_context*, enum nk_style_cursor, const struct nk_cursor*); +NK_API void nk_style_load_all_cursors(struct nk_context*, struct nk_cursor*); +NK_API const char* nk_style_get_color_by_name(enum nk_style_colors); +NK_API void nk_style_set_font(struct nk_context*, const struct nk_user_font*); +NK_API int nk_style_set_cursor(struct nk_context*, enum nk_style_cursor); +NK_API void nk_style_show_cursor(struct nk_context*); +NK_API void nk_style_hide_cursor(struct nk_context*); + +NK_API int nk_style_push_font(struct nk_context*, const struct nk_user_font*); +NK_API int nk_style_push_float(struct nk_context*, float*, float); +NK_API int nk_style_push_vec2(struct nk_context*, struct nk_vec2*, struct nk_vec2); +NK_API int nk_style_push_style_item(struct nk_context*, struct nk_style_item*, struct nk_style_item); +NK_API int nk_style_push_flags(struct nk_context*, nk_flags*, nk_flags); +NK_API int nk_style_push_color(struct nk_context*, struct nk_color*, struct nk_color); + +NK_API int nk_style_pop_font(struct nk_context*); +NK_API int nk_style_pop_float(struct nk_context*); +NK_API int nk_style_pop_vec2(struct nk_context*); +NK_API int nk_style_pop_style_item(struct nk_context*); +NK_API int nk_style_pop_flags(struct nk_context*); +NK_API int nk_style_pop_color(struct nk_context*); +/* ============================================================================= + * + * COLOR + * + * ============================================================================= */ +NK_API struct nk_color nk_rgb(int r, int g, int b); +NK_API struct nk_color nk_rgb_iv(const int *rgb); +NK_API struct nk_color nk_rgb_bv(const nk_byte* rgb); +NK_API struct nk_color nk_rgb_f(float r, float g, float b); +NK_API struct nk_color nk_rgb_fv(const float *rgb); +NK_API struct nk_color nk_rgb_hex(const char *rgb); + +NK_API struct nk_color nk_rgba(int r, int g, int b, int a); +NK_API struct nk_color nk_rgba_u32(nk_uint); +NK_API struct nk_color nk_rgba_iv(const int *rgba); +NK_API struct nk_color nk_rgba_bv(const nk_byte *rgba); +NK_API struct nk_color nk_rgba_f(float r, float g, float b, float a); +NK_API struct nk_color nk_rgba_fv(const float *rgba); +NK_API struct nk_color nk_rgba_hex(const char *rgb); + +NK_API struct nk_color nk_hsv(int h, int s, int v); +NK_API struct nk_color nk_hsv_iv(const int *hsv); +NK_API struct nk_color nk_hsv_bv(const nk_byte *hsv); +NK_API struct nk_color nk_hsv_f(float h, float s, float v); +NK_API struct nk_color nk_hsv_fv(const float *hsv); + +NK_API struct nk_color nk_hsva(int h, int s, int v, int a); +NK_API struct nk_color nk_hsva_iv(const int *hsva); +NK_API struct nk_color nk_hsva_bv(const nk_byte *hsva); +NK_API struct nk_color nk_hsva_f(float h, float s, float v, float a); +NK_API struct nk_color nk_hsva_fv(const float *hsva); + +/* color (conversion nuklear --> user) */ +NK_API void nk_color_f(float *r, float *g, float *b, float *a, struct nk_color); +NK_API void nk_color_fv(float *rgba_out, struct nk_color); +NK_API void nk_color_d(double *r, double *g, double *b, double *a, struct nk_color); +NK_API void nk_color_dv(double *rgba_out, struct nk_color); + +NK_API nk_uint nk_color_u32(struct nk_color); +NK_API void nk_color_hex_rgba(char *output, struct nk_color); +NK_API void nk_color_hex_rgb(char *output, struct nk_color); + +NK_API void nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color); +NK_API void nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color); +NK_API void nk_color_hsv_iv(int *hsv_out, struct nk_color); +NK_API void nk_color_hsv_bv(nk_byte *hsv_out, struct nk_color); +NK_API void nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color); +NK_API void nk_color_hsv_fv(float *hsv_out, struct nk_color); + +NK_API void nk_color_hsva_i(int *h, int *s, int *v, int *a, struct nk_color); +NK_API void nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color); +NK_API void nk_color_hsva_iv(int *hsva_out, struct nk_color); +NK_API void nk_color_hsva_bv(nk_byte *hsva_out, struct nk_color); +NK_API void nk_color_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_color); +NK_API void nk_color_hsva_fv(float *hsva_out, struct nk_color); +/* ============================================================================= + * + * IMAGE + * + * ============================================================================= */ +NK_API nk_handle nk_handle_ptr(void*); +NK_API nk_handle nk_handle_id(int); +NK_API struct nk_image nk_image_handle(nk_handle); +NK_API struct nk_image nk_image_ptr(void*); +NK_API struct nk_image nk_image_id(int); +NK_API int nk_image_is_subimage(const struct nk_image* img); +NK_API struct nk_image nk_subimage_ptr(void*, unsigned short w, unsigned short h, struct nk_rect sub_region); +NK_API struct nk_image nk_subimage_id(int, unsigned short w, unsigned short h, struct nk_rect sub_region); +NK_API struct nk_image nk_subimage_handle(nk_handle, unsigned short w, unsigned short h, struct nk_rect sub_region); +/* ============================================================================= + * + * MATH + * + * ============================================================================= */ +NK_API nk_hash nk_murmur_hash(const void *key, int len, nk_hash seed); +NK_API void nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, float pad_x, float pad_y, enum nk_heading); + +NK_API struct nk_vec2 nk_vec2(float x, float y); +NK_API struct nk_vec2 nk_vec2i(int x, int y); +NK_API struct nk_vec2 nk_vec2v(const float *xy); +NK_API struct nk_vec2 nk_vec2iv(const int *xy); + +NK_API struct nk_rect nk_get_null_rect(void); +NK_API struct nk_rect nk_rect(float x, float y, float w, float h); +NK_API struct nk_rect nk_recti(int x, int y, int w, int h); +NK_API struct nk_rect nk_recta(struct nk_vec2 pos, struct nk_vec2 size); +NK_API struct nk_rect nk_rectv(const float *xywh); +NK_API struct nk_rect nk_rectiv(const int *xywh); +NK_API struct nk_vec2 nk_rect_pos(struct nk_rect); +NK_API struct nk_vec2 nk_rect_size(struct nk_rect); +/* ============================================================================= + * + * STRING + * + * ============================================================================= */ +NK_API int nk_strlen(const char *str); +NK_API int nk_stricmp(const char *s1, const char *s2); +NK_API int nk_stricmpn(const char *s1, const char *s2, int n); +NK_API int nk_strtoi(const char *str, const char **endptr); +NK_API float nk_strtof(const char *str, const char **endptr); +NK_API double nk_strtod(const char *str, const char **endptr); +NK_API int nk_strfilter(const char *text, const char *regexp); +NK_API int nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score); +NK_API int nk_strmatch_fuzzy_text(const char *txt, int txt_len, const char *pattern, int *out_score); +/* ============================================================================= + * + * UTF-8 + * + * ============================================================================= */ +NK_API int nk_utf_decode(const char*, nk_rune*, int); +NK_API int nk_utf_encode(nk_rune, char*, int); +NK_API int nk_utf_len(const char*, int byte_len); +NK_API const char* nk_utf_at(const char *buffer, int length, int index, nk_rune *unicode, int *len); +/* =============================================================== + * + * FONT + * + * ===============================================================*/ +/* Font handling in this library was designed to be quite customizable and lets + you decide what you want to use and what you want to provide. There are three + different ways to use the font atlas. The first two will use your font + handling scheme and only requires essential data to run nuklear. The next + slightly more advanced features is font handling with vertex buffer output. + Finally the most complex API wise is using nuklears font baking API. + + 1.) Using your own implementation without vertex buffer output + -------------------------------------------------------------- + So first up the easiest way to do font handling is by just providing a + `nk_user_font` struct which only requires the height in pixel of the used + font and a callback to calculate the width of a string. This way of handling + fonts is best fitted for using the normal draw shape command API where you + do all the text drawing yourself and the library does not require any kind + of deeper knowledge about which font handling mechanism you use. + IMPORTANT: the `nk_user_font` pointer provided to nuklear has to persist + over the complete life time! I know this sucks but it is currently the only + way to switch between fonts. + + float your_text_width_calculation(nk_handle handle, float height, const char *text, int len) + { + your_font_type *type = handle.ptr; + float text_width = ...; + return text_width; + } + + struct nk_user_font font; + font.userdata.ptr = &your_font_class_or_struct; + font.height = your_font_height; + font.width = your_text_width_calculation; + + struct nk_context ctx; + nk_init_default(&ctx, &font); + + 2.) Using your own implementation with vertex buffer output + -------------------------------------------------------------- + While the first approach works fine if you don't want to use the optional + vertex buffer output it is not enough if you do. To get font handling working + for these cases you have to provide two additional parameters inside the + `nk_user_font`. First a texture atlas handle used to draw text as subimages + of a bigger font atlas texture and a callback to query a character's glyph + information (offset, size, ...). So it is still possible to provide your own + font and use the vertex buffer output. + + float your_text_width_calculation(nk_handle handle, float height, const char *text, int len) + { + your_font_type *type = handle.ptr; + float text_width = ...; + return text_width; + } + void query_your_font_glyph(nk_handle handle, float font_height, struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint) + { + your_font_type *type = handle.ptr; + glyph.width = ...; + glyph.height = ...; + glyph.xadvance = ...; + glyph.uv[0].x = ...; + glyph.uv[0].y = ...; + glyph.uv[1].x = ...; + glyph.uv[1].y = ...; + glyph.offset.x = ...; + glyph.offset.y = ...; + } + + struct nk_user_font font; + font.userdata.ptr = &your_font_class_or_struct; + font.height = your_font_height; + font.width = your_text_width_calculation; + font.query = query_your_font_glyph; + font.texture.id = your_font_texture; + + struct nk_context ctx; + nk_init_default(&ctx, &font); + + 3.) Nuklear font baker + ------------------------------------ + The final approach if you do not have a font handling functionality or don't + want to use it in this library is by using the optional font baker. + The font baker API's can be used to create a font plus font atlas texture + and can be used with or without the vertex buffer output. + + It still uses the `nk_user_font` struct and the two different approaches + previously stated still work. The font baker is not located inside + `nk_context` like all other systems since it can be understood as more of + an extension to nuklear and does not really depend on any `nk_context` state. + + Font baker need to be initialized first by one of the nk_font_atlas_init_xxx + functions. If you don't care about memory just call the default version + `nk_font_atlas_init_default` which will allocate all memory from the standard library. + If you want to control memory allocation but you don't care if the allocated + memory is temporary and therefore can be freed directly after the baking process + is over or permanent you can call `nk_font_atlas_init`. + + After successfull intializing the font baker you can add Truetype(.ttf) fonts from + different sources like memory or from file by calling one of the `nk_font_atlas_add_xxx`. + functions. Adding font will permanently store each font, font config and ttf memory block(!) + inside the font atlas and allows to reuse the font atlas. If you don't want to reuse + the font baker by for example adding additional fonts you can call + `nk_font_atlas_cleanup` after the baking process is over (after calling nk_font_atlas_end). + + As soon as you added all fonts you wanted you can now start the baking process + for every selected glyphes to image by calling `nk_font_atlas_bake`. + The baking process returns image memory, width and height which can be used to + either create your own image object or upload it to any graphics library. + No matter which case you finally have to call `nk_font_atlas_end` which + will free all temporary memory including the font atlas image so make sure + you created our texture beforehand. `nk_font_atlas_end` requires a handle + to your font texture or object and optionally fills a `struct nk_draw_null_texture` + which can be used for the optional vertex output. If you don't want it just + set the argument to `NULL`. + + At this point you are done and if you don't want to reuse the font atlas you + can call `nk_font_atlas_cleanup` to free all truetype blobs and configuration + memory. Finally if you don't use the font atlas and any of it's fonts anymore + you need to call `nk_font_atlas_clear` to free all memory still being used. + + struct nk_font_atlas atlas; + nk_font_atlas_init_default(&atlas); + nk_font_atlas_begin(&atlas); + nk_font *font = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font.ttf", 13, 0); + nk_font *font2 = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font2.ttf", 16, 0); + const void* img = nk_font_atlas_bake(&atlas, &img_width, &img_height, NK_FONT_ATLAS_RGBA32); + nk_font_atlas_end(&atlas, nk_handle_id(texture), 0); + + struct nk_context ctx; + nk_init_default(&ctx, &font->handle); + while (1) { + + } + nk_font_atlas_clear(&atlas); + + The font baker API is probably the most complex API inside this library and + I would suggest reading some of my examples `example/` to get a grip on how + to use the font atlas. There are a number of details I left out. For example + how to merge fonts, configure a font with `nk_font_config` to use other languages, + use another texture coodinate format and a lot more: + + struct nk_font_config cfg = nk_font_config(font_pixel_height); + cfg.merge_mode = nk_false or nk_true; + cfg.range = nk_font_korean_glyph_ranges(); + cfg.coord_type = NK_COORD_PIXEL; + nk_font *font = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font.ttf", 13, &cfg); + +*/ +struct nk_user_font_glyph; +typedef float(*nk_text_width_f)(nk_handle, float h, const char*, int len); +typedef void(*nk_query_font_glyph_f)(nk_handle handle, float font_height, + struct nk_user_font_glyph *glyph, + nk_rune codepoint, nk_rune next_codepoint); + +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +struct nk_user_font_glyph { + struct nk_vec2 uv[2]; + /* texture coordinates */ + struct nk_vec2 offset; + /* offset between top left and glyph */ + float width, height; + /* size of the glyph */ + float xadvance; + /* offset to the next glyph */ +}; +#endif + +struct nk_user_font { + nk_handle userdata; + /* user provided font handle */ + float height; + /* max height of the font */ + nk_text_width_f width; + /* font string width in pixel callback */ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_query_font_glyph_f query; + /* font glyph callback to query drawing info */ + nk_handle texture; + /* texture handle to the used font atlas or texture */ +#endif +}; + +#ifdef NK_INCLUDE_FONT_BAKING +enum nk_font_coord_type { + NK_COORD_UV, /* texture coordinates inside font glyphs are clamped between 0-1 */ + NK_COORD_PIXEL /* texture coordinates inside font glyphs are in absolute pixel */ +}; + +struct nk_baked_font { + float height; + /* height of the font */ + float ascent, descent; + /* font glyphs ascent and descent */ + nk_rune glyph_offset; + /* glyph array offset inside the font glyph baking output array */ + nk_rune glyph_count; + /* number of glyphs of this font inside the glyph baking array output */ + const nk_rune *ranges; + /* font codepoint ranges as pairs of (from/to) and 0 as last element */ +}; + +struct nk_font_config { + struct nk_font_config *next; + /* NOTE: only used internally */ + void *ttf_blob; + /* pointer to loaded TTF file memory block. + * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ + nk_size ttf_size; + /* size of the loaded TTF file memory block + * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ + + unsigned char ttf_data_owned_by_atlas; + /* used inside font atlas: default to: 0*/ + unsigned char merge_mode; + /* merges this font into the last font */ + unsigned char pixel_snap; + /* align every character to pixel boundary (if true set oversample (1,1)) */ + unsigned char oversample_v, oversample_h; + /* rasterize at hight quality for sub-pixel position */ + unsigned char padding[3]; + + float size; + /* baked pixel height of the font */ + enum nk_font_coord_type coord_type; + /* texture coordinate format with either pixel or UV coordinates */ + struct nk_vec2 spacing; + /* extra pixel spacing between glyphs */ + const nk_rune *range; + /* list of unicode ranges (2 values per range, zero terminated) */ + struct nk_baked_font *font; + /* font to setup in the baking process: NOTE: not needed for font atlas */ + nk_rune fallback_glyph; + /* fallback glyph to use if a given rune is not found */ +}; + +struct nk_font_glyph { + nk_rune codepoint; + float xadvance; + float x0, y0, x1, y1, w, h; + float u0, v0, u1, v1; +}; + +struct nk_font { + struct nk_font *next; + struct nk_user_font handle; + struct nk_baked_font info; + float scale; + struct nk_font_glyph *glyphs; + const struct nk_font_glyph *fallback; + nk_rune fallback_codepoint; + nk_handle texture; + struct nk_font_config *config; +}; + +enum nk_font_atlas_format { + NK_FONT_ATLAS_ALPHA8, + NK_FONT_ATLAS_RGBA32 +}; + +struct nk_font_atlas { + void *pixel; + int tex_width; + int tex_height; + + struct nk_allocator permanent; + struct nk_allocator temporary; + + struct nk_recti custom; + struct nk_cursor cursors[NK_CURSOR_COUNT]; + + int glyph_count; + struct nk_font_glyph *glyphs; + struct nk_font *default_font; + struct nk_font *fonts; + struct nk_font_config *config; + int font_num; +}; + +/* some language glyph codepoint ranges */ +NK_API const nk_rune *nk_font_default_glyph_ranges(void); +NK_API const nk_rune *nk_font_chinese_glyph_ranges(void); +NK_API const nk_rune *nk_font_cyrillic_glyph_ranges(void); +NK_API const nk_rune *nk_font_korean_glyph_ranges(void); + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_font_atlas_init_default(struct nk_font_atlas*); +#endif +NK_API void nk_font_atlas_init(struct nk_font_atlas*, struct nk_allocator*); +NK_API void nk_font_atlas_init_custom(struct nk_font_atlas*, struct nk_allocator *persistent, struct nk_allocator *transient); +NK_API void nk_font_atlas_begin(struct nk_font_atlas*); +NK_API struct nk_font_config nk_font_config(float pixel_height); +NK_API struct nk_font *nk_font_atlas_add(struct nk_font_atlas*, const struct nk_font_config*); +#ifdef NK_INCLUDE_DEFAULT_FONT +NK_API struct nk_font* nk_font_atlas_add_default(struct nk_font_atlas*, float height, const struct nk_font_config*); +#endif +NK_API struct nk_font* nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, nk_size size, float height, const struct nk_font_config *config); +#ifdef NK_INCLUDE_STANDARD_IO +NK_API struct nk_font* nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, float height, const struct nk_font_config*); +#endif +NK_API struct nk_font *nk_font_atlas_add_compressed(struct nk_font_atlas*, void *memory, nk_size size, float height, const struct nk_font_config*); +NK_API struct nk_font* nk_font_atlas_add_compressed_base85(struct nk_font_atlas*, const char *data, float height, const struct nk_font_config *config); +NK_API const void* nk_font_atlas_bake(struct nk_font_atlas*, int *width, int *height, enum nk_font_atlas_format); +NK_API void nk_font_atlas_end(struct nk_font_atlas*, nk_handle tex, struct nk_draw_null_texture*); +NK_API const struct nk_font_glyph* nk_font_find_glyph(struct nk_font*, nk_rune unicode); +NK_API void nk_font_atlas_cleanup(struct nk_font_atlas *atlas); +NK_API void nk_font_atlas_clear(struct nk_font_atlas*); + +#endif + +/* ============================================================== + * + * MEMORY BUFFER + * + * ===============================================================*/ +/* A basic (double)-buffer with linear allocation and resetting as only + freeing policy. The buffer's main purpose is to control all memory management + inside the GUI toolkit and still leave memory control as much as possible in + the hand of the user while also making sure the library is easy to use if + not as much control is needed. + In general all memory inside this library can be provided from the user in + three different ways. + + The first way and the one providing most control is by just passing a fixed + size memory block. In this case all control lies in the hand of the user + since he can exactly control where the memory comes from and how much memory + the library should consume. Of course using the fixed size API removes the + ability to automatically resize a buffer if not enough memory is provided so + you have to take over the resizing. While being a fixed sized buffer sounds + quite limiting, it is very effective in this library since the actual memory + consumption is quite stable and has a fixed upper bound for a lot of cases. + + If you don't want to think about how much memory the library should allocate + at all time or have a very dynamic UI with unpredictable memory consumption + habits but still want control over memory allocation you can use the dynamic + allocator based API. The allocator consists of two callbacks for allocating + and freeing memory and optional userdata so you can plugin your own allocator. + + The final and easiest way can be used by defining + NK_INCLUDE_DEFAULT_ALLOCATOR which uses the standard library memory + allocation functions malloc and free and takes over complete control over + memory in this library. +*/ +struct nk_memory_status { + void *memory; + unsigned int type; + nk_size size; + nk_size allocated; + nk_size needed; + nk_size calls; +}; + +enum nk_allocation_type { + NK_BUFFER_FIXED, + NK_BUFFER_DYNAMIC +}; + +enum nk_buffer_allocation_type { + NK_BUFFER_FRONT, + NK_BUFFER_BACK, + NK_BUFFER_MAX +}; + +struct nk_buffer_marker { + int active; + nk_size offset; +}; + +struct nk_memory {void *ptr;nk_size size;}; +struct nk_buffer { + struct nk_buffer_marker marker[NK_BUFFER_MAX]; + /* buffer marker to free a buffer to a certain offset */ + struct nk_allocator pool; + /* allocator callback for dynamic buffers */ + enum nk_allocation_type type; + /* memory management type */ + struct nk_memory memory; + /* memory and size of the current memory block */ + float grow_factor; + /* growing factor for dynamic memory management */ + nk_size allocated; + /* total amount of memory allocated */ + nk_size needed; + /* totally consumed memory given that enough memory is present */ + nk_size calls; + /* number of allocation calls */ + nk_size size; + /* current size of the buffer */ +}; + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_buffer_init_default(struct nk_buffer*); +#endif +NK_API void nk_buffer_init(struct nk_buffer*, const struct nk_allocator*, nk_size size); +NK_API void nk_buffer_init_fixed(struct nk_buffer*, void *memory, nk_size size); +NK_API void nk_buffer_info(struct nk_memory_status*, struct nk_buffer*); +NK_API void nk_buffer_push(struct nk_buffer*, enum nk_buffer_allocation_type type, const void *memory, nk_size size, nk_size align); +NK_API void nk_buffer_mark(struct nk_buffer*, enum nk_buffer_allocation_type type); +NK_API void nk_buffer_reset(struct nk_buffer*, enum nk_buffer_allocation_type type); +NK_API void nk_buffer_clear(struct nk_buffer*); +NK_API void nk_buffer_free(struct nk_buffer*); +NK_API void *nk_buffer_memory(struct nk_buffer*); +NK_API const void *nk_buffer_memory_const(const struct nk_buffer*); +NK_API nk_size nk_buffer_total(struct nk_buffer*); + +/* ============================================================== + * + * STRING + * + * ===============================================================*/ +/* Basic string buffer which is only used in context with the text editor + * to manage and manipulate dynamic or fixed size string content. This is _NOT_ + * the default string handling method. The only instance you should have any contact + * with this API is if you interact with an `nk_text_edit` object inside one of the + * copy and paste functions and even there only for more advanced cases. */ +struct nk_str { + struct nk_buffer buffer; + int len; /* in codepoints/runes/glyphs */ +}; + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_str_init_default(struct nk_str*); +#endif +NK_API void nk_str_init(struct nk_str*, const struct nk_allocator*, nk_size size); +NK_API void nk_str_init_fixed(struct nk_str*, void *memory, nk_size size); +NK_API void nk_str_clear(struct nk_str*); +NK_API void nk_str_free(struct nk_str*); + +NK_API int nk_str_append_text_char(struct nk_str*, const char*, int); +NK_API int nk_str_append_str_char(struct nk_str*, const char*); +NK_API int nk_str_append_text_utf8(struct nk_str*, const char*, int); +NK_API int nk_str_append_str_utf8(struct nk_str*, const char*); +NK_API int nk_str_append_text_runes(struct nk_str*, const nk_rune*, int); +NK_API int nk_str_append_str_runes(struct nk_str*, const nk_rune*); + +NK_API int nk_str_insert_at_char(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_at_rune(struct nk_str*, int pos, const char*, int); + +NK_API int nk_str_insert_text_char(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_str_char(struct nk_str*, int pos, const char*); +NK_API int nk_str_insert_text_utf8(struct nk_str*, int pos, const char*, int); +NK_API int nk_str_insert_str_utf8(struct nk_str*, int pos, const char*); +NK_API int nk_str_insert_text_runes(struct nk_str*, int pos, const nk_rune*, int); +NK_API int nk_str_insert_str_runes(struct nk_str*, int pos, const nk_rune*); + +NK_API void nk_str_remove_chars(struct nk_str*, int len); +NK_API void nk_str_remove_runes(struct nk_str *str, int len); +NK_API void nk_str_delete_chars(struct nk_str*, int pos, int len); +NK_API void nk_str_delete_runes(struct nk_str*, int pos, int len); + +NK_API char *nk_str_at_char(struct nk_str*, int pos); +NK_API char *nk_str_at_rune(struct nk_str*, int pos, nk_rune *unicode, int *len); +NK_API nk_rune nk_str_rune_at(const struct nk_str*, int pos); +NK_API const char *nk_str_at_char_const(const struct nk_str*, int pos); +NK_API const char *nk_str_at_const(const struct nk_str*, int pos, nk_rune *unicode, int *len); + +NK_API char *nk_str_get(struct nk_str*); +NK_API const char *nk_str_get_const(const struct nk_str*); +NK_API int nk_str_len(struct nk_str*); +NK_API int nk_str_len_char(struct nk_str*); + +/*=============================================================== + * + * TEXT EDITOR + * + * ===============================================================*/ +/* Editing text in this library is handled by either `nk_edit_string` or + * `nk_edit_buffer`. But like almost everything in this library there are multiple + * ways of doing it and a balance between control and ease of use with memory + * as well as functionality controlled by flags. + * + * This library generally allows three different levels of memory control: + * First of is the most basic way of just providing a simple char array with + * string length. This method is probably the easiest way of handling simple + * user text input. Main upside is complete control over memory while the biggest + * downside in comparsion with the other two approaches is missing undo/redo. + * + * For UIs that require undo/redo the second way was created. It is based on + * a fixed size nk_text_edit struct, which has an internal undo/redo stack. + * This is mainly useful if you want something more like a text editor but don't want + * to have a dynamically growing buffer. + * + * The final way is using a dynamically growing nk_text_edit struct, which + * has both a default version if you don't care where memory comes from and an + * allocator version if you do. While the text editor is quite powerful for its + * complexity I would not recommend editing gigabytes of data with it. + * It is rather designed for uses cases which make sense for a GUI library not for + * an full blown text editor. + */ +#ifndef NK_TEXTEDIT_UNDOSTATECOUNT +#define NK_TEXTEDIT_UNDOSTATECOUNT 99 +#endif + +#ifndef NK_TEXTEDIT_UNDOCHARCOUNT +#define NK_TEXTEDIT_UNDOCHARCOUNT 999 +#endif + +struct nk_text_edit; +struct nk_clipboard { + nk_handle userdata; + nk_plugin_paste paste; + nk_plugin_copy copy; +}; + +struct nk_text_undo_record { + int where; + short insert_length; + short delete_length; + short char_storage; +}; + +struct nk_text_undo_state { + struct nk_text_undo_record undo_rec[NK_TEXTEDIT_UNDOSTATECOUNT]; + nk_rune undo_char[NK_TEXTEDIT_UNDOCHARCOUNT]; + short undo_point; + short redo_point; + short undo_char_point; + short redo_char_point; +}; + +enum nk_text_edit_type { + NK_TEXT_EDIT_SINGLE_LINE, + NK_TEXT_EDIT_MULTI_LINE +}; + +enum nk_text_edit_mode { + NK_TEXT_EDIT_MODE_VIEW, + NK_TEXT_EDIT_MODE_INSERT, + NK_TEXT_EDIT_MODE_REPLACE +}; + +struct nk_text_edit { + struct nk_clipboard clip; + struct nk_str string; + nk_plugin_filter filter; + struct nk_vec2 scrollbar; + + int cursor; + int select_start; + int select_end; + unsigned char mode; + unsigned char cursor_at_end_of_line; + unsigned char initialized; + unsigned char has_preferred_x; + unsigned char single_line; + unsigned char active; + unsigned char padding1; + float preferred_x; + struct nk_text_undo_state undo; +}; + +/* filter function */ +NK_API int nk_filter_default(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_ascii(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_float(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_decimal(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_hex(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_oct(const struct nk_text_edit*, nk_rune unicode); +NK_API int nk_filter_binary(const struct nk_text_edit*, nk_rune unicode); + +/* text editor */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void nk_textedit_init_default(struct nk_text_edit*); +#endif +NK_API void nk_textedit_init(struct nk_text_edit*, struct nk_allocator*, nk_size size); +NK_API void nk_textedit_init_fixed(struct nk_text_edit*, void *memory, nk_size size); +NK_API void nk_textedit_free(struct nk_text_edit*); +NK_API void nk_textedit_text(struct nk_text_edit*, const char*, int total_len); +NK_API void nk_textedit_delete(struct nk_text_edit*, int where, int len); +NK_API void nk_textedit_delete_selection(struct nk_text_edit*); +NK_API void nk_textedit_select_all(struct nk_text_edit*); +NK_API int nk_textedit_cut(struct nk_text_edit*); +NK_API int nk_textedit_paste(struct nk_text_edit*, char const*, int len); +NK_API void nk_textedit_undo(struct nk_text_edit*); +NK_API void nk_textedit_redo(struct nk_text_edit*); + +/* =============================================================== + * + * DRAWING + * + * ===============================================================*/ +/* This library was designed to be render backend agnostic so it does + not draw anything to screen. Instead all drawn shapes, widgets + are made of, are buffered into memory and make up a command queue. + Each frame therefore fills the command buffer with draw commands + that then need to be executed by the user and his own render backend. + After that the command buffer needs to be cleared and a new frame can be + started. It is probably important to note that the command buffer is the main + drawing API and the optional vertex buffer API only takes this format and + converts it into a hardware accessible format. + + To use the command queue to draw your own widgets you can access the + command buffer of each window by calling `nk_window_get_canvas` after + previously having called `nk_begin`: + + void draw_red_rectangle_widget(struct nk_context *ctx) + { + struct nk_command_buffer *canvas; + struct nk_input *input = &ctx->input; + canvas = nk_window_get_canvas(ctx); + + struct nk_rect space; + enum nk_widget_layout_states state; + state = nk_widget(&space, ctx); + if (!state) return; + + if (state != NK_WIDGET_ROM) + update_your_widget_by_user_input(...); + nk_fill_rect(canvas, space, 0, nk_rgb(255,0,0)); + } + + if (nk_begin(...)) { + nk_layout_row_dynamic(ctx, 25, 1); + draw_red_rectangle_widget(ctx); + } + nk_end(..) + + Important to know if you want to create your own widgets is the `nk_widget` + call. It allocates space on the panel reserved for this widget to be used, + but also returns the state of the widget space. If your widget is not seen and does + not have to be updated it is '0' and you can just return. If it only has + to be drawn the state will be `NK_WIDGET_ROM` otherwise you can do both + update and draw your widget. The reason for seperating is to only draw and + update what is actually neccessary which is crucial for performance. +*/ +enum nk_command_type { + NK_COMMAND_NOP, + NK_COMMAND_SCISSOR, + NK_COMMAND_LINE, + NK_COMMAND_CURVE, + NK_COMMAND_RECT, + NK_COMMAND_RECT_FILLED, + NK_COMMAND_RECT_MULTI_COLOR, + NK_COMMAND_CIRCLE, + NK_COMMAND_CIRCLE_FILLED, + NK_COMMAND_ARC, + NK_COMMAND_ARC_FILLED, + NK_COMMAND_TRIANGLE, + NK_COMMAND_TRIANGLE_FILLED, + NK_COMMAND_POLYGON, + NK_COMMAND_POLYGON_FILLED, + NK_COMMAND_POLYLINE, + NK_COMMAND_TEXT, + NK_COMMAND_IMAGE, + NK_COMMAND_CUSTOM +}; + +/* command base and header of every command inside the buffer */ +struct nk_command { + enum nk_command_type type; + nk_size next; +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +struct nk_command_scissor { + struct nk_command header; + short x, y; + unsigned short w, h; +}; + +struct nk_command_line { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i begin; + struct nk_vec2i end; + struct nk_color color; +}; + +struct nk_command_curve { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i begin; + struct nk_vec2i end; + struct nk_vec2i ctrl[2]; + struct nk_color color; +}; + +struct nk_command_rect { + struct nk_command header; + unsigned short rounding; + unsigned short line_thickness; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_rect_filled { + struct nk_command header; + unsigned short rounding; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_rect_multi_color { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_color left; + struct nk_color top; + struct nk_color bottom; + struct nk_color right; +}; + +struct nk_command_triangle { + struct nk_command header; + unsigned short line_thickness; + struct nk_vec2i a; + struct nk_vec2i b; + struct nk_vec2i c; + struct nk_color color; +}; + +struct nk_command_triangle_filled { + struct nk_command header; + struct nk_vec2i a; + struct nk_vec2i b; + struct nk_vec2i c; + struct nk_color color; +}; + +struct nk_command_circle { + struct nk_command header; + short x, y; + unsigned short line_thickness; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_circle_filled { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_color color; +}; + +struct nk_command_arc { + struct nk_command header; + short cx, cy; + unsigned short r; + unsigned short line_thickness; + float a[2]; + struct nk_color color; +}; + +struct nk_command_arc_filled { + struct nk_command header; + short cx, cy; + unsigned short r; + float a[2]; + struct nk_color color; +}; + +struct nk_command_polygon { + struct nk_command header; + struct nk_color color; + unsigned short line_thickness; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_polygon_filled { + struct nk_command header; + struct nk_color color; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_polyline { + struct nk_command header; + struct nk_color color; + unsigned short line_thickness; + unsigned short point_count; + struct nk_vec2i points[1]; +}; + +struct nk_command_image { + struct nk_command header; + short x, y; + unsigned short w, h; + struct nk_image img; + struct nk_color col; +}; + +typedef void (*nk_command_custom_callback)(void *canvas, short x,short y, + unsigned short w, unsigned short h, nk_handle callback_data); +struct nk_command_custom { + struct nk_command header; + short x, y; + unsigned short w, h; + nk_handle callback_data; + nk_command_custom_callback callback; +}; + +struct nk_command_text { + struct nk_command header; + const struct nk_user_font *font; + struct nk_color background; + struct nk_color foreground; + short x, y; + unsigned short w, h; + float height; + int length; + char string[1]; +}; + +enum nk_command_clipping { + NK_CLIPPING_OFF = nk_false, + NK_CLIPPING_ON = nk_true +}; + +struct nk_command_buffer { + struct nk_buffer *base; + struct nk_rect clip; + int use_clipping; + nk_handle userdata; + nk_size begin, end, last; +}; + +/* shape outlines */ +NK_API void nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float line_thickness, struct nk_color); +NK_API void nk_stroke_curve(struct nk_command_buffer*, float, float, float, float, float, float, float, float, float line_thickness, struct nk_color); +NK_API void nk_stroke_rect(struct nk_command_buffer*, struct nk_rect, float rounding, float line_thickness, struct nk_color); +NK_API void nk_stroke_circle(struct nk_command_buffer*, struct nk_rect, float line_thickness, struct nk_color); +NK_API void nk_stroke_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, float line_thickness, struct nk_color); +NK_API void nk_stroke_triangle(struct nk_command_buffer*, float, float, float, float, float, float, float line_thichness, struct nk_color); +NK_API void nk_stroke_polyline(struct nk_command_buffer*, float *points, int point_count, float line_thickness, struct nk_color col); +NK_API void nk_stroke_polygon(struct nk_command_buffer*, float*, int point_count, float line_thickness, struct nk_color); + +/* filled shades */ +NK_API void nk_fill_rect(struct nk_command_buffer*, struct nk_rect, float rounding, struct nk_color); +NK_API void nk_fill_rect_multi_color(struct nk_command_buffer*, struct nk_rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom); +NK_API void nk_fill_circle(struct nk_command_buffer*, struct nk_rect, struct nk_color); +NK_API void nk_fill_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, struct nk_color); +NK_API void nk_fill_triangle(struct nk_command_buffer*, float x0, float y0, float x1, float y1, float x2, float y2, struct nk_color); +NK_API void nk_fill_polygon(struct nk_command_buffer*, float*, int point_count, struct nk_color); + +/* misc */ +NK_API void nk_draw_image(struct nk_command_buffer*, struct nk_rect, const struct nk_image*, struct nk_color); +NK_API void nk_draw_text(struct nk_command_buffer*, struct nk_rect, const char *text, int len, const struct nk_user_font*, struct nk_color, struct nk_color); +NK_API void nk_push_scissor(struct nk_command_buffer*, struct nk_rect); +NK_API void nk_push_custom(struct nk_command_buffer*, struct nk_rect, nk_command_custom_callback, nk_handle usr); + +/* =============================================================== + * + * INPUT + * + * ===============================================================*/ +struct nk_mouse_button { + int down; + unsigned int clicked; + struct nk_vec2 clicked_pos; +}; +struct nk_mouse { + struct nk_mouse_button buttons[NK_BUTTON_MAX]; + struct nk_vec2 pos; + struct nk_vec2 prev; + struct nk_vec2 delta; + struct nk_vec2 scroll_delta; + unsigned char grab; + unsigned char grabbed; + unsigned char ungrab; +}; + +struct nk_key { + int down; + unsigned int clicked; +}; +struct nk_keyboard { + struct nk_key keys[NK_KEY_MAX]; + char text[NK_INPUT_MAX]; + int text_len; +}; + +struct nk_input { + struct nk_keyboard keyboard; + struct nk_mouse mouse; +}; + +NK_API int nk_input_has_mouse_click(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_has_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API int nk_input_has_mouse_click_down_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect, int down); +NK_API int nk_input_is_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API int nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, int down); +NK_API int nk_input_any_mouse_click_in_rect(const struct nk_input*, struct nk_rect); +NK_API int nk_input_is_mouse_prev_hovering_rect(const struct nk_input*, struct nk_rect); +NK_API int nk_input_is_mouse_hovering_rect(const struct nk_input*, struct nk_rect); +NK_API int nk_input_mouse_clicked(const struct nk_input*, enum nk_buttons, struct nk_rect); +NK_API int nk_input_is_mouse_down(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_is_mouse_pressed(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_is_mouse_released(const struct nk_input*, enum nk_buttons); +NK_API int nk_input_is_key_pressed(const struct nk_input*, enum nk_keys); +NK_API int nk_input_is_key_released(const struct nk_input*, enum nk_keys); +NK_API int nk_input_is_key_down(const struct nk_input*, enum nk_keys); + +/* =============================================================== + * + * DRAW LIST + * + * ===============================================================*/ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +/* The optional vertex buffer draw list provides a 2D drawing context + with antialiasing functionality which takes basic filled or outlined shapes + or a path and outputs vertexes, elements and draw commands. + The actual draw list API is not required to be used directly while using this + library since converting the default library draw command output is done by + just calling `nk_convert` but I decided to still make this library accessible + since it can be useful. + + The draw list is based on a path buffering and polygon and polyline + rendering API which allows a lot of ways to draw 2D content to screen. + In fact it is probably more powerful than needed but allows even more crazy + things than this library provides by default. +*/ +typedef nk_ushort nk_draw_index; +enum nk_draw_list_stroke { + NK_STROKE_OPEN = nk_false, + /* build up path has no connection back to the beginning */ + NK_STROKE_CLOSED = nk_true + /* build up path has a connection back to the beginning */ +}; + +enum nk_draw_vertex_layout_attribute { + NK_VERTEX_POSITION, + NK_VERTEX_COLOR, + NK_VERTEX_TEXCOORD, + NK_VERTEX_ATTRIBUTE_COUNT +}; + +enum nk_draw_vertex_layout_format { + NK_FORMAT_SCHAR, + NK_FORMAT_SSHORT, + NK_FORMAT_SINT, + NK_FORMAT_UCHAR, + NK_FORMAT_USHORT, + NK_FORMAT_UINT, + NK_FORMAT_FLOAT, + NK_FORMAT_DOUBLE, + +NK_FORMAT_COLOR_BEGIN, + NK_FORMAT_R8G8B8 = NK_FORMAT_COLOR_BEGIN, + NK_FORMAT_R16G15B16, + NK_FORMAT_R32G32B32, + + NK_FORMAT_R8G8B8A8, + NK_FORMAT_B8G8R8A8, + NK_FORMAT_R16G15B16A16, + NK_FORMAT_R32G32B32A32, + NK_FORMAT_R32G32B32A32_FLOAT, + NK_FORMAT_R32G32B32A32_DOUBLE, + + NK_FORMAT_RGB32, + NK_FORMAT_RGBA32, +NK_FORMAT_COLOR_END = NK_FORMAT_RGBA32, + NK_FORMAT_COUNT +}; + +#define NK_VERTEX_LAYOUT_END NK_VERTEX_ATTRIBUTE_COUNT,NK_FORMAT_COUNT,0 +struct nk_draw_vertex_layout_element { + enum nk_draw_vertex_layout_attribute attribute; + enum nk_draw_vertex_layout_format format; + nk_size offset; +}; + +struct nk_draw_command { + unsigned int elem_count; + /* number of elements in the current draw batch */ + struct nk_rect clip_rect; + /* current screen clipping rectangle */ + nk_handle texture; + /* current texture to set */ +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +struct nk_draw_list { + struct nk_rect clip_rect; + struct nk_vec2 circle_vtx[12]; + struct nk_convert_config config; + + struct nk_buffer *buffer; + struct nk_buffer *vertices; + struct nk_buffer *elements; + + unsigned int element_count; + unsigned int vertex_count; + unsigned int cmd_count; + nk_size cmd_offset; + + unsigned int path_count; + unsigned int path_offset; + + enum nk_anti_aliasing line_AA; + enum nk_anti_aliasing shape_AA; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif +}; + +/* draw list */ +NK_API void nk_draw_list_init(struct nk_draw_list*); +NK_API void nk_draw_list_setup(struct nk_draw_list*, const struct nk_convert_config*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, enum nk_anti_aliasing line_aa,enum nk_anti_aliasing shape_aa); +NK_API void nk_draw_list_clear(struct nk_draw_list*); + +/* drawing */ +#define nk_draw_list_foreach(cmd, can, b) for((cmd)=nk__draw_list_begin(can, b); (cmd)!=0; (cmd)=nk__draw_list_next(cmd, b, can)) +NK_API const struct nk_draw_command* nk__draw_list_begin(const struct nk_draw_list*, const struct nk_buffer*); +NK_API const struct nk_draw_command* nk__draw_list_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_draw_list*); +NK_API const struct nk_draw_command* nk__draw_list_end(const struct nk_draw_list*, const struct nk_buffer*); +NK_API void nk_draw_list_clear(struct nk_draw_list *list); + +/* path */ +NK_API void nk_draw_list_path_clear(struct nk_draw_list*); +NK_API void nk_draw_list_path_line_to(struct nk_draw_list*, struct nk_vec2 pos); +NK_API void nk_draw_list_path_arc_to_fast(struct nk_draw_list*, struct nk_vec2 center, float radius, int a_min, int a_max); +NK_API void nk_draw_list_path_arc_to(struct nk_draw_list*, struct nk_vec2 center, float radius, float a_min, float a_max, unsigned int segments); +NK_API void nk_draw_list_path_rect_to(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, float rounding); +NK_API void nk_draw_list_path_curve_to(struct nk_draw_list*, struct nk_vec2 p2, struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments); +NK_API void nk_draw_list_path_fill(struct nk_draw_list*, struct nk_color); +NK_API void nk_draw_list_path_stroke(struct nk_draw_list*, struct nk_color, enum nk_draw_list_stroke closed, float thickness); + +/* stroke */ +NK_API void nk_draw_list_stroke_line(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_color, float thickness); +NK_API void nk_draw_list_stroke_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding, float thickness); +NK_API void nk_draw_list_stroke_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color, float thickness); +NK_API void nk_draw_list_stroke_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color, unsigned int segs, float thickness); +NK_API void nk_draw_list_stroke_curve(struct nk_draw_list*, struct nk_vec2 p0, struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, struct nk_color, unsigned int segments, float thickness); +NK_API void nk_draw_list_stroke_poly_line(struct nk_draw_list*, const struct nk_vec2 *pnts, const unsigned int cnt, struct nk_color, enum nk_draw_list_stroke, float thickness, enum nk_anti_aliasing); + +/* fill */ +NK_API void nk_draw_list_fill_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding); +NK_API void nk_draw_list_fill_rect_multi_color(struct nk_draw_list*, struct nk_rect rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom); +NK_API void nk_draw_list_fill_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color); +NK_API void nk_draw_list_fill_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color col, unsigned int segs); +NK_API void nk_draw_list_fill_poly_convex(struct nk_draw_list*, const struct nk_vec2 *points, const unsigned int count, struct nk_color, enum nk_anti_aliasing); + +/* misc */ +NK_API void nk_draw_list_add_image(struct nk_draw_list*, struct nk_image texture, struct nk_rect rect, struct nk_color); +NK_API void nk_draw_list_add_text(struct nk_draw_list*, const struct nk_user_font*, struct nk_rect, const char *text, int len, float font_height, struct nk_color); +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void nk_draw_list_push_userdata(struct nk_draw_list*, nk_handle userdata); +#endif + +#endif + +/* =============================================================== + * + * GUI + * + * ===============================================================*/ +enum nk_style_item_type { + NK_STYLE_ITEM_COLOR, + NK_STYLE_ITEM_IMAGE +}; + +union nk_style_item_data { + struct nk_image image; + struct nk_color color; +}; + +struct nk_style_item { + enum nk_style_item_type type; + union nk_style_item_data data; +}; + +struct nk_style_text { + struct nk_color color; + struct nk_vec2 padding; +}; + +struct nk_style_button { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* text */ + struct nk_color text_background; + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + nk_flags text_alignment; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + struct nk_vec2 image_padding; + struct nk_vec2 touch_padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle userdata); + void(*draw_end)(struct nk_command_buffer*, nk_handle userdata); +}; + +struct nk_style_toggle { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + + /* text */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + struct nk_color text_background; + nk_flags text_alignment; + + /* properties */ + struct nk_vec2 padding; + struct nk_vec2 touch_padding; + float spacing; + float border; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_selectable { + /* background (inactive) */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item pressed; + + /* background (active) */ + struct nk_style_item normal_active; + struct nk_style_item hover_active; + struct nk_style_item pressed_active; + + /* text color (inactive) */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_pressed; + + /* text color (active) */ + struct nk_color text_normal_active; + struct nk_color text_hover_active; + struct nk_color text_pressed_active; + struct nk_color text_background; + nk_flags text_alignment; + + /* properties */ + float rounding; + struct nk_vec2 padding; + struct nk_vec2 touch_padding; + struct nk_vec2 image_padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_slider { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* background bar */ + struct nk_color bar_normal; + struct nk_color bar_hover; + struct nk_color bar_active; + struct nk_color bar_filled; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + + /* properties */ + float border; + float rounding; + float bar_height; + struct nk_vec2 padding; + struct nk_vec2 spacing; + struct nk_vec2 cursor_size; + + /* optional buttons */ + int show_buttons; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + enum nk_symbol_type inc_symbol; + enum nk_symbol_type dec_symbol; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_progress { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + struct nk_color cursor_border_color; + + /* properties */ + float rounding; + float border; + float cursor_border; + float cursor_rounding; + struct nk_vec2 padding; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_scrollbar { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* cursor */ + struct nk_style_item cursor_normal; + struct nk_style_item cursor_hover; + struct nk_style_item cursor_active; + struct nk_color cursor_border_color; + + /* properties */ + float border; + float rounding; + float border_cursor; + float rounding_cursor; + struct nk_vec2 padding; + + /* optional buttons */ + int show_buttons; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + enum nk_symbol_type inc_symbol; + enum nk_symbol_type dec_symbol; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_edit { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + struct nk_style_scrollbar scrollbar; + + /* cursor */ + struct nk_color cursor_normal; + struct nk_color cursor_hover; + struct nk_color cursor_text_normal; + struct nk_color cursor_text_hover; + + /* text (unselected) */ + struct nk_color text_normal; + struct nk_color text_hover; + struct nk_color text_active; + + /* text (selected) */ + struct nk_color selected_normal; + struct nk_color selected_hover; + struct nk_color selected_text_normal; + struct nk_color selected_text_hover; + + /* properties */ + float border; + float rounding; + float cursor_size; + struct nk_vec2 scrollbar_size; + struct nk_vec2 padding; + float row_padding; +}; + +struct nk_style_property { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* text */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* symbols */ + enum nk_symbol_type sym_left; + enum nk_symbol_type sym_right; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; + + struct nk_style_edit edit; + struct nk_style_button inc_button; + struct nk_style_button dec_button; + + /* optional user callbacks */ + nk_handle userdata; + void(*draw_begin)(struct nk_command_buffer*, nk_handle); + void(*draw_end)(struct nk_command_buffer*, nk_handle); +}; + +struct nk_style_chart { + /* colors */ + struct nk_style_item background; + struct nk_color border_color; + struct nk_color selected_color; + struct nk_color color; + + /* properties */ + float border; + float rounding; + struct nk_vec2 padding; +}; + +struct nk_style_combo { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + struct nk_color border_color; + + /* label */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* symbol */ + struct nk_color symbol_normal; + struct nk_color symbol_hover; + struct nk_color symbol_active; + + /* button */ + struct nk_style_button button; + enum nk_symbol_type sym_normal; + enum nk_symbol_type sym_hover; + enum nk_symbol_type sym_active; + + /* properties */ + float border; + float rounding; + struct nk_vec2 content_padding; + struct nk_vec2 button_padding; + struct nk_vec2 spacing; +}; + +struct nk_style_tab { + /* background */ + struct nk_style_item background; + struct nk_color border_color; + struct nk_color text; + + /* button */ + struct nk_style_button tab_maximize_button; + struct nk_style_button tab_minimize_button; + struct nk_style_button node_maximize_button; + struct nk_style_button node_minimize_button; + enum nk_symbol_type sym_minimize; + enum nk_symbol_type sym_maximize; + + /* properties */ + float border; + float rounding; + float indent; + struct nk_vec2 padding; + struct nk_vec2 spacing; +}; + +enum nk_style_header_align { + NK_HEADER_LEFT, + NK_HEADER_RIGHT +}; +struct nk_style_window_header { + /* background */ + struct nk_style_item normal; + struct nk_style_item hover; + struct nk_style_item active; + + /* button */ + struct nk_style_button close_button; + struct nk_style_button minimize_button; + enum nk_symbol_type close_symbol; + enum nk_symbol_type minimize_symbol; + enum nk_symbol_type maximize_symbol; + + /* title */ + struct nk_color label_normal; + struct nk_color label_hover; + struct nk_color label_active; + + /* properties */ + enum nk_style_header_align align; + struct nk_vec2 padding; + struct nk_vec2 label_padding; + struct nk_vec2 spacing; +}; + +struct nk_style_window { + struct nk_style_window_header header; + struct nk_style_item fixed_background; + struct nk_color background; + + struct nk_color border_color; + struct nk_color popup_border_color; + struct nk_color combo_border_color; + struct nk_color contextual_border_color; + struct nk_color menu_border_color; + struct nk_color group_border_color; + struct nk_color tooltip_border_color; + struct nk_style_item scaler; + + float border; + float combo_border; + float contextual_border; + float menu_border; + float group_border; + float tooltip_border; + float popup_border; + float min_row_height_padding; + + float rounding; + struct nk_vec2 spacing; + struct nk_vec2 scrollbar_size; + struct nk_vec2 min_size; + + struct nk_vec2 padding; + struct nk_vec2 group_padding; + struct nk_vec2 popup_padding; + struct nk_vec2 combo_padding; + struct nk_vec2 contextual_padding; + struct nk_vec2 menu_padding; + struct nk_vec2 tooltip_padding; +}; + +struct nk_style { + const struct nk_user_font *font; + const struct nk_cursor *cursors[NK_CURSOR_COUNT]; + const struct nk_cursor *cursor_active; + struct nk_cursor *cursor_last; + int cursor_visible; + + struct nk_style_text text; + struct nk_style_button button; + struct nk_style_button contextual_button; + struct nk_style_button menu_button; + struct nk_style_toggle option; + struct nk_style_toggle checkbox; + struct nk_style_selectable selectable; + struct nk_style_slider slider; + struct nk_style_progress progress; + struct nk_style_property property; + struct nk_style_edit edit; + struct nk_style_chart chart; + struct nk_style_scrollbar scrollh; + struct nk_style_scrollbar scrollv; + struct nk_style_tab tab; + struct nk_style_combo combo; + struct nk_style_window window; +}; + +NK_API struct nk_style_item nk_style_item_image(struct nk_image img); +NK_API struct nk_style_item nk_style_item_color(struct nk_color); +NK_API struct nk_style_item nk_style_item_hide(void); + +/*============================================================== + * PANEL + * =============================================================*/ +#ifndef NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS +#define NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS 16 +#endif +#ifndef NK_CHART_MAX_SLOT +#define NK_CHART_MAX_SLOT 4 +#endif + +enum nk_panel_type { + NK_PANEL_WINDOW = NK_FLAG(0), + NK_PANEL_GROUP = NK_FLAG(1), + NK_PANEL_POPUP = NK_FLAG(2), + NK_PANEL_CONTEXTUAL = NK_FLAG(4), + NK_PANEL_COMBO = NK_FLAG(5), + NK_PANEL_MENU = NK_FLAG(6), + NK_PANEL_TOOLTIP = NK_FLAG(7) +}; +enum nk_panel_set { + NK_PANEL_SET_NONBLOCK = NK_PANEL_CONTEXTUAL|NK_PANEL_COMBO|NK_PANEL_MENU|NK_PANEL_TOOLTIP, + NK_PANEL_SET_POPUP = NK_PANEL_SET_NONBLOCK|NK_PANEL_POPUP, + NK_PANEL_SET_SUB = NK_PANEL_SET_POPUP|NK_PANEL_GROUP +}; + +struct nk_chart_slot { + enum nk_chart_type type; + struct nk_color color; + struct nk_color highlight; + float min, max, range; + int count; + struct nk_vec2 last; + int index; +}; + +struct nk_chart { + int slot; + float x, y, w, h; + struct nk_chart_slot slots[NK_CHART_MAX_SLOT]; +}; + +enum nk_panel_row_layout_type { + NK_LAYOUT_DYNAMIC_FIXED = 0, + NK_LAYOUT_DYNAMIC_ROW, + NK_LAYOUT_DYNAMIC_FREE, + NK_LAYOUT_DYNAMIC, + NK_LAYOUT_STATIC_FIXED, + NK_LAYOUT_STATIC_ROW, + NK_LAYOUT_STATIC_FREE, + NK_LAYOUT_STATIC, + NK_LAYOUT_TEMPLATE, + NK_LAYOUT_COUNT +}; +struct nk_row_layout { + enum nk_panel_row_layout_type type; + int index; + float height; + float min_height; + int columns; + const float *ratio; + float item_width; + float item_height; + float item_offset; + float filled; + struct nk_rect item; + int tree_depth; + float templates[NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS]; +}; + +struct nk_popup_buffer { + nk_size begin; + nk_size parent; + nk_size last; + nk_size end; + int active; +}; + +struct nk_menu_state { + float x, y, w, h; + struct nk_scroll offset; +}; + +struct nk_panel { + enum nk_panel_type type; + nk_flags flags; + struct nk_rect bounds; + nk_uint *offset_x; + nk_uint *offset_y; + float at_x, at_y, max_x; + float footer_height; + float header_height; + float border; + unsigned int has_scrolling; + struct nk_rect clip; + struct nk_menu_state menu; + struct nk_row_layout row; + struct nk_chart chart; + struct nk_command_buffer *buffer; + struct nk_panel *parent; +}; + +/*============================================================== + * WINDOW + * =============================================================*/ +#ifndef NK_WINDOW_MAX_NAME +#define NK_WINDOW_MAX_NAME 64 +#endif + +struct nk_table; +enum nk_window_flags { + NK_WINDOW_PRIVATE = NK_FLAG(11), + NK_WINDOW_DYNAMIC = NK_WINDOW_PRIVATE, + /* special window type growing up in height while being filled to a certain maximum height */ + NK_WINDOW_ROM = NK_FLAG(12), + /* sets window widgets into a read only mode and does not allow input changes */ + NK_WINDOW_NOT_INTERACTIVE = NK_WINDOW_ROM|NK_WINDOW_NO_INPUT, + /* prevents all interaction caused by input to either window or widgets inside */ + NK_WINDOW_HIDDEN = NK_FLAG(13), + /* Hides window and stops any window interaction and drawing */ + NK_WINDOW_CLOSED = NK_FLAG(14), + /* Directly closes and frees the window at the end of the frame */ + NK_WINDOW_MINIMIZED = NK_FLAG(15), + /* marks the window as minimized */ + NK_WINDOW_REMOVE_ROM = NK_FLAG(16) + /* Removes read only mode at the end of the window */ +}; + +struct nk_popup_state { + struct nk_window *win; + enum nk_panel_type type; + struct nk_popup_buffer buf; + nk_hash name; + int active; + unsigned combo_count; + unsigned con_count, con_old; + unsigned active_con; + struct nk_rect header; +}; + +struct nk_edit_state { + nk_hash name; + unsigned int seq; + unsigned int old; + int active, prev; + int cursor; + int sel_start; + int sel_end; + struct nk_scroll scrollbar; + unsigned char mode; + unsigned char single_line; +}; + +struct nk_property_state { + int active, prev; + char buffer[NK_MAX_NUMBER_BUFFER]; + int length; + int cursor; + int select_start; + int select_end; + nk_hash name; + unsigned int seq; + unsigned int old; + int state; +}; + +struct nk_window { + unsigned int seq; + nk_hash name; + char name_string[NK_WINDOW_MAX_NAME]; + nk_flags flags; + + struct nk_rect bounds; + struct nk_scroll scrollbar; + struct nk_command_buffer buffer; + struct nk_panel *layout; + float scrollbar_hiding_timer; + + /* persistent widget state */ + struct nk_property_state property; + struct nk_popup_state popup; + struct nk_edit_state edit; + unsigned int scrolled; + + struct nk_table *tables; + unsigned int table_count; + + /* window list hooks */ + struct nk_window *next; + struct nk_window *prev; + struct nk_window *parent; +}; + +/*============================================================== + * STACK + * =============================================================*/ +/* The style modifier stack can be used to temporarily change a + * property inside `nk_style`. For example if you want a special + * red button you can temporarily push the old button color onto a stack + * draw the button with a red color and then you just pop the old color + * back from the stack: + * + * nk_style_push_style_item(ctx, &ctx->style.button.normal, nk_style_item_color(nk_rgb(255,0,0))); + * nk_style_push_style_item(ctx, &ctx->style.button.hover, nk_style_item_color(nk_rgb(255,0,0))); + * nk_style_push_style_item(ctx, &ctx->style.button.active, nk_style_item_color(nk_rgb(255,0,0))); + * nk_style_push_vec2(ctx, &cx->style.button.padding, nk_vec2(2,2)); + * + * nk_button(...); + * + * nk_style_pop_style_item(ctx); + * nk_style_pop_style_item(ctx); + * nk_style_pop_style_item(ctx); + * nk_style_pop_vec2(ctx); + * + * Nuklear has a stack for style_items, float properties, vector properties, + * flags, colors, fonts and for button_behavior. Each has it's own fixed size stack + * which can be changed at compile time. + */ +#ifndef NK_BUTTON_BEHAVIOR_STACK_SIZE +#define NK_BUTTON_BEHAVIOR_STACK_SIZE 8 +#endif + +#ifndef NK_FONT_STACK_SIZE +#define NK_FONT_STACK_SIZE 8 +#endif + +#ifndef NK_STYLE_ITEM_STACK_SIZE +#define NK_STYLE_ITEM_STACK_SIZE 16 +#endif + +#ifndef NK_FLOAT_STACK_SIZE +#define NK_FLOAT_STACK_SIZE 32 +#endif + +#ifndef NK_VECTOR_STACK_SIZE +#define NK_VECTOR_STACK_SIZE 16 +#endif + +#ifndef NK_FLAGS_STACK_SIZE +#define NK_FLAGS_STACK_SIZE 32 +#endif + +#ifndef NK_COLOR_STACK_SIZE +#define NK_COLOR_STACK_SIZE 32 +#endif + +#define NK_CONFIGURATION_STACK_TYPE(prefix, name, type)\ + struct nk_config_stack_##name##_element {\ + prefix##_##type *address;\ + prefix##_##type old_value;\ + } +#define NK_CONFIG_STACK(type,size)\ + struct nk_config_stack_##type {\ + int head;\ + struct nk_config_stack_##type##_element elements[size];\ + } + +#define nk_float float +NK_CONFIGURATION_STACK_TYPE(struct nk, style_item, style_item); +NK_CONFIGURATION_STACK_TYPE(nk ,float, float); +NK_CONFIGURATION_STACK_TYPE(struct nk, vec2, vec2); +NK_CONFIGURATION_STACK_TYPE(nk ,flags, flags); +NK_CONFIGURATION_STACK_TYPE(struct nk, color, color); +NK_CONFIGURATION_STACK_TYPE(const struct nk, user_font, user_font*); +NK_CONFIGURATION_STACK_TYPE(enum nk, button_behavior, button_behavior); + +NK_CONFIG_STACK(style_item, NK_STYLE_ITEM_STACK_SIZE); +NK_CONFIG_STACK(float, NK_FLOAT_STACK_SIZE); +NK_CONFIG_STACK(vec2, NK_VECTOR_STACK_SIZE); +NK_CONFIG_STACK(flags, NK_FLAGS_STACK_SIZE); +NK_CONFIG_STACK(color, NK_COLOR_STACK_SIZE); +NK_CONFIG_STACK(user_font, NK_FONT_STACK_SIZE); +NK_CONFIG_STACK(button_behavior, NK_BUTTON_BEHAVIOR_STACK_SIZE); + +struct nk_configuration_stacks { + struct nk_config_stack_style_item style_items; + struct nk_config_stack_float floats; + struct nk_config_stack_vec2 vectors; + struct nk_config_stack_flags flags; + struct nk_config_stack_color colors; + struct nk_config_stack_user_font fonts; + struct nk_config_stack_button_behavior button_behaviors; +}; + +/*============================================================== + * CONTEXT + * =============================================================*/ +#define NK_VALUE_PAGE_CAPACITY \ + (((NK_MAX(sizeof(struct nk_window),sizeof(struct nk_panel)) / sizeof(nk_uint))) / 2) + +struct nk_table { + unsigned int seq; + unsigned int size; + nk_hash keys[NK_VALUE_PAGE_CAPACITY]; + nk_uint values[NK_VALUE_PAGE_CAPACITY]; + struct nk_table *next, *prev; +}; + +union nk_page_data { + struct nk_table tbl; + struct nk_panel pan; + struct nk_window win; +}; + +struct nk_page_element { + union nk_page_data data; + struct nk_page_element *next; + struct nk_page_element *prev; +}; + +struct nk_page { + unsigned int size; + struct nk_page *next; + struct nk_page_element win[1]; +}; + +struct nk_pool { + struct nk_allocator alloc; + enum nk_allocation_type type; + unsigned int page_count; + struct nk_page *pages; + struct nk_page_element *freelist; + unsigned capacity; + nk_size size; + nk_size cap; +}; + +struct nk_context { +/* public: can be accessed freely */ + struct nk_input input; + struct nk_style style; + struct nk_buffer memory; + struct nk_clipboard clip; + nk_flags last_widget_state; + enum nk_button_behavior button_behavior; + struct nk_configuration_stacks stacks; + float delta_time_seconds; + +/* private: + should only be accessed if you + know what you are doing */ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + struct nk_draw_list draw_list; +#endif +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_handle userdata; +#endif + /* text editor objects are quite big because of an internal + * undo/redo stack. Therefore it does not make sense to have one for + * each window for temporary use cases, so I only provide *one* instance + * for all windows. This works because the content is cleared anyway */ + struct nk_text_edit text_edit; + /* draw buffer used for overlay drawing operation like cursor */ + struct nk_command_buffer overlay; + + /* windows */ + int build; + int use_pool; + struct nk_pool pool; + struct nk_window *begin; + struct nk_window *end; + struct nk_window *active; + struct nk_window *current; + struct nk_page_element *freelist; + unsigned int count; + unsigned int seq; +}; + +/* ============================================================== + * MATH + * =============================================================== */ +#define NK_PI 3.141592654f +#define NK_UTF_INVALID 0xFFFD +#define NK_MAX_FLOAT_PRECISION 2 + +#define NK_UNUSED(x) ((void)(x)) +#define NK_SATURATE(x) (NK_MAX(0, NK_MIN(1.0f, x))) +#define NK_LEN(a) (sizeof(a)/sizeof(a)[0]) +#define NK_ABS(a) (((a) < 0) ? -(a) : (a)) +#define NK_BETWEEN(x, a, b) ((a) <= (x) && (x) < (b)) +#define NK_INBOX(px, py, x, y, w, h)\ + (NK_BETWEEN(px,x,x+w) && NK_BETWEEN(py,y,y+h)) +#define NK_INTERSECT(x0, y0, w0, h0, x1, y1, w1, h1) \ + (!(((x1 > (x0 + w0)) || ((x1 + w1) < x0) || (y1 > (y0 + h0)) || (y1 + h1) < y0))) +#define NK_CONTAINS(x, y, w, h, bx, by, bw, bh)\ + (NK_INBOX(x,y, bx, by, bw, bh) && NK_INBOX(x+w,y+h, bx, by, bw, bh)) + +#define nk_vec2_sub(a, b) nk_vec2((a).x - (b).x, (a).y - (b).y) +#define nk_vec2_add(a, b) nk_vec2((a).x + (b).x, (a).y + (b).y) +#define nk_vec2_len_sqr(a) ((a).x*(a).x+(a).y*(a).y) +#define nk_vec2_muls(a, t) nk_vec2((a).x * (t), (a).y * (t)) + +#define nk_ptr_add(t, p, i) ((t*)((void*)((nk_byte*)(p) + (i)))) +#define nk_ptr_add_const(t, p, i) ((const t*)((const void*)((const nk_byte*)(p) + (i)))) +#define nk_zero_struct(s) nk_zero(&s, sizeof(s)) + +/* ============================================================== + * ALIGNMENT + * =============================================================== */ +/* Pointer to Integer type conversion for pointer alignment */ +#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC*/ +# define NK_UINT_TO_PTR(x) ((void*)(__PTRDIFF_TYPE__)(x)) +# define NK_PTR_TO_UINT(x) ((nk_size)(__PTRDIFF_TYPE__)(x)) +#elif !defined(__GNUC__) /* works for compilers other than LLVM */ +# define NK_UINT_TO_PTR(x) ((void*)&((char*)0)[x]) +# define NK_PTR_TO_UINT(x) ((nk_size)(((char*)x)-(char*)0)) +#elif defined(NK_USE_FIXED_TYPES) /* used if we have */ +# define NK_UINT_TO_PTR(x) ((void*)(uintptr_t)(x)) +# define NK_PTR_TO_UINT(x) ((uintptr_t)(x)) +#else /* generates warning but works */ +# define NK_UINT_TO_PTR(x) ((void*)(x)) +# define NK_PTR_TO_UINT(x) ((nk_size)(x)) +#endif + +#define NK_ALIGN_PTR(x, mask)\ + (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x) + (mask-1)) & ~(mask-1)))) +#define NK_ALIGN_PTR_BACK(x, mask)\ + (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x)) & ~(mask-1)))) + +#define NK_OFFSETOF(st,m) ((nk_ptr)&(((st*)0)->m)) +#define NK_CONTAINER_OF(ptr,type,member)\ + (type*)((void*)((char*)(1 ? (ptr): &((type*)0)->member) - NK_OFFSETOF(type, member))) + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +template struct nk_alignof; +template struct nk_helper{enum {value = size_diff};}; +template struct nk_helper{enum {value = nk_alignof::value};}; +template struct nk_alignof{struct Big {T x; char c;}; enum { + diff = sizeof(Big) - sizeof(T), value = nk_helper::value};}; +#define NK_ALIGNOF(t) (nk_alignof::value) +#elif defined(_MSC_VER) +#define NK_ALIGNOF(t) (__alignof(t)) +#else +#define NK_ALIGNOF(t) ((char*)(&((struct {char c; t _h;}*)0)->_h) - (char*)0) +#endif + +#endif /* NK_H_ */ +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_IMPLEMENTATION + +#ifndef NK_POOL_DEFAULT_CAPACITY +#define NK_POOL_DEFAULT_CAPACITY 16 +#endif + +#ifndef NK_DEFAULT_COMMAND_BUFFER_SIZE +#define NK_DEFAULT_COMMAND_BUFFER_SIZE (4*1024) +#endif + +#ifndef NK_BUFFER_DEFAULT_INITIAL_SIZE +#define NK_BUFFER_DEFAULT_INITIAL_SIZE (4*1024) +#endif + +/* standard library headers */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +#include /* malloc, free */ +#endif +#ifdef NK_INCLUDE_STANDARD_IO +#include /* fopen, fclose,... */ +#endif +#ifdef NK_INCLUDE_STANDARD_VARARGS +#include /* valist, va_start, va_end, ... */ +#endif +#ifndef NK_ASSERT +#include +#define NK_ASSERT(expr) assert(expr) +#endif + +#ifndef NK_MEMSET +#define NK_MEMSET nk_memset +#endif +#ifndef NK_MEMCPY +#define NK_MEMCPY nk_memcopy +#endif +#ifndef NK_SQRT +#define NK_SQRT nk_sqrt +#endif +#ifndef NK_SIN +#define NK_SIN nk_sin +#endif +#ifndef NK_COS +#define NK_COS nk_cos +#endif +#ifndef NK_STRTOD +#define NK_STRTOD nk_strtod +#endif +#ifndef NK_DTOA +#define NK_DTOA nk_dtoa +#endif + +#define NK_DEFAULT (-1) + +#ifndef NK_VSNPRINTF +/* If your compiler does support `vsnprintf` I would highly recommend + * defining this to vsnprintf instead since `vsprintf` is basically + * unbelievable unsafe and should *NEVER* be used. But I have to support + * it since C89 only provides this unsafe version. */ + #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) ||\ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) ||\ + (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) ||\ + defined(_ISOC99_SOURCE) || defined(_BSD_SOURCE) + #define NK_VSNPRINTF(s,n,f,a) vsnprintf(s,n,f,a) + #else + #define NK_VSNPRINTF(s,n,f,a) vsprintf(s,f,a) + #endif +#endif + +#define NK_SCHAR_MIN (-127) +#define NK_SCHAR_MAX 127 +#define NK_UCHAR_MIN 0 +#define NK_UCHAR_MAX 256 +#define NK_SSHORT_MIN (-32767) +#define NK_SSHORT_MAX 32767 +#define NK_USHORT_MIN 0 +#define NK_USHORT_MAX 65535 +#define NK_SINT_MIN (-2147483647) +#define NK_SINT_MAX 2147483647 +#define NK_UINT_MIN 0 +#define NK_UINT_MAX 4294967295u + +/* Make sure correct type size: + * This will fire with a negative subscript error if the type sizes + * are set incorrectly by the compiler, and compile out if not */ +NK_STATIC_ASSERT(sizeof(nk_size) >= sizeof(void*)); +NK_STATIC_ASSERT(sizeof(nk_ptr) == sizeof(void*)); +NK_STATIC_ASSERT(sizeof(nk_flags) >= 4); +NK_STATIC_ASSERT(sizeof(nk_rune) >= 4); +NK_STATIC_ASSERT(sizeof(nk_ushort) == 2); +NK_STATIC_ASSERT(sizeof(nk_short) == 2); +NK_STATIC_ASSERT(sizeof(nk_uint) == 4); +NK_STATIC_ASSERT(sizeof(nk_int) == 4); +NK_STATIC_ASSERT(sizeof(nk_byte) == 1); + +NK_GLOBAL const struct nk_rect nk_null_rect = {-8192.0f, -8192.0f, 16384, 16384}; +#define NK_FLOAT_PRECISION 0.00000000000001 + +NK_GLOBAL const struct nk_color nk_red = {255,0,0,255}; +NK_GLOBAL const struct nk_color nk_green = {0,255,0,255}; +NK_GLOBAL const struct nk_color nk_blue = {0,0,255,255}; +NK_GLOBAL const struct nk_color nk_white = {255,255,255,255}; +NK_GLOBAL const struct nk_color nk_black = {0,0,0,255}; +NK_GLOBAL const struct nk_color nk_yellow = {255,255,0,255}; + +/* + * ============================================================== + * + * MATH + * + * =============================================================== + */ +/* Since nuklear is supposed to work on all systems providing floating point + math without any dependencies I also had to implement my own math functions + for sqrt, sin and cos. Since the actual highly accurate implementations for + the standard library functions are quite complex and I do not need high + precision for my use cases I use approximations. + + Sqrt + ---- + For square root nuklear uses the famous fast inverse square root: + https://en.wikipedia.org/wiki/Fast_inverse_square_root with + slightly tweaked magic constant. While on todays hardware it is + probably not faster it is still fast and accurate enough for + nuklear's use cases. IMPORTANT: this requires float format IEEE 754 + + Sine/Cosine + ----------- + All constants inside both function are generated Remez's minimax + approximations for value range 0...2*PI. The reason why I decided to + approximate exactly that range is that nuklear only needs sine and + cosine to generate circles which only requires that exact range. + In addition I used Remez instead of Taylor for additional precision: + www.lolengine.net/blog/2011/12/21/better-function-approximatations. + + The tool I used to generate constants for both sine and cosine + (it can actually approximate a lot more functions) can be + found here: www.lolengine.net/wiki/oss/lolremez +*/ +NK_INTERN float +nk_inv_sqrt(float number) +{ + float x2; + const float threehalfs = 1.5f; + union {nk_uint i; float f;} conv = {0}; + conv.f = number; + x2 = number * 0.5f; + conv.i = 0x5f375A84 - (conv.i >> 1); + conv.f = conv.f * (threehalfs - (x2 * conv.f * conv.f)); + return conv.f; +} + +NK_INTERN float +nk_sqrt(float x) +{ + return x * nk_inv_sqrt(x); +} + +NK_INTERN float +nk_sin(float x) +{ + NK_STORAGE const float a0 = +1.91059300966915117e-31f; + NK_STORAGE const float a1 = +1.00086760103908896f; + NK_STORAGE const float a2 = -1.21276126894734565e-2f; + NK_STORAGE const float a3 = -1.38078780785773762e-1f; + NK_STORAGE const float a4 = -2.67353392911981221e-2f; + NK_STORAGE const float a5 = +2.08026600266304389e-2f; + NK_STORAGE const float a6 = -3.03996055049204407e-3f; + NK_STORAGE const float a7 = +1.38235642404333740e-4f; + return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); +} + +NK_INTERN float +nk_cos(float x) +{ + NK_STORAGE const float a0 = +1.00238601909309722f; + NK_STORAGE const float a1 = -3.81919947353040024e-2f; + NK_STORAGE const float a2 = -3.94382342128062756e-1f; + NK_STORAGE const float a3 = -1.18134036025221444e-1f; + NK_STORAGE const float a4 = +1.07123798512170878e-1f; + NK_STORAGE const float a5 = -1.86637164165180873e-2f; + NK_STORAGE const float a6 = +9.90140908664079833e-4f; + NK_STORAGE const float a7 = -5.23022132118824778e-14f; + return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); +} + +NK_INTERN nk_uint +nk_round_up_pow2(nk_uint v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +NK_API struct nk_rect +nk_get_null_rect(void) +{ + return nk_null_rect; +} + +NK_API struct nk_rect +nk_rect(float x, float y, float w, float h) +{ + struct nk_rect r; + r.x = x; r.y = y; + r.w = w; r.h = h; + return r; +} + +NK_API struct nk_rect +nk_recti(int x, int y, int w, int h) +{ + struct nk_rect r; + r.x = (float)x; + r.y = (float)y; + r.w = (float)w; + r.h = (float)h; + return r; +} + +NK_API struct nk_rect +nk_recta(struct nk_vec2 pos, struct nk_vec2 size) +{ + return nk_rect(pos.x, pos.y, size.x, size.y); +} + +NK_API struct nk_rect +nk_rectv(const float *r) +{ + return nk_rect(r[0], r[1], r[2], r[3]); +} + +NK_API struct nk_rect +nk_rectiv(const int *r) +{ + return nk_recti(r[0], r[1], r[2], r[3]); +} + +NK_API struct nk_vec2 +nk_rect_pos(struct nk_rect r) +{ + struct nk_vec2 ret; + ret.x = r.x; ret.y = r.y; + return ret; +} + +NK_API struct nk_vec2 +nk_rect_size(struct nk_rect r) +{ + struct nk_vec2 ret; + ret.x = r.w; ret.y = r.h; + return ret; +} + +NK_INTERN struct nk_rect +nk_shrink_rect(struct nk_rect r, float amount) +{ + struct nk_rect res; + r.w = NK_MAX(r.w, 2 * amount); + r.h = NK_MAX(r.h, 2 * amount); + res.x = r.x + amount; + res.y = r.y + amount; + res.w = r.w - 2 * amount; + res.h = r.h - 2 * amount; + return res; +} + +NK_INTERN struct nk_rect +nk_pad_rect(struct nk_rect r, struct nk_vec2 pad) +{ + r.w = NK_MAX(r.w, 2 * pad.x); + r.h = NK_MAX(r.h, 2 * pad.y); + r.x += pad.x; r.y += pad.y; + r.w -= 2 * pad.x; + r.h -= 2 * pad.y; + return r; +} + +NK_API struct nk_vec2 +nk_vec2(float x, float y) +{ + struct nk_vec2 ret; + ret.x = x; ret.y = y; + return ret; +} + +NK_API struct nk_vec2 +nk_vec2i(int x, int y) +{ + struct nk_vec2 ret; + ret.x = (float)x; + ret.y = (float)y; + return ret; +} + +NK_API struct nk_vec2 +nk_vec2v(const float *v) +{ + return nk_vec2(v[0], v[1]); +} + +NK_API struct nk_vec2 +nk_vec2iv(const int *v) +{ + return nk_vec2i(v[0], v[1]); +} + +/* + * ============================================================== + * + * UTIL + * + * =============================================================== + */ +NK_INTERN int nk_str_match_here(const char *regexp, const char *text); +NK_INTERN int nk_str_match_star(int c, const char *regexp, const char *text); +NK_INTERN int nk_is_lower(int c) {return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);} +NK_INTERN int nk_is_upper(int c){return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);} +NK_INTERN int nk_to_upper(int c) {return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;} +NK_INTERN int nk_to_lower(int c) {return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;} + +NK_INTERN void* +nk_memcopy(void *dst0, const void *src0, nk_size length) +{ + nk_ptr t; + char *dst = (char*)dst0; + const char *src = (const char*)src0; + if (length == 0 || dst == src) + goto done; + + #define nk_word int + #define nk_wsize sizeof(nk_word) + #define nk_wmask (nk_wsize-1) + #define NK_TLOOP(s) if (t) NK_TLOOP1(s) + #define NK_TLOOP1(s) do { s; } while (--t) + + if (dst < src) { + t = (nk_ptr)src; /* only need low bits */ + if ((t | (nk_ptr)dst) & nk_wmask) { + if ((t ^ (nk_ptr)dst) & nk_wmask || length < nk_wsize) + t = length; + else + t = nk_wsize - (t & nk_wmask); + length -= t; + NK_TLOOP1(*dst++ = *src++); + } + t = length / nk_wsize; + NK_TLOOP(*(nk_word*)(void*)dst = *(const nk_word*)(const void*)src; + src += nk_wsize; dst += nk_wsize); + t = length & nk_wmask; + NK_TLOOP(*dst++ = *src++); + } else { + src += length; + dst += length; + t = (nk_ptr)src; + if ((t | (nk_ptr)dst) & nk_wmask) { + if ((t ^ (nk_ptr)dst) & nk_wmask || length <= nk_wsize) + t = length; + else + t &= nk_wmask; + length -= t; + NK_TLOOP1(*--dst = *--src); + } + t = length / nk_wsize; + NK_TLOOP(src -= nk_wsize; dst -= nk_wsize; + *(nk_word*)(void*)dst = *(const nk_word*)(const void*)src); + t = length & nk_wmask; + NK_TLOOP(*--dst = *--src); + } + #undef nk_word + #undef nk_wsize + #undef nk_wmask + #undef NK_TLOOP + #undef NK_TLOOP1 +done: + return (dst0); +} + +NK_INTERN void +nk_memset(void *ptr, int c0, nk_size size) +{ + #define nk_word unsigned + #define nk_wsize sizeof(nk_word) + #define nk_wmask (nk_wsize - 1) + nk_byte *dst = (nk_byte*)ptr; + unsigned c = 0; + nk_size t = 0; + + if ((c = (nk_byte)c0) != 0) { + c = (c << 8) | c; /* at least 16-bits */ + if (sizeof(unsigned int) > 2) + c = (c << 16) | c; /* at least 32-bits*/ + } + + /* too small of a word count */ + dst = (nk_byte*)ptr; + if (size < 3 * nk_wsize) { + while (size--) *dst++ = (nk_byte)c0; + return; + } + + /* align destination */ + if ((t = NK_PTR_TO_UINT(dst) & nk_wmask) != 0) { + t = nk_wsize -t; + size -= t; + do { + *dst++ = (nk_byte)c0; + } while (--t != 0); + } + + /* fill word */ + t = size / nk_wsize; + do { + *(nk_word*)((void*)dst) = c; + dst += nk_wsize; + } while (--t != 0); + + /* fill trailing bytes */ + t = (size & nk_wmask); + if (t != 0) { + do { + *dst++ = (nk_byte)c0; + } while (--t != 0); + } + + #undef nk_word + #undef nk_wsize + #undef nk_wmask +} + +NK_INTERN void +nk_zero(void *ptr, nk_size size) +{ + NK_ASSERT(ptr); + NK_MEMSET(ptr, 0, size); +} + +NK_API int +nk_strlen(const char *str) +{ + int siz = 0; + NK_ASSERT(str); + while (str && *str++ != '\0') siz++; + return siz; +} + +NK_API int +nk_strtoi(const char *str, const char **endptr) +{ + int neg = 1; + const char *p = str; + int value = 0; + + NK_ASSERT(str); + if (!str) return 0; + + /* skip whitespace */ + while (*p == ' ') p++; + if (*p == '-') { + neg = -1; + p++; + } + while (*p && *p >= '0' && *p <= '9') { + value = value * 10 + (int) (*p - '0'); + p++; + } + if (endptr) + *endptr = p; + return neg*value; +} + +NK_API double +nk_strtod(const char *str, const char **endptr) +{ + double m; + double neg = 1.0; + const char *p = str; + double value = 0; + double number = 0; + + NK_ASSERT(str); + if (!str) return 0; + + /* skip whitespace */ + while (*p == ' ') p++; + if (*p == '-') { + neg = -1.0; + p++; + } + + while (*p && *p != '.' && *p != 'e') { + value = value * 10.0 + (double) (*p - '0'); + p++; + } + + if (*p == '.') { + p++; + for(m = 0.1; *p && *p != 'e'; p++ ) { + value = value + (double) (*p - '0') * m; + m *= 0.1; + } + } + if (*p == 'e') { + int i, pow, div; + p++; + if (*p == '-') { + div = nk_true; + p++; + } else if (*p == '+') { + div = nk_false; + p++; + } else div = nk_false; + + for (pow = 0; *p; p++) + pow = pow * 10 + (int) (*p - '0'); + + for (m = 1.0, i = 0; i < pow; i++) + m *= 10.0; + + if (div) + value /= m; + else value *= m; + } + number = value * neg; + if (endptr) + *endptr = p; + return number; +} + +NK_API float +nk_strtof(const char *str, const char **endptr) +{ + float float_value; + double double_value; + double_value = NK_STRTOD(str, endptr); + float_value = (float)double_value; + return float_value; +} + +NK_API int +nk_stricmp(const char *s1, const char *s2) +{ + nk_int c1,c2,d; + do { + c1 = *s1++; + c2 = *s2++; + d = c1 - c2; + while (d) { + if (c1 <= 'Z' && c1 >= 'A') { + d += ('a' - 'A'); + if (!d) break; + } + if (c2 <= 'Z' && c2 >= 'A') { + d -= ('a' - 'A'); + if (!d) break; + } + return ((d >= 0) << 1) - 1; + } + } while (c1); + return 0; +} + +NK_API int +nk_stricmpn(const char *s1, const char *s2, int n) +{ + int c1,c2,d; + NK_ASSERT(n >= 0); + do { + c1 = *s1++; + c2 = *s2++; + if (!n--) return 0; + + d = c1 - c2; + while (d) { + if (c1 <= 'Z' && c1 >= 'A') { + d += ('a' - 'A'); + if (!d) break; + } + if (c2 <= 'Z' && c2 >= 'A') { + d -= ('a' - 'A'); + if (!d) break; + } + return ((d >= 0) << 1) - 1; + } + } while (c1); + return 0; +} + +NK_INTERN int +nk_str_match_here(const char *regexp, const char *text) +{ + if (regexp[0] == '\0') + return 1; + if (regexp[1] == '*') + return nk_str_match_star(regexp[0], regexp+2, text); + if (regexp[0] == '$' && regexp[1] == '\0') + return *text == '\0'; + if (*text!='\0' && (regexp[0]=='.' || regexp[0]==*text)) + return nk_str_match_here(regexp+1, text+1); + return 0; +} + +NK_INTERN int +nk_str_match_star(int c, const char *regexp, const char *text) +{ + do {/* a '* matches zero or more instances */ + if (nk_str_match_here(regexp, text)) + return 1; + } while (*text != '\0' && (*text++ == c || c == '.')); + return 0; +} + +NK_API int +nk_strfilter(const char *text, const char *regexp) +{ + /* + c matches any literal character c + . matches any single character + ^ matches the beginning of the input string + $ matches the end of the input string + * matches zero or more occurrences of the previous character*/ + if (regexp[0] == '^') + return nk_str_match_here(regexp+1, text); + do { /* must look even if string is empty */ + if (nk_str_match_here(regexp, text)) + return 1; + } while (*text++ != '\0'); + return 0; +} + +NK_API int +nk_strmatch_fuzzy_text(const char *str, int str_len, + const char *pattern, int *out_score) +{ + /* Returns true if each character in pattern is found sequentially within str + * if found then outScore is also set. Score value has no intrinsic meaning. + * Range varies with pattern. Can only compare scores with same search pattern. */ + + /* ------- scores --------- */ + /* bonus for adjacent matches */ + #define NK_ADJACENCY_BONUS 5 + /* bonus if match occurs after a separator */ + #define NK_SEPARATOR_BONUS 10 + /* bonus if match is uppercase and prev is lower */ + #define NK_CAMEL_BONUS 10 + /* penalty applied for every letter in str before the first match */ + #define NK_LEADING_LETTER_PENALTY (-3) + /* maximum penalty for leading letters */ + #define NK_MAX_LEADING_LETTER_PENALTY (-9) + /* penalty for every letter that doesn't matter */ + #define NK_UNMATCHED_LETTER_PENALTY (-1) + + /* loop variables */ + int score = 0; + char const * pattern_iter = pattern; + int str_iter = 0; + int prev_matched = nk_false; + int prev_lower = nk_false; + /* true so if first letter match gets separator bonus*/ + int prev_separator = nk_true; + + /* use "best" matched letter if multiple string letters match the pattern */ + char const * best_letter = 0; + int best_letter_score = 0; + + /* loop over strings */ + NK_ASSERT(str); + NK_ASSERT(pattern); + if (!str || !str_len || !pattern) return 0; + while (str_iter < str_len) + { + const char pattern_letter = *pattern_iter; + const char str_letter = str[str_iter]; + + int next_match = *pattern_iter != '\0' && + nk_to_lower(pattern_letter) == nk_to_lower(str_letter); + int rematch = best_letter && nk_to_upper(*best_letter) == nk_to_upper(str_letter); + + int advanced = next_match && best_letter; + int pattern_repeat = best_letter && *pattern_iter != '\0'; + pattern_repeat = pattern_repeat && + nk_to_lower(*best_letter) == nk_to_lower(pattern_letter); + + if (advanced || pattern_repeat) { + score += best_letter_score; + best_letter = 0; + best_letter_score = 0; + } + + if (next_match || rematch) + { + int new_score = 0; + /* Apply penalty for each letter before the first pattern match */ + if (pattern_iter == pattern) { + int count = (int)(&str[str_iter] - str); + int penalty = NK_LEADING_LETTER_PENALTY * count; + if (penalty < NK_MAX_LEADING_LETTER_PENALTY) + penalty = NK_MAX_LEADING_LETTER_PENALTY; + + score += penalty; + } + + /* apply bonus for consecutive bonuses */ + if (prev_matched) + new_score += NK_ADJACENCY_BONUS; + + /* apply bonus for matches after a separator */ + if (prev_separator) + new_score += NK_SEPARATOR_BONUS; + + /* apply bonus across camel case boundaries */ + if (prev_lower && nk_is_upper(str_letter)) + new_score += NK_CAMEL_BONUS; + + /* update pattern iter IFF the next pattern letter was matched */ + if (next_match) + ++pattern_iter; + + /* update best letter in str which may be for a "next" letter or a rematch */ + if (new_score >= best_letter_score) { + /* apply penalty for now skipped letter */ + if (best_letter != 0) + score += NK_UNMATCHED_LETTER_PENALTY; + + best_letter = &str[str_iter]; + best_letter_score = new_score; + } + prev_matched = nk_true; + } else { + score += NK_UNMATCHED_LETTER_PENALTY; + prev_matched = nk_false; + } + + /* separators should be more easily defined */ + prev_lower = nk_is_lower(str_letter) != 0; + prev_separator = str_letter == '_' || str_letter == ' '; + + ++str_iter; + } + + /* apply score for last match */ + if (best_letter) + score += best_letter_score; + + /* did not match full pattern */ + if (*pattern_iter != '\0') + return nk_false; + + if (out_score) + *out_score = score; + return nk_true; +} + +NK_API int +nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score) +{return nk_strmatch_fuzzy_text(str, nk_strlen(str), pattern, out_score);} + +NK_INTERN int +nk_string_float_limit(char *string, int prec) +{ + int dot = 0; + char *c = string; + while (*c) { + if (*c == '.') { + dot = 1; + c++; + continue; + } + if (dot == (prec+1)) { + *c = 0; + break; + } + if (dot > 0) dot++; + c++; + } + return (int)(c - string); +} + +NK_INTERN double +nk_pow(double x, int n) +{ + /* check the sign of n */ + double r = 1; + int plus = n >= 0; + n = (plus) ? n : -n; + while (n > 0) { + if ((n & 1) == 1) + r *= x; + n /= 2; + x *= x; + } + return plus ? r : 1.0 / r; +} + +NK_INTERN int +nk_ifloord(double x) +{ + x = (double)((int)x - ((x < 0.0) ? 1 : 0)); + return (int)x; +} + +NK_INTERN int +nk_ifloorf(float x) +{ + x = (float)((int)x - ((x < 0.0f) ? 1 : 0)); + return (int)x; +} + +NK_INTERN int +nk_iceilf(float x) +{ + if (x >= 0) { + int i = (int)x; + return i; + } else { + int t = (int)x; + float r = x - (float)t; + return (r > 0.0f) ? t+1: t; + } +} + +NK_INTERN int +nk_log10(double n) +{ + int neg; + int ret; + int exp = 0; + + neg = (n < 0) ? 1 : 0; + ret = (neg) ? (int)-n : (int)n; + while ((ret / 10) > 0) { + ret /= 10; + exp++; + } + if (neg) exp = -exp; + return exp; +} + +NK_INTERN void +nk_strrev_ascii(char *s) +{ + int len = nk_strlen(s); + int end = len / 2; + int i = 0; + char t; + for (; i < end; ++i) { + t = s[i]; + s[i] = s[len - 1 - i]; + s[len -1 - i] = t; + } +} + +NK_INTERN char* +nk_itoa(char *s, long n) +{ + long i = 0; + if (n == 0) { + s[i++] = '0'; + s[i] = 0; + return s; + } + if (n < 0) { + s[i++] = '-'; + n = -n; + } + while (n > 0) { + s[i++] = (char)('0' + (n % 10)); + n /= 10; + } + s[i] = 0; + if (s[0] == '-') + ++s; + + nk_strrev_ascii(s); + return s; +} + +NK_INTERN char* +nk_dtoa(char *s, double n) +{ + int useExp = 0; + int digit = 0, m = 0, m1 = 0; + char *c = s; + int neg = 0; + + NK_ASSERT(s); + if (!s) return 0; + + if (n == 0.0) { + s[0] = '0'; s[1] = '\0'; + return s; + } + + neg = (n < 0); + if (neg) n = -n; + + /* calculate magnitude */ + m = nk_log10(n); + useExp = (m >= 14 || (neg && m >= 9) || m <= -9); + if (neg) *(c++) = '-'; + + /* set up for scientific notation */ + if (useExp) { + if (m < 0) + m -= 1; + n = n / (double)nk_pow(10.0, m); + m1 = m; + m = 0; + } + if (m < 1.0) { + m = 0; + } + + /* convert the number */ + while (n > NK_FLOAT_PRECISION || m >= 0) { + double weight = nk_pow(10.0, m); + if (weight > 0) { + double t = (double)n / weight; + digit = nk_ifloord(t); + n -= ((double)digit * weight); + *(c++) = (char)('0' + (char)digit); + } + if (m == 0 && n > 0) + *(c++) = '.'; + m--; + } + + if (useExp) { + /* convert the exponent */ + int i, j; + *(c++) = 'e'; + if (m1 > 0) { + *(c++) = '+'; + } else { + *(c++) = '-'; + m1 = -m1; + } + m = 0; + while (m1 > 0) { + *(c++) = (char)('0' + (char)(m1 % 10)); + m1 /= 10; + m++; + } + c -= m; + for (i = 0, j = m-1; i= buf_size) break; + iter++; + + /* flag arguments */ + while (*iter) { + if (*iter == '-') flag |= NK_ARG_FLAG_LEFT; + else if (*iter == '+') flag |= NK_ARG_FLAG_PLUS; + else if (*iter == ' ') flag |= NK_ARG_FLAG_SPACE; + else if (*iter == '#') flag |= NK_ARG_FLAG_NUM; + else if (*iter == '0') flag |= NK_ARG_FLAG_ZERO; + else break; + iter++; + } + + /* width argument */ + width = NK_DEFAULT; + if (*iter >= '1' && *iter <= '9') { + const char *end; + width = nk_strtoi(iter, &end); + if (end == iter) + width = -1; + else iter = end; + } else if (*iter == '*') { + width = va_arg(args, int); + iter++; + } + + /* precision argument */ + precision = NK_DEFAULT; + if (*iter == '.') { + iter++; + if (*iter == '*') { + precision = va_arg(args, int); + iter++; + } else { + const char *end; + precision = nk_strtoi(iter, &end); + if (end == iter) + precision = -1; + else iter = end; + } + } + + /* length modifier */ + if (*iter == 'h') { + if (*(iter+1) == 'h') { + arg_type = NK_ARG_TYPE_CHAR; + iter++; + } else arg_type = NK_ARG_TYPE_SHORT; + iter++; + } else if (*iter == 'l') { + arg_type = NK_ARG_TYPE_LONG; + iter++; + } else arg_type = NK_ARG_TYPE_DEFAULT; + + /* specifier */ + if (*iter == '%') { + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_ASSERT(precision == NK_DEFAULT); + NK_ASSERT(width == NK_DEFAULT); + if (len < buf_size) + buf[len++] = '%'; + } else if (*iter == 's') { + /* string */ + const char *str = va_arg(args, const char*); + NK_ASSERT(str != buf && "buffer and argument are not allowed to overlap!"); + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_ASSERT(precision == NK_DEFAULT); + NK_ASSERT(width == NK_DEFAULT); + if (str == buf) return -1; + while (str && *str && len < buf_size) + buf[len++] = *str++; + } else if (*iter == 'n') { + /* current length callback */ + signed int *n = va_arg(args, int*); + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_ASSERT(precision == NK_DEFAULT); + NK_ASSERT(width == NK_DEFAULT); + if (n) *n = len; + } else if (*iter == 'c' || *iter == 'i' || *iter == 'd') { + /* signed integer */ + long value = 0; + const char *num_iter; + int num_len, num_print, padding; + int cur_precision = NK_MAX(precision, 1); + int cur_width = NK_MAX(width, 0); + + /* retrieve correct value type */ + if (arg_type == NK_ARG_TYPE_CHAR) + value = (signed char)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_SHORT) + value = (signed short)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_LONG) + value = va_arg(args, signed long); + else if (*iter == 'c') + value = (unsigned char)va_arg(args, int); + else value = va_arg(args, signed int); + + /* convert number to string */ + nk_itoa(number_buffer, value); + num_len = nk_strlen(number_buffer); + padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0); + if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE)) + padding = NK_MAX(padding-1, 0); + + /* fill left padding up to a total of `width` characters */ + if (!(flag & NK_ARG_FLAG_LEFT)) { + while (padding-- > 0 && (len < buf_size)) { + if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT)) + buf[len++] = '0'; + else buf[len++] = ' '; + } + } + + /* copy string value representation into buffer */ + if ((flag & NK_ARG_FLAG_PLUS) && value >= 0 && len < buf_size) + buf[len++] = '+'; + else if ((flag & NK_ARG_FLAG_SPACE) && value >= 0 && len < buf_size) + buf[len++] = ' '; + + /* fill up to precision number of digits with '0' */ + num_print = NK_MAX(cur_precision, num_len); + while (precision && (num_print > num_len) && (len < buf_size)) { + buf[len++] = '0'; + num_print--; + } + + /* copy string value representation into buffer */ + num_iter = number_buffer; + while (precision && *num_iter && len < buf_size) + buf[len++] = *num_iter++; + + /* fill right padding up to width characters */ + if (flag & NK_ARG_FLAG_LEFT) { + while ((padding-- > 0) && (len < buf_size)) + buf[len++] = ' '; + } + } else if (*iter == 'o' || *iter == 'x' || *iter == 'X' || *iter == 'u') { + /* unsigned integer */ + unsigned long value = 0; + int num_len = 0, num_print, padding = 0; + int cur_precision = NK_MAX(precision, 1); + int cur_width = NK_MAX(width, 0); + unsigned int base = (*iter == 'o') ? 8: (*iter == 'u')? 10: 16; + + /* print oct/hex/dec value */ + const char *upper_output_format = "0123456789ABCDEF"; + const char *lower_output_format = "0123456789abcdef"; + const char *output_format = (*iter == 'x') ? + lower_output_format: upper_output_format; + + /* retrieve correct value type */ + if (arg_type == NK_ARG_TYPE_CHAR) + value = (unsigned char)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_SHORT) + value = (unsigned short)va_arg(args, int); + else if (arg_type == NK_ARG_TYPE_LONG) + value = va_arg(args, unsigned long); + else value = va_arg(args, unsigned int); + + do { + /* convert decimal number into hex/oct number */ + int digit = output_format[value % base]; + if (num_len < NK_MAX_NUMBER_BUFFER) + number_buffer[num_len++] = (char)digit; + value /= base; + } while (value > 0); + + num_print = NK_MAX(cur_precision, num_len); + padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0); + if (flag & NK_ARG_FLAG_NUM) + padding = NK_MAX(padding-1, 0); + + /* fill left padding up to a total of `width` characters */ + if (!(flag & NK_ARG_FLAG_LEFT)) { + while ((padding-- > 0) && (len < buf_size)) { + if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT)) + buf[len++] = '0'; + else buf[len++] = ' '; + } + } + + /* fill up to precision number of digits */ + if (num_print && (flag & NK_ARG_FLAG_NUM)) { + if ((*iter == 'o') && (len < buf_size)) { + buf[len++] = '0'; + } else if ((*iter == 'x') && ((len+1) < buf_size)) { + buf[len++] = '0'; + buf[len++] = 'x'; + } else if ((*iter == 'X') && ((len+1) < buf_size)) { + buf[len++] = '0'; + buf[len++] = 'X'; + } + } + while (precision && (num_print > num_len) && (len < buf_size)) { + buf[len++] = '0'; + num_print--; + } + + /* reverse number direction */ + while (num_len > 0) { + if (precision && (len < buf_size)) + buf[len++] = number_buffer[num_len-1]; + num_len--; + } + + /* fill right padding up to width characters */ + if (flag & NK_ARG_FLAG_LEFT) { + while ((padding-- > 0) && (len < buf_size)) + buf[len++] = ' '; + } + } else if (*iter == 'f') { + /* floating point */ + const char *num_iter; + int cur_precision = (precision < 0) ? 6: precision; + int prefix, cur_width = NK_MAX(width, 0); + double value = va_arg(args, double); + int num_len = 0, frac_len = 0, dot = 0; + int padding = 0; + + NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); + NK_DTOA(number_buffer, value); + num_len = nk_strlen(number_buffer); + + /* calculate padding */ + num_iter = number_buffer; + while (*num_iter && *num_iter != '.') + num_iter++; + + prefix = (*num_iter == '.')?(int)(num_iter - number_buffer)+1:0; + padding = NK_MAX(cur_width - (prefix + NK_MIN(cur_precision, num_len - prefix)) , 0); + if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE)) + padding = NK_MAX(padding-1, 0); + + /* fill left padding up to a total of `width` characters */ + if (!(flag & NK_ARG_FLAG_LEFT)) { + while (padding-- > 0 && (len < buf_size)) { + if (flag & NK_ARG_FLAG_ZERO) + buf[len++] = '0'; + else buf[len++] = ' '; + } + } + + /* copy string value representation into buffer */ + num_iter = number_buffer; + if ((flag & NK_ARG_FLAG_PLUS) && (value >= 0) && (len < buf_size)) + buf[len++] = '+'; + else if ((flag & NK_ARG_FLAG_SPACE) && (value >= 0) && (len < buf_size)) + buf[len++] = ' '; + while (*num_iter) { + if (dot) frac_len++; + if (len < buf_size) + buf[len++] = *num_iter; + if (*num_iter == '.') dot = 1; + if (frac_len >= cur_precision) break; + num_iter++; + } + + /* fill number up to precision */ + while (frac_len < cur_precision) { + if (!dot && len < buf_size) { + buf[len++] = '.'; + dot = 1; + } + if (len < buf_size) + buf[len++] = '0'; + frac_len++; + } + + /* fill right padding up to width characters */ + if (flag & NK_ARG_FLAG_LEFT) { + while ((padding-- > 0) && (len < buf_size)) + buf[len++] = ' '; + } + } else { + /* Specifier not supported: g,G,e,E,p,z */ + NK_ASSERT(0 && "specifier is not supported!"); + return result; + } + } + buf[(len >= buf_size)?(buf_size-1):len] = 0; + result = (len >= buf_size)?-1:len; + return result; +} +#endif + +NK_INTERN int +nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args) +{ + int result = -1; + NK_ASSERT(buf); + NK_ASSERT(buf_size); + if (!buf || !buf_size || !fmt) return 0; +#ifdef NK_INCLUDE_STANDARD_IO + result = NK_VSNPRINTF(buf, (nk_size)buf_size, fmt, args); + result = (result >= buf_size) ? -1: result; + buf[buf_size-1] = 0; +#else + result = nk_vsnprintf(buf, buf_size, fmt, args); +#endif + return result; +} +#endif + +NK_API nk_hash +nk_murmur_hash(const void * key, int len, nk_hash seed) +{ + /* 32-Bit MurmurHash3: https://code.google.com/p/smhasher/wiki/MurmurHash3*/ + #define NK_ROTL(x,r) ((x) << (r) | ((x) >> (32 - r))) + union {const nk_uint *i; const nk_byte *b;} conv = {0}; + const nk_byte *data = (const nk_byte*)key; + const int nblocks = len/4; + nk_uint h1 = seed; + const nk_uint c1 = 0xcc9e2d51; + const nk_uint c2 = 0x1b873593; + const nk_byte *tail; + const nk_uint *blocks; + nk_uint k1; + int i; + + /* body */ + if (!key) return 0; + conv.b = (data + nblocks*4); + blocks = (const nk_uint*)conv.i; + for (i = -nblocks; i; ++i) { + k1 = blocks[i]; + k1 *= c1; + k1 = NK_ROTL(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = NK_ROTL(h1,13); + h1 = h1*5+0xe6546b64; + } + + /* tail */ + tail = (const nk_byte*)(data + nblocks*4); + k1 = 0; + switch (len & 3) { + case 3: k1 ^= (nk_uint)(tail[2] << 16); + case 2: k1 ^= (nk_uint)(tail[1] << 8u); + case 1: k1 ^= tail[0]; + k1 *= c1; + k1 = NK_ROTL(k1,15); + k1 *= c2; + h1 ^= k1; + default: break; + } + + /* finalization */ + h1 ^= (nk_uint)len; + /* fmix32 */ + h1 ^= h1 >> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >> 16; + + #undef NK_ROTL + return h1; +} + +#ifdef NK_INCLUDE_STANDARD_IO +NK_INTERN char* +nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc) +{ + char *buf; + FILE *fd; + long ret; + + NK_ASSERT(path); + NK_ASSERT(siz); + NK_ASSERT(alloc); + if (!path || !siz || !alloc) + return 0; + + fd = fopen(path, "rb"); + if (!fd) return 0; + fseek(fd, 0, SEEK_END); + ret = ftell(fd); + if (ret < 0) { + fclose(fd); + return 0; + } + *siz = (nk_size)ret; + fseek(fd, 0, SEEK_SET); + buf = (char*)alloc->alloc(alloc->userdata,0, *siz); + NK_ASSERT(buf); + if (!buf) { + fclose(fd); + return 0; + } + *siz = (nk_size)fread(buf, *siz, 1, fd); + fclose(fd); + return buf; +} +#endif + +/* + * ============================================================== + * + * COLOR + * + * =============================================================== + */ +NK_INTERN int +nk_parse_hex(const char *p, int length) +{ + int i = 0; + int len = 0; + while (len < length) { + i <<= 4; + if (p[len] >= 'a' && p[len] <= 'f') + i += ((p[len] - 'a') + 10); + else if (p[len] >= 'A' && p[len] <= 'F') + i += ((p[len] - 'A') + 10); + else i += (p[len] - '0'); + len++; + } + return i; +} + +NK_API struct nk_color +nk_rgba(int r, int g, int b, int a) +{ + struct nk_color ret; + ret.r = (nk_byte)NK_CLAMP(0, r, 255); + ret.g = (nk_byte)NK_CLAMP(0, g, 255); + ret.b = (nk_byte)NK_CLAMP(0, b, 255); + ret.a = (nk_byte)NK_CLAMP(0, a, 255); + return ret; +} + +NK_API struct nk_color +nk_rgb_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = 255; + return col; +} + +NK_API struct nk_color +nk_rgba_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = (nk_byte)nk_parse_hex(c+6, 2); + return col; +} + +NK_API void +nk_color_hex_rgba(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[1] = (char)NK_TO_HEX((col.r & 0x0F)); + output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[3] = (char)NK_TO_HEX((col.g & 0x0F)); + output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[5] = (char)NK_TO_HEX((col.b & 0x0F)); + output[6] = (char)NK_TO_HEX((col.a & 0xF0) >> 4); + output[7] = (char)NK_TO_HEX((col.a & 0x0F)); + output[8] = '\0'; + #undef NK_TO_HEX +} + +NK_API void +nk_color_hex_rgb(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[1] = (char)NK_TO_HEX((col.r & 0x0F)); + output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[3] = (char)NK_TO_HEX((col.g & 0x0F)); + output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[5] = (char)NK_TO_HEX((col.b & 0x0F)); + output[6] = '\0'; + #undef NK_TO_HEX +} + +NK_API struct nk_color +nk_rgba_iv(const int *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_rgba_bv(const nk_byte *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_rgb(int r, int g, int b) +{ + struct nk_color ret; + ret.r = (nk_byte)NK_CLAMP(0, r, 255); + ret.g = (nk_byte)NK_CLAMP(0, g, 255); + ret.b = (nk_byte)NK_CLAMP(0, b, 255); + ret.a = (nk_byte)255; + return ret; +} + +NK_API struct nk_color +nk_rgb_iv(const int *c) +{ + return nk_rgb(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_rgb_bv(const nk_byte* c) +{ + return nk_rgb(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_rgba_u32(nk_uint in) +{ + struct nk_color ret; + ret.r = (in & 0xFF); + ret.g = ((in >> 8) & 0xFF); + ret.b = ((in >> 16) & 0xFF); + ret.a = (nk_byte)((in >> 24) & 0xFF); + return ret; +} + +NK_API struct nk_color +nk_rgba_f(float r, float g, float b, float a) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = (nk_byte)(NK_SATURATE(a) * 255.0f); + return ret; +} + +NK_API struct nk_color +nk_rgba_fv(const float *c) +{ + return nk_rgba_f(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_rgb_f(float r, float g, float b) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = 255; + return ret; +} + +NK_API struct nk_color +nk_rgb_fv(const float *c) +{ + return nk_rgb_f(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsv(int h, int s, int v) +{ + return nk_hsva(h, s, v, 255); +} + +NK_API struct nk_color +nk_hsv_iv(const int *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsv_bv(const nk_byte *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsv_f(float h, float s, float v) +{ + return nk_hsva_f(h, s, v, 1.0f); +} + +NK_API struct nk_color +nk_hsv_fv(const float *c) +{ + return nk_hsv_f(c[0], c[1], c[2]); +} + +NK_API struct nk_color +nk_hsva(int h, int s, int v, int a) +{ + float hf = ((float)NK_CLAMP(0, h, 255)) / 255.0f; + float sf = ((float)NK_CLAMP(0, s, 255)) / 255.0f; + float vf = ((float)NK_CLAMP(0, v, 255)) / 255.0f; + float af = ((float)NK_CLAMP(0, a, 255)) / 255.0f; + return nk_hsva_f(hf, sf, vf, af); +} + +NK_API struct nk_color +nk_hsva_iv(const int *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_hsva_bv(const nk_byte *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} + +NK_API struct nk_color +nk_hsva_f(float h, float s, float v, float a) +{ + struct nk_colorf out = {0,0,0,0}; + float p, q, t, f; + int i; + + if (s <= 0.0f) { + out.r = v; out.g = v; out.b = v; + return nk_rgb_f(out.r, out.g, out.b); + } + + h = h / (60.0f/360.0f); + i = (int)h; + f = h - (float)i; + p = v * (1.0f - s); + q = v * (1.0f - (s * f)); + t = v * (1.0f - s * (1.0f - f)); + + switch (i) { + case 0: default: out.r = v; out.g = t; out.b = p; break; + case 1: out.r = q; out.g = v; out.b = p; break; + case 2: out.r = p; out.g = v; out.b = t; break; + case 3: out.r = p; out.g = q; out.b = v; break; + case 4: out.r = t; out.g = p; out.b = v; break; + case 5: out.r = v; out.g = p; out.b = q; break; + } + return nk_rgba_f(out.r, out.g, out.b, a); +} + +NK_API struct nk_color +nk_hsva_fv(const float *c) +{ + return nk_hsva_f(c[0], c[1], c[2], c[3]); +} + +NK_API nk_uint +nk_color_u32(struct nk_color in) +{ + nk_uint out = (nk_uint)in.r; + out |= ((nk_uint)in.g << 8); + out |= ((nk_uint)in.b << 16); + out |= ((nk_uint)in.a << 24); + return out; +} + +NK_API void +nk_color_f(float *r, float *g, float *b, float *a, struct nk_color in) +{ + NK_STORAGE const float s = 1.0f/255.0f; + *r = (float)in.r * s; + *g = (float)in.g * s; + *b = (float)in.b * s; + *a = (float)in.a * s; +} + +NK_API void +nk_color_fv(float *c, struct nk_color in) +{ + nk_color_f(&c[0], &c[1], &c[2], &c[3], in); +} + +NK_API void +nk_color_d(double *r, double *g, double *b, double *a, struct nk_color in) +{ + NK_STORAGE const double s = 1.0/255.0; + *r = (double)in.r * s; + *g = (double)in.g * s; + *b = (double)in.b * s; + *a = (double)in.a * s; +} + +NK_API void +nk_color_dv(double *c, struct nk_color in) +{ + nk_color_d(&c[0], &c[1], &c[2], &c[3], in); +} + +NK_API void +nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color in) +{ + float a; + nk_color_hsva_f(out_h, out_s, out_v, &a, in); +} + +NK_API void +nk_color_hsv_fv(float *out, struct nk_color in) +{ + float a; + nk_color_hsva_f(&out[0], &out[1], &out[2], &a, in); +} + +NK_API void +nk_color_hsva_f(float *out_h, float *out_s, + float *out_v, float *out_a, struct nk_color in) +{ + float chroma; + float K = 0.0f; + float r,g,b,a; + + nk_color_f(&r,&g,&b,&a, in); + if (g < b) { + const float t = g; g = b; b = t; + K = -1.f; + } + if (r < g) { + const float t = r; r = g; g = t; + K = -2.f/6.0f - K; + } + chroma = r - ((g < b) ? g: b); + *out_h = NK_ABS(K + (g - b)/(6.0f * chroma + 1e-20f)); + *out_s = chroma / (r + 1e-20f); + *out_v = r; + *out_a = (float)in.a / 255.0f; +} + +NK_API void +nk_color_hsva_fv(float *out, struct nk_color in) +{ + nk_color_hsva_f(&out[0], &out[1], &out[2], &out[3], in); +} + +NK_API void +nk_color_hsva_i(int *out_h, int *out_s, int *out_v, + int *out_a, struct nk_color in) +{ + float h,s,v,a; + nk_color_hsva_f(&h, &s, &v, &a, in); + *out_h = (nk_byte)(h * 255.0f); + *out_s = (nk_byte)(s * 255.0f); + *out_v = (nk_byte)(v * 255.0f); + *out_a = (nk_byte)(a * 255.0f); +} + +NK_API void +nk_color_hsva_iv(int *out, struct nk_color in) +{ + nk_color_hsva_i(&out[0], &out[1], &out[2], &out[3], in); +} + +NK_API void +nk_color_hsva_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; + out[3] = (nk_byte)tmp[3]; +} + +NK_API void +nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + *h = (nk_byte)tmp[0]; + *s = (nk_byte)tmp[1]; + *v = (nk_byte)tmp[2]; + *a = (nk_byte)tmp[3]; +} + +NK_API void +nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color in) +{ + int a; + nk_color_hsva_i(out_h, out_s, out_v, &a, in); +} + +NK_API void +nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + *out_h = (nk_byte)tmp[0]; + *out_s = (nk_byte)tmp[1]; + *out_v = (nk_byte)tmp[2]; +} + +NK_API void +nk_color_hsv_iv(int *out, struct nk_color in) +{ + nk_color_hsv_i(&out[0], &out[1], &out[2], in); +} + +NK_API void +nk_color_hsv_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsv_i(&tmp[0], &tmp[1], &tmp[2], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; +} +/* + * ============================================================== + * + * IMAGE + * + * =============================================================== + */ +NK_API nk_handle +nk_handle_ptr(void *ptr) +{ + nk_handle handle = {0}; + handle.ptr = ptr; + return handle; +} + +NK_API nk_handle +nk_handle_id(int id) +{ + nk_handle handle; + nk_zero_struct(handle); + handle.id = id; + return handle; +} + +NK_API struct nk_image +nk_subimage_ptr(void *ptr, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.ptr = ptr; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} + +NK_API struct nk_image +nk_subimage_id(int id, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} + +NK_API struct nk_image +nk_subimage_handle(nk_handle handle, unsigned short w, unsigned short h, + struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle = handle; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} + +NK_API struct nk_image +nk_image_handle(nk_handle handle) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle = handle; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} + +NK_API struct nk_image +nk_image_ptr(void *ptr) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + NK_ASSERT(ptr); + s.handle.ptr = ptr; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} + +NK_API struct nk_image +nk_image_id(int id) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} + +NK_API int +nk_image_is_subimage(const struct nk_image* img) +{ + NK_ASSERT(img); + return !(img->w == 0 && img->h == 0); +} + +NK_INTERN void +nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, + float x1, float y1) +{ + NK_ASSERT(a); + NK_ASSERT(clip); + clip->x = NK_MAX(a->x, x0); + clip->y = NK_MAX(a->y, y0); + clip->w = NK_MIN(a->x + a->w, x1) - clip->x; + clip->h = NK_MIN(a->y + a->h, y1) - clip->y; + clip->w = NK_MAX(0, clip->w); + clip->h = NK_MAX(0, clip->h); +} + +NK_API void +nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, + float pad_x, float pad_y, enum nk_heading direction) +{ + float w_half, h_half; + NK_ASSERT(result); + + r.w = NK_MAX(2 * pad_x, r.w); + r.h = NK_MAX(2 * pad_y, r.h); + r.w = r.w - 2 * pad_x; + r.h = r.h - 2 * pad_y; + + r.x = r.x + pad_x; + r.y = r.y + pad_y; + + w_half = r.w / 2.0f; + h_half = r.h / 2.0f; + + if (direction == NK_UP) { + result[0] = nk_vec2(r.x + w_half, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + r.h); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_RIGHT) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + h_half); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_DOWN) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + w_half, r.y + r.h); + } else { + result[0] = nk_vec2(r.x, r.y + h_half); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + r.w, r.y + r.h); + } +} + +NK_INTERN int +nk_text_clamp(const struct nk_user_font *font, const char *text, + int text_len, float space, int *glyphs, float *text_width, + nk_rune *sep_list, int sep_count) +{ + int i = 0; + int glyph_len = 0; + float last_width = 0; + nk_rune unicode = 0; + float width = 0; + int len = 0; + int g = 0; + float s; + + int sep_len = 0; + int sep_g = 0; + float sep_width = 0; + sep_count = NK_MAX(sep_count,0); + + glyph_len = nk_utf_decode(text, &unicode, text_len); + while (glyph_len && (width < space) && (len < text_len)) { + len += glyph_len; + s = font->width(font->userdata, font->height, text, len); + for (i = 0; i < sep_count; ++i) { + if (unicode != sep_list[i]) continue; + sep_width = last_width = width; + sep_g = g+1; + sep_len = len; + break; + } + if (i == sep_count){ + last_width = sep_width = width; + sep_g = g+1; + } + width = s; + glyph_len = nk_utf_decode(&text[len], &unicode, text_len - len); + g++; + } + if (len >= text_len) { + *glyphs = g; + *text_width = last_width; + return len; + } else { + *glyphs = sep_g; + *text_width = sep_width; + return (!sep_len) ? len: sep_len; + } +} + +enum {NK_DO_NOT_STOP_ON_NEW_LINE, NK_STOP_ON_NEW_LINE}; +NK_INTERN struct nk_vec2 +nk_text_calculate_text_bounds(const struct nk_user_font *font, + const char *begin, int byte_len, float row_height, const char **remaining, + struct nk_vec2 *out_offset, int *glyphs, int op) +{ + float line_height = row_height; + struct nk_vec2 text_size = nk_vec2(0,0); + float line_width = 0.0f; + + float glyph_width; + int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + if (!begin || byte_len <= 0 || !font) + return nk_vec2(0,row_height); + + glyph_len = nk_utf_decode(begin, &unicode, byte_len); + if (!glyph_len) return text_size; + glyph_width = font->width(font->userdata, font->height, begin, glyph_len); + + *glyphs = 0; + while ((text_len < byte_len) && glyph_len) { + if (unicode == '\n') { + text_size.x = NK_MAX(text_size.x, line_width); + text_size.y += line_height; + line_width = 0; + *glyphs+=1; + if (op == NK_STOP_ON_NEW_LINE) + break; + + text_len++; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + if (unicode == '\r') { + text_len++; + *glyphs+=1; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + continue; + } + + *glyphs = *glyphs + 1; + text_len += glyph_len; + line_width += (float)glyph_width; + glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); + glyph_width = font->width(font->userdata, font->height, begin+text_len, glyph_len); + continue; + } + + if (text_size.x < line_width) + text_size.x = line_width; + if (out_offset) + *out_offset = nk_vec2(line_width, text_size.y + line_height); + if (line_width > 0 || text_size.y == 0.0f) + text_size.y += line_height; + if (remaining) + *remaining = begin+text_len; + return text_size; +} + +/* ============================================================== + * + * UTF-8 + * + * ===============================================================*/ +NK_GLOBAL const nk_byte nk_utfbyte[NK_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +NK_GLOBAL const nk_byte nk_utfmask[NK_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +NK_GLOBAL const nk_uint nk_utfmin[NK_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x10000}; +NK_GLOBAL const nk_uint nk_utfmax[NK_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +NK_INTERN int +nk_utf_validate(nk_rune *u, int i) +{ + NK_ASSERT(u); + if (!u) return 0; + if (!NK_BETWEEN(*u, nk_utfmin[i], nk_utfmax[i]) || + NK_BETWEEN(*u, 0xD800, 0xDFFF)) + *u = NK_UTF_INVALID; + for (i = 1; *u > nk_utfmax[i]; ++i); + return i; +} + +NK_INTERN nk_rune +nk_utf_decode_byte(char c, int *i) +{ + NK_ASSERT(i); + if (!i) return 0; + for(*i = 0; *i < (int)NK_LEN(nk_utfmask); ++(*i)) { + if (((nk_byte)c & nk_utfmask[*i]) == nk_utfbyte[*i]) + return (nk_byte)(c & ~nk_utfmask[*i]); + } + return 0; +} + +NK_API int +nk_utf_decode(const char *c, nk_rune *u, int clen) +{ + int i, j, len, type=0; + nk_rune udecoded; + + NK_ASSERT(c); + NK_ASSERT(u); + + if (!c || !u) return 0; + if (!clen) return 0; + *u = NK_UTF_INVALID; + + udecoded = nk_utf_decode_byte(c[0], &len); + if (!NK_BETWEEN(len, 1, NK_UTF_SIZE)) + return 1; + + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | nk_utf_decode_byte(c[i], &type); + if (type != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + nk_utf_validate(u, len); + return len; +} + +NK_INTERN char +nk_utf_encode_byte(nk_rune u, int i) +{ + return (char)((nk_utfbyte[i]) | ((nk_byte)u & ~nk_utfmask[i])); +} + +NK_API int +nk_utf_encode(nk_rune u, char *c, int clen) +{ + int len, i; + len = nk_utf_validate(&u, 0); + if (clen < len || !len || len > NK_UTF_SIZE) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = nk_utf_encode_byte(u, 0); + u >>= 6; + } + c[0] = nk_utf_encode_byte(u, len); + return len; +} + +NK_API int +nk_utf_len(const char *str, int len) +{ + const char *text; + int glyphs = 0; + int text_len; + int glyph_len; + int src_len = 0; + nk_rune unicode; + + NK_ASSERT(str); + if (!str || !len) return 0; + + text = str; + text_len = len; + glyph_len = nk_utf_decode(text, &unicode, text_len); + while (glyph_len && src_len < len) { + glyphs++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, &unicode, text_len - src_len); + } + return glyphs; +} + +NK_API const char* +nk_utf_at(const char *buffer, int length, int index, + nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + const char *text; + int text_len; + + NK_ASSERT(buffer); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!buffer || !unicode || !len) return 0; + if (index < 0) { + *unicode = NK_UTF_INVALID; + *len = 0; + return 0; + } + + text = buffer; + text_len = length; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == index) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != index) return 0; + return buffer + src_len; +} + +/* ============================================================== + * + * BUFFER + * + * ===============================================================*/ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_INTERN void* nk_malloc(nk_handle unused, void *old,nk_size size) +{NK_UNUSED(unused); NK_UNUSED(old); return malloc(size);} +NK_INTERN void nk_mfree(nk_handle unused, void *ptr) +{NK_UNUSED(unused); free(ptr);} + +NK_API void +nk_buffer_init_default(struct nk_buffer *buffer) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + nk_buffer_init(buffer, &alloc, NK_BUFFER_DEFAULT_INITIAL_SIZE); +} +#endif + +NK_API void +nk_buffer_init(struct nk_buffer *b, const struct nk_allocator *a, + nk_size initial_size) +{ + NK_ASSERT(b); + NK_ASSERT(a); + NK_ASSERT(initial_size); + if (!b || !a || !initial_size) return; + + nk_zero(b, sizeof(*b)); + b->type = NK_BUFFER_DYNAMIC; + b->memory.ptr = a->alloc(a->userdata,0, initial_size); + b->memory.size = initial_size; + b->size = initial_size; + b->grow_factor = 2.0f; + b->pool = *a; +} + +NK_API void +nk_buffer_init_fixed(struct nk_buffer *b, void *m, nk_size size) +{ + NK_ASSERT(b); + NK_ASSERT(m); + NK_ASSERT(size); + if (!b || !m || !size) return; + + nk_zero(b, sizeof(*b)); + b->type = NK_BUFFER_FIXED; + b->memory.ptr = m; + b->memory.size = size; + b->size = size; +} + +NK_INTERN void* +nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment, + enum nk_buffer_allocation_type type) +{ + void *memory = 0; + switch (type) { + default: + case NK_BUFFER_MAX: + case NK_BUFFER_FRONT: + if (align) { + memory = NK_ALIGN_PTR(unaligned, align); + *alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); + } else { + memory = unaligned; + *alignment = 0; + } + break; + case NK_BUFFER_BACK: + if (align) { + memory = NK_ALIGN_PTR_BACK(unaligned, align); + *alignment = (nk_size)((nk_byte*)unaligned - (nk_byte*)memory); + } else { + memory = unaligned; + *alignment = 0; + } + break; + } + return memory; +} + +NK_INTERN void* +nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size) +{ + void *temp; + nk_size buffer_size; + + NK_ASSERT(b); + NK_ASSERT(size); + if (!b || !size || !b->pool.alloc || !b->pool.free) + return 0; + + buffer_size = b->memory.size; + temp = b->pool.alloc(b->pool.userdata, b->memory.ptr, capacity); + NK_ASSERT(temp); + if (!temp) return 0; + + *size = capacity; + if (temp != b->memory.ptr) { + NK_MEMCPY(temp, b->memory.ptr, buffer_size); + b->pool.free(b->pool.userdata, b->memory.ptr); + } + + if (b->size == buffer_size) { + /* no back buffer so just set correct size */ + b->size = capacity; + return temp; + } else { + /* copy back buffer to the end of the new buffer */ + void *dst, *src; + nk_size back_size; + back_size = buffer_size - b->size; + dst = nk_ptr_add(void, temp, capacity - back_size); + src = nk_ptr_add(void, temp, b->size); + NK_MEMCPY(dst, src, back_size); + b->size = capacity - back_size; + } + return temp; +} + +NK_INTERN void* +nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, + nk_size size, nk_size align) +{ + int full; + nk_size alignment; + void *unaligned; + void *memory; + + NK_ASSERT(b); + NK_ASSERT(size); + if (!b || !size) return 0; + b->needed += size; + + /* calculate total size with needed alignment + size */ + if (type == NK_BUFFER_FRONT) + unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); + else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size); + memory = nk_buffer_align(unaligned, align, &alignment, type); + + /* check if buffer has enough memory*/ + if (type == NK_BUFFER_FRONT) + full = ((b->allocated + size + alignment) > b->size); + else full = ((b->size - NK_MIN(b->size,(size + alignment))) <= b->allocated); + + if (full) { + nk_size capacity; + if (b->type != NK_BUFFER_DYNAMIC) + return 0; + NK_ASSERT(b->pool.alloc && b->pool.free); + if (b->type != NK_BUFFER_DYNAMIC || !b->pool.alloc || !b->pool.free) + return 0; + + /* buffer is full so allocate bigger buffer if dynamic */ + capacity = (nk_size)((float)b->memory.size * b->grow_factor); + capacity = NK_MAX(capacity, nk_round_up_pow2((nk_uint)(b->allocated + size))); + b->memory.ptr = nk_buffer_realloc(b, capacity, &b->memory.size); + if (!b->memory.ptr) return 0; + + /* align newly allocated pointer */ + if (type == NK_BUFFER_FRONT) + unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); + else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size); + memory = nk_buffer_align(unaligned, align, &alignment, type); + } + if (type == NK_BUFFER_FRONT) + b->allocated += size + alignment; + else b->size -= (size + alignment); + b->needed += alignment; + b->calls++; + return memory; +} + +NK_API void +nk_buffer_push(struct nk_buffer *b, enum nk_buffer_allocation_type type, + const void *memory, nk_size size, nk_size align) +{ + void *mem = nk_buffer_alloc(b, type, size, align); + if (!mem) return; + NK_MEMCPY(mem, memory, size); +} + +NK_API void +nk_buffer_mark(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) +{ + NK_ASSERT(buffer); + if (!buffer) return; + buffer->marker[type].active = nk_true; + if (type == NK_BUFFER_BACK) + buffer->marker[type].offset = buffer->size; + else buffer->marker[type].offset = buffer->allocated; +} + +NK_API void +nk_buffer_reset(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) +{ + NK_ASSERT(buffer); + if (!buffer) return; + if (type == NK_BUFFER_BACK) { + /* reset back buffer either back to marker or empty */ + buffer->needed -= (buffer->memory.size - buffer->marker[type].offset); + if (buffer->marker[type].active) + buffer->size = buffer->marker[type].offset; + else buffer->size = buffer->memory.size; + buffer->marker[type].active = nk_false; + } else { + /* reset front buffer either back to back marker or empty */ + buffer->needed -= (buffer->allocated - buffer->marker[type].offset); + if (buffer->marker[type].active) + buffer->allocated = buffer->marker[type].offset; + else buffer->allocated = 0; + buffer->marker[type].active = nk_false; + } +} + +NK_API void +nk_buffer_clear(struct nk_buffer *b) +{ + NK_ASSERT(b); + if (!b) return; + b->allocated = 0; + b->size = b->memory.size; + b->calls = 0; + b->needed = 0; +} + +NK_API void +nk_buffer_free(struct nk_buffer *b) +{ + NK_ASSERT(b); + if (!b || !b->memory.ptr) return; + if (b->type == NK_BUFFER_FIXED) return; + if (!b->pool.free) return; + NK_ASSERT(b->pool.free); + b->pool.free(b->pool.userdata, b->memory.ptr); +} + +NK_API void +nk_buffer_info(struct nk_memory_status *s, struct nk_buffer *b) +{ + NK_ASSERT(b); + NK_ASSERT(s); + if (!s || !b) return; + s->allocated = b->allocated; + s->size = b->memory.size; + s->needed = b->needed; + s->memory = b->memory.ptr; + s->calls = b->calls; +} + +NK_API void* +nk_buffer_memory(struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.ptr; +} + +NK_API const void* +nk_buffer_memory_const(const struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.ptr; +} + +NK_API nk_size +nk_buffer_total(struct nk_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return 0; + return buffer->memory.size; +} + +/* + * ============================================================== + * + * STRING + * + * =============================================================== + */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_str_init_default(struct nk_str *str) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + nk_buffer_init(&str->buffer, &alloc, 32); + str->len = 0; +} +#endif + +NK_API void +nk_str_init(struct nk_str *str, const struct nk_allocator *alloc, nk_size size) +{ + nk_buffer_init(&str->buffer, alloc, size); + str->len = 0; +} + +NK_API void +nk_str_init_fixed(struct nk_str *str, void *memory, nk_size size) +{ + nk_buffer_init_fixed(&str->buffer, memory, size); + str->len = 0; +} + +NK_API int +nk_str_append_text_char(struct nk_str *s, const char *str, int len) +{ + char *mem; + NK_ASSERT(s); + NK_ASSERT(str); + if (!s || !str || !len) return 0; + mem = (char*)nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); + if (!mem) return 0; + NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); + s->len += nk_utf_len(str, len); + return len; +} + +NK_API int +nk_str_append_str_char(struct nk_str *s, const char *str) +{ + return nk_str_append_text_char(s, str, nk_strlen(str)); +} + +NK_API int +nk_str_append_text_utf8(struct nk_str *str, const char *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_rune unicode; + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) + byte_len += nk_utf_decode(text+byte_len, &unicode, 4); + nk_str_append_text_char(str, text, byte_len); + return len; +} + +NK_API int +nk_str_append_str_utf8(struct nk_str *str, const char *text) +{ + int runes = 0; + int byte_len = 0; + int num_runes = 0; + int glyph_len = 0; + nk_rune unicode; + if (!str || !text) return 0; + + glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); + while (unicode != '\0' && glyph_len) { + glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); + byte_len += glyph_len; + num_runes++; + } + nk_str_append_text_char(str, text, byte_len); + return runes; +} + +NK_API int +nk_str_append_text_runes(struct nk_str *str, const nk_rune *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_glyph glyph; + + NK_ASSERT(str); + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) { + byte_len = nk_utf_encode(text[i], glyph, NK_UTF_SIZE); + if (!byte_len) break; + nk_str_append_text_char(str, glyph, byte_len); + } + return len; +} + +NK_API int +nk_str_append_str_runes(struct nk_str *str, const nk_rune *runes) +{ + int i = 0; + nk_glyph glyph; + int byte_len; + NK_ASSERT(str); + if (!str || !runes) return 0; + while (runes[i] != '\0') { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + nk_str_append_text_char(str, glyph, byte_len); + i++; + } + return i; +} + +NK_API int +nk_str_insert_at_char(struct nk_str *s, int pos, const char *str, int len) +{ + int i; + void *mem; + char *src; + char *dst; + + int copylen; + NK_ASSERT(s); + NK_ASSERT(str); + NK_ASSERT(len >= 0); + if (!s || !str || !len || (nk_size)pos > s->buffer.allocated) return 0; + if ((s->buffer.allocated + (nk_size)len >= s->buffer.memory.size) && + (s->buffer.type == NK_BUFFER_FIXED)) return 0; + + copylen = (int)s->buffer.allocated - pos; + if (!copylen) { + nk_str_append_text_char(s, str, len); + return 1; + } + mem = nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); + if (!mem) return 0; + + /* memmove */ + NK_ASSERT(((int)pos + (int)len + ((int)copylen - 1)) >= 0); + NK_ASSERT(((int)pos + ((int)copylen - 1)) >= 0); + dst = nk_ptr_add(char, s->buffer.memory.ptr, pos + len + (copylen - 1)); + src = nk_ptr_add(char, s->buffer.memory.ptr, pos + (copylen-1)); + for (i = 0; i < copylen; ++i) *dst-- = *src--; + mem = nk_ptr_add(void, s->buffer.memory.ptr, pos); + NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); + s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); + return 1; +} + +NK_API int +nk_str_insert_at_rune(struct nk_str *str, int pos, const char *cstr, int len) +{ + int glyph_len; + nk_rune unicode; + const char *begin; + const char *buffer; + + NK_ASSERT(str); + NK_ASSERT(cstr); + NK_ASSERT(len); + if (!str || !cstr || !len) return 0; + begin = nk_str_at_rune(str, pos, &unicode, &glyph_len); + if (!str->len) + return nk_str_append_text_char(str, cstr, len); + buffer = nk_str_get_const(str); + if (!begin) return 0; + return nk_str_insert_at_char(str, (int)(begin - buffer), cstr, len); +} + +NK_API int +nk_str_insert_text_char(struct nk_str *str, int pos, const char *text, int len) +{ + return nk_str_insert_text_utf8(str, pos, text, len); +} + +NK_API int +nk_str_insert_str_char(struct nk_str *str, int pos, const char *text) +{ + return nk_str_insert_text_utf8(str, pos, text, nk_strlen(text)); +} + +NK_API int +nk_str_insert_text_utf8(struct nk_str *str, int pos, const char *text, int len) +{ + int i = 0; + int byte_len = 0; + nk_rune unicode; + + NK_ASSERT(str); + NK_ASSERT(text); + if (!str || !text || !len) return 0; + for (i = 0; i < len; ++i) + byte_len += nk_utf_decode(text+byte_len, &unicode, 4); + nk_str_insert_at_rune(str, pos, text, byte_len); + return len; +} + +NK_API int +nk_str_insert_str_utf8(struct nk_str *str, int pos, const char *text) +{ + int runes = 0; + int byte_len = 0; + int num_runes = 0; + int glyph_len = 0; + nk_rune unicode; + if (!str || !text) return 0; + + glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); + while (unicode != '\0' && glyph_len) { + glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); + byte_len += glyph_len; + num_runes++; + } + nk_str_insert_at_rune(str, pos, text, byte_len); + return runes; +} + +NK_API int +nk_str_insert_text_runes(struct nk_str *str, int pos, const nk_rune *runes, int len) +{ + int i = 0; + int byte_len = 0; + nk_glyph glyph; + + NK_ASSERT(str); + if (!str || !runes || !len) return 0; + for (i = 0; i < len; ++i) { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + if (!byte_len) break; + nk_str_insert_at_rune(str, pos+i, glyph, byte_len); + } + return len; +} + +NK_API int +nk_str_insert_str_runes(struct nk_str *str, int pos, const nk_rune *runes) +{ + int i = 0; + nk_glyph glyph; + int byte_len; + NK_ASSERT(str); + if (!str || !runes) return 0; + while (runes[i] != '\0') { + byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); + nk_str_insert_at_rune(str, pos+i, glyph, byte_len); + i++; + } + return i; +} + +NK_API void +nk_str_remove_chars(struct nk_str *s, int len) +{ + NK_ASSERT(s); + NK_ASSERT(len >= 0); + if (!s || len < 0 || (nk_size)len > s->buffer.allocated) return; + NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); + s->buffer.allocated -= (nk_size)len; + s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); +} + +NK_API void +nk_str_remove_runes(struct nk_str *str, int len) +{ + int index; + const char *begin; + const char *end; + nk_rune unicode; + + NK_ASSERT(str); + NK_ASSERT(len >= 0); + if (!str || len < 0) return; + if (len >= str->len) { + str->len = 0; + return; + } + + index = str->len - len; + begin = nk_str_at_rune(str, index, &unicode, &len); + end = (const char*)str->buffer.memory.ptr + str->buffer.allocated; + nk_str_remove_chars(str, (int)(end-begin)+1); +} + +NK_API void +nk_str_delete_chars(struct nk_str *s, int pos, int len) +{ + NK_ASSERT(s); + if (!s || !len || (nk_size)pos > s->buffer.allocated || + (nk_size)(pos + len) > s->buffer.allocated) return; + + if ((nk_size)(pos + len) < s->buffer.allocated) { + /* memmove */ + char *dst = nk_ptr_add(char, s->buffer.memory.ptr, pos); + char *src = nk_ptr_add(char, s->buffer.memory.ptr, pos + len); + NK_MEMCPY(dst, src, s->buffer.allocated - (nk_size)(pos + len)); + NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); + s->buffer.allocated -= (nk_size)len; + } else nk_str_remove_chars(s, len); + s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); +} + +NK_API void +nk_str_delete_runes(struct nk_str *s, int pos, int len) +{ + char *temp; + nk_rune unicode; + char *begin; + char *end; + int unused; + + NK_ASSERT(s); + NK_ASSERT(s->len >= pos + len); + if (s->len < pos + len) + len = NK_CLAMP(0, (s->len - pos), s->len); + if (!len) return; + + temp = (char *)s->buffer.memory.ptr; + begin = nk_str_at_rune(s, pos, &unicode, &unused); + if (!begin) return; + s->buffer.memory.ptr = begin; + end = nk_str_at_rune(s, len, &unicode, &unused); + s->buffer.memory.ptr = temp; + if (!end) return; + nk_str_delete_chars(s, (int)(begin - temp), (int)(end - begin)); +} + +NK_API char* +nk_str_at_char(struct nk_str *s, int pos) +{ + NK_ASSERT(s); + if (!s || pos > (int)s->buffer.allocated) return 0; + return nk_ptr_add(char, s->buffer.memory.ptr, pos); +} + +NK_API char* +nk_str_at_rune(struct nk_str *str, int pos, nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + char *text; + int text_len; + + NK_ASSERT(str); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!str || !unicode || !len) return 0; + if (pos < 0) { + *unicode = 0; + *len = 0; + return 0; + } + + text = (char*)str->buffer.memory.ptr; + text_len = (int)str->buffer.allocated; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == pos) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != pos) return 0; + return text + src_len; +} + +NK_API const char* +nk_str_at_char_const(const struct nk_str *s, int pos) +{ + NK_ASSERT(s); + if (!s || pos > (int)s->buffer.allocated) return 0; + return nk_ptr_add(char, s->buffer.memory.ptr, pos); +} + +NK_API const char* +nk_str_at_const(const struct nk_str *str, int pos, nk_rune *unicode, int *len) +{ + int i = 0; + int src_len = 0; + int glyph_len = 0; + char *text; + int text_len; + + NK_ASSERT(str); + NK_ASSERT(unicode); + NK_ASSERT(len); + + if (!str || !unicode || !len) return 0; + if (pos < 0) { + *unicode = 0; + *len = 0; + return 0; + } + + text = (char*)str->buffer.memory.ptr; + text_len = (int)str->buffer.allocated; + glyph_len = nk_utf_decode(text, unicode, text_len); + while (glyph_len) { + if (i == pos) { + *len = glyph_len; + break; + } + + i++; + src_len = src_len + glyph_len; + glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); + } + if (i != pos) return 0; + return text + src_len; +} + +NK_API nk_rune +nk_str_rune_at(const struct nk_str *str, int pos) +{ + int len; + nk_rune unicode = 0; + nk_str_at_const(str, pos, &unicode, &len); + return unicode; +} + +NK_API char* +nk_str_get(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (char*)s->buffer.memory.ptr; +} + +NK_API const char* +nk_str_get_const(const struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (const char*)s->buffer.memory.ptr; +} + +NK_API int +nk_str_len(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return s->len; +} + +NK_API int +nk_str_len_char(struct nk_str *s) +{ + NK_ASSERT(s); + if (!s || !s->len || !s->buffer.allocated) return 0; + return (int)s->buffer.allocated; +} + +NK_API void +nk_str_clear(struct nk_str *str) +{ + NK_ASSERT(str); + nk_buffer_clear(&str->buffer); + str->len = 0; +} + +NK_API void +nk_str_free(struct nk_str *str) +{ + NK_ASSERT(str); + nk_buffer_free(&str->buffer); + str->len = 0; +} + +/* + * ============================================================== + * + * Command buffer + * + * =============================================================== +*/ +NK_INTERN void +nk_command_buffer_init(struct nk_command_buffer *cmdbuf, + struct nk_buffer *buffer, enum nk_command_clipping clip) +{ + NK_ASSERT(cmdbuf); + NK_ASSERT(buffer); + if (!cmdbuf || !buffer) return; + cmdbuf->base = buffer; + cmdbuf->use_clipping = clip; + cmdbuf->begin = buffer->allocated; + cmdbuf->end = buffer->allocated; + cmdbuf->last = buffer->allocated; +} + +NK_INTERN void +nk_command_buffer_reset(struct nk_command_buffer *buffer) +{ + NK_ASSERT(buffer); + if (!buffer) return; + buffer->begin = 0; + buffer->end = 0; + buffer->last = 0; + buffer->clip = nk_null_rect; +#ifdef NK_INCLUDE_COMMAND_USERDATA + buffer->userdata.ptr = 0; +#endif +} + +NK_INTERN void* +nk_command_buffer_push(struct nk_command_buffer* b, + enum nk_command_type t, nk_size size) +{ + NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_command); + struct nk_command *cmd; + nk_size alignment; + void *unaligned; + void *memory; + + NK_ASSERT(b); + NK_ASSERT(b->base); + if (!b) return 0; + cmd = (struct nk_command*)nk_buffer_alloc(b->base,NK_BUFFER_FRONT,size,align); + if (!cmd) return 0; + + /* make sure the offset to the next command is aligned */ + b->last = (nk_size)((nk_byte*)cmd - (nk_byte*)b->base->memory.ptr); + unaligned = (nk_byte*)cmd + size; + memory = NK_ALIGN_PTR(unaligned, align); + alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); +#ifdef NK_ZERO_COMMAND_MEMORY + NK_MEMSET(cmd, 0, size + alignment); +#endif + + cmd->type = t; + cmd->next = b->base->allocated + alignment; +#ifdef NK_INCLUDE_COMMAND_USERDATA + cmd->userdata = b->userdata; +#endif + b->end = cmd->next; + return cmd; +} + +NK_API void +nk_push_scissor(struct nk_command_buffer *b, struct nk_rect r) +{ + struct nk_command_scissor *cmd; + NK_ASSERT(b); + if (!b) return; + + b->clip.x = r.x; + b->clip.y = r.y; + b->clip.w = r.w; + b->clip.h = r.h; + cmd = (struct nk_command_scissor*) + nk_command_buffer_push(b, NK_COMMAND_SCISSOR, sizeof(*cmd)); + + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); +} + +NK_API void +nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, + float x1, float y1, float line_thickness, struct nk_color c) +{ + struct nk_command_line *cmd; + NK_ASSERT(b); + if (!b || line_thickness <= 0) return; + cmd = (struct nk_command_line*) + nk_command_buffer_push(b, NK_COMMAND_LINE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->begin.x = (short)x0; + cmd->begin.y = (short)y0; + cmd->end.x = (short)x1; + cmd->end.y = (short)y1; + cmd->color = c; +} + +NK_API void +nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay, + float ctrl0x, float ctrl0y, float ctrl1x, float ctrl1y, + float bx, float by, float line_thickness, struct nk_color col) +{ + struct nk_command_curve *cmd; + NK_ASSERT(b); + if (!b || col.a == 0 || line_thickness <= 0) return; + + cmd = (struct nk_command_curve*) + nk_command_buffer_push(b, NK_COMMAND_CURVE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->begin.x = (short)ax; + cmd->begin.y = (short)ay; + cmd->ctrl[0].x = (short)ctrl0x; + cmd->ctrl[0].y = (short)ctrl0y; + cmd->ctrl[1].x = (short)ctrl1x; + cmd->ctrl[1].y = (short)ctrl1y; + cmd->end.x = (short)bx; + cmd->end.y = (short)by; + cmd->color = col; +} + +NK_API void +nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect, + float rounding, float line_thickness, struct nk_color c) +{ + struct nk_command_rect *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || rect.w == 0 || rect.h == 0 || line_thickness <= 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + cmd = (struct nk_command_rect*) + nk_command_buffer_push(b, NK_COMMAND_RECT, sizeof(*cmd)); + if (!cmd) return; + cmd->rounding = (unsigned short)rounding; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->color = c; +} + +NK_API void +nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect, + float rounding, struct nk_color c) +{ + struct nk_command_rect_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || rect.w == 0 || rect.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect_filled*) + nk_command_buffer_push(b, NK_COMMAND_RECT_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->rounding = (unsigned short)rounding; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->color = c; +} + +NK_API void +nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect, + struct nk_color left, struct nk_color top, struct nk_color right, + struct nk_color bottom) +{ + struct nk_command_rect_multi_color *cmd; + NK_ASSERT(b); + if (!b || rect.w == 0 || rect.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + clip->x, clip->y, clip->w, clip->h)) return; + } + + cmd = (struct nk_command_rect_multi_color*) + nk_command_buffer_push(b, NK_COMMAND_RECT_MULTI_COLOR, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)rect.x; + cmd->y = (short)rect.y; + cmd->w = (unsigned short)NK_MAX(0, rect.w); + cmd->h = (unsigned short)NK_MAX(0, rect.h); + cmd->left = left; + cmd->top = top; + cmd->right = right; + cmd->bottom = bottom; +} + +NK_API void +nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r, + float line_thickness, struct nk_color c) +{ + struct nk_command_circle *cmd; + if (!b || r.w == 0 || r.h == 0 || line_thickness <= 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_circle*) + nk_command_buffer_push(b, NK_COMMAND_CIRCLE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(r.w, 0); + cmd->h = (unsigned short)NK_MAX(r.h, 0); + cmd->color = c; +} + +NK_API void +nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c) +{ + struct nk_command_circle_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || r.w == 0 || r.h == 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_circle_filled*) + nk_command_buffer_push(b, NK_COMMAND_CIRCLE_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(r.w, 0); + cmd->h = (unsigned short)NK_MAX(r.h, 0); + cmd->color = c; +} + +NK_API void +nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius, + float a_min, float a_max, float line_thickness, struct nk_color c) +{ + struct nk_command_arc *cmd; + if (!b || c.a == 0 || line_thickness <= 0) return; + cmd = (struct nk_command_arc*) + nk_command_buffer_push(b, NK_COMMAND_ARC, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->cx = (short)cx; + cmd->cy = (short)cy; + cmd->r = (unsigned short)radius; + cmd->a[0] = a_min; + cmd->a[1] = a_max; + cmd->color = c; +} + +NK_API void +nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius, + float a_min, float a_max, struct nk_color c) +{ + struct nk_command_arc_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + cmd = (struct nk_command_arc_filled*) + nk_command_buffer_push(b, NK_COMMAND_ARC_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->cx = (short)cx; + cmd->cy = (short)cy; + cmd->r = (unsigned short)radius; + cmd->a[0] = a_min; + cmd->a[1] = a_max; + cmd->color = c; +} + +NK_API void +nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, + float y1, float x2, float y2, float line_thickness, struct nk_color c) +{ + struct nk_command_triangle *cmd; + NK_ASSERT(b); + if (!b || c.a == 0 || line_thickness <= 0) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_triangle*) + nk_command_buffer_push(b, NK_COMMAND_TRIANGLE, sizeof(*cmd)); + if (!cmd) return; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->a.x = (short)x0; + cmd->a.y = (short)y0; + cmd->b.x = (short)x1; + cmd->b.y = (short)y1; + cmd->c.x = (short)x2; + cmd->c.y = (short)y2; + cmd->color = c; +} + +NK_API void +nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, + float y1, float x2, float y2, struct nk_color c) +{ + struct nk_command_triangle_filled *cmd; + NK_ASSERT(b); + if (!b || c.a == 0) return; + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *clip = &b->clip; + if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) && + !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) + return; + } + + cmd = (struct nk_command_triangle_filled*) + nk_command_buffer_push(b, NK_COMMAND_TRIANGLE_FILLED, sizeof(*cmd)); + if (!cmd) return; + cmd->a.x = (short)x0; + cmd->a.y = (short)y0; + cmd->b.x = (short)x1; + cmd->b.y = (short)y1; + cmd->c.x = (short)x2; + cmd->c.y = (short)y2; + cmd->color = c; +} + +NK_API void +nk_stroke_polygon(struct nk_command_buffer *b, float *points, int point_count, + float line_thickness, struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polygon *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0 || line_thickness <= 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polygon*) nk_command_buffer_push(b, NK_COMMAND_POLYGON, size); + if (!cmd) return; + cmd->color = col; + cmd->line_thickness = (unsigned short)line_thickness; + cmd->point_count = (unsigned short)point_count; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2]; + cmd->points[i].y = (short)points[i*2+1]; + } +} + +NK_API void +nk_fill_polygon(struct nk_command_buffer *b, float *points, int point_count, + struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polygon_filled *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polygon_filled*) + nk_command_buffer_push(b, NK_COMMAND_POLYGON_FILLED, size); + if (!cmd) return; + cmd->color = col; + cmd->point_count = (unsigned short)point_count; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2+0]; + cmd->points[i].y = (short)points[i*2+1]; + } +} + +NK_API void +nk_stroke_polyline(struct nk_command_buffer *b, float *points, int point_count, + float line_thickness, struct nk_color col) +{ + int i; + nk_size size = 0; + struct nk_command_polyline *cmd; + + NK_ASSERT(b); + if (!b || col.a == 0 || line_thickness <= 0) return; + size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; + cmd = (struct nk_command_polyline*) nk_command_buffer_push(b, NK_COMMAND_POLYLINE, size); + if (!cmd) return; + cmd->color = col; + cmd->point_count = (unsigned short)point_count; + cmd->line_thickness = (unsigned short)line_thickness; + for (i = 0; i < point_count; ++i) { + cmd->points[i].x = (short)points[i*2]; + cmd->points[i].y = (short)points[i*2+1]; + } +} + +NK_API void +nk_draw_image(struct nk_command_buffer *b, struct nk_rect r, + const struct nk_image *img, struct nk_color col) +{ + struct nk_command_image *cmd; + NK_ASSERT(b); + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + cmd = (struct nk_command_image*) + nk_command_buffer_push(b, NK_COMMAND_IMAGE, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); + cmd->img = *img; + cmd->col = col; +} + +NK_API void +nk_push_custom(struct nk_command_buffer *b, struct nk_rect r, + nk_command_custom_callback cb, nk_handle usr) +{ + struct nk_command_custom *cmd; + NK_ASSERT(b); + if (!b) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + cmd = (struct nk_command_custom*) + nk_command_buffer_push(b, NK_COMMAND_CUSTOM, sizeof(*cmd)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)NK_MAX(0, r.w); + cmd->h = (unsigned short)NK_MAX(0, r.h); + cmd->callback_data = usr; + cmd->callback = cb; +} + +NK_API void +nk_draw_text(struct nk_command_buffer *b, struct nk_rect r, + const char *string, int length, const struct nk_user_font *font, + struct nk_color bg, struct nk_color fg) +{ + float text_width = 0; + struct nk_command_text *cmd; + + NK_ASSERT(b); + NK_ASSERT(font); + if (!b || !string || !length || (bg.a == 0 && fg.a == 0)) return; + if (b->use_clipping) { + const struct nk_rect *c = &b->clip; + if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) + return; + } + + /* make sure text fits inside bounds */ + text_width = font->width(font->userdata, font->height, string, length); + if (text_width > r.w){ + int glyphs = 0; + float txt_width = (float)text_width; + length = nk_text_clamp(font, string, length, r.w, &glyphs, &txt_width, 0,0); + } + + if (!length) return; + cmd = (struct nk_command_text*) + nk_command_buffer_push(b, NK_COMMAND_TEXT, sizeof(*cmd) + (nk_size)(length + 1)); + if (!cmd) return; + cmd->x = (short)r.x; + cmd->y = (short)r.y; + cmd->w = (unsigned short)r.w; + cmd->h = (unsigned short)r.h; + cmd->background = bg; + cmd->foreground = fg; + cmd->font = font; + cmd->length = length; + cmd->height = font->height; + NK_MEMCPY(cmd->string, string, (nk_size)length); + cmd->string[length] = '\0'; +} + +/* ============================================================== + * + * DRAW LIST + * + * ===============================================================*/ +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_API void +nk_draw_list_init(struct nk_draw_list *list) +{ + nk_size i = 0; + NK_ASSERT(list); + if (!list) return; + nk_zero(list, sizeof(*list)); + for (i = 0; i < NK_LEN(list->circle_vtx); ++i) { + const float a = ((float)i / (float)NK_LEN(list->circle_vtx)) * 2 * NK_PI; + list->circle_vtx[i].x = (float)NK_COS(a); + list->circle_vtx[i].y = (float)NK_SIN(a); + } +} + +NK_API void +nk_draw_list_setup(struct nk_draw_list *canvas, const struct nk_convert_config *config, + struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, + enum nk_anti_aliasing line_aa, enum nk_anti_aliasing shape_aa) +{ + NK_ASSERT(canvas); + NK_ASSERT(config); + NK_ASSERT(cmds); + NK_ASSERT(vertices); + NK_ASSERT(elements); + if (!canvas || !config || !cmds || !vertices || !elements) + return; + + canvas->buffer = cmds; + canvas->config = *config; + canvas->elements = elements; + canvas->vertices = vertices; + canvas->line_AA = line_aa; + canvas->shape_AA = shape_aa; + canvas->clip_rect = nk_null_rect; +} + +NK_API const struct nk_draw_command* +nk__draw_list_begin(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) +{ + nk_byte *memory; + nk_size offset; + const struct nk_draw_command *cmd; + + NK_ASSERT(buffer); + if (!buffer || !buffer->size || !canvas->cmd_count) + return 0; + + memory = (nk_byte*)buffer->memory.ptr; + offset = buffer->memory.size - canvas->cmd_offset; + cmd = nk_ptr_add(const struct nk_draw_command, memory, offset); + return cmd; +} + +NK_API const struct nk_draw_command* +nk__draw_list_end(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) +{ + nk_size size; + nk_size offset; + nk_byte *memory; + const struct nk_draw_command *end; + + NK_ASSERT(buffer); + NK_ASSERT(canvas); + if (!buffer || !canvas) + return 0; + + memory = (nk_byte*)buffer->memory.ptr; + size = buffer->memory.size; + offset = size - canvas->cmd_offset; + end = nk_ptr_add(const struct nk_draw_command, memory, offset); + end -= (canvas->cmd_count-1); + return end; +} + +NK_API const struct nk_draw_command* +nk__draw_list_next(const struct nk_draw_command *cmd, + const struct nk_buffer *buffer, const struct nk_draw_list *canvas) +{ + const struct nk_draw_command *end; + NK_ASSERT(buffer); + NK_ASSERT(canvas); + if (!cmd || !buffer || !canvas) + return 0; + + end = nk__draw_list_end(canvas, buffer); + if (cmd <= end) return 0; + return (cmd-1); +} + +NK_API void +nk_draw_list_clear(struct nk_draw_list *list) +{ + NK_ASSERT(list); + if (!list) return; + if (list->buffer) + nk_buffer_clear(list->buffer); + if (list->vertices) + nk_buffer_clear(list->vertices); + if (list->elements) + nk_buffer_clear(list->elements); + + list->element_count = 0; + list->vertex_count = 0; + list->cmd_offset = 0; + list->cmd_count = 0; + list->path_count = 0; + list->vertices = 0; + list->elements = 0; + list->clip_rect = nk_null_rect; +} + +NK_INTERN struct nk_vec2* +nk_draw_list_alloc_path(struct nk_draw_list *list, int count) +{ + struct nk_vec2 *points; + NK_STORAGE const nk_size point_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size point_size = sizeof(struct nk_vec2); + points = (struct nk_vec2*) + nk_buffer_alloc(list->buffer, NK_BUFFER_FRONT, + point_size * (nk_size)count, point_align); + + if (!points) return 0; + if (!list->path_offset) { + void *memory = nk_buffer_memory(list->buffer); + list->path_offset = (unsigned int)((nk_byte*)points - (nk_byte*)memory); + } + list->path_count += (unsigned int)count; + return points; +} + +NK_INTERN struct nk_vec2 +nk_draw_list_path_last(struct nk_draw_list *list) +{ + void *memory; + struct nk_vec2 *point; + NK_ASSERT(list->path_count); + memory = nk_buffer_memory(list->buffer); + point = nk_ptr_add(struct nk_vec2, memory, list->path_offset); + point += (list->path_count-1); + return *point; +} + +NK_INTERN struct nk_draw_command* +nk_draw_list_push_command(struct nk_draw_list *list, struct nk_rect clip, + nk_handle texture) +{ + NK_STORAGE const nk_size cmd_align = NK_ALIGNOF(struct nk_draw_command); + NK_STORAGE const nk_size cmd_size = sizeof(struct nk_draw_command); + struct nk_draw_command *cmd; + + NK_ASSERT(list); + cmd = (struct nk_draw_command*) + nk_buffer_alloc(list->buffer, NK_BUFFER_BACK, cmd_size, cmd_align); + + if (!cmd) return 0; + if (!list->cmd_count) { + nk_byte *memory = (nk_byte*)nk_buffer_memory(list->buffer); + nk_size total = nk_buffer_total(list->buffer); + memory = nk_ptr_add(nk_byte, memory, total); + list->cmd_offset = (nk_size)(memory - (nk_byte*)cmd); + } + + cmd->elem_count = 0; + cmd->clip_rect = clip; + cmd->texture = texture; +#ifdef NK_INCLUDE_COMMAND_USERDATA + cmd->userdata = list->userdata; +#endif + + list->cmd_count++; + list->clip_rect = clip; + return cmd; +} + +NK_INTERN struct nk_draw_command* +nk_draw_list_command_last(struct nk_draw_list *list) +{ + void *memory; + nk_size size; + struct nk_draw_command *cmd; + NK_ASSERT(list->cmd_count); + + memory = nk_buffer_memory(list->buffer); + size = nk_buffer_total(list->buffer); + cmd = nk_ptr_add(struct nk_draw_command, memory, size - list->cmd_offset); + return (cmd - (list->cmd_count-1)); +} + +NK_INTERN void +nk_draw_list_add_clip(struct nk_draw_list *list, struct nk_rect rect) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + nk_draw_list_push_command(list, rect, list->config.null.texture); + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) + prev->clip_rect = rect; + nk_draw_list_push_command(list, rect, prev->texture); + } +} + +NK_INTERN void +nk_draw_list_push_image(struct nk_draw_list *list, nk_handle texture) +{ + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) { + nk_draw_list_push_command(list, nk_null_rect, texture); + } else { + struct nk_draw_command *prev = nk_draw_list_command_last(list); + if (prev->elem_count == 0) + prev->texture = texture; + else if (prev->texture.id != texture.id) + nk_draw_list_push_command(list, prev->clip_rect, texture); + } +} + +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void +nk_draw_list_push_userdata(struct nk_draw_list *list, nk_handle userdata) +{ + list->userdata = userdata; +} +#endif + +NK_INTERN void* +nk_draw_list_alloc_vertices(struct nk_draw_list *list, nk_size count) +{ + void *vtx; + NK_ASSERT(list); + if (!list) return 0; + vtx = nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, + list->config.vertex_size*count, list->config.vertex_alignment); + if (!vtx) return 0; + list->vertex_count += (unsigned int)count; + return vtx; +} + +NK_INTERN nk_draw_index* +nk_draw_list_alloc_elements(struct nk_draw_list *list, nk_size count) +{ + nk_draw_index *ids; + struct nk_draw_command *cmd; + NK_STORAGE const nk_size elem_align = NK_ALIGNOF(nk_draw_index); + NK_STORAGE const nk_size elem_size = sizeof(nk_draw_index); + NK_ASSERT(list); + if (!list) return 0; + + ids = (nk_draw_index*) + nk_buffer_alloc(list->elements, NK_BUFFER_FRONT, elem_size*count, elem_align); + if (!ids) return 0; + cmd = nk_draw_list_command_last(list); + list->element_count += (unsigned int)count; + cmd->elem_count += (unsigned int)count; + return ids; +} + +NK_INTERN int +nk_draw_vertex_layout_element_is_end_of_layout( + const struct nk_draw_vertex_layout_element *element) +{ + return (element->attribute == NK_VERTEX_ATTRIBUTE_COUNT || + element->format == NK_FORMAT_COUNT); +} + +NK_INTERN void +nk_draw_vertex_color(void *attribute, const float *values, + enum nk_draw_vertex_layout_format format) +{ + /* if this triggers you tried to provide a value format for a color */ + NK_ASSERT(format >= NK_FORMAT_COLOR_BEGIN); + NK_ASSERT(format <= NK_FORMAT_COLOR_END); + if (format < NK_FORMAT_COLOR_BEGIN || format > NK_FORMAT_COLOR_END) return; + + switch (format) { + default: NK_ASSERT(0 && "Invalid vertex layout color format"); break; + case NK_FORMAT_R8G8B8A8: + case NK_FORMAT_R8G8B8: { + struct nk_color col = nk_rgba_fv(values); + NK_MEMCPY(attribute, &col.r, sizeof(col)); + } break; + case NK_FORMAT_B8G8R8A8: { + struct nk_color col = nk_rgba_fv(values); + struct nk_color bgra = nk_rgba(col.b, col.g, col.r, col.a); + NK_MEMCPY(attribute, &bgra, sizeof(bgra)); + } break; + case NK_FORMAT_R16G15B16: { + nk_ushort col[3]; + col[0] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[0] * NK_USHORT_MAX, NK_USHORT_MAX); + col[1] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[1] * NK_USHORT_MAX, NK_USHORT_MAX); + col[2] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[2] * NK_USHORT_MAX, NK_USHORT_MAX); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_R16G15B16A16: { + nk_ushort col[4]; + col[0] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[0] * NK_USHORT_MAX, NK_USHORT_MAX); + col[1] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[1] * NK_USHORT_MAX, NK_USHORT_MAX); + col[2] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[2] * NK_USHORT_MAX, NK_USHORT_MAX); + col[3] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[3] * NK_USHORT_MAX, NK_USHORT_MAX); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_R32G32B32: { + nk_uint col[3]; + col[0] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[0] * NK_UINT_MAX, NK_UINT_MAX); + col[1] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[1] * NK_UINT_MAX, NK_UINT_MAX); + col[2] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[2] * NK_UINT_MAX, NK_UINT_MAX); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_R32G32B32A32: { + nk_uint col[4]; + col[0] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[0] * NK_UINT_MAX, NK_UINT_MAX); + col[1] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[1] * NK_UINT_MAX, NK_UINT_MAX); + col[2] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[2] * NK_UINT_MAX, NK_UINT_MAX); + col[3] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[3] * NK_UINT_MAX, NK_UINT_MAX); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_R32G32B32A32_FLOAT: + NK_MEMCPY(attribute, values, sizeof(float)*4); + break; + case NK_FORMAT_R32G32B32A32_DOUBLE: { + double col[4]; + col[0] = (double)NK_SATURATE(values[0]); + col[1] = (double)NK_SATURATE(values[1]); + col[2] = (double)NK_SATURATE(values[2]); + col[3] = (double)NK_SATURATE(values[3]); + NK_MEMCPY(attribute, col, sizeof(col)); + } break; + case NK_FORMAT_RGB32: + case NK_FORMAT_RGBA32: { + struct nk_color col = nk_rgba_fv(values); + nk_uint color = nk_color_u32(col); + NK_MEMCPY(attribute, &color, sizeof(color)); + } break; + } +} + +NK_INTERN void +nk_draw_vertex_element(void *dst, const float *values, int value_count, + enum nk_draw_vertex_layout_format format) +{ + int value_index; + void *attribute = dst; + /* if this triggers you tried to provide a color format for a value */ + NK_ASSERT(format < NK_FORMAT_COLOR_BEGIN); + if (format >= NK_FORMAT_COLOR_BEGIN && format <= NK_FORMAT_COLOR_END) return; + for (value_index = 0; value_index < value_count; ++value_index) { + switch (format) { + default: NK_ASSERT(0 && "invalid vertex layout format"); break; + case NK_FORMAT_SCHAR: { + char value = (char)NK_CLAMP(NK_SCHAR_MIN, values[value_index], NK_SCHAR_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(char)); + } break; + case NK_FORMAT_SSHORT: { + nk_short value = (nk_short)NK_CLAMP(NK_SSHORT_MIN, values[value_index], NK_SSHORT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(value)); + } break; + case NK_FORMAT_SINT: { + nk_int value = (nk_int)NK_CLAMP(NK_SINT_MIN, values[value_index], NK_SINT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(nk_int)); + } break; + case NK_FORMAT_UCHAR: { + unsigned char value = (unsigned char)NK_CLAMP(NK_UCHAR_MIN, values[value_index], NK_UCHAR_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(unsigned char)); + } break; + case NK_FORMAT_USHORT: { + nk_ushort value = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[value_index], NK_USHORT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(value)); + } break; + case NK_FORMAT_UINT: { + nk_uint value = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[value_index], NK_UINT_MAX); + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(nk_uint)); + } break; + case NK_FORMAT_FLOAT: + NK_MEMCPY(attribute, &values[value_index], sizeof(values[value_index])); + attribute = (void*)((char*)attribute + sizeof(float)); + break; + case NK_FORMAT_DOUBLE: { + double value = (double)values[value_index]; + NK_MEMCPY(attribute, &value, sizeof(value)); + attribute = (void*)((char*)attribute + sizeof(double)); + } break; + } + } +} + +NK_INTERN void* +nk_draw_vertex(void *dst, const struct nk_convert_config *config, + struct nk_vec2 pos, struct nk_vec2 uv, struct nk_colorf color) +{ + void *result = (void*)((char*)dst + config->vertex_size); + const struct nk_draw_vertex_layout_element *elem_iter = config->vertex_layout; + while (!nk_draw_vertex_layout_element_is_end_of_layout(elem_iter)) { + void *address = (void*)((char*)dst + elem_iter->offset); + switch (elem_iter->attribute) { + case NK_VERTEX_ATTRIBUTE_COUNT: + default: NK_ASSERT(0 && "wrong element attribute"); + case NK_VERTEX_POSITION: nk_draw_vertex_element(address, &pos.x, 2, elem_iter->format); break; + case NK_VERTEX_TEXCOORD: nk_draw_vertex_element(address, &uv.x, 2, elem_iter->format); break; + case NK_VERTEX_COLOR: nk_draw_vertex_color(address, &color.r, elem_iter->format); break; + } + elem_iter++; + } + return result; +} + +NK_API void +nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *points, + const unsigned int points_count, struct nk_color color, enum nk_draw_list_stroke closed, + float thickness, enum nk_anti_aliasing aliasing) +{ + nk_size count; + int thick_line; + struct nk_colorf col; + struct nk_colorf col_trans; + NK_ASSERT(list); + if (!list || points_count < 2) return; + + color.a = (nk_byte)((float)color.a * list->config.global_alpha); + count = points_count; + if (!closed) count = points_count-1; + thick_line = thickness > 1.0f; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_draw_list_push_userdata(list, list->userdata); +#endif + + color.a = (nk_byte)((float)color.a * list->config.global_alpha); + nk_color_fv(&col.r, color); + col_trans = col; + col_trans.a = 0; + + if (aliasing == NK_ANTI_ALIASING_ON) { + /* ANTI-ALIASED STROKE */ + const float AA_SIZE = 1.0f; + NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); + + /* allocate vertices and elements */ + nk_size i1 = 0; + nk_size vertex_offset; + nk_size index = list->vertex_count; + + const nk_size idx_count = (thick_line) ? (count * 18) : (count * 12); + const nk_size vtx_count = (thick_line) ? (points_count * 4): (points_count *3); + + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + nk_size size; + struct nk_vec2 *normals, *temp; + if (!vtx || !ids) return; + + /* temporary allocate normals + points */ + vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr); + nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); + size = pnt_size * ((thick_line) ? 5 : 3) * points_count; + normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); + NK_ASSERT(normals); + if (!normals) return; + temp = normals + points_count; + + /* make sure vertex pointer is still correct */ + vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset); + + /* calculate normals */ + for (i1 = 0; i1 < count; ++i1) { + const nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + struct nk_vec2 diff = nk_vec2_sub(points[i2], points[i1]); + float len; + + /* vec2 inverted length */ + len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + + diff = nk_vec2_muls(diff, len); + normals[i1].x = diff.y; + normals[i1].y = -diff.x; + } + + if (!closed) + normals[points_count-1] = normals[points_count-2]; + + if (!thick_line) { + nk_size idx1, i; + if (!closed) { + struct nk_vec2 d; + temp[0] = nk_vec2_add(points[0], nk_vec2_muls(normals[0], AA_SIZE)); + temp[1] = nk_vec2_sub(points[0], nk_vec2_muls(normals[0], AA_SIZE)); + d = nk_vec2_muls(normals[points_count-1], AA_SIZE); + temp[(points_count-1) * 2 + 0] = nk_vec2_add(points[points_count-1], d); + temp[(points_count-1) * 2 + 1] = nk_vec2_sub(points[points_count-1], d); + } + + /* fill elements */ + idx1 = index; + for (i1 = 0; i1 < count; i1++) { + struct nk_vec2 dm; + float dmr2; + nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); + nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 3); + + /* average normals */ + dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); + dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f/dmr2; + scale = NK_MIN(100.0f, scale); + dm = nk_vec2_muls(dm, scale); + } + + dm = nk_vec2_muls(dm, AA_SIZE); + temp[i2*2+0] = nk_vec2_add(points[i2], dm); + temp[i2*2+1] = nk_vec2_sub(points[i2], dm); + + ids[0] = (nk_draw_index)(idx2 + 0); ids[1] = (nk_draw_index)(idx1+0); + ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); + ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+0); + ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); + ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); + ids[10]= (nk_draw_index)(idx2 + 0); ids[11]= (nk_draw_index)(idx2+1); + ids += 12; + idx1 = idx2; + } + + /* fill vertices */ + for (i = 0; i < points_count; ++i) { + const struct nk_vec2 uv = list->config.null.uv; + vtx = nk_draw_vertex(vtx, &list->config, points[i], uv, col); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+0], uv, col_trans); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+1], uv, col_trans); + } + } else { + nk_size idx1, i; + const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; + if (!closed) { + struct nk_vec2 d1 = nk_vec2_muls(normals[0], half_inner_thickness + AA_SIZE); + struct nk_vec2 d2 = nk_vec2_muls(normals[0], half_inner_thickness); + + temp[0] = nk_vec2_add(points[0], d1); + temp[1] = nk_vec2_add(points[0], d2); + temp[2] = nk_vec2_sub(points[0], d2); + temp[3] = nk_vec2_sub(points[0], d1); + + d1 = nk_vec2_muls(normals[points_count-1], half_inner_thickness + AA_SIZE); + d2 = nk_vec2_muls(normals[points_count-1], half_inner_thickness); + + temp[(points_count-1)*4+0] = nk_vec2_add(points[points_count-1], d1); + temp[(points_count-1)*4+1] = nk_vec2_add(points[points_count-1], d2); + temp[(points_count-1)*4+2] = nk_vec2_sub(points[points_count-1], d2); + temp[(points_count-1)*4+3] = nk_vec2_sub(points[points_count-1], d1); + } + + /* add all elements */ + idx1 = index; + for (i1 = 0; i1 < count; ++i1) { + struct nk_vec2 dm_out, dm_in; + const nk_size i2 = ((i1+1) == points_count) ? 0: (i1 + 1); + nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 4); + + /* average normals */ + struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); + float dmr2 = dm.x * dm.x + dm.y* dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f/dmr2; + scale = NK_MIN(100.0f, scale); + dm = nk_vec2_muls(dm, scale); + } + + dm_out = nk_vec2_muls(dm, ((half_inner_thickness) + AA_SIZE)); + dm_in = nk_vec2_muls(dm, half_inner_thickness); + temp[i2*4+0] = nk_vec2_add(points[i2], dm_out); + temp[i2*4+1] = nk_vec2_add(points[i2], dm_in); + temp[i2*4+2] = nk_vec2_sub(points[i2], dm_in); + temp[i2*4+3] = nk_vec2_sub(points[i2], dm_out); + + /* add indexes */ + ids[0] = (nk_draw_index)(idx2 + 1); ids[1] = (nk_draw_index)(idx1+1); + ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); + ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+1); + ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); + ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); + ids[10]= (nk_draw_index)(idx2 + 0); ids[11] = (nk_draw_index)(idx2+1); + ids[12]= (nk_draw_index)(idx2 + 2); ids[13] = (nk_draw_index)(idx1+2); + ids[14]= (nk_draw_index)(idx1 + 3); ids[15] = (nk_draw_index)(idx1+3); + ids[16]= (nk_draw_index)(idx2 + 3); ids[17] = (nk_draw_index)(idx2+2); + ids += 18; + idx1 = idx2; + } + + /* add vertices */ + for (i = 0; i < points_count; ++i) { + const struct nk_vec2 uv = list->config.null.uv; + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+0], uv, col_trans); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+1], uv, col); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+2], uv, col); + vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+3], uv, col_trans); + } + } + /* free temporary normals + points */ + nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); + } else { + /* NON ANTI-ALIASED STROKE */ + nk_size i1 = 0; + nk_size idx = list->vertex_count; + const nk_size idx_count = count * 6; + const nk_size vtx_count = count * 4; + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + if (!vtx || !ids) return; + + for (i1 = 0; i1 < count; ++i1) { + float dx, dy; + const struct nk_vec2 uv = list->config.null.uv; + const nk_size i2 = ((i1+1) == points_count) ? 0 : i1 + 1; + const struct nk_vec2 p1 = points[i1]; + const struct nk_vec2 p2 = points[i2]; + struct nk_vec2 diff = nk_vec2_sub(p2, p1); + float len; + + /* vec2 inverted length */ + len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + diff = nk_vec2_muls(diff, len); + + /* add vertices */ + dx = diff.x * (thickness * 0.5f); + dy = diff.y * (thickness * 0.5f); + + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x + dy, p1.y - dx), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x + dy, p2.y - dx), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x - dy, p2.y + dx), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x - dy, p1.y + dx), uv, col); + + ids[0] = (nk_draw_index)(idx+0); ids[1] = (nk_draw_index)(idx+1); + ids[2] = (nk_draw_index)(idx+2); ids[3] = (nk_draw_index)(idx+0); + ids[4] = (nk_draw_index)(idx+2); ids[5] = (nk_draw_index)(idx+3); + + ids += 6; + idx += 4; + } + } +} + +NK_API void +nk_draw_list_fill_poly_convex(struct nk_draw_list *list, + const struct nk_vec2 *points, const unsigned int points_count, + struct nk_color color, enum nk_anti_aliasing aliasing) +{ + struct nk_colorf col; + struct nk_colorf col_trans; + + NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); + NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); + NK_ASSERT(list); + if (!list || points_count < 3) return; + +#ifdef NK_INCLUDE_COMMAND_USERDATA + nk_draw_list_push_userdata(list, list->userdata); +#endif + + color.a = (nk_byte)((float)color.a * list->config.global_alpha); + nk_color_fv(&col.r, color); + col_trans = col; + col_trans.a = 0; + + if (aliasing == NK_ANTI_ALIASING_ON) { + nk_size i = 0; + nk_size i0 = 0; + nk_size i1 = 0; + + const float AA_SIZE = 1.0f; + nk_size vertex_offset = 0; + nk_size index = list->vertex_count; + + const nk_size idx_count = (points_count-2)*3 + points_count*6; + const nk_size vtx_count = (points_count*2); + + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + nk_size size = 0; + struct nk_vec2 *normals = 0; + unsigned int vtx_inner_idx = (unsigned int)(index + 0); + unsigned int vtx_outer_idx = (unsigned int)(index + 1); + if (!vtx || !ids) return; + + /* temporary allocate normals */ + vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr); + nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); + size = pnt_size * points_count; + normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); + NK_ASSERT(normals); + if (!normals) return; + vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset); + + /* add elements */ + for (i = 2; i < points_count; i++) { + ids[0] = (nk_draw_index)(vtx_inner_idx); + ids[1] = (nk_draw_index)(vtx_inner_idx + ((i-1) << 1)); + ids[2] = (nk_draw_index)(vtx_inner_idx + (i << 1)); + ids += 3; + } + + /* compute normals */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + struct nk_vec2 p0 = points[i0]; + struct nk_vec2 p1 = points[i1]; + struct nk_vec2 diff = nk_vec2_sub(p1, p0); + + /* vec2 inverted length */ + float len = nk_vec2_len_sqr(diff); + if (len != 0.0f) + len = nk_inv_sqrt(len); + else len = 1.0f; + diff = nk_vec2_muls(diff, len); + + normals[i0].x = diff.y; + normals[i0].y = -diff.x; + } + + /* add vertices + indexes */ + for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { + const struct nk_vec2 uv = list->config.null.uv; + struct nk_vec2 n0 = normals[i0]; + struct nk_vec2 n1 = normals[i1]; + struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(n0, n1), 0.5f); + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) { + float scale = 1.0f / dmr2; + scale = NK_MIN(scale, 100.0f); + dm = nk_vec2_muls(dm, scale); + } + dm = nk_vec2_muls(dm, AA_SIZE * 0.5f); + + /* add vertices */ + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_sub(points[i1], dm), uv, col); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_add(points[i1], dm), uv, col_trans); + + /* add indexes */ + ids[0] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); + ids[1] = (nk_draw_index)(vtx_inner_idx+(i0<<1)); + ids[2] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); + ids[3] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); + ids[4] = (nk_draw_index)(vtx_outer_idx+(i1<<1)); + ids[5] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); + ids += 6; + } + /* free temporary normals + points */ + nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); + } else { + nk_size i = 0; + nk_size index = list->vertex_count; + const nk_size idx_count = (points_count-2)*3; + const nk_size vtx_count = points_count; + void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); + nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); + + if (!vtx || !ids) return; + for (i = 0; i < vtx_count; ++i) + vtx = nk_draw_vertex(vtx, &list->config, points[i], list->config.null.uv, col); + for (i = 2; i < points_count; ++i) { + ids[0] = (nk_draw_index)index; + ids[1] = (nk_draw_index)(index+ i - 1); + ids[2] = (nk_draw_index)(index+i); + ids += 3; + } + } +} + +NK_API void +nk_draw_list_path_clear(struct nk_draw_list *list) +{ + NK_ASSERT(list); + if (!list) return; + nk_buffer_reset(list->buffer, NK_BUFFER_FRONT); + list->path_count = 0; + list->path_offset = 0; +} + +NK_API void +nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos) +{ + struct nk_vec2 *points = 0; + struct nk_draw_command *cmd = 0; + NK_ASSERT(list); + if (!list) return; + if (!list->cmd_count) + nk_draw_list_add_clip(list, nk_null_rect); + + cmd = nk_draw_list_command_last(list); + if (cmd && cmd->texture.ptr != list->config.null.texture.ptr) + nk_draw_list_push_image(list, list->config.null.texture); + + points = nk_draw_list_alloc_path(list, 1); + if (!points) return; + points[0] = pos; +} + +NK_API void +nk_draw_list_path_arc_to_fast(struct nk_draw_list *list, struct nk_vec2 center, + float radius, int a_min, int a_max) +{ + int a = 0; + NK_ASSERT(list); + if (!list) return; + if (a_min <= a_max) { + for (a = a_min; a <= a_max; a++) { + const struct nk_vec2 c = list->circle_vtx[(nk_size)a % NK_LEN(list->circle_vtx)]; + const float x = center.x + c.x * radius; + const float y = center.y + c.y * radius; + nk_draw_list_path_line_to(list, nk_vec2(x, y)); + } + } +} + +NK_API void +nk_draw_list_path_arc_to(struct nk_draw_list *list, struct nk_vec2 center, + float radius, float a_min, float a_max, unsigned int segments) +{ + unsigned int i = 0; + NK_ASSERT(list); + if (!list) return; + if (radius == 0.0f) return; + for (i = 0; i <= segments; ++i) { + const float a = a_min + ((float)i / ((float)segments) * (a_max - a_min)); + const float x = center.x + (float)NK_COS(a) * radius; + const float y = center.y + (float)NK_SIN(a) * radius; + nk_draw_list_path_line_to(list, nk_vec2(x, y)); + } +} + +NK_API void +nk_draw_list_path_rect_to(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, float rounding) +{ + float r; + NK_ASSERT(list); + if (!list) return; + r = rounding; + r = NK_MIN(r, ((b.x-a.x) < 0) ? -(b.x-a.x): (b.x-a.x)); + r = NK_MIN(r, ((b.y-a.y) < 0) ? -(b.y-a.y): (b.y-a.y)); + + if (r == 0.0f) { + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, nk_vec2(b.x,a.y)); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, nk_vec2(a.x,b.y)); + } else { + nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, a.y + r), r, 6, 9); + nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, a.y + r), r, 9, 12); + nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, b.y - r), r, 0, 3); + nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, b.y - r), r, 3, 6); + } +} + +NK_API void +nk_draw_list_path_curve_to(struct nk_draw_list *list, struct nk_vec2 p2, + struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments) +{ + float t_step; + unsigned int i_step; + struct nk_vec2 p1; + + NK_ASSERT(list); + NK_ASSERT(list->path_count); + if (!list || !list->path_count) return; + num_segments = NK_MAX(num_segments, 1); + + p1 = nk_draw_list_path_last(list); + t_step = 1.0f/(float)num_segments; + for (i_step = 1; i_step <= num_segments; ++i_step) { + float t = t_step * (float)i_step; + float u = 1.0f - t; + float w1 = u*u*u; + float w2 = 3*u*u*t; + float w3 = 3*u*t*t; + float w4 = t * t *t; + float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; + float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; + nk_draw_list_path_line_to(list, nk_vec2(x,y)); + } +} + +NK_API void +nk_draw_list_path_fill(struct nk_draw_list *list, struct nk_color color) +{ + struct nk_vec2 *points; + NK_ASSERT(list); + if (!list) return; + points = (struct nk_vec2*)nk_buffer_memory(list->buffer); + nk_draw_list_fill_poly_convex(list, points, list->path_count, color, list->config.shape_AA); + nk_draw_list_path_clear(list); +} + +NK_API void +nk_draw_list_path_stroke(struct nk_draw_list *list, struct nk_color color, + enum nk_draw_list_stroke closed, float thickness) +{ + struct nk_vec2 *points; + NK_ASSERT(list); + if (!list) return; + points = (struct nk_vec2*)nk_buffer_memory(list->buffer); + nk_draw_list_stroke_poly_line(list, points, list->path_count, color, + closed, thickness, list->config.line_AA); + nk_draw_list_path_clear(list); +} + +NK_API void +nk_draw_list_stroke_line(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_color col, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + if (list->line_AA == NK_ANTI_ALIASING_ON) { + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + } else { + nk_draw_list_path_line_to(list, nk_vec2_sub(a,nk_vec2(0.5f,0.5f))); + nk_draw_list_path_line_to(list, nk_vec2_sub(b,nk_vec2(0.5f,0.5f))); + } + nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); +} + +NK_API void +nk_draw_list_fill_rect(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color col, float rounding) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + + if (list->line_AA == NK_ANTI_ALIASING_ON) { + nk_draw_list_path_rect_to(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); + } else { + nk_draw_list_path_rect_to(list, nk_vec2(rect.x-0.5f, rect.y-0.5f), + nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); + } nk_draw_list_path_fill(list, col); +} + +NK_API void +nk_draw_list_stroke_rect(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color col, float rounding, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + if (list->line_AA == NK_ANTI_ALIASING_ON) { + nk_draw_list_path_rect_to(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); + } else { + nk_draw_list_path_rect_to(list, nk_vec2(rect.x-0.5f, rect.y-0.5f), + nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); + } nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} + +NK_API void +nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rect, + struct nk_color left, struct nk_color top, struct nk_color right, + struct nk_color bottom) +{ + void *vtx; + struct nk_colorf col_left, col_top; + struct nk_colorf col_right, col_bottom; + nk_draw_index *idx; + nk_draw_index index; + + nk_color_fv(&col_left.r, left); + nk_color_fv(&col_right.r, right); + nk_color_fv(&col_top.r, top); + nk_color_fv(&col_bottom.r, bottom); + + NK_ASSERT(list); + if (!list) return; + + nk_draw_list_push_image(list, list->config.null.texture); + index = (nk_draw_index)list->vertex_count; + vtx = nk_draw_list_alloc_vertices(list, 4); + idx = nk_draw_list_alloc_elements(list, 6); + if (!vtx || !idx) return; + + idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); + idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); + idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); + + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y), list->config.null.uv, col_left); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y), list->config.null.uv, col_top); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y + rect.h), list->config.null.uv, col_right); + vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y + rect.h), list->config.null.uv, col_bottom); +} + +NK_API void +nk_draw_list_fill_triangle(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_vec2 c, struct nk_color col) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, c); + nk_draw_list_path_fill(list, col); +} + +NK_API void +nk_draw_list_stroke_triangle(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 b, struct nk_vec2 c, struct nk_color col, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, a); + nk_draw_list_path_line_to(list, b); + nk_draw_list_path_line_to(list, c); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} + +NK_API void +nk_draw_list_fill_circle(struct nk_draw_list *list, struct nk_vec2 center, + float radius, struct nk_color col, unsigned int segs) +{ + float a_max; + NK_ASSERT(list); + if (!list || !col.a) return; + a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; + nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + nk_draw_list_path_fill(list, col); +} + +NK_API void +nk_draw_list_stroke_circle(struct nk_draw_list *list, struct nk_vec2 center, + float radius, struct nk_color col, unsigned int segs, float thickness) +{ + float a_max; + NK_ASSERT(list); + if (!list || !col.a) return; + a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; + nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); + nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); +} + +NK_API void +nk_draw_list_stroke_curve(struct nk_draw_list *list, struct nk_vec2 p0, + struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, + struct nk_color col, unsigned int segments, float thickness) +{ + NK_ASSERT(list); + if (!list || !col.a) return; + nk_draw_list_path_line_to(list, p0); + nk_draw_list_path_curve_to(list, cp0, cp1, p1, segments); + nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); +} + +NK_INTERN void +nk_draw_list_push_rect_uv(struct nk_draw_list *list, struct nk_vec2 a, + struct nk_vec2 c, struct nk_vec2 uva, struct nk_vec2 uvc, + struct nk_color color) +{ + void *vtx; + struct nk_vec2 uvb; + struct nk_vec2 uvd; + struct nk_vec2 b; + struct nk_vec2 d; + + struct nk_colorf col; + nk_draw_index *idx; + nk_draw_index index; + NK_ASSERT(list); + if (!list) return; + + nk_color_fv(&col.r, color); + uvb = nk_vec2(uvc.x, uva.y); + uvd = nk_vec2(uva.x, uvc.y); + b = nk_vec2(c.x, a.y); + d = nk_vec2(a.x, c.y); + + index = (nk_draw_index)list->vertex_count; + vtx = nk_draw_list_alloc_vertices(list, 4); + idx = nk_draw_list_alloc_elements(list, 6); + if (!vtx || !idx) return; + + idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); + idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); + idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); + + vtx = nk_draw_vertex(vtx, &list->config, a, uva, col); + vtx = nk_draw_vertex(vtx, &list->config, b, uvb, col); + vtx = nk_draw_vertex(vtx, &list->config, c, uvc, col); + vtx = nk_draw_vertex(vtx, &list->config, d, uvd, col); +} + +NK_API void +nk_draw_list_add_image(struct nk_draw_list *list, struct nk_image texture, + struct nk_rect rect, struct nk_color color) +{ + NK_ASSERT(list); + if (!list) return; + /* push new command with given texture */ + nk_draw_list_push_image(list, texture.handle); + if (nk_image_is_subimage(&texture)) { + /* add region inside of the texture */ + struct nk_vec2 uv[2]; + uv[0].x = (float)texture.region[0]/(float)texture.w; + uv[0].y = (float)texture.region[1]/(float)texture.h; + uv[1].x = (float)(texture.region[0] + texture.region[2])/(float)texture.w; + uv[1].y = (float)(texture.region[1] + texture.region[3])/(float)texture.h; + nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), uv[0], uv[1], color); + } else nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), + nk_vec2(rect.x + rect.w, rect.y + rect.h), + nk_vec2(0.0f, 0.0f), nk_vec2(1.0f, 1.0f),color); +} + +NK_API void +nk_draw_list_add_text(struct nk_draw_list *list, const struct nk_user_font *font, + struct nk_rect rect, const char *text, int len, float font_height, + struct nk_color fg) +{ + float x = 0; + int text_len = 0; + nk_rune unicode = 0; + nk_rune next = 0; + int glyph_len = 0; + int next_glyph_len = 0; + struct nk_user_font_glyph g; + + NK_ASSERT(list); + if (!list || !len || !text) return; + if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, + list->clip_rect.x, list->clip_rect.y, list->clip_rect.w, list->clip_rect.h)) return; + + nk_draw_list_push_image(list, font->texture); + x = rect.x; + glyph_len = nk_utf_decode(text, &unicode, len); + if (!glyph_len) return; + + /* draw every glyph image */ + fg.a = (nk_byte)((float)fg.a * list->config.global_alpha); + while (text_len < len && glyph_len) { + float gx, gy, gh, gw; + float char_width = 0; + if (unicode == NK_UTF_INVALID) break; + + /* query currently drawn glyph information */ + next_glyph_len = nk_utf_decode(text + text_len + glyph_len, &next, (int)len - text_len); + font->query(font->userdata, font_height, &g, unicode, + (next == NK_UTF_INVALID) ? '\0' : next); + + /* calculate and draw glyph drawing rectangle and image */ + gx = x + g.offset.x; + gy = rect.y + g.offset.y; + gw = g.width; gh = g.height; + char_width = g.xadvance; + nk_draw_list_push_rect_uv(list, nk_vec2(gx,gy), nk_vec2(gx + gw, gy+ gh), + g.uv[0], g.uv[1], fg); + + /* offset next glyph */ + text_len += glyph_len; + x += char_width; + glyph_len = next_glyph_len; + unicode = next; + } +} + +NK_API nk_flags +nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, + struct nk_buffer *vertices, struct nk_buffer *elements, + const struct nk_convert_config *config) +{ + nk_flags res = NK_CONVERT_SUCCESS; + const struct nk_command *cmd; + NK_ASSERT(ctx); + NK_ASSERT(cmds); + NK_ASSERT(vertices); + NK_ASSERT(elements); + NK_ASSERT(config); + NK_ASSERT(config->vertex_layout); + NK_ASSERT(config->vertex_size); + if (!ctx || !cmds || !vertices || !elements || !config || !config->vertex_layout) + return NK_CONVERT_INVALID_PARAM; + + nk_draw_list_setup(&ctx->draw_list, config, cmds, vertices, elements, + config->line_AA, config->shape_AA); + nk_foreach(cmd, ctx) + { +#ifdef NK_INCLUDE_COMMAND_USERDATA + ctx->draw_list.userdata = cmd->userdata; +#endif + switch (cmd->type) { + case NK_COMMAND_NOP: break; + case NK_COMMAND_SCISSOR: { + const struct nk_command_scissor *s = (const struct nk_command_scissor*)cmd; + nk_draw_list_add_clip(&ctx->draw_list, nk_rect(s->x, s->y, s->w, s->h)); + } break; + case NK_COMMAND_LINE: { + const struct nk_command_line *l = (const struct nk_command_line*)cmd; + nk_draw_list_stroke_line(&ctx->draw_list, nk_vec2(l->begin.x, l->begin.y), + nk_vec2(l->end.x, l->end.y), l->color, l->line_thickness); + } break; + case NK_COMMAND_CURVE: { + const struct nk_command_curve *q = (const struct nk_command_curve*)cmd; + nk_draw_list_stroke_curve(&ctx->draw_list, nk_vec2(q->begin.x, q->begin.y), + nk_vec2(q->ctrl[0].x, q->ctrl[0].y), nk_vec2(q->ctrl[1].x, + q->ctrl[1].y), nk_vec2(q->end.x, q->end.y), q->color, + config->curve_segment_count, q->line_thickness); + } break; + case NK_COMMAND_RECT: { + const struct nk_command_rect *r = (const struct nk_command_rect*)cmd; + nk_draw_list_stroke_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->color, (float)r->rounding, r->line_thickness); + } break; + case NK_COMMAND_RECT_FILLED: { + const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled*)cmd; + nk_draw_list_fill_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->color, (float)r->rounding); + } break; + case NK_COMMAND_RECT_MULTI_COLOR: { + const struct nk_command_rect_multi_color *r = (const struct nk_command_rect_multi_color*)cmd; + nk_draw_list_fill_rect_multi_color(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), + r->left, r->top, r->right, r->bottom); + } break; + case NK_COMMAND_CIRCLE: { + const struct nk_command_circle *c = (const struct nk_command_circle*)cmd; + nk_draw_list_stroke_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, + (float)c->y + (float)c->h/2), (float)c->w/2, c->color, + config->circle_segment_count, c->line_thickness); + } break; + case NK_COMMAND_CIRCLE_FILLED: { + const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; + nk_draw_list_fill_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, + (float)c->y + (float)c->h/2), (float)c->w/2, c->color, + config->circle_segment_count); + } break; + case NK_COMMAND_ARC: { + const struct nk_command_arc *c = (const struct nk_command_arc*)cmd; + nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); + nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, + c->a[0], c->a[1], config->arc_segment_count); + nk_draw_list_path_stroke(&ctx->draw_list, c->color, NK_STROKE_CLOSED, c->line_thickness); + } break; + case NK_COMMAND_ARC_FILLED: { + const struct nk_command_arc_filled *c = (const struct nk_command_arc_filled*)cmd; + nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); + nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, + c->a[0], c->a[1], config->arc_segment_count); + nk_draw_list_path_fill(&ctx->draw_list, c->color); + } break; + case NK_COMMAND_TRIANGLE: { + const struct nk_command_triangle *t = (const struct nk_command_triangle*)cmd; + nk_draw_list_stroke_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), + nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color, + t->line_thickness); + } break; + case NK_COMMAND_TRIANGLE_FILLED: { + const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled*)cmd; + nk_draw_list_fill_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), + nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color); + } break; + case NK_COMMAND_POLYGON: { + int i; + const struct nk_command_polygon*p = (const struct nk_command_polygon*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_CLOSED, p->line_thickness); + } break; + case NK_COMMAND_POLYGON_FILLED: { + int i; + const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_fill(&ctx->draw_list, p->color); + } break; + case NK_COMMAND_POLYLINE: { + int i; + const struct nk_command_polyline *p = (const struct nk_command_polyline*)cmd; + for (i = 0; i < p->point_count; ++i) { + struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); + nk_draw_list_path_line_to(&ctx->draw_list, pnt); + } + nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_OPEN, p->line_thickness); + } break; + case NK_COMMAND_TEXT: { + const struct nk_command_text *t = (const struct nk_command_text*)cmd; + nk_draw_list_add_text(&ctx->draw_list, t->font, nk_rect(t->x, t->y, t->w, t->h), + t->string, t->length, t->height, t->foreground); + } break; + case NK_COMMAND_IMAGE: { + const struct nk_command_image *i = (const struct nk_command_image*)cmd; + nk_draw_list_add_image(&ctx->draw_list, i->img, nk_rect(i->x, i->y, i->w, i->h), i->col); + } break; + case NK_COMMAND_CUSTOM: { + const struct nk_command_custom *c = (const struct nk_command_custom*)cmd; + c->callback(&ctx->draw_list, c->x, c->y, c->w, c->h, c->callback_data); + } break; + default: break; + } + } + res |= (cmds->needed > cmds->allocated + (cmds->memory.size - cmds->size)) ? NK_CONVERT_COMMAND_BUFFER_FULL: 0; + res |= (vertices->needed > vertices->allocated) ? NK_CONVERT_VERTEX_BUFFER_FULL: 0; + res |= (elements->needed > elements->allocated) ? NK_CONVERT_ELEMENT_BUFFER_FULL: 0; + return res; +} +NK_API const struct nk_draw_command* +nk__draw_begin(const struct nk_context *ctx, + const struct nk_buffer *buffer) +{return nk__draw_list_begin(&ctx->draw_list, buffer);} + +NK_API const struct nk_draw_command* +nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buffer) +{return nk__draw_list_end(&ctx->draw_list, buffer);} + +NK_API const struct nk_draw_command* +nk__draw_next(const struct nk_draw_command *cmd, + const struct nk_buffer *buffer, const struct nk_context *ctx) +{return nk__draw_list_next(cmd, buffer, &ctx->draw_list);} + +#endif + +/* + * ============================================================== + * + * FONT HANDLING + * + * =============================================================== + */ +#ifdef NK_INCLUDE_FONT_BAKING +/* ------------------------------------------------------------- + * + * RECT PACK + * + * --------------------------------------------------------------*/ +/* stb_rect_pack.h - v0.05 - public domain - rectangle packing */ +/* Sean Barrett 2014 */ +#define NK_RP__MAXVAL 0xffff +typedef unsigned short nk_rp_coord; + +struct nk_rp_rect { + /* reserved for your use: */ + int id; + /* input: */ + nk_rp_coord w, h; + /* output: */ + nk_rp_coord x, y; + int was_packed; + /* non-zero if valid packing */ +}; /* 16 bytes, nominally */ + +struct nk_rp_node { + nk_rp_coord x,y; + struct nk_rp_node *next; +}; + +struct nk_rp_context { + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + struct nk_rp_node *active_head; + struct nk_rp_node *free_head; + struct nk_rp_node extra[2]; + /* we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' */ +}; + +struct nk_rp__findresult { + int x,y; + struct nk_rp_node **prev_link; +}; + +enum NK_RP_HEURISTIC { + NK_RP_HEURISTIC_Skyline_default=0, + NK_RP_HEURISTIC_Skyline_BL_sortHeight = NK_RP_HEURISTIC_Skyline_default, + NK_RP_HEURISTIC_Skyline_BF_sortHeight +}; +enum NK_RP_INIT_STATE{NK_RP__INIT_skyline = 1}; + +NK_INTERN void +nk_rp_setup_allow_out_of_mem(struct nk_rp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + /* if it's ok to run out of memory, then don't bother aligning them; */ + /* this gives better packing, but may fail due to OOM (even though */ + /* the rectangles easily fit). @TODO a smarter approach would be to only */ + /* quantize once we've hit OOM, then we could get rid of this parameter. */ + context->align = 1; + else { + /* if it's not ok to run out of memory, then quantize the widths */ + /* so that num_nodes is always enough nodes. */ + /* */ + /* I.e. num_nodes * align >= width */ + /* align >= width / num_nodes */ + /* align = ceil(width/num_nodes) */ + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +NK_INTERN void +nk_rp_init_target(struct nk_rp_context *context, int width, int height, + struct nk_rp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + NK_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = 0; + context->init_mode = NK_RP__INIT_skyline; + context->heuristic = NK_RP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + nk_rp_setup_allow_out_of_mem(context, 0); + + /* node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) */ + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (nk_rp_coord) width; + context->extra[1].y = 65535; + context->extra[1].next = 0; +} + +/* find minimum y position if it starts at x1 */ +NK_INTERN int +nk_rp__skyline_find_min_y(struct nk_rp_context *c, struct nk_rp_node *first, + int x0, int width, int *pwaste) +{ + struct nk_rp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + NK_ASSERT(first->x <= x0); + NK_UNUSED(c); + + NK_ASSERT(node->next->x > x0); + /* we ended up handling this in the caller for efficiency */ + NK_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) + { + if (node->y > min_y) { + /* raise min_y higher. */ + /* we've accounted for all waste up to min_y, */ + /* but we'll now add more waste for everything we've visited */ + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + /* the first time through, visited_width might be reduced */ + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + /* add waste area */ + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + *pwaste = waste_area; + return min_y; +} + +NK_INTERN struct nk_rp__findresult +nk_rp__skyline_find_best_pos(struct nk_rp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + struct nk_rp__findresult fr; + struct nk_rp_node **prev, *node, *tail, **best = 0; + + /* align to multiple of c->align */ + width = (width + c->align - 1); + width -= width % c->align; + NK_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = nk_rp__skyline_find_min_y(c, node, node->x, width, &waste); + /* actually just want to test BL */ + if (c->heuristic == NK_RP_HEURISTIC_Skyline_BL_sortHeight) { + /* bottom left */ + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + /* best-fit */ + if (y + height <= c->height) { + /* can only use it if it first vertically */ + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + best_x = (best == 0) ? 0 : (*best)->x; + + /* if doing best-fit (BF), we also have to try aligning right edge to each node position */ + /* */ + /* e.g, if fitting */ + /* */ + /* ____________________ */ + /* |____________________| */ + /* */ + /* into */ + /* */ + /* | | */ + /* | ____________| */ + /* |____________| */ + /* */ + /* then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned */ + /* */ + /* This makes BF take about 2x the time */ + if (c->heuristic == NK_RP_HEURISTIC_Skyline_BF_sortHeight) + { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + /* find first node that's admissible */ + while (tail->x < width) + tail = tail->next; + while (tail) + { + int xpos = tail->x - width; + int y,waste; + NK_ASSERT(xpos >= 0); + /* find the left position that matches this */ + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + NK_ASSERT(node->next->x > xpos && node->x <= xpos); + y = nk_rp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + NK_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +NK_INTERN struct nk_rp__findresult +nk_rp__skyline_pack_rectangle(struct nk_rp_context *context, int width, int height) +{ + /* find best position according to heuristic */ + struct nk_rp__findresult res = nk_rp__skyline_find_best_pos(context, width, height); + struct nk_rp_node *node, *cur; + + /* bail if: */ + /* 1. it failed */ + /* 2. the best node doesn't fit (we don't always check this) */ + /* 3. we're out of memory */ + if (res.prev_link == 0 || res.y + height > context->height || context->free_head == 0) { + res.prev_link = 0; + return res; + } + + /* on success, create new node */ + node = context->free_head; + node->x = (nk_rp_coord) res.x; + node->y = (nk_rp_coord) (res.y + height); + + context->free_head = node->next; + + /* insert the new node into the right starting point, and */ + /* let 'cur' point to the remaining nodes needing to be */ + /* stitched back in */ + cur = *res.prev_link; + if (cur->x < res.x) { + /* preserve the existing one, so start testing with the next one */ + struct nk_rp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + /* from here, traverse cur and free the nodes, until we get to one */ + /* that shouldn't be freed */ + while (cur->next && cur->next->x <= res.x + width) { + struct nk_rp_node *next = cur->next; + /* move the current node to the free list */ + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + /* stitch the list back in */ + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (nk_rp_coord) (res.x + width); + return res; +} + +NK_INTERN int +nk_rect_height_compare(const void *a, const void *b) +{ + const struct nk_rp_rect *p = (const struct nk_rp_rect *) a; + const struct nk_rp_rect *q = (const struct nk_rp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +NK_INTERN int +nk_rect_original_order(const void *a, const void *b) +{ + const struct nk_rp_rect *p = (const struct nk_rp_rect *) a; + const struct nk_rp_rect *q = (const struct nk_rp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +NK_INTERN void +nk_rp_qsort(struct nk_rp_rect *array, unsigned int len, int(*cmp)(const void*,const void*)) +{ + /* iterative quick sort */ + #define NK_MAX_SORT_STACK 64 + unsigned right, left = 0, stack[NK_MAX_SORT_STACK], pos = 0; + unsigned seed = len/2 * 69069+1; + for (;;) { + for (; left+1 < len; len++) { + struct nk_rp_rect pivot, tmp; + if (pos == NK_MAX_SORT_STACK) len = stack[pos = 0]; + pivot = array[left+seed%(len-left)]; + seed = seed * 69069 + 1; + stack[pos++] = len; + for (right = left-1;;) { + while (cmp(&array[++right], &pivot) < 0); + while (cmp(&pivot, &array[--len]) < 0); + if (right >= len) break; + tmp = array[right]; + array[right] = array[len]; + array[len] = tmp; + } + } + if (pos == 0) break; + left = len; + len = stack[--pos]; + } + #undef NK_MAX_SORT_STACK +} + +NK_INTERN void +nk_rp_pack_rects(struct nk_rp_context *context, struct nk_rp_rect *rects, int num_rects) +{ + int i; + /* we use the 'was_packed' field internally to allow sorting/unsorting */ + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + } + + /* sort according to heuristic */ + nk_rp_qsort(rects, (unsigned)num_rects, nk_rect_height_compare); + + for (i=0; i < num_rects; ++i) { + struct nk_rp__findresult fr = nk_rp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (nk_rp_coord) fr.x; + rects[i].y = (nk_rp_coord) fr.y; + } else { + rects[i].x = rects[i].y = NK_RP__MAXVAL; + } + } + + /* unsort */ + nk_rp_qsort(rects, (unsigned)num_rects, nk_rect_original_order); + + /* set was_packed flags */ + for (i=0; i < num_rects; ++i) + rects[i].was_packed = !(rects[i].x == NK_RP__MAXVAL && rects[i].y == NK_RP__MAXVAL); +} + +/* + * ============================================================== + * + * TRUETYPE + * + * =============================================================== + */ +/* stb_truetype.h - v1.07 - public domain */ +#define NK_TT_MAX_OVERSAMPLE 8 +#define NK_TT__OVER_MASK (NK_TT_MAX_OVERSAMPLE-1) + +struct nk_tt_bakedchar { + unsigned short x0,y0,x1,y1; + /* coordinates of bbox in bitmap */ + float xoff,yoff,xadvance; +}; + +struct nk_tt_aligned_quad{ + float x0,y0,s0,t0; /* top-left */ + float x1,y1,s1,t1; /* bottom-right */ +}; + +struct nk_tt_packedchar { + unsigned short x0,y0,x1,y1; + /* coordinates of bbox in bitmap */ + float xoff,yoff,xadvance; + float xoff2,yoff2; +}; + +struct nk_tt_pack_range { + float font_size; + int first_unicode_codepoint_in_range; + /* if non-zero, then the chars are continuous, and this is the first codepoint */ + int *array_of_unicode_codepoints; + /* if non-zero, then this is an array of unicode codepoints */ + int num_chars; + struct nk_tt_packedchar *chardata_for_range; /* output */ + unsigned char h_oversample, v_oversample; + /* don't set these, they're used internally */ +}; + +struct nk_tt_pack_context { + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +struct nk_tt_fontinfo { + const unsigned char* data; /* pointer to .ttf file */ + int fontstart;/* offset of start of font */ + int numGlyphs;/* number of glyphs, needed for range checking */ + int loca,head,glyf,hhea,hmtx,kern; /* table locations as offset from start of .ttf */ + int index_map; /* a cmap mapping for our chosen character encoding */ + int indexToLocFormat; /* format needed to map from glyph index to glyph */ +}; + +enum { + NK_TT_vmove=1, + NK_TT_vline, + NK_TT_vcurve +}; + +struct nk_tt_vertex { + short x,y,cx,cy; + unsigned char type,padding; +}; + +struct nk_tt__bitmap{ + int w,h,stride; + unsigned char *pixels; +}; + +struct nk_tt__hheap_chunk { + struct nk_tt__hheap_chunk *next; +}; +struct nk_tt__hheap { + struct nk_allocator alloc; + struct nk_tt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +}; + +struct nk_tt__edge { + float x0,y0, x1,y1; + int invert; +}; + +struct nk_tt__active_edge { + struct nk_tt__active_edge *next; + float fx,fdx,fdy; + float direction; + float sy; + float ey; +}; +struct nk_tt__point {float x,y;}; + +#define NK_TT_MACSTYLE_DONTCARE 0 +#define NK_TT_MACSTYLE_BOLD 1 +#define NK_TT_MACSTYLE_ITALIC 2 +#define NK_TT_MACSTYLE_UNDERSCORE 4 +#define NK_TT_MACSTYLE_NONE 8 +/* <= not same as 0, this makes us check the bitfield is 0 */ + +enum { /* platformID */ + NK_TT_PLATFORM_ID_UNICODE =0, + NK_TT_PLATFORM_ID_MAC =1, + NK_TT_PLATFORM_ID_ISO =2, + NK_TT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { /* encodingID for NK_TT_PLATFORM_ID_UNICODE */ + NK_TT_UNICODE_EID_UNICODE_1_0 =0, + NK_TT_UNICODE_EID_UNICODE_1_1 =1, + NK_TT_UNICODE_EID_ISO_10646 =2, + NK_TT_UNICODE_EID_UNICODE_2_0_BMP=3, + NK_TT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { /* encodingID for NK_TT_PLATFORM_ID_MICROSOFT */ + NK_TT_MS_EID_SYMBOL =0, + NK_TT_MS_EID_UNICODE_BMP =1, + NK_TT_MS_EID_SHIFTJIS =2, + NK_TT_MS_EID_UNICODE_FULL =10 +}; + +enum { /* encodingID for NK_TT_PLATFORM_ID_MAC; same as Script Manager codes */ + NK_TT_MAC_EID_ROMAN =0, NK_TT_MAC_EID_ARABIC =4, + NK_TT_MAC_EID_JAPANESE =1, NK_TT_MAC_EID_HEBREW =5, + NK_TT_MAC_EID_CHINESE_TRAD =2, NK_TT_MAC_EID_GREEK =6, + NK_TT_MAC_EID_KOREAN =3, NK_TT_MAC_EID_RUSSIAN =7 +}; + +enum { /* languageID for NK_TT_PLATFORM_ID_MICROSOFT; same as LCID... */ + /* problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs */ + NK_TT_MS_LANG_ENGLISH =0x0409, NK_TT_MS_LANG_ITALIAN =0x0410, + NK_TT_MS_LANG_CHINESE =0x0804, NK_TT_MS_LANG_JAPANESE =0x0411, + NK_TT_MS_LANG_DUTCH =0x0413, NK_TT_MS_LANG_KOREAN =0x0412, + NK_TT_MS_LANG_FRENCH =0x040c, NK_TT_MS_LANG_RUSSIAN =0x0419, + NK_TT_MS_LANG_GERMAN =0x0407, NK_TT_MS_LANG_SPANISH =0x0409, + NK_TT_MS_LANG_HEBREW =0x040d, NK_TT_MS_LANG_SWEDISH =0x041D +}; + +enum { /* languageID for NK_TT_PLATFORM_ID_MAC */ + NK_TT_MAC_LANG_ENGLISH =0 , NK_TT_MAC_LANG_JAPANESE =11, + NK_TT_MAC_LANG_ARABIC =12, NK_TT_MAC_LANG_KOREAN =23, + NK_TT_MAC_LANG_DUTCH =4 , NK_TT_MAC_LANG_RUSSIAN =32, + NK_TT_MAC_LANG_FRENCH =1 , NK_TT_MAC_LANG_SPANISH =6 , + NK_TT_MAC_LANG_GERMAN =2 , NK_TT_MAC_LANG_SWEDISH =5 , + NK_TT_MAC_LANG_HEBREW =10, NK_TT_MAC_LANG_CHINESE_SIMPLIFIED =33, + NK_TT_MAC_LANG_ITALIAN =3 , NK_TT_MAC_LANG_CHINESE_TRAD =19 +}; + +#define nk_ttBYTE(p) (* (const nk_byte *) (p)) +#define nk_ttCHAR(p) (* (const char *) (p)) + +#if defined(NK_BIGENDIAN) && !defined(NK_ALLOW_UNALIGNED_TRUETYPE) + #define nk_ttUSHORT(p) (* (nk_ushort *) (p)) + #define nk_ttSHORT(p) (* (nk_short *) (p)) + #define nk_ttULONG(p) (* (nk_uint *) (p)) + #define nk_ttLONG(p) (* (nk_int *) (p)) +#else + static nk_ushort nk_ttUSHORT(const nk_byte *p) { return (nk_ushort)(p[0]*256 + p[1]); } + static nk_short nk_ttSHORT(const nk_byte *p) { return (nk_short)(p[0]*256 + p[1]); } + static nk_uint nk_ttULONG(const nk_byte *p) { return (nk_uint)((p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]); } +#endif + +#define nk_tt_tag4(p,c0,c1,c2,c3)\ + ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define nk_tt_tag(p,str) nk_tt_tag4(p,str[0],str[1],str[2],str[3]) + +NK_INTERN int nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *alloc, + int glyph_index, struct nk_tt_vertex **pvertices); + +NK_INTERN nk_uint +nk_tt__find_table(const nk_byte *data, nk_uint fontstart, const char *tag) +{ + /* @OPTIMIZE: binary search */ + nk_int num_tables = nk_ttUSHORT(data+fontstart+4); + nk_uint tabledir = fontstart + 12; + nk_int i; + for (i = 0; i < num_tables; ++i) { + nk_uint loc = tabledir + (nk_uint)(16*i); + if (nk_tt_tag(data+loc+0, tag)) + return nk_ttULONG(data+loc+8); + } + return 0; +} + +NK_INTERN int +nk_tt_InitFont(struct nk_tt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + nk_uint cmap, t; + nk_int i,numTables; + const nk_byte *data = (const nk_byte *) data2; + + info->data = data; + info->fontstart = fontstart; + + cmap = nk_tt__find_table(data, (nk_uint)fontstart, "cmap"); /* required */ + info->loca = (int)nk_tt__find_table(data, (nk_uint)fontstart, "loca"); /* required */ + info->head = (int)nk_tt__find_table(data, (nk_uint)fontstart, "head"); /* required */ + info->glyf = (int)nk_tt__find_table(data, (nk_uint)fontstart, "glyf"); /* required */ + info->hhea = (int)nk_tt__find_table(data, (nk_uint)fontstart, "hhea"); /* required */ + info->hmtx = (int)nk_tt__find_table(data, (nk_uint)fontstart, "hmtx"); /* required */ + info->kern = (int)nk_tt__find_table(data, (nk_uint)fontstart, "kern"); /* not required */ + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = nk_tt__find_table(data, (nk_uint)fontstart, "maxp"); + if (t) info->numGlyphs = nk_ttUSHORT(data+t+4); + else info->numGlyphs = 0xffff; + + /* find a cmap encoding table we understand *now* to avoid searching */ + /* later. (todo: could make this installable) */ + /* the same regardless of glyph. */ + numTables = nk_ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) + { + nk_uint encoding_record = cmap + 4 + 8 * (nk_uint)i; + /* find an encoding we understand: */ + switch(nk_ttUSHORT(data+encoding_record)) { + case NK_TT_PLATFORM_ID_MICROSOFT: + switch (nk_ttUSHORT(data+encoding_record+2)) { + case NK_TT_MS_EID_UNICODE_BMP: + case NK_TT_MS_EID_UNICODE_FULL: + /* MS/Unicode */ + info->index_map = (int)(cmap + nk_ttULONG(data+encoding_record+4)); + break; + default: break; + } break; + case NK_TT_PLATFORM_ID_UNICODE: + /* Mac/iOS has these */ + /* all the encodingIDs are unicode, so we don't bother to check it */ + info->index_map = (int)(cmap + nk_ttULONG(data+encoding_record+4)); + break; + default: break; + } + } + if (info->index_map == 0) + return 0; + info->indexToLocFormat = nk_ttUSHORT(data+info->head + 50); + return 1; +} + +NK_INTERN int +nk_tt_FindGlyphIndex(const struct nk_tt_fontinfo *info, int unicode_codepoint) +{ + const nk_byte *data = info->data; + nk_uint index_map = (nk_uint)info->index_map; + + nk_ushort format = nk_ttUSHORT(data + index_map + 0); + if (format == 0) { /* apple byte encoding */ + nk_int bytes = nk_ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return nk_ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + nk_uint first = nk_ttUSHORT(data + index_map + 6); + nk_uint count = nk_ttUSHORT(data + index_map + 8); + if ((nk_uint) unicode_codepoint >= first && (nk_uint) unicode_codepoint < first+count) + return nk_ttUSHORT(data + index_map + 10 + (unicode_codepoint - (int)first)*2); + return 0; + } else if (format == 2) { + NK_ASSERT(0); /* @TODO: high-byte mapping for japanese/chinese/korean */ + return 0; + } else if (format == 4) { /* standard mapping for windows fonts: binary search collection of ranges */ + nk_ushort segcount = nk_ttUSHORT(data+index_map+6) >> 1; + nk_ushort searchRange = nk_ttUSHORT(data+index_map+8) >> 1; + nk_ushort entrySelector = nk_ttUSHORT(data+index_map+10); + nk_ushort rangeShift = nk_ttUSHORT(data+index_map+12) >> 1; + + /* do a binary search of the segments */ + nk_uint endCount = index_map + 14; + nk_uint search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + /* they lie from endCount .. endCount + segCount */ + /* but searchRange is the nearest power of two, so... */ + if (unicode_codepoint >= nk_ttUSHORT(data + search + rangeShift*2)) + search += (nk_uint)(rangeShift*2); + + /* now decrement to bias correctly to find smallest */ + search -= 2; + while (entrySelector) { + nk_ushort end; + searchRange >>= 1; + end = nk_ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += (nk_uint)(searchRange*2); + --entrySelector; + } + search += 2; + + { + nk_ushort offset, start; + nk_ushort item = (nk_ushort) ((search - endCount) >> 1); + + NK_ASSERT(unicode_codepoint <= nk_ttUSHORT(data + endCount + 2*item)); + start = nk_ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = nk_ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (nk_ushort) (unicode_codepoint + nk_ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return nk_ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + nk_uint ngroups = nk_ttULONG(data+index_map+12); + nk_int low,high; + low = 0; high = (nk_int)ngroups; + /* Binary search the right group. */ + while (low < high) { + nk_int mid = low + ((high-low) >> 1); /* rounds down, so low <= mid < high */ + nk_uint start_char = nk_ttULONG(data+index_map+16+mid*12); + nk_uint end_char = nk_ttULONG(data+index_map+16+mid*12+4); + if ((nk_uint) unicode_codepoint < start_char) + high = mid; + else if ((nk_uint) unicode_codepoint > end_char) + low = mid+1; + else { + nk_uint start_glyph = nk_ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return (int)start_glyph + (int)unicode_codepoint - (int)start_char; + else /* format == 13 */ + return (int)start_glyph; + } + } + return 0; /* not found */ + } + /* @TODO */ + NK_ASSERT(0); + return 0; +} + +NK_INTERN void +nk_tt_setvertex(struct nk_tt_vertex *v, nk_byte type, nk_int x, nk_int y, nk_int cx, nk_int cy) +{ + v->type = type; + v->x = (nk_short) x; + v->y = (nk_short) y; + v->cx = (nk_short) cx; + v->cy = (nk_short) cy; +} + +NK_INTERN int +nk_tt__GetGlyfOffset(const struct nk_tt_fontinfo *info, int glyph_index) +{ + int g1,g2; + if (glyph_index >= info->numGlyphs) return -1; /* glyph index out of range */ + if (info->indexToLocFormat >= 2) return -1; /* unknown index->glyph map format */ + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + nk_ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + nk_ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + (int)nk_ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + (int)nk_ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + return g1==g2 ? -1 : g1; /* if length is 0, return -1 */ +} + +NK_INTERN int +nk_tt_GetGlyphBox(const struct nk_tt_fontinfo *info, int glyph_index, + int *x0, int *y0, int *x1, int *y1) +{ + int g = nk_tt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = nk_ttSHORT(info->data + g + 2); + if (y0) *y0 = nk_ttSHORT(info->data + g + 4); + if (x1) *x1 = nk_ttSHORT(info->data + g + 6); + if (y1) *y1 = nk_ttSHORT(info->data + g + 8); + return 1; +} + +NK_INTERN int +stbtt__close_shape(struct nk_tt_vertex *vertices, int num_vertices, int was_off, + int start_off, nk_int sx, nk_int sy, nk_int scx, nk_int scy, nk_int cx, nk_int cy) +{ + if (start_off) { + if (was_off) + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve,sx,sy,cx,cy); + else + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vline,sx,sy,0,0); + } + return num_vertices; +} + +NK_INTERN int +nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *alloc, + int glyph_index, struct nk_tt_vertex **pvertices) +{ + nk_short numberOfContours; + const nk_byte *endPtsOfContours; + const nk_byte *data = info->data; + struct nk_tt_vertex *vertices=0; + int num_vertices=0; + int g = nk_tt__GetGlyfOffset(info, glyph_index); + *pvertices = 0; + + if (g < 0) return 0; + numberOfContours = nk_ttSHORT(data + g); + if (numberOfContours > 0) { + nk_byte flags=0,flagcount; + nk_int ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + nk_int x,y,cx,cy,sx,sy, scx,scy; + const nk_byte *points; + endPtsOfContours = (data + g + 10); + ins = nk_ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+nk_ttUSHORT(endPtsOfContours + numberOfContours*2-2); + m = n + 2*numberOfContours; /* a loose bound on how many vertices we might need */ + vertices = (struct nk_tt_vertex *)alloc->alloc(alloc->userdata, 0, (nk_size)m * sizeof(vertices[0])); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + /* in first pass, we load uninterpreted data into the allocated array */ + /* above, shifted to the end of the array so we won't overwrite it when */ + /* we create our final data starting from the front */ + off = m - n; /* starting offset for uninterpreted data, regardless of how m ends up being calculated */ + + /* first load flags */ + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else --flagcount; + vertices[off+i].type = flags; + } + + /* now load x coordinates */ + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + nk_short dx = *points++; + x += (flags & 16) ? dx : -dx; /* ??? */ + } else { + if (!(flags & 16)) { + x = x + (nk_short) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (nk_short) x; + } + + /* now load y coordinates */ + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + nk_short dy = *points++; + y += (flags & 32) ? dy : -dy; /* ??? */ + } else { + if (!(flags & 32)) { + y = y + (nk_short) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (nk_short) y; + } + + /* now convert them to our format */ + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) + { + flags = vertices[off+i].type; + x = (nk_short) vertices[off+i].x; + y = (nk_short) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + /* now start the new one */ + start_off = !(flags & 1); + if (start_off) { + /* if we start off with an off-curve point, then when we need to find a point on the curve */ + /* where we can start, and we need to save some state for when we wraparound. */ + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + /* next point is also a curve point, so interpolate an on-point curve */ + sx = (x + (nk_int) vertices[off+i+1].x) >> 1; + sy = (y + (nk_int) vertices[off+i+1].y) >> 1; + } else { + /* otherwise just use the next point as our start point */ + sx = (nk_int) vertices[off+i+1].x; + sy = (nk_int) vertices[off+i+1].y; + ++i; /* we're using point i+1 as the starting point, so skip it */ + } + } else { + sx = x; + sy = y; + } + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + nk_ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) + { /* if it's a curve */ + if (was_off) /* two off-curve control points in a row means interpolate an on-curve midpoint */ + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vcurve, x,y, cx, cy); + else nk_tt_setvertex(&vertices[num_vertices++], NK_TT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + /* Compound shapes. */ + int more = 1; + const nk_byte *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + + while (more) + { + nk_ushort flags, gidx; + int comp_num_verts = 0, i; + struct nk_tt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = (nk_ushort)nk_ttSHORT(comp); comp+=2; + gidx = (nk_ushort)nk_ttSHORT(comp); comp+=2; + + if (flags & 2) { /* XY values */ + if (flags & 1) { /* shorts */ + mtx[4] = nk_ttSHORT(comp); comp+=2; + mtx[5] = nk_ttSHORT(comp); comp+=2; + } else { + mtx[4] = nk_ttCHAR(comp); comp+=1; + mtx[5] = nk_ttCHAR(comp); comp+=1; + } + } else { + /* @TODO handle matching point */ + NK_ASSERT(0); + } + if (flags & (1<<3)) { /* WE_HAVE_A_SCALE */ + mtx[0] = mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { /* WE_HAVE_AN_X_AND_YSCALE */ + mtx[0] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { /* WE_HAVE_A_TWO_BY_TWO */ + mtx[0] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = nk_ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = nk_ttSHORT(comp)/16384.0f; comp+=2; + } + + /* Find transformation scales. */ + m = (float) NK_SQRT(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) NK_SQRT(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + /* Get indexed glyph. */ + comp_num_verts = nk_tt_GetGlyphShape(info, alloc, gidx, &comp_verts); + if (comp_num_verts > 0) + { + /* Transform vertices. */ + for (i = 0; i < comp_num_verts; ++i) { + struct nk_tt_vertex* v = &comp_verts[i]; + short x,y; + x=v->x; y=v->y; + v->x = (short)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (short)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (short)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (short)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + /* Append vertices. */ + tmp = (struct nk_tt_vertex*)alloc->alloc(alloc->userdata, 0, + (nk_size)(num_vertices+comp_num_verts)*sizeof(struct nk_tt_vertex)); + if (!tmp) { + if (vertices) alloc->free(alloc->userdata, vertices); + if (comp_verts) alloc->free(alloc->userdata, comp_verts); + return 0; + } + if (num_vertices > 0) NK_MEMCPY(tmp, vertices, (nk_size)num_vertices*sizeof(struct nk_tt_vertex)); + NK_MEMCPY(tmp+num_vertices, comp_verts, (nk_size)comp_num_verts*sizeof(struct nk_tt_vertex)); + if (vertices) alloc->free(alloc->userdata,vertices); + vertices = tmp; + alloc->free(alloc->userdata,comp_verts); + num_vertices += comp_num_verts; + } + /* More components ? */ + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + /* @TODO other compound variations? */ + NK_ASSERT(0); + } else { + /* numberOfCounters == 0, do nothing */ + } + *pvertices = vertices; + return num_vertices; +} + +NK_INTERN void +nk_tt_GetGlyphHMetrics(const struct nk_tt_fontinfo *info, int glyph_index, + int *advanceWidth, int *leftSideBearing) +{ + nk_ushort numOfLongHorMetrics = nk_ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) + *advanceWidth = nk_ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) + *leftSideBearing = nk_ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) + *advanceWidth = nk_ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) + *leftSideBearing = nk_ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +NK_INTERN void +nk_tt_GetFontVMetrics(const struct nk_tt_fontinfo *info, + int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = nk_ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = nk_ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = nk_ttSHORT(info->data+info->hhea + 8); +} + +NK_INTERN float +nk_tt_ScaleForPixelHeight(const struct nk_tt_fontinfo *info, float height) +{ + int fheight = nk_ttSHORT(info->data + info->hhea + 4) - nk_ttSHORT(info->data + info->hhea + 6); + return (float) height / (float)fheight; +} + +NK_INTERN float +nk_tt_ScaleForMappingEmToPixels(const struct nk_tt_fontinfo *info, float pixels) +{ + int unitsPerEm = nk_ttUSHORT(info->data + info->head + 18); + return pixels / (float)unitsPerEm; +} + +/*------------------------------------------------------------- + * antialiasing software rasterizer + * --------------------------------------------------------------*/ +NK_INTERN void +nk_tt_GetGlyphBitmapBoxSubpixel(const struct nk_tt_fontinfo *font, + int glyph, float scale_x, float scale_y,float shift_x, float shift_y, + int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!nk_tt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + /* e.g. space character */ + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + /* move to integral bboxes (treating pixels as little squares, what pixels get touched)? */ + if (ix0) *ix0 = nk_ifloorf((float)x0 * scale_x + shift_x); + if (iy0) *iy0 = nk_ifloorf((float)-y1 * scale_y + shift_y); + if (ix1) *ix1 = nk_iceilf ((float)x1 * scale_x + shift_x); + if (iy1) *iy1 = nk_iceilf ((float)-y0 * scale_y + shift_y); + } +} + +NK_INTERN void +nk_tt_GetGlyphBitmapBox(const struct nk_tt_fontinfo *font, int glyph, + float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + nk_tt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +/*------------------------------------------------------------- + * Rasterizer + * --------------------------------------------------------------*/ +NK_INTERN void* +nk_tt__hheap_alloc(struct nk_tt__hheap *hh, nk_size size) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + struct nk_tt__hheap_chunk *c = (struct nk_tt__hheap_chunk *) + hh->alloc.alloc(hh->alloc.userdata, 0, + sizeof(struct nk_tt__hheap_chunk) + size * (nk_size)count); + if (c == 0) return 0; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + size * (nk_size)hh->num_remaining_in_head_chunk; + } +} + +NK_INTERN void +nk_tt__hheap_free(struct nk_tt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +NK_INTERN void +nk_tt__hheap_cleanup(struct nk_tt__hheap *hh) +{ + struct nk_tt__hheap_chunk *c = hh->head; + while (c) { + struct nk_tt__hheap_chunk *n = c->next; + hh->alloc.free(hh->alloc.userdata, c); + c = n; + } +} + +NK_INTERN struct nk_tt__active_edge* +nk_tt__new_active(struct nk_tt__hheap *hh, struct nk_tt__edge *e, + int off_x, float start_point) +{ + struct nk_tt__active_edge *z = (struct nk_tt__active_edge *) + nk_tt__hheap_alloc(hh, sizeof(*z)); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + /*STBTT_assert(e->y0 <= start_point); */ + if (!z) return z; + z->fdx = dxdy; + z->fdy = (dxdy != 0) ? (1/dxdy): 0; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= (float)off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} + +NK_INTERN void +nk_tt__handle_clipped_edge(float *scanline, int x, struct nk_tt__active_edge *e, + float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + NK_ASSERT(y0 < y1); + NK_ASSERT(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) NK_ASSERT(x1 <= x+1); + else if (x0 == x+1) NK_ASSERT(x1 >= x); + else if (x0 <= x) NK_ASSERT(x1 <= x); + else if (x0 >= x+1) NK_ASSERT(x1 >= x+1); + else NK_ASSERT(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1); + else { + NK_ASSERT(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + /* coverage = 1 - average x position */ + scanline[x] += (float)e->direction * (float)(y1-y0) * (1.0f-((x0-(float)x)+(x1-(float)x))/2.0f); + } +} + +NK_INTERN void +nk_tt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, + struct nk_tt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + while (e) + { + /* brute force every pixel */ + /* compute intersection points with top & bottom */ + NK_ASSERT(e->ey >= y_top); + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + nk_tt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + nk_tt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + nk_tt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float y0,y1; + float dy = e->fdy; + NK_ASSERT(e->sy <= y_bottom && e->ey >= y_top); + + /* compute endpoints of line segment clipped to this scanline (if the */ + /* line segment starts on this scanline. x0 is the intersection of the */ + /* line with y_top, but that may be off the line segment. */ + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + y0 = e->sy; + } else { + x_top = x0; + y0 = y_top; + } + + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + y1 = e->ey; + } else { + x_bottom = xb; + y1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) + { + /* from here on, we don't have to range check x values */ + if ((int) x_top == (int) x_bottom) { + float height; + /* simple case, only spans one pixel */ + int x = (int) x_top; + height = y1 - y0; + NK_ASSERT(x >= 0 && x < len); + scanline[x] += e->direction * (1.0f-(((float)x_top - (float)x) + ((float)x_bottom-(float)x))/2.0f) * (float)height; + scanline_fill[x] += e->direction * (float)height; /* everything right of this pixel is filled */ + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + /* covers 2+ pixels */ + if (x_top > x_bottom) + { + /* flip scanline vertically; signed area is the same */ + float t; + y0 = y_bottom - (y0 - y_top); + y1 = y_bottom - (y1 - y_top); + t = y0; y0 = y1; y1 = t; + t = x_bottom; x_bottom = x_top; x_top = t; + dx = -dx; + dy = -dy; + t = x0; x0 = xb; xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + /* compute intersection with y axis at x1+1 */ + y_crossing = ((float)x1+1 - (float)x0) * (float)dy + (float)y_top; + + sign = e->direction; + /* area of the rectangle covered from y0..y_crossing */ + area = sign * (y_crossing-y0); + /* area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) */ + scanline[x1] += area * (1.0f-((float)((float)x_top - (float)x1)+(float)(x1+1-x1))/2.0f); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += (float)dy * (float)(x2 - (x1+1)); + + scanline[x2] += area + sign * (1.0f-((float)(x2-x2)+((float)x_bottom-(float)x2))/2.0f) * (y1-y_crossing); + scanline_fill[x2] += sign * (y1-y0); + } + } + else + { + /* if edge goes outside of box we're drawing, we require */ + /* clipping logic. since this does not match the intended use */ + /* of this library, we use a different, very slow brute */ + /* force implementation */ + int x; + for (x=0; x < len; ++x) + { + /* cases: */ + /* */ + /* there can be up to two intersections with the pixel. any intersection */ + /* with left or right edges can be handled by splitting into two (or three) */ + /* regions. intersections with top & bottom do not necessitate case-wise logic. */ + /* */ + /* the old way of doing this found the intersections with the left & right edges, */ + /* then used some simple logic to produce up to three segments in sorted order */ + /* from top-to-bottom. however, this had a problem: if an x edge was epsilon */ + /* across the x border, then the corresponding y position might not be distinct */ + /* from the other y segment, and it might ignored as an empty segment. to avoid */ + /* that, we need to explicitly produce segments based on x positions. */ + + /* rename variables to clear pairs */ + float ya = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + float yb,y2; + + yb = ((float)x - x0) / dx + y_top; + y2 = ((float)x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { /* three segments descending down-right */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { /* three segments descending down-left */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3); + } else if (x0 < x1 && x3 > x1) { /* two segments across x, down-right */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3); + } else if (x3 < x1 && x0 > x1) { /* two segments across x, down-left */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x1,yb); + nk_tt__handle_clipped_edge(scanline,x,e, x1,yb, x3,y3); + } else if (x0 < x2 && x3 > x2) { /* two segments across x+1, down-right */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { /* two segments across x+1, down-left */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x2,y2); + nk_tt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { /* one segment */ + nk_tt__handle_clipped_edge(scanline,x,e, x0,ya, x3,y3); + } + } + } + } + e = e->next; + } +} + +/* directly AA rasterize edges w/o supersampling */ +NK_INTERN void +nk_tt__rasterize_sorted_edges(struct nk_tt__bitmap *result, struct nk_tt__edge *e, + int n, int vsubsample, int off_x, int off_y, struct nk_allocator *alloc) +{ + struct nk_tt__hheap hh; + struct nk_tt__active_edge *active = 0; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + NK_UNUSED(vsubsample); + nk_zero_struct(hh); + hh.alloc = *alloc; + + if (result->w > 64) + scanline = (float *) alloc->alloc(alloc->userdata,0, (nk_size)(result->w*2+1) * sizeof(float)); + else scanline = scanline_data; + + scanline2 = scanline + result->w; + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) + { + /* find center of pixel for this scanline */ + float scan_y_top = (float)y + 0.0f; + float scan_y_bottom = (float)y + 1.0f; + struct nk_tt__active_edge **step = &active; + + NK_MEMSET(scanline , 0, (nk_size)result->w*sizeof(scanline[0])); + NK_MEMSET(scanline2, 0, (nk_size)(result->w+1)*sizeof(scanline[0])); + + /* update all active edges; */ + /* remove all active edges that terminate before the top of this scanline */ + while (*step) { + struct nk_tt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; /* delete from list */ + NK_ASSERT(z->direction); + z->direction = 0; + nk_tt__hheap_free(&hh, z); + } else { + step = &((*step)->next); /* advance through list */ + } + } + + /* insert all edges that start before the bottom of this scanline */ + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + struct nk_tt__active_edge *z = nk_tt__new_active(&hh, e, off_x, scan_y_top); + if (z != 0) { + NK_ASSERT(z->ey >= scan_y_top); + /* insert at front */ + z->next = active; + active = z; + } + } + ++e; + } + + /* now process all active edges */ + if (active) + nk_tt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) NK_ABS(k) * 255.0f + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + /* advance all the edges */ + step = &active; + while (*step) { + struct nk_tt__active_edge *z = *step; + z->fx += z->fdx; /* advance to position for current scanline */ + step = &((*step)->next); /* advance through list */ + } + ++y; + ++j; + } + nk_tt__hheap_cleanup(&hh); + if (scanline != scanline_data) + alloc->free(alloc->userdata, scanline); +} + +#define NK_TT__COMPARE(a,b) ((a)->y0 < (b)->y0) +NK_INTERN void +nk_tt__sort_edges_ins_sort(struct nk_tt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + struct nk_tt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + struct nk_tt__edge *b = &p[j-1]; + int c = NK_TT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +NK_INTERN void +nk_tt__sort_edges_quicksort(struct nk_tt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + struct nk_tt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = NK_TT__COMPARE(&p[0],&p[m]); + c12 = NK_TT__COMPARE(&p[m],&p[n-1]); + + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = NK_TT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!NK_TT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!NK_TT__COMPARE(&p[0], &p[j])) break; + } + + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + + } + + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + nk_tt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + nk_tt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +NK_INTERN void +nk_tt__sort_edges(struct nk_tt__edge *p, int n) +{ + nk_tt__sort_edges_quicksort(p, n); + nk_tt__sort_edges_ins_sort(p, n); +} + +NK_INTERN void +nk_tt__rasterize(struct nk_tt__bitmap *result, struct nk_tt__point *pts, + int *wcount, int windings, float scale_x, float scale_y, + float shift_x, float shift_y, int off_x, int off_y, int invert, + struct nk_allocator *alloc) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + struct nk_tt__edge *e; + int n,i,j,k,m; + int vsubsample = 1; + /* vsubsample should divide 255 evenly; otherwise we won't reach full opacity */ + + /* now we have to blow out the windings into explicit edge lists */ + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (struct nk_tt__edge*) + alloc->alloc(alloc->userdata, 0,(sizeof(*e) * (nk_size)(n+1))); + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) + { + struct nk_tt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + /* skip the edge if horizontal */ + if (p[j].y == p[k].y) + continue; + + /* add edge from j to k to the list */ + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * (float)vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * (float)vsubsample; + ++n; + } + } + + /* now sort the edges by their highest point (should snap to integer, and then by x) */ + /*STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); */ + nk_tt__sort_edges(e, n); + /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */ + nk_tt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, alloc); + alloc->free(alloc->userdata, e); +} + +NK_INTERN void +nk_tt__add_point(struct nk_tt__point *points, int n, float x, float y) +{ + if (!points) return; /* during first pass, it's unallocated */ + points[n].x = x; + points[n].y = y; +} + +NK_INTERN int +nk_tt__tesselate_curve(struct nk_tt__point *points, int *num_points, + float x0, float y0, float x1, float y1, float x2, float y2, + float objspace_flatness_squared, int n) +{ + /* tesselate until threshold p is happy... + * @TODO warped to compensate for non-linear stretching */ + /* midpoint */ + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + /* versus directly drawn line */ + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) /* 65536 segments on one curve better be enough! */ + return 1; + + /* half-pixel error allowed... need to be smaller if AA */ + if (dx*dx+dy*dy > objspace_flatness_squared) { + nk_tt__tesselate_curve(points, num_points, x0,y0, + (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + nk_tt__tesselate_curve(points, num_points, mx,my, + (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + nk_tt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +/* returns number of contours */ +NK_INTERN struct nk_tt__point* +nk_tt_FlattenCurves(struct nk_tt_vertex *vertices, int num_verts, + float objspace_flatness, int **contour_lengths, int *num_contours, + struct nk_allocator *alloc) +{ + struct nk_tt__point *points=0; + int num_points=0; + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i; + int n=0; + int start=0; + int pass; + + /* count how many "moves" there are to get the contour count */ + for (i=0; i < num_verts; ++i) + if (vertices[i].type == NK_TT_vmove) ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) + alloc->alloc(alloc->userdata,0, (sizeof(**contour_lengths) * (nk_size)n)); + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + /* make two passes through the points so we don't need to realloc */ + for (pass=0; pass < 2; ++pass) + { + float x=0,y=0; + if (pass == 1) { + points = (struct nk_tt__point *) + alloc->alloc(alloc->userdata,0, (nk_size)num_points * sizeof(points[0])); + if (points == 0) goto error; + } + num_points = 0; + n= -1; + + for (i=0; i < num_verts; ++i) + { + switch (vertices[i].type) { + case NK_TT_vmove: + /* start the next contour */ + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + nk_tt__add_point(points, num_points++, x,y); + break; + case NK_TT_vline: + x = vertices[i].x, y = vertices[i].y; + nk_tt__add_point(points, num_points++, x, y); + break; + case NK_TT_vcurve: + nk_tt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + default: break; + } + } + (*contour_lengths)[n] = num_points - start; + } + return points; + +error: + alloc->free(alloc->userdata, points); + alloc->free(alloc->userdata, *contour_lengths); + *contour_lengths = 0; + *num_contours = 0; + return 0; +} + +NK_INTERN void +nk_tt_Rasterize(struct nk_tt__bitmap *result, float flatness_in_pixels, + struct nk_tt_vertex *vertices, int num_verts, + float scale_x, float scale_y, float shift_x, float shift_y, + int x_off, int y_off, int invert, struct nk_allocator *alloc) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + struct nk_tt__point *windings = nk_tt_FlattenCurves(vertices, num_verts, + flatness_in_pixels / scale, &winding_lengths, &winding_count, alloc); + + NK_ASSERT(alloc); + if (windings) { + nk_tt__rasterize(result, windings, winding_lengths, winding_count, + scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, alloc); + alloc->free(alloc->userdata, winding_lengths); + alloc->free(alloc->userdata, windings); + } +} + +NK_INTERN void +nk_tt_MakeGlyphBitmapSubpixel(const struct nk_tt_fontinfo *info, unsigned char *output, + int out_w, int out_h, int out_stride, float scale_x, float scale_y, + float shift_x, float shift_y, int glyph, struct nk_allocator *alloc) +{ + int ix0,iy0; + struct nk_tt_vertex *vertices; + int num_verts = nk_tt_GetGlyphShape(info, alloc, glyph, &vertices); + struct nk_tt__bitmap gbm; + + nk_tt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, + shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + nk_tt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, + shift_x, shift_y, ix0,iy0, 1, alloc); + alloc->free(alloc->userdata, vertices); +} + +/*------------------------------------------------------------- + * Bitmap baking + * --------------------------------------------------------------*/ +NK_INTERN int +nk_tt_PackBegin(struct nk_tt_pack_context *spc, unsigned char *pixels, + int pw, int ph, int stride_in_bytes, int padding, struct nk_allocator *alloc) +{ + int num_nodes = pw - padding; + struct nk_rp_context *context = (struct nk_rp_context *) + alloc->alloc(alloc->userdata,0, sizeof(*context)); + struct nk_rp_node *nodes = (struct nk_rp_node*) + alloc->alloc(alloc->userdata,0, (sizeof(*nodes ) * (nk_size)num_nodes)); + + if (context == 0 || nodes == 0) { + if (context != 0) alloc->free(alloc->userdata, context); + if (nodes != 0) alloc->free(alloc->userdata, nodes); + return 0; + } + + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = (stride_in_bytes != 0) ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + nk_rp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + if (pixels) + NK_MEMSET(pixels, 0, (nk_size)(pw*ph)); /* background of 0 around pixels */ + return 1; +} + +NK_INTERN void +nk_tt_PackEnd(struct nk_tt_pack_context *spc, struct nk_allocator *alloc) +{ + alloc->free(alloc->userdata, spc->nodes); + alloc->free(alloc->userdata, spc->pack_info); +} + +NK_INTERN void +nk_tt_PackSetOversampling(struct nk_tt_pack_context *spc, + unsigned int h_oversample, unsigned int v_oversample) +{ + NK_ASSERT(h_oversample <= NK_TT_MAX_OVERSAMPLE); + NK_ASSERT(v_oversample <= NK_TT_MAX_OVERSAMPLE); + if (h_oversample <= NK_TT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= NK_TT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +NK_INTERN void +nk_tt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, + int kernel_width) +{ + unsigned char buffer[NK_TT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + + for (j=0; j < h; ++j) + { + int i; + unsigned int total; + NK_MEMSET(buffer, 0, (nk_size)kernel_width); + + total = 0; + + /* make kernel_width a constant in common cases so compiler can optimize out the divide */ + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)pixels[i] - buffer[i & NK_TT__OVER_MASK]; + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += (unsigned int)(pixels[i] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / (unsigned int)kernel_width); + } + break; + } + + for (; i < w; ++i) { + NK_ASSERT(pixels[i] == 0); + total -= (unsigned int)(buffer[i & NK_TT__OVER_MASK]); + pixels[i] = (unsigned char) (total / (unsigned int)kernel_width); + } + pixels += stride_in_bytes; + } +} + +NK_INTERN void +nk_tt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, + int kernel_width) +{ + unsigned char buffer[NK_TT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + + for (j=0; j < w; ++j) + { + int i; + unsigned int total; + NK_MEMSET(buffer, 0, (nk_size)kernel_width); + + total = 0; + + /* make kernel_width a constant in common cases so compiler can optimize out the divide */ + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += (unsigned int)(pixels[i*stride_in_bytes] - buffer[i & NK_TT__OVER_MASK]); + buffer[(i+kernel_width) & NK_TT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / (unsigned int)kernel_width); + } + break; + } + + for (; i < h; ++i) { + NK_ASSERT(pixels[i*stride_in_bytes] == 0); + total -= (unsigned int)(buffer[i & NK_TT__OVER_MASK]); + pixels[i*stride_in_bytes] = (unsigned char) (total / (unsigned int)kernel_width); + } + pixels += 1; + } +} + +NK_INTERN float +nk_tt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + /* The prefilter is a box filter of width "oversample", */ + /* which shifts phase by (oversample - 1)/2 pixels in */ + /* oversampled space. We want to shift in the opposite */ + /* direction to counter this. */ + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +/* rects array must be big enough to accommodate all characters in the given ranges */ +NK_INTERN int +nk_tt_PackFontRangesGatherRects(struct nk_tt_pack_context *spc, + struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges, + int num_ranges, struct nk_rp_rect *rects) +{ + int i,j,k; + k = 0; + + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = (fh > 0) ? nk_tt_ScaleForPixelHeight(info, fh): + nk_tt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].first_unicode_codepoint_in_range ? + ranges[i].first_unicode_codepoint_in_range + j : + ranges[i].array_of_unicode_codepoints[j]; + + int glyph = nk_tt_FindGlyphIndex(info, codepoint); + nk_tt_GetGlyphBitmapBoxSubpixel(info,glyph, scale * (float)spc->h_oversample, + scale * (float)spc->v_oversample, 0,0, &x0,&y0,&x1,&y1); + rects[k].w = (nk_rp_coord) (x1-x0 + spc->padding + (int)spc->h_oversample-1); + rects[k].h = (nk_rp_coord) (y1-y0 + spc->padding + (int)spc->v_oversample-1); + ++k; + } + } + return k; +} + +NK_INTERN int +nk_tt_PackFontRangesRenderIntoRects(struct nk_tt_pack_context *spc, + struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges, + int num_ranges, struct nk_rp_rect *rects, struct nk_allocator *alloc) +{ + int i,j,k, return_value = 1; + /* save current values */ + int old_h_over = (int)spc->h_oversample; + int old_v_over = (int)spc->v_oversample; + /* rects array must be big enough to accommodate all characters in the given ranges */ + + k = 0; + for (i=0; i < num_ranges; ++i) + { + float fh = ranges[i].font_size; + float recip_h,recip_v,sub_x,sub_y; + float scale = fh > 0 ? nk_tt_ScaleForPixelHeight(info, fh): + nk_tt_ScaleForMappingEmToPixels(info, -fh); + + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + + recip_h = 1.0f / (float)spc->h_oversample; + recip_v = 1.0f / (float)spc->v_oversample; + + sub_x = nk_tt__oversample_shift((int)spc->h_oversample); + sub_y = nk_tt__oversample_shift((int)spc->v_oversample); + + for (j=0; j < ranges[i].num_chars; ++j) + { + struct nk_rp_rect *r = &rects[k]; + if (r->was_packed) + { + struct nk_tt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].first_unicode_codepoint_in_range ? + ranges[i].first_unicode_codepoint_in_range + j : + ranges[i].array_of_unicode_codepoints[j]; + int glyph = nk_tt_FindGlyphIndex(info, codepoint); + nk_rp_coord pad = (nk_rp_coord) spc->padding; + + /* pad on left and top */ + r->x = (nk_rp_coord)((int)r->x + (int)pad); + r->y = (nk_rp_coord)((int)r->y + (int)pad); + r->w = (nk_rp_coord)((int)r->w - (int)pad); + r->h = (nk_rp_coord)((int)r->h - (int)pad); + + nk_tt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + nk_tt_GetGlyphBitmapBox(info, glyph, scale * (float)spc->h_oversample, + (scale * (float)spc->v_oversample), &x0,&y0,&x1,&y1); + nk_tt_MakeGlyphBitmapSubpixel(info, spc->pixels + r->x + r->y*spc->stride_in_bytes, + (int)(r->w - spc->h_oversample+1), (int)(r->h - spc->v_oversample+1), + spc->stride_in_bytes, scale * (float)spc->h_oversample, + scale * (float)spc->v_oversample, 0,0, glyph, alloc); + + if (spc->h_oversample > 1) + nk_tt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, (int)spc->h_oversample); + + if (spc->v_oversample > 1) + nk_tt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, (int)spc->v_oversample); + + bc->x0 = (nk_ushort) r->x; + bc->y0 = (nk_ushort) r->y; + bc->x1 = (nk_ushort) (r->x + r->w); + bc->y1 = (nk_ushort) (r->y + r->h); + bc->xadvance = scale * (float)advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = ((float)x0 + r->w) * recip_h + sub_x; + bc->yoff2 = ((float)y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; /* if any fail, report failure */ + } + ++k; + } + } + /* restore original values */ + spc->h_oversample = (unsigned int)old_h_over; + spc->v_oversample = (unsigned int)old_v_over; + return return_value; +} + +NK_INTERN void +nk_tt_GetPackedQuad(struct nk_tt_packedchar *chardata, int pw, int ph, + int char_index, float *xpos, float *ypos, struct nk_tt_aligned_quad *q, + int align_to_integer) +{ + float ipw = 1.0f / (float)pw, iph = 1.0f / (float)ph; + struct nk_tt_packedchar *b = (struct nk_tt_packedchar*)(chardata + char_index); + if (align_to_integer) { + int tx = nk_ifloorf((*xpos + b->xoff) + 0.5f); + int ty = nk_ifloorf((*ypos + b->yoff) + 0.5f); + + float x = (float)tx; + float y = (float)ty; + + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + *xpos += b->xadvance; +} + +/* ------------------------------------------------------------- + * + * FONT BAKING + * + * --------------------------------------------------------------*/ +struct nk_font_bake_data { + struct nk_tt_fontinfo info; + struct nk_rp_rect *rects; + struct nk_tt_pack_range *ranges; + nk_rune range_count; +}; + +struct nk_font_baker { + struct nk_allocator alloc; + struct nk_tt_pack_context spc; + struct nk_font_bake_data *build; + struct nk_tt_packedchar *packed_chars; + struct nk_rp_rect *rects; + struct nk_tt_pack_range *ranges; +}; + +NK_GLOBAL const nk_size nk_rect_align = NK_ALIGNOF(struct nk_rp_rect); +NK_GLOBAL const nk_size nk_range_align = NK_ALIGNOF(struct nk_tt_pack_range); +NK_GLOBAL const nk_size nk_char_align = NK_ALIGNOF(struct nk_tt_packedchar); +NK_GLOBAL const nk_size nk_build_align = NK_ALIGNOF(struct nk_font_bake_data); +NK_GLOBAL const nk_size nk_baker_align = NK_ALIGNOF(struct nk_font_baker); + +NK_INTERN int +nk_range_count(const nk_rune *range) +{ + const nk_rune *iter = range; + NK_ASSERT(range); + if (!range) return 0; + while (*(iter++) != 0); + return (iter == range) ? 0 : (int)((iter - range)/2); +} + +NK_INTERN int +nk_range_glyph_count(const nk_rune *range, int count) +{ + int i = 0; + int total_glyphs = 0; + for (i = 0; i < count; ++i) { + int diff; + nk_rune f = range[(i*2)+0]; + nk_rune t = range[(i*2)+1]; + NK_ASSERT(t >= f); + diff = (int)((t - f) + 1); + total_glyphs += diff; + } + return total_glyphs; +} + +NK_API const nk_rune* +nk_font_default_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = {0x0020, 0x00FF, 0}; + return ranges; +} + +NK_API const nk_rune* +nk_font_chinese_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x3000, 0x30FF, + 0x31F0, 0x31FF, + 0xFF00, 0xFFEF, + 0x4e00, 0x9FAF, + 0 + }; + return ranges; +} + +NK_API const nk_rune* +nk_font_cyrillic_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x0400, 0x052F, + 0x2DE0, 0x2DFF, + 0xA640, 0xA69F, + 0 + }; + return ranges; +} + +NK_API const nk_rune* +nk_font_korean_glyph_ranges(void) +{ + NK_STORAGE const nk_rune ranges[] = { + 0x0020, 0x00FF, + 0x3131, 0x3163, + 0xAC00, 0xD79D, + 0 + }; + return ranges; +} + +NK_INTERN void +nk_font_baker_memory(nk_size *temp, int *glyph_count, + struct nk_font_config *config_list, int count) +{ + int range_count = 0; + int total_range_count = 0; + struct nk_font_config *iter; + + NK_ASSERT(config_list); + NK_ASSERT(glyph_count); + if (!config_list) { + *temp = 0; + *glyph_count = 0; + return; + } + + *glyph_count = 0; + if (!config_list->range) + config_list->range = nk_font_default_glyph_ranges(); + for (iter = config_list; iter; iter = iter->next) { + range_count = nk_range_count(iter->range); + total_range_count += range_count; + *glyph_count += nk_range_glyph_count(iter->range, range_count); + } + + *temp = (nk_size)*glyph_count * sizeof(struct nk_rp_rect); + *temp += (nk_size)total_range_count * sizeof(struct nk_tt_pack_range); + *temp += (nk_size)*glyph_count * sizeof(struct nk_tt_packedchar); + *temp += (nk_size)count * sizeof(struct nk_font_bake_data); + *temp += sizeof(struct nk_font_baker); + *temp += nk_rect_align + nk_range_align + nk_char_align; + *temp += nk_build_align + nk_baker_align; +} + +NK_INTERN struct nk_font_baker* +nk_font_baker(void *memory, int glyph_count, int count, struct nk_allocator *alloc) +{ + struct nk_font_baker *baker; + if (!memory) return 0; + /* setup baker inside a memory block */ + baker = (struct nk_font_baker*)NK_ALIGN_PTR(memory, nk_baker_align); + baker->build = (struct nk_font_bake_data*)NK_ALIGN_PTR((baker + 1), nk_build_align); + baker->packed_chars = (struct nk_tt_packedchar*)NK_ALIGN_PTR((baker->build + count), nk_char_align); + baker->rects = (struct nk_rp_rect*)NK_ALIGN_PTR((baker->packed_chars + glyph_count), nk_rect_align); + baker->ranges = (struct nk_tt_pack_range*)NK_ALIGN_PTR((baker->rects + glyph_count), nk_range_align); + baker->alloc = *alloc; + return baker; +} + +NK_INTERN int +nk_font_bake_pack(struct nk_font_baker *baker, + nk_size *image_memory, int *width, int *height, struct nk_recti *custom, + const struct nk_font_config *config_list, int count, + struct nk_allocator *alloc) +{ + NK_STORAGE const nk_size max_height = 1024 * 32; + const struct nk_font_config *config_iter; + int total_glyph_count = 0; + int total_range_count = 0; + int range_count = 0; + int i = 0; + + NK_ASSERT(image_memory); + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(config_list); + NK_ASSERT(count); + NK_ASSERT(alloc); + + if (!image_memory || !width || !height || !config_list || !count) return nk_false; + for (config_iter = config_list; config_iter; config_iter = config_iter->next) { + range_count = nk_range_count(config_iter->range); + total_range_count += range_count; + total_glyph_count += nk_range_glyph_count(config_iter->range, range_count); + } + + /* setup font baker from temporary memory */ + for (config_iter = config_list; config_iter; config_iter = config_iter->next) { + const struct nk_font_config *cfg = config_iter; + if (!nk_tt_InitFont(&baker->build[i++].info, (const unsigned char*)cfg->ttf_blob, 0)) + return nk_false; + } + + *height = 0; + *width = (total_glyph_count > 1000) ? 1024 : 512; + nk_tt_PackBegin(&baker->spc, 0, (int)*width, (int)max_height, 0, 1, alloc); + { + int input_i = 0; + int range_n = 0; + int rect_n = 0; + int char_n = 0; + + if (custom) { + /* pack custom user data first so it will be in the upper left corner*/ + struct nk_rp_rect custom_space; + nk_zero(&custom_space, sizeof(custom_space)); + custom_space.w = (nk_rp_coord)((custom->w * 2) + 1); + custom_space.h = (nk_rp_coord)(custom->h + 1); + + nk_tt_PackSetOversampling(&baker->spc, 1, 1); + nk_rp_pack_rects((struct nk_rp_context*)baker->spc.pack_info, &custom_space, 1); + *height = NK_MAX(*height, (int)(custom_space.y + custom_space.h)); + + custom->x = (short)custom_space.x; + custom->y = (short)custom_space.y; + custom->w = (short)custom_space.w; + custom->h = (short)custom_space.h; + } + + /* first font pass: pack all glyphs */ + for (input_i = 0, config_iter = config_list; input_i < count && config_iter; + input_i++, config_iter = config_iter->next) + { + int n = 0; + int glyph_count; + const nk_rune *in_range; + const struct nk_font_config *cfg = config_iter; + struct nk_font_bake_data *tmp = &baker->build[input_i]; + + /* count glyphs + ranges in current font */ + glyph_count = 0; range_count = 0; + for (in_range = cfg->range; in_range[0] && in_range[1]; in_range += 2) { + glyph_count += (int)(in_range[1] - in_range[0]) + 1; + range_count++; + } + + /* setup ranges */ + tmp->ranges = baker->ranges + range_n; + tmp->range_count = (nk_rune)range_count; + range_n += range_count; + for (i = 0; i < range_count; ++i) { + in_range = &cfg->range[i * 2]; + tmp->ranges[i].font_size = cfg->size; + tmp->ranges[i].first_unicode_codepoint_in_range = (int)in_range[0]; + tmp->ranges[i].num_chars = (int)(in_range[1]- in_range[0]) + 1; + tmp->ranges[i].chardata_for_range = baker->packed_chars + char_n; + char_n += tmp->ranges[i].num_chars; + } + + /* pack */ + tmp->rects = baker->rects + rect_n; + rect_n += glyph_count; + nk_tt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); + n = nk_tt_PackFontRangesGatherRects(&baker->spc, &tmp->info, + tmp->ranges, (int)tmp->range_count, tmp->rects); + nk_rp_pack_rects((struct nk_rp_context*)baker->spc.pack_info, tmp->rects, (int)n); + + /* texture height */ + for (i = 0; i < n; ++i) { + if (tmp->rects[i].was_packed) + *height = NK_MAX(*height, tmp->rects[i].y + tmp->rects[i].h); + } + } + NK_ASSERT(rect_n == total_glyph_count); + NK_ASSERT(char_n == total_glyph_count); + NK_ASSERT(range_n == total_range_count); + } + *height = (int)nk_round_up_pow2((nk_uint)*height); + *image_memory = (nk_size)(*width) * (nk_size)(*height); + return nk_true; +} + +NK_INTERN void +nk_font_bake(struct nk_font_baker *baker, void *image_memory, int width, int height, + struct nk_font_glyph *glyphs, int glyphs_count, + const struct nk_font_config *config_list, int font_count) +{ + int input_i = 0; + nk_rune glyph_n = 0; + const struct nk_font_config *config_iter; + + NK_ASSERT(image_memory); + NK_ASSERT(width); + NK_ASSERT(height); + NK_ASSERT(config_list); + NK_ASSERT(baker); + NK_ASSERT(font_count); + NK_ASSERT(glyphs_count); + if (!image_memory || !width || !height || !config_list || + !font_count || !glyphs || !glyphs_count) + return; + + /* second font pass: render glyphs */ + nk_zero(image_memory, (nk_size)((nk_size)width * (nk_size)height)); + baker->spc.pixels = (unsigned char*)image_memory; + baker->spc.height = (int)height; + for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter; + ++input_i, config_iter = config_iter->next) + { + const struct nk_font_config *cfg = config_iter; + struct nk_font_bake_data *tmp = &baker->build[input_i]; + nk_tt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); + nk_tt_PackFontRangesRenderIntoRects(&baker->spc, &tmp->info, tmp->ranges, + (int)tmp->range_count, tmp->rects, &baker->alloc); + } + nk_tt_PackEnd(&baker->spc, &baker->alloc); + + /* third pass: setup font and glyphs */ + for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter; + ++input_i, config_iter = config_iter->next) + { + nk_size i = 0; + int char_idx = 0; + nk_rune glyph_count = 0; + const struct nk_font_config *cfg = config_iter; + struct nk_font_bake_data *tmp = &baker->build[input_i]; + struct nk_baked_font *dst_font = cfg->font; + + float font_scale = nk_tt_ScaleForPixelHeight(&tmp->info, cfg->size); + int unscaled_ascent, unscaled_descent, unscaled_line_gap; + nk_tt_GetFontVMetrics(&tmp->info, &unscaled_ascent, &unscaled_descent, + &unscaled_line_gap); + + /* fill baked font */ + if (!cfg->merge_mode) { + dst_font->ranges = cfg->range; + dst_font->height = cfg->size; + dst_font->ascent = ((float)unscaled_ascent * font_scale); + dst_font->descent = ((float)unscaled_descent * font_scale); + dst_font->glyph_offset = glyph_n; + } + + /* fill own baked font glyph array */ + for (i = 0; i < tmp->range_count; ++i) + { + struct nk_tt_pack_range *range = &tmp->ranges[i]; + for (char_idx = 0; char_idx < range->num_chars; char_idx++) + { + nk_rune codepoint = 0; + float dummy_x = 0, dummy_y = 0; + struct nk_tt_aligned_quad q; + struct nk_font_glyph *glyph; + + /* query glyph bounds from stb_truetype */ + const struct nk_tt_packedchar *pc = &range->chardata_for_range[char_idx]; + if (!pc->x0 && !pc->x1 && !pc->y0 && !pc->y1) continue; + codepoint = (nk_rune)(range->first_unicode_codepoint_in_range + char_idx); + nk_tt_GetPackedQuad(range->chardata_for_range, (int)width, + (int)height, char_idx, &dummy_x, &dummy_y, &q, 0); + + /* fill own glyph type with data */ + glyph = &glyphs[dst_font->glyph_offset + (unsigned int)glyph_count]; + glyph->codepoint = codepoint; + glyph->x0 = q.x0; glyph->y0 = q.y0; + glyph->x1 = q.x1; glyph->y1 = q.y1; + glyph->y0 += (dst_font->ascent + 0.5f); + glyph->y1 += (dst_font->ascent + 0.5f); + glyph->w = glyph->x1 - glyph->x0 + 0.5f; + glyph->h = glyph->y1 - glyph->y0; + + if (cfg->coord_type == NK_COORD_PIXEL) { + glyph->u0 = q.s0 * (float)width; + glyph->v0 = q.t0 * (float)height; + glyph->u1 = q.s1 * (float)width; + glyph->v1 = q.t1 * (float)height; + } else { + glyph->u0 = q.s0; + glyph->v0 = q.t0; + glyph->u1 = q.s1; + glyph->v1 = q.t1; + } + glyph->xadvance = (pc->xadvance + cfg->spacing.x); + if (cfg->pixel_snap) + glyph->xadvance = (float)(int)(glyph->xadvance + 0.5f); + glyph_count++; + } + } + dst_font->glyph_count = glyph_count; + glyph_n += dst_font->glyph_count; + } +} + +NK_INTERN void +nk_font_bake_custom_data(void *img_memory, int img_width, int img_height, + struct nk_recti img_dst, const char *texture_data_mask, int tex_width, + int tex_height, char white, char black) +{ + nk_byte *pixels; + int y = 0; + int x = 0; + int n = 0; + + NK_ASSERT(img_memory); + NK_ASSERT(img_width); + NK_ASSERT(img_height); + NK_ASSERT(texture_data_mask); + NK_UNUSED(tex_height); + if (!img_memory || !img_width || !img_height || !texture_data_mask) + return; + + pixels = (nk_byte*)img_memory; + for (y = 0, n = 0; y < tex_height; ++y) { + for (x = 0; x < tex_width; ++x, ++n) { + const int off0 = ((img_dst.x + x) + (img_dst.y + y) * img_width); + const int off1 = off0 + 1 + tex_width; + pixels[off0] = (texture_data_mask[n] == white) ? 0xFF : 0x00; + pixels[off1] = (texture_data_mask[n] == black) ? 0xFF : 0x00; + } + } +} + +NK_INTERN void +nk_font_bake_convert(void *out_memory, int img_width, int img_height, + const void *in_memory) +{ + int n = 0; + nk_rune *dst; + const nk_byte *src; + + NK_ASSERT(out_memory); + NK_ASSERT(in_memory); + NK_ASSERT(img_width); + NK_ASSERT(img_height); + if (!out_memory || !in_memory || !img_height || !img_width) return; + + dst = (nk_rune*)out_memory; + src = (const nk_byte*)in_memory; + for (n = (int)(img_width * img_height); n > 0; n--) + *dst++ = ((nk_rune)(*src++) << 24) | 0x00FFFFFF; +} + +/* ------------------------------------------------------------- + * + * FONT + * + * --------------------------------------------------------------*/ +NK_INTERN float +nk_font_text_width(nk_handle handle, float height, const char *text, int len) +{ + nk_rune unicode; + int text_len = 0; + float text_width = 0; + int glyph_len = 0; + float scale = 0; + + struct nk_font *font = (struct nk_font*)handle.ptr; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + if (!font || !text || !len) + return 0; + + scale = height/font->info.height; + glyph_len = text_len = nk_utf_decode(text, &unicode, (int)len); + if (!glyph_len) return 0; + while (text_len <= (int)len && glyph_len) { + const struct nk_font_glyph *g; + if (unicode == NK_UTF_INVALID) break; + + /* query currently drawn glyph information */ + g = nk_font_find_glyph(font, unicode); + text_width += g->xadvance * scale; + + /* offset next glyph */ + glyph_len = nk_utf_decode(text + text_len, &unicode, (int)len - text_len); + text_len += glyph_len; + } + return text_width; +} + +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT +NK_INTERN void +nk_font_query_font_glyph(nk_handle handle, float height, + struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint) +{ + float scale; + const struct nk_font_glyph *g; + struct nk_font *font; + + NK_ASSERT(glyph); + NK_UNUSED(next_codepoint); + + font = (struct nk_font*)handle.ptr; + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + if (!font || !glyph) + return; + + scale = height/font->info.height; + g = nk_font_find_glyph(font, codepoint); + glyph->width = (g->x1 - g->x0) * scale; + glyph->height = (g->y1 - g->y0) * scale; + glyph->offset = nk_vec2(g->x0 * scale, g->y0 * scale); + glyph->xadvance = (g->xadvance * scale); + glyph->uv[0] = nk_vec2(g->u0, g->v0); + glyph->uv[1] = nk_vec2(g->u1, g->v1); +} +#endif + +NK_API const struct nk_font_glyph* +nk_font_find_glyph(struct nk_font *font, nk_rune unicode) +{ + int i = 0; + int count; + int total_glyphs = 0; + const struct nk_font_glyph *glyph = 0; + + NK_ASSERT(font); + NK_ASSERT(font->glyphs); + NK_ASSERT(font->info.ranges); + if (!font || !font->glyphs) return 0; + + glyph = font->fallback; + count = nk_range_count(font->info.ranges); + for (i = 0; i < count; ++i) { + nk_rune f = font->info.ranges[(i*2)+0]; + nk_rune t = font->info.ranges[(i*2)+1]; + int diff = (int)((t - f) + 1); + if (unicode >= f && unicode <= t) + return &font->glyphs[((nk_rune)total_glyphs + (unicode - f))]; + total_glyphs += diff; + } + return glyph; +} + +NK_INTERN void +nk_font_init(struct nk_font *font, float pixel_height, + nk_rune fallback_codepoint, struct nk_font_glyph *glyphs, + const struct nk_baked_font *baked_font, nk_handle atlas) +{ + struct nk_baked_font baked; + NK_ASSERT(font); + NK_ASSERT(glyphs); + NK_ASSERT(baked_font); + if (!font || !glyphs || !baked_font) + return; + + baked = *baked_font; + font->fallback = 0; + font->info = baked; + font->scale = (float)pixel_height / (float)font->info.height; + font->glyphs = &glyphs[baked_font->glyph_offset]; + font->texture = atlas; + font->fallback_codepoint = fallback_codepoint; + font->fallback = nk_font_find_glyph(font, fallback_codepoint); + + font->handle.height = font->info.height * font->scale; + font->handle.width = nk_font_text_width; + font->handle.userdata.ptr = font; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + font->handle.query = nk_font_query_font_glyph; + font->handle.texture = font->texture; +#endif +} + +/* --------------------------------------------------------------------------- + * + * DEFAULT FONT + * + * ProggyClean.ttf + * Copyright (c) 2004, 2005 Tristan Grimmer + * MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) + * Download and more information at http://upperbounds.net + *-----------------------------------------------------------------------------*/ +#ifdef NK_INCLUDE_DEFAULT_FONT + + #ifdef __clang__ +#pragma clang diagnostic push + +#pragma clang diagnostic ignored "-Woverlength-strings" +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverlength-strings" +#endif + +NK_GLOBAL const char nk_proggy_clean_ttf_compressed_data_base85[11980+1] = + "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" + "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" + "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." + "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" + "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" + "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" + "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" + "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" + "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" + "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" + "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" + "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" + "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" + "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" + "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" + "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" + "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" + "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" + "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" + "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" + "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" + ".m7jilQ02'0-VWAg
TlGW'b)Tq7VT9q^*^$$.:&N@@" + "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" + "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" + "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" + "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" + "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" + "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" + "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" + "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" + "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" + "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" + "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" + "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" + "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" + "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" + "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" + ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" + "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" + "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" + "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" + "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" + "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" + "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" + ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" + "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" + "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" + "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" + "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" + "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; +#endif /* NK_INCLUDE_DEFAULT_FONT */ + +#define NK_CURSOR_DATA_W 90 +#define NK_CURSOR_DATA_H 27 +NK_GLOBAL const char nk_custom_cursor_data[NK_CURSOR_DATA_W * NK_CURSOR_DATA_H + 1] = +{ + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" + "..- -X.....X- X.X - X.X -X.....X - X.....X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X" + "X - X.X - X.....X - X.....X -X...X - X...X" + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" + "X..X - X.X - X.X - X.X -XX X.X - X.X XX" + "X...X - X.X - X.X - XX X.X XX - X.X - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" + "X.X X..X - -X.......X- X.......X - XX XX - " + "XX X..X - - X.....X - X.....X - X.X X.X - " + " X..X - X...X - X...X - X..X X..X - " + " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " + "------------ - X - X -X.....................X- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#elif defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#endif + +NK_INTERN unsigned int +nk_decompress_length(unsigned char *input) +{ + return (unsigned int)((input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]); +} + +NK_GLOBAL unsigned char *nk__barrier; +NK_GLOBAL unsigned char *nk__barrier2; +NK_GLOBAL unsigned char *nk__barrier3; +NK_GLOBAL unsigned char *nk__barrier4; +NK_GLOBAL unsigned char *nk__dout; + +NK_INTERN void +nk__match(unsigned char *data, unsigned int length) +{ + /* INVERSE of memmove... write each byte before copying the next...*/ + NK_ASSERT (nk__dout + length <= nk__barrier); + if (nk__dout + length > nk__barrier) { nk__dout += length; return; } + if (data < nk__barrier4) { nk__dout = nk__barrier+1; return; } + while (length--) *nk__dout++ = *data++; +} + +NK_INTERN void +nk__lit(unsigned char *data, unsigned int length) +{ + NK_ASSERT (nk__dout + length <= nk__barrier); + if (nk__dout + length > nk__barrier) { nk__dout += length; return; } + if (data < nk__barrier2) { nk__dout = nk__barrier+1; return; } + NK_MEMCPY(nk__dout, data, length); + nk__dout += length; +} + +#define nk__in2(x) ((i[x] << 8) + i[(x)+1]) +#define nk__in3(x) ((i[x] << 16) + nk__in2((x)+1)) +#define nk__in4(x) ((i[x] << 24) + nk__in3((x)+1)) + +NK_INTERN unsigned char* +nk_decompress_token(unsigned char *i) +{ + if (*i >= 0x20) { /* use fewer if's for cases that expand small */ + if (*i >= 0x80) nk__match(nk__dout-i[1]-1, (unsigned int)i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) nk__match(nk__dout-(nk__in2(0) - 0x4000 + 1), (unsigned int)i[2]+1), i += 3; + else /* *i >= 0x20 */ nk__lit(i+1, (unsigned int)i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); + } else { /* more ifs for cases that expand large, since overhead is amortized */ + if (*i >= 0x18) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x180000 + 1), (unsigned int)i[3]+1), i += 4; + else if (*i >= 0x10) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x100000 + 1), (unsigned int)nk__in2(3)+1), i += 5; + else if (*i >= 0x08) nk__lit(i+2, (unsigned int)nk__in2(0) - 0x0800 + 1), i += 2 + (nk__in2(0) - 0x0800 + 1); + else if (*i == 0x07) nk__lit(i+3, (unsigned int)nk__in2(1) + 1), i += 3 + (nk__in2(1) + 1); + else if (*i == 0x06) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), i[4]+1u), i += 5; + else if (*i == 0x04) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), (unsigned int)nk__in2(4)+1u), i += 6; + } + return i; +} + +NK_INTERN unsigned int +nk_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0]; s2 += s1; + s1 += buffer[1]; s2 += s1; + s1 += buffer[2]; s2 += s1; + s1 += buffer[3]; s2 += s1; + s1 += buffer[4]; s2 += s1; + s1 += buffer[5]; s2 += s1; + s1 += buffer[6]; s2 += s1; + s1 += buffer[7]; s2 += s1; + buffer += 8; + } + for (; i < blocklen; ++i) { + s1 += *buffer++; s2 += s1; + } + + s1 %= ADLER_MOD; s2 %= ADLER_MOD; + buflen -= (unsigned int)blocklen; + blocklen = 5552; + } + return (unsigned int)(s2 << 16) + (unsigned int)s1; +} + +NK_INTERN unsigned int +nk_decompress(unsigned char *output, unsigned char *i, unsigned int length) +{ + unsigned int olen; + if (nk__in4(0) != 0x57bC0000) return 0; + if (nk__in4(4) != 0) return 0; /* error! stream is > 4GB */ + olen = nk_decompress_length(i); + nk__barrier2 = i; + nk__barrier3 = i+length; + nk__barrier = output + olen; + nk__barrier4 = output; + i += 16; + + nk__dout = output; + for (;;) { + unsigned char *old_i = i; + i = nk_decompress_token(i); + if (i == old_i) { + if (*i == 0x05 && i[1] == 0xfa) { + NK_ASSERT(nk__dout == output + olen); + if (nk__dout != output + olen) return 0; + if (nk_adler32(1, output, olen) != (unsigned int) nk__in4(2)) + return 0; + return olen; + } else { + NK_ASSERT(0); /* NOTREACHED */ + return 0; + } + } + NK_ASSERT(nk__dout <= output + olen); + if (nk__dout > output + olen) + return 0; + } +} + +NK_INTERN unsigned int +nk_decode_85_byte(char c) +{ return (unsigned int)((c >= '\\') ? c-36 : c-35); } + +NK_INTERN void +nk_decode_85(unsigned char* dst, const unsigned char* src) +{ + while (*src) + { + unsigned int tmp = + nk_decode_85_byte((char)src[0]) + + 85 * (nk_decode_85_byte((char)src[1]) + + 85 * (nk_decode_85_byte((char)src[2]) + + 85 * (nk_decode_85_byte((char)src[3]) + + 85 * nk_decode_85_byte((char)src[4])))); + + /* we can't assume little-endianess. */ + dst[0] = (unsigned char)((tmp >> 0) & 0xFF); + dst[1] = (unsigned char)((tmp >> 8) & 0xFF); + dst[2] = (unsigned char)((tmp >> 16) & 0xFF); + dst[3] = (unsigned char)((tmp >> 24) & 0xFF); + + src += 5; + dst += 4; + } +} + +/* ------------------------------------------------------------- + * + * FONT ATLAS + * + * --------------------------------------------------------------*/ +NK_API struct nk_font_config +nk_font_config(float pixel_height) +{ + struct nk_font_config cfg; + nk_zero_struct(cfg); + cfg.ttf_blob = 0; + cfg.ttf_size = 0; + cfg.ttf_data_owned_by_atlas = 0; + cfg.size = pixel_height; + cfg.oversample_h = 3; + cfg.oversample_v = 1; + cfg.pixel_snap = 0; + cfg.coord_type = NK_COORD_UV; + cfg.spacing = nk_vec2(0,0); + cfg.range = nk_font_default_glyph_ranges(); + cfg.merge_mode = 0; + cfg.fallback_glyph = '?'; + cfg.font = 0; + return cfg; +} + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_font_atlas_init_default(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + if (!atlas) return; + nk_zero_struct(*atlas); + atlas->temporary.userdata.ptr = 0; + atlas->temporary.alloc = nk_malloc; + atlas->temporary.free = nk_mfree; + atlas->permanent.userdata.ptr = 0; + atlas->permanent.alloc = nk_malloc; + atlas->permanent.free = nk_mfree; +} +#endif + +NK_API void +nk_font_atlas_init(struct nk_font_atlas *atlas, struct nk_allocator *alloc) +{ + NK_ASSERT(atlas); + NK_ASSERT(alloc); + if (!atlas || !alloc) return; + nk_zero_struct(*atlas); + atlas->permanent = *alloc; + atlas->temporary = *alloc; +} + +NK_API void +nk_font_atlas_init_custom(struct nk_font_atlas *atlas, + struct nk_allocator *permanent, struct nk_allocator *temporary) +{ + NK_ASSERT(atlas); + NK_ASSERT(permanent); + NK_ASSERT(temporary); + if (!atlas || !permanent || !temporary) return; + nk_zero_struct(*atlas); + atlas->permanent = *permanent; + atlas->temporary = *temporary; +} + +NK_API void +nk_font_atlas_begin(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc && atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc && atlas->permanent.free); + if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free || + !atlas->temporary.alloc || !atlas->temporary.free) return; + if (atlas->glyphs) { + atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); + atlas->glyphs = 0; + } + if (atlas->pixel) { + atlas->permanent.free(atlas->permanent.userdata, atlas->pixel); + atlas->pixel = 0; + } +} + +NK_API struct nk_font* +nk_font_atlas_add(struct nk_font_atlas *atlas, const struct nk_font_config *config) +{ + struct nk_font *font = 0; + struct nk_font_config *cfg; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + + NK_ASSERT(config); + NK_ASSERT(config->ttf_blob); + NK_ASSERT(config->ttf_size); + NK_ASSERT(config->size > 0.0f); + + if (!atlas || !config || !config->ttf_blob || !config->ttf_size || config->size <= 0.0f|| + !atlas->permanent.alloc || !atlas->permanent.free || + !atlas->temporary.alloc || !atlas->temporary.free) + return 0; + + /* allocate and insert font config into list */ + cfg = (struct nk_font_config*) + atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font_config)); + NK_MEMCPY(cfg, config, sizeof(*config)); + if (!atlas->config) { + atlas->config = cfg; + cfg->next = 0; + } else { + cfg->next = atlas->config; + atlas->config = cfg; + } + + /* allocate new font */ + if (!config->merge_mode) { + font = (struct nk_font*) + atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font)); + NK_ASSERT(font); + if (!font) return 0; + font->config = cfg; + } else { + NK_ASSERT(atlas->font_num); + font = atlas->fonts; + font->config = cfg; + } + + /* insert font into list */ + if (!config->merge_mode) { + if (!atlas->fonts) { + atlas->fonts = font; + font->next = 0; + } else { + font->next = atlas->fonts; + atlas->fonts = font; + } + cfg->font = &font->info; + } + + /* create own copy of .TTF font blob */ + if (!config->ttf_data_owned_by_atlas) { + cfg->ttf_blob = atlas->permanent.alloc(atlas->permanent.userdata,0, cfg->ttf_size); + NK_ASSERT(cfg->ttf_blob); + if (!cfg->ttf_blob) { + atlas->font_num++; + return 0; + } + NK_MEMCPY(cfg->ttf_blob, config->ttf_blob, cfg->ttf_size); + cfg->ttf_data_owned_by_atlas = 1; + } + atlas->font_num++; + return font; +} + +NK_API struct nk_font* +nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, + nk_size size, float height, const struct nk_font_config *config) +{ + struct nk_font_config cfg; + NK_ASSERT(memory); + NK_ASSERT(size); + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !atlas->temporary.alloc || !atlas->temporary.free || !memory || !size || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = memory; + cfg.ttf_size = size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 0; + return nk_font_atlas_add(atlas, &cfg); +} + +#ifdef NK_INCLUDE_STANDARD_IO +NK_API struct nk_font* +nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, + float height, const struct nk_font_config *config) +{ + nk_size size; + char *memory; + struct nk_font_config cfg; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + + if (!atlas || !file_path) return 0; + memory = nk_file_load(file_path, &size, &atlas->permanent); + if (!memory) return 0; + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = memory; + cfg.ttf_size = size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 1; + return nk_font_atlas_add(atlas, &cfg); +} +#endif + +NK_API struct nk_font* +nk_font_atlas_add_compressed(struct nk_font_atlas *atlas, + void *compressed_data, nk_size compressed_size, float height, + const struct nk_font_config *config) +{ + unsigned int decompressed_size; + void *decompressed_data; + struct nk_font_config cfg; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + + NK_ASSERT(compressed_data); + NK_ASSERT(compressed_size); + if (!atlas || !compressed_data || !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + + decompressed_size = nk_decompress_length((unsigned char*)compressed_data); + decompressed_data = atlas->permanent.alloc(atlas->permanent.userdata,0,decompressed_size); + NK_ASSERT(decompressed_data); + if (!decompressed_data) return 0; + nk_decompress((unsigned char*)decompressed_data, (unsigned char*)compressed_data, + (unsigned int)compressed_size); + + cfg = (config) ? *config: nk_font_config(height); + cfg.ttf_blob = decompressed_data; + cfg.ttf_size = decompressed_size; + cfg.size = height; + cfg.ttf_data_owned_by_atlas = 1; + return nk_font_atlas_add(atlas, &cfg); +} + +NK_API struct nk_font* +nk_font_atlas_add_compressed_base85(struct nk_font_atlas *atlas, + const char *data_base85, float height, const struct nk_font_config *config) +{ + int compressed_size; + void *compressed_data; + struct nk_font *font; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + + NK_ASSERT(data_base85); + if (!atlas || !data_base85 || !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + + compressed_size = (((int)nk_strlen(data_base85) + 4) / 5) * 4; + compressed_data = atlas->temporary.alloc(atlas->temporary.userdata,0, (nk_size)compressed_size); + NK_ASSERT(compressed_data); + if (!compressed_data) return 0; + nk_decode_85((unsigned char*)compressed_data, (const unsigned char*)data_base85); + font = nk_font_atlas_add_compressed(atlas, compressed_data, + (nk_size)compressed_size, height, config); + atlas->temporary.free(atlas->temporary.userdata, compressed_data); + return font; +} + +#ifdef NK_INCLUDE_DEFAULT_FONT +NK_API struct nk_font* +nk_font_atlas_add_default(struct nk_font_atlas *atlas, + float pixel_height, const struct nk_font_config *config) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + return nk_font_atlas_add_compressed_base85(atlas, + nk_proggy_clean_ttf_compressed_data_base85, pixel_height, config); +} +#endif + +NK_API const void* +nk_font_atlas_bake(struct nk_font_atlas *atlas, int *width, int *height, + enum nk_font_atlas_format fmt) +{ + int i = 0; + void *tmp = 0; + nk_size tmp_size, img_size; + struct nk_font *font_iter; + struct nk_font_baker *baker; + + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + + NK_ASSERT(width); + NK_ASSERT(height); + if (!atlas || !width || !height || + !atlas->temporary.alloc || !atlas->temporary.free || + !atlas->permanent.alloc || !atlas->permanent.free) + return 0; + +#ifdef NK_INCLUDE_DEFAULT_FONT + /* no font added so just use default font */ + if (!atlas->font_num) + atlas->default_font = nk_font_atlas_add_default(atlas, 13.0f, 0); +#endif + NK_ASSERT(atlas->font_num); + if (!atlas->font_num) return 0; + + /* allocate temporary baker memory required for the baking process */ + nk_font_baker_memory(&tmp_size, &atlas->glyph_count, atlas->config, atlas->font_num); + tmp = atlas->temporary.alloc(atlas->temporary.userdata,0, tmp_size); + NK_ASSERT(tmp); + if (!tmp) goto failed; + + /* allocate glyph memory for all fonts */ + baker = nk_font_baker(tmp, atlas->glyph_count, atlas->font_num, &atlas->temporary); + atlas->glyphs = (struct nk_font_glyph*)atlas->permanent.alloc( + atlas->permanent.userdata,0, sizeof(struct nk_font_glyph)*(nk_size)atlas->glyph_count); + NK_ASSERT(atlas->glyphs); + if (!atlas->glyphs) + goto failed; + + /* pack all glyphs into a tight fit space */ + atlas->custom.w = (NK_CURSOR_DATA_W*2)+1; + atlas->custom.h = NK_CURSOR_DATA_H + 1; + if (!nk_font_bake_pack(baker, &img_size, width, height, &atlas->custom, + atlas->config, atlas->font_num, &atlas->temporary)) + goto failed; + + /* allocate memory for the baked image font atlas */ + atlas->pixel = atlas->temporary.alloc(atlas->temporary.userdata,0, img_size); + NK_ASSERT(atlas->pixel); + if (!atlas->pixel) + goto failed; + + /* bake glyphs and custom white pixel into image */ + nk_font_bake(baker, atlas->pixel, *width, *height, + atlas->glyphs, atlas->glyph_count, atlas->config, atlas->font_num); + nk_font_bake_custom_data(atlas->pixel, *width, *height, atlas->custom, + nk_custom_cursor_data, NK_CURSOR_DATA_W, NK_CURSOR_DATA_H, '.', 'X'); + + if (fmt == NK_FONT_ATLAS_RGBA32) { + /* convert alpha8 image into rgba32 image */ + void *img_rgba = atlas->temporary.alloc(atlas->temporary.userdata,0, + (nk_size)(*width * *height * 4)); + NK_ASSERT(img_rgba); + if (!img_rgba) goto failed; + nk_font_bake_convert(img_rgba, *width, *height, atlas->pixel); + atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); + atlas->pixel = img_rgba; + } + atlas->tex_width = *width; + atlas->tex_height = *height; + + /* initialize each font */ + for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) { + struct nk_font *font = font_iter; + struct nk_font_config *config = font->config; + nk_font_init(font, config->size, config->fallback_glyph, atlas->glyphs, + config->font, nk_handle_ptr(0)); + } + + /* initialize each cursor */ + {NK_STORAGE const struct nk_vec2 nk_cursor_data[NK_CURSOR_COUNT][3] = { + /* Pos ----- Size ------- Offset --*/ + {{ 0, 3}, {12,19}, { 0, 0}}, + {{13, 0}, { 7,16}, { 4, 8}}, + {{31, 0}, {23,23}, {11,11}}, + {{21, 0}, { 9, 23}, { 5,11}}, + {{55,18}, {23, 9}, {11, 5}}, + {{73, 0}, {17,17}, { 9, 9}}, + {{55, 0}, {17,17}, { 9, 9}} + }; + for (i = 0; i < NK_CURSOR_COUNT; ++i) { + struct nk_cursor *cursor = &atlas->cursors[i]; + cursor->img.w = (unsigned short)*width; + cursor->img.h = (unsigned short)*height; + cursor->img.region[0] = (unsigned short)(atlas->custom.x + nk_cursor_data[i][0].x); + cursor->img.region[1] = (unsigned short)(atlas->custom.y + nk_cursor_data[i][0].y); + cursor->img.region[2] = (unsigned short)nk_cursor_data[i][1].x; + cursor->img.region[3] = (unsigned short)nk_cursor_data[i][1].y; + cursor->size = nk_cursor_data[i][1]; + cursor->offset = nk_cursor_data[i][2]; + }} + /* free temporary memory */ + atlas->temporary.free(atlas->temporary.userdata, tmp); + return atlas->pixel; + +failed: + /* error so cleanup all memory */ + if (tmp) atlas->temporary.free(atlas->temporary.userdata, tmp); + if (atlas->glyphs) { + atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); + atlas->glyphs = 0; + } + if (atlas->pixel) { + atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); + atlas->pixel = 0; + } + return 0; +} + +NK_API void +nk_font_atlas_end(struct nk_font_atlas *atlas, nk_handle texture, + struct nk_draw_null_texture *null) +{ + int i = 0; + struct nk_font *font_iter; + NK_ASSERT(atlas); + if (!atlas) { + if (!null) return; + null->texture = texture; + null->uv = nk_vec2(0.5f,0.5f); + } + if (null) { + null->texture = texture; + null->uv.x = (atlas->custom.x + 0.5f)/(float)atlas->tex_width; + null->uv.y = (atlas->custom.y + 0.5f)/(float)atlas->tex_height; + } + for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) { + font_iter->texture = texture; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + font_iter->handle.texture = texture; +#endif + } + for (i = 0; i < NK_CURSOR_COUNT; ++i) + atlas->cursors[i].img.handle = texture; + + atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); + atlas->pixel = 0; + atlas->tex_width = 0; + atlas->tex_height = 0; + atlas->custom.x = 0; + atlas->custom.y = 0; + atlas->custom.w = 0; + atlas->custom.h = 0; +} + +NK_API void +nk_font_atlas_cleanup(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + + if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free) return; + if (atlas->config) { + struct nk_font_config *iter, *next; + for (iter = atlas->config; iter; iter = next) { + next = iter->next; + atlas->permanent.free(atlas->permanent.userdata, iter->ttf_blob); + atlas->permanent.free(atlas->permanent.userdata, iter); + } + atlas->config = 0; + } +} + +NK_API void +nk_font_atlas_clear(struct nk_font_atlas *atlas) +{ + NK_ASSERT(atlas); + NK_ASSERT(atlas->temporary.alloc); + NK_ASSERT(atlas->temporary.free); + NK_ASSERT(atlas->permanent.alloc); + NK_ASSERT(atlas->permanent.free); + if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free) return; + + nk_font_atlas_cleanup(atlas); + if (atlas->fonts) { + struct nk_font *iter, *next; + for (iter = atlas->fonts; iter; iter = next) { + next = iter->next; + atlas->permanent.free(atlas->permanent.userdata, iter); + } + atlas->fonts = 0; + } + if (atlas->glyphs) + atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); + nk_zero_struct(*atlas); +} +#endif +/* ============================================================== + * + * INPUT + * + * ===============================================================*/ +NK_API void +nk_input_begin(struct nk_context *ctx) +{ + int i; + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + for (i = 0; i < NK_BUTTON_MAX; ++i) + in->mouse.buttons[i].clicked = 0; + + in->keyboard.text_len = 0; + in->mouse.scroll_delta = nk_vec2(0,0); + in->mouse.prev.x = in->mouse.pos.x; + in->mouse.prev.y = in->mouse.pos.y; + in->mouse.delta.x = 0; + in->mouse.delta.y = 0; + for (i = 0; i < NK_KEY_MAX; i++) + in->keyboard.keys[i].clicked = 0; +} + +NK_API void +nk_input_end(struct nk_context *ctx) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + if (in->mouse.grab) + in->mouse.grab = 0; + if (in->mouse.ungrab) { + in->mouse.grabbed = 0; + in->mouse.ungrab = 0; + in->mouse.grab = 0; + } +} + +NK_API void +nk_input_motion(struct nk_context *ctx, int x, int y) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + in->mouse.pos.x = (float)x; + in->mouse.pos.y = (float)y; + in->mouse.delta.x = in->mouse.pos.x - in->mouse.prev.x; + in->mouse.delta.y = in->mouse.pos.y - in->mouse.prev.y; +} + +NK_API void +nk_input_key(struct nk_context *ctx, enum nk_keys key, int down) +{ + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + if (in->keyboard.keys[key].down != down) + in->keyboard.keys[key].clicked++; + in->keyboard.keys[key].down = down; +} + +NK_API void +nk_input_button(struct nk_context *ctx, enum nk_buttons id, int x, int y, int down) +{ + struct nk_mouse_button *btn; + struct nk_input *in; + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + if (in->mouse.buttons[id].down == down) return; + + btn = &in->mouse.buttons[id]; + btn->clicked_pos.x = (float)x; + btn->clicked_pos.y = (float)y; + btn->down = down; + btn->clicked++; +} + +NK_API void +nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val) +{ + NK_ASSERT(ctx); + if (!ctx) return; + ctx->input.mouse.scroll_delta.x += val.x; + ctx->input.mouse.scroll_delta.y += val.y; +} + +NK_API void +nk_input_glyph(struct nk_context *ctx, const nk_glyph glyph) +{ + int len = 0; + nk_rune unicode; + struct nk_input *in; + + NK_ASSERT(ctx); + if (!ctx) return; + in = &ctx->input; + + len = nk_utf_decode(glyph, &unicode, NK_UTF_SIZE); + if (len && ((in->keyboard.text_len + len) < NK_INPUT_MAX)) { + nk_utf_encode(unicode, &in->keyboard.text[in->keyboard.text_len], + NK_INPUT_MAX - in->keyboard.text_len); + in->keyboard.text_len += len; + } +} + +NK_API void +nk_input_char(struct nk_context *ctx, char c) +{ + nk_glyph glyph; + NK_ASSERT(ctx); + if (!ctx) return; + glyph[0] = c; + nk_input_glyph(ctx, glyph); +} + +NK_API void +nk_input_unicode(struct nk_context *ctx, nk_rune unicode) +{ + nk_glyph rune; + NK_ASSERT(ctx); + if (!ctx) return; + nk_utf_encode(unicode, rune, NK_UTF_SIZE); + nk_input_glyph(ctx, rune); +} + +NK_API int +nk_input_has_mouse_click(const struct nk_input *i, enum nk_buttons id) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (btn->clicked && btn->down == nk_false) ? nk_true : nk_false; +} + +NK_API int +nk_input_has_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + if (!NK_INBOX(btn->clicked_pos.x,btn->clicked_pos.y,b.x,b.y,b.w,b.h)) + return nk_false; + return nk_true; +} + +NK_API int +nk_input_has_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b, int down) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return nk_input_has_mouse_click_in_rect(i, id, b) && (btn->down == down); +} + +NK_API int +nk_input_is_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (nk_input_has_mouse_click_down_in_rect(i, id, b, nk_false) && + btn->clicked) ? nk_true : nk_false; +} + +NK_API int +nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, + struct nk_rect b, int down) +{ + const struct nk_mouse_button *btn; + if (!i) return nk_false; + btn = &i->mouse.buttons[id]; + return (nk_input_has_mouse_click_down_in_rect(i, id, b, down) && + btn->clicked) ? nk_true : nk_false; +} + +NK_API int +nk_input_any_mouse_click_in_rect(const struct nk_input *in, struct nk_rect b) +{ + int i, down = 0; + for (i = 0; i < NK_BUTTON_MAX; ++i) + down = down || nk_input_is_mouse_click_in_rect(in, (enum nk_buttons)i, b); + return down; +} + +NK_API int +nk_input_is_mouse_hovering_rect(const struct nk_input *i, struct nk_rect rect) +{ + if (!i) return nk_false; + return NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h); +} + +NK_API int +nk_input_is_mouse_prev_hovering_rect(const struct nk_input *i, struct nk_rect rect) +{ + if (!i) return nk_false; + return NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h); +} + +NK_API int +nk_input_mouse_clicked(const struct nk_input *i, enum nk_buttons id, struct nk_rect rect) +{ + if (!i) return nk_false; + if (!nk_input_is_mouse_hovering_rect(i, rect)) return nk_false; + return nk_input_is_mouse_click_in_rect(i, id, rect); +} + +NK_API int +nk_input_is_mouse_down(const struct nk_input *i, enum nk_buttons id) +{ + if (!i) return nk_false; + return i->mouse.buttons[id].down; +} + +NK_API int +nk_input_is_mouse_pressed(const struct nk_input *i, enum nk_buttons id) +{ + const struct nk_mouse_button *b; + if (!i) return nk_false; + b = &i->mouse.buttons[id]; + if (b->down && b->clicked) + return nk_true; + return nk_false; +} + +NK_API int +nk_input_is_mouse_released(const struct nk_input *i, enum nk_buttons id) +{ + if (!i) return nk_false; + return (!i->mouse.buttons[id].down && i->mouse.buttons[id].clicked); +} + +NK_API int +nk_input_is_key_pressed(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if ((k->down && k->clicked) || (!k->down && k->clicked >= 2)) + return nk_true; + return nk_false; +} + +NK_API int +nk_input_is_key_released(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if ((!k->down && k->clicked) || (k->down && k->clicked >= 2)) + return nk_true; + return nk_false; +} + +NK_API int +nk_input_is_key_down(const struct nk_input *i, enum nk_keys key) +{ + const struct nk_key *k; + if (!i) return nk_false; + k = &i->keyboard.keys[key]; + if (k->down) return nk_true; + return nk_false; +} + +/* + * ============================================================== + * + * TEXT EDITOR + * + * =============================================================== + */ +/* stb_textedit.h - v1.8 - public domain - Sean Barrett */ +struct nk_text_find { + float x,y; /* position of n'th character */ + float height; /* height of line */ + int first_char, length; /* first char of row, and length */ + int prev_first; /*_ first char of previous row */ +}; + +struct nk_text_edit_row { + float x0,x1; + /* starting x location, end x location (allows for align=right, etc) */ + float baseline_y_delta; + /* position of baseline relative to previous row's baseline*/ + float ymin,ymax; + /* height of row above and below baseline */ + int num_chars; +}; + +/* forward declarations */ +NK_INTERN void nk_textedit_makeundo_delete(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_insert(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_replace(struct nk_text_edit*, int, int, int); +#define NK_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) + +NK_INTERN float +nk_textedit_get_width(const struct nk_text_edit *edit, int line_start, int char_id, + const struct nk_user_font *font) +{ + int len = 0; + nk_rune unicode = 0; + const char *str = nk_str_at_const(&edit->string, line_start + char_id, &unicode, &len); + return font->width(font->userdata, font->height, str, len); +} + +NK_INTERN void +nk_textedit_layout_row(struct nk_text_edit_row *r, struct nk_text_edit *edit, + int line_start_id, float row_height, const struct nk_user_font *font) +{ + int l; + int glyphs = 0; + nk_rune unicode; + const char *remaining; + int len = nk_str_len_char(&edit->string); + const char *end = nk_str_get_const(&edit->string) + len; + const char *text = nk_str_at_const(&edit->string, line_start_id, &unicode, &l); + const struct nk_vec2 size = nk_text_calculate_text_bounds(font, + text, (int)(end - text), row_height, &remaining, 0, &glyphs, NK_STOP_ON_NEW_LINE); + + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = glyphs; +} + +NK_INTERN int +nk_textedit_locate_coord(struct nk_text_edit *edit, float x, float y, + const struct nk_user_font *font, float row_height) +{ + struct nk_text_edit_row r; + int n = edit->string.len; + float base_y = 0, prev_x; + int i=0, k; + + r.x0 = r.x1 = 0; + r.ymin = r.ymax = 0; + r.num_chars = 0; + + /* search rows to find one that straddles 'y' */ + while (i < n) { + nk_textedit_layout_row(&r, edit, i, row_height, font); + if (r.num_chars <= 0) + return n; + + if (i==0 && y < base_y + r.ymin) + return 0; + + if (y < base_y + r.ymax) + break; + + i += r.num_chars; + base_y += r.baseline_y_delta; + } + + /* below all text, return 'after' last character */ + if (i >= n) + return n; + + /* check if it's before the beginning of the line */ + if (x < r.x0) + return i; + + /* check if it's before the end of the line */ + if (x < r.x1) { + /* search characters in row for one that straddles 'x' */ + k = i; + prev_x = r.x0; + for (i=0; i < r.num_chars; ++i) { + float w = nk_textedit_get_width(edit, k, i, font); + if (x < prev_x+w) { + if (x < prev_x+w/2) + return k+i; + else return k+i+1; + } + prev_x += w; + } + /* shouldn't happen, but if it does, fall through to end-of-line case */ + } + + /* if the last character is a newline, return that. + * otherwise return 'after' the last character */ + if (nk_str_rune_at(&edit->string, i+r.num_chars-1) == '\n') + return i+r.num_chars-1; + else return i+r.num_chars; +} + +NK_INTERN void +nk_textedit_click(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API click: on mouse down, move the cursor to the clicked location, + * and reset the selection */ + state->cursor = nk_textedit_locate_coord(state, x, y, font, row_height); + state->select_start = state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; +} + +NK_INTERN void +nk_textedit_drag(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API drag: on mouse drag, move the cursor and selection endpoint + * to the clicked location */ + int p = nk_textedit_locate_coord(state, x, y, font, row_height); + if (state->select_start == state->select_end) + state->select_start = state->cursor; + state->cursor = state->select_end = p; +} + +NK_INTERN void +nk_textedit_find_charpos(struct nk_text_find *find, struct nk_text_edit *state, + int n, int single_line, const struct nk_user_font *font, float row_height) +{ + /* find the x/y location of a character, and remember info about the previous + * row in case we get a move-up event (for page up, we'll have to rescan) */ + struct nk_text_edit_row r; + int prev_start = 0; + int z = state->string.len; + int i=0, first; + + nk_zero_struct(r); + if (n == z) { + /* if it's at the end, then find the last line -- simpler than trying to + explicitly handle this case in the regular code */ + nk_textedit_layout_row(&r, state, 0, row_height, font); + if (single_line) { + find->first_char = 0; + find->length = z; + } else { + while (i < z) { + prev_start = i; + i += r.num_chars; + nk_textedit_layout_row(&r, state, i, row_height, font); + } + + find->first_char = i; + find->length = r.num_chars; + } + find->x = r.x1; + find->y = r.ymin; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + return; + } + + /* search rows to find the one that straddles character n */ + find->y = 0; + + for(;;) { + nk_textedit_layout_row(&r, state, i, row_height, font); + if (n < i + r.num_chars) break; + prev_start = i; + i += r.num_chars; + find->y += r.baseline_y_delta; + } + + find->first_char = first = i; + find->length = r.num_chars; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + + /* now scan to find xpos */ + find->x = r.x0; + for (i=0; first+i < n; ++i) + find->x += nk_textedit_get_width(state, first, i, font); +} + +NK_INTERN void +nk_textedit_clamp(struct nk_text_edit *state) +{ + /* make the selection/cursor state valid if client altered the string */ + int n = state->string.len; + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start > n) state->select_start = n; + if (state->select_end > n) state->select_end = n; + /* if clamping forced them to be equal, move the cursor to match */ + if (state->select_start == state->select_end) + state->cursor = state->select_start; + } + if (state->cursor > n) state->cursor = n; +} + +NK_API void +nk_textedit_delete(struct nk_text_edit *state, int where, int len) +{ + /* delete characters while updating undo */ + nk_textedit_makeundo_delete(state, where, len); + nk_str_delete_runes(&state->string, where, len); + state->has_preferred_x = 0; +} + +NK_API void +nk_textedit_delete_selection(struct nk_text_edit *state) +{ + /* delete the section */ + nk_textedit_clamp(state); + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start < state->select_end) { + nk_textedit_delete(state, state->select_start, + state->select_end - state->select_start); + state->select_end = state->cursor = state->select_start; + } else { + nk_textedit_delete(state, state->select_end, + state->select_start - state->select_end); + state->select_start = state->cursor = state->select_end; + } + state->has_preferred_x = 0; + } +} + +NK_INTERN void +nk_textedit_sortselection(struct nk_text_edit *state) +{ + /* canonicalize the selection so start <= end */ + if (state->select_end < state->select_start) { + int temp = state->select_end; + state->select_end = state->select_start; + state->select_start = temp; + } +} + +NK_INTERN void +nk_textedit_move_to_first(struct nk_text_edit *state) +{ + /* move cursor to first character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + state->cursor = state->select_start; + state->select_end = state->select_start; + state->has_preferred_x = 0; + } +} + +NK_INTERN void +nk_textedit_move_to_last(struct nk_text_edit *state) +{ + /* move cursor to last character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->select_start = state->select_end; + state->has_preferred_x = 0; + } +} + +NK_INTERN int +nk_is_word_boundary( struct nk_text_edit *state, int idx) +{ + int len; + nk_rune c; + if (idx <= 0) return 1; + if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1; + return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' || + c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || + c == '|'); +} + +NK_INTERN int +nk_textedit_move_to_word_previous(struct nk_text_edit *state) +{ + int c = state->cursor - 1; + while( c >= 0 && !nk_is_word_boundary(state, c)) + --c; + + if( c < 0 ) + c = 0; + + return c; +} + +NK_INTERN int +nk_textedit_move_to_word_next(struct nk_text_edit *state) +{ + const int len = state->string.len; + int c = state->cursor+1; + while( c < len && !nk_is_word_boundary(state, c)) + ++c; + + if( c > len ) + c = len; + + return c; +} + +NK_INTERN void +nk_textedit_prep_selection_at_cursor(struct nk_text_edit *state) +{ + /* update selection and cursor to match each other */ + if (!NK_TEXT_HAS_SELECTION(state)) + state->select_start = state->select_end = state->cursor; + else state->cursor = state->select_end; +} + +NK_API int +nk_textedit_cut(struct nk_text_edit *state) +{ + /* API cut: delete selection */ + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + return 0; + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_delete_selection(state); /* implicitly clamps */ + state->has_preferred_x = 0; + return 1; + } + return 0; +} + +NK_API int +nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len) +{ + /* API paste: replace existing selection with passed-in text */ + int glyphs; + const char *text = (const char *) ctext; + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) return 0; + + /* if there's a selection, the paste should delete it */ + nk_textedit_clamp(state); + nk_textedit_delete_selection(state); + + /* try to insert the characters */ + glyphs = nk_utf_len(ctext, len); + if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) { + nk_textedit_makeundo_insert(state, state->cursor, glyphs); + state->cursor += len; + state->has_preferred_x = 0; + return 1; + } + /* remove the undo since we didn't actually insert the characters */ + if (state->undo.undo_point) + --state->undo.undo_point; + return 0; +} + +NK_API void +nk_textedit_text(struct nk_text_edit *state, const char *text, int total_len) +{ + nk_rune unicode; + int glyph_len; + int text_len = 0; + + NK_ASSERT(state); + NK_ASSERT(text); + if (!text || !total_len || state->mode == NK_TEXT_EDIT_MODE_VIEW) return; + + glyph_len = nk_utf_decode(text, &unicode, total_len); + while ((text_len < total_len) && glyph_len) + { + /* don't insert a backward delete, just process the event */ + if (unicode == 127) goto next; + /* can't add newline in single-line mode */ + if (unicode == '\n' && state->single_line) goto next; + /* filter incoming text */ + if (state->filter && !state->filter(state, unicode)) goto next; + + if (!NK_TEXT_HAS_SELECTION(state) && + state->cursor < state->string.len) + { + if (state->mode == NK_TEXT_EDIT_MODE_REPLACE) { + nk_textedit_makeundo_replace(state, state->cursor, 1, 1); + nk_str_delete_runes(&state->string, state->cursor, 1); + } + if (nk_str_insert_text_utf8(&state->string, state->cursor, + text+text_len, 1)) + { + ++state->cursor; + state->has_preferred_x = 0; + } + } else { + nk_textedit_delete_selection(state); /* implicitly clamps */ + if (nk_str_insert_text_utf8(&state->string, state->cursor, + text+text_len, 1)) + { + nk_textedit_makeundo_insert(state, state->cursor, 1); + ++state->cursor; + state->has_preferred_x = 0; + } + } + next: + text_len += glyph_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len); + } +} + +NK_INTERN void +nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, + const struct nk_user_font *font, float row_height) +{ +retry: + switch (key) + { + case NK_KEY_NONE: + case NK_KEY_CTRL: + case NK_KEY_ENTER: + case NK_KEY_SHIFT: + case NK_KEY_TAB: + case NK_KEY_COPY: + case NK_KEY_CUT: + case NK_KEY_PASTE: + case NK_KEY_MAX: + default: break; + case NK_KEY_TEXT_UNDO: + nk_textedit_undo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_REDO: + nk_textedit_redo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_SELECT_ALL: + nk_textedit_select_all(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_INSERT_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + state->mode = NK_TEXT_EDIT_MODE_INSERT; + break; + case NK_KEY_TEXT_REPLACE_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + state->mode = NK_TEXT_EDIT_MODE_REPLACE; + break; + case NK_KEY_TEXT_RESET_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_INSERT || + state->mode == NK_TEXT_EDIT_MODE_REPLACE) + state->mode = NK_TEXT_EDIT_MODE_VIEW; + break; + + case NK_KEY_LEFT: + if (shift_mod) { + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + /* move selection left */ + if (state->select_end > 0) + --state->select_end; + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to start of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else if (state->cursor > 0) + --state->cursor; + state->has_preferred_x = 0; + } break; + + case NK_KEY_RIGHT: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + /* move selection right */ + ++state->select_end; + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to end of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else ++state->cursor; + nk_textedit_clamp(state); + state->has_preferred_x = 0; + } break; + + case NK_KEY_TEXT_WORD_LEFT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_previous(state); + state->select_end = state->cursor; + nk_textedit_clamp(state ); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else { + state->cursor = nk_textedit_move_to_word_previous(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_TEXT_WORD_RIGHT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_next(state); + state->select_end = state->cursor; + nk_textedit_clamp(state); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else { + state->cursor = nk_textedit_move_to_word_next(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_DOWN: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down in single-line behave like left&right */ + key = NK_KEY_RIGHT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* now find character position down a row */ + if (find.length) + { + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + int start = find.first_char + find.length; + + state->cursor = start; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars && x < row.x1; ++i) { + float dx = nk_textedit_get_width(state, start, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) + state->select_end = state->cursor; + } + } break; + + case NK_KEY_UP: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down become left&right */ + key = NK_KEY_LEFT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* can only go up if there's a previous row */ + if (find.prev_first != find.first_char) { + /* now find character position up a row */ + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + + state->cursor = find.prev_first; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars && x < row.x1; ++i) { + float dx = nk_textedit_get_width(state, find.prev_first, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) state->select_end = state->cursor; + } + } break; + + case NK_KEY_DEL: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + break; + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + int n = state->string.len; + if (state->cursor < n) + nk_textedit_delete(state, state->cursor, 1); + } + state->has_preferred_x = 0; + break; + + case NK_KEY_BACKSPACE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + break; + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + nk_textedit_clamp(state); + if (state->cursor > 0) { + nk_textedit_delete(state, state->cursor-1, 1); + --state->cursor; + } + } + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_START: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = 0; + state->has_preferred_x = 0; + } else { + state->cursor = state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_END: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = state->string.len; + state->has_preferred_x = 0; + } else { + state->cursor = state->string.len; + state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_LINE_START: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + if (state->string.len && state->cursor == state->string.len) + --state->cursor; + nk_textedit_find_charpos(&find, state,state->cursor, state->single_line, + font, row_height); + state->cursor = state->select_end = find.first_char; + state->has_preferred_x = 0; + } else { + struct nk_text_find find; + if (state->string.len && state->cursor == state->string.len) + --state->cursor; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->cursor = find.first_char; + state->has_preferred_x = 0; + } + } break; + + case NK_KEY_TEXT_LINE_END: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + state->select_end = state->cursor; + } else { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + }} break; + } +} + +NK_INTERN void +nk_textedit_flush_redo(struct nk_text_undo_state *state) +{ + state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; +} + +NK_INTERN void +nk_textedit_discard_undo(struct nk_text_undo_state *state) +{ + /* discard the oldest entry in the undo list */ + if (state->undo_point > 0) { + /* if the 0th undo state has characters, clean those up */ + if (state->undo_rec[0].char_storage >= 0) { + int n = state->undo_rec[0].insert_length, i; + /* delete n characters from all other records */ + state->undo_char_point = (short)(state->undo_char_point - n); + NK_MEMCPY(state->undo_char, state->undo_char + n, + (nk_size)state->undo_char_point*sizeof(nk_rune)); + for (i=0; i < state->undo_point; ++i) { + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage - n); + } + } + --state->undo_point; + NK_MEMCPY(state->undo_rec, state->undo_rec+1, + (nk_size)((nk_size)state->undo_point * sizeof(state->undo_rec[0]))); + } +} + +NK_INTERN void +nk_textedit_discard_redo(struct nk_text_undo_state *state) +{ +/* discard the oldest entry in the redo list--it's bad if this + ever happens, but because undo & redo have to store the actual + characters in different cases, the redo character buffer can + fill up even though the undo buffer didn't */ + nk_size num; + int k = NK_TEXTEDIT_UNDOSTATECOUNT-1; + if (state->redo_point <= k) { + /* if the k'th undo state has characters, clean those up */ + if (state->undo_rec[k].char_storage >= 0) { + int n = state->undo_rec[k].insert_length, i; + /* delete n characters from all other records */ + state->redo_char_point = (short)(state->redo_char_point + n); + num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point); + NK_MEMCPY(state->undo_char + state->redo_char_point, + state->undo_char + state->redo_char_point-n, num * sizeof(char)); + for (i = state->redo_point; i < k; ++i) { + if (state->undo_rec[i].char_storage >= 0) { + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage + n); + } + } + } + ++state->redo_point; + num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point); + if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1, + state->undo_rec + state->redo_point, num * sizeof(state->undo_rec[0])); + } +} + +NK_INTERN struct nk_text_undo_record* +nk_textedit_create_undo_record(struct nk_text_undo_state *state, int numchars) +{ + /* any time we create a new undo record, we discard redo*/ + nk_textedit_flush_redo(state); + + /* if we have no free records, we have to make room, + * by sliding the existing records down */ + if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + nk_textedit_discard_undo(state); + + /* if the characters to store won't possibly fit in the buffer, + * we can't undo */ + if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) { + state->undo_point = 0; + state->undo_char_point = 0; + return 0; + } + + /* if we don't have enough free characters in the buffer, + * we have to make room */ + while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT) + nk_textedit_discard_undo(state); + return &state->undo_rec[state->undo_point++]; +} + +NK_INTERN nk_rune* +nk_textedit_createundo(struct nk_text_undo_state *state, int pos, + int insert_len, int delete_len) +{ + struct nk_text_undo_record *r = nk_textedit_create_undo_record(state, insert_len); + if (r == 0) + return 0; + + r->where = pos; + r->insert_length = (short) insert_len; + r->delete_length = (short) delete_len; + + if (insert_len == 0) { + r->char_storage = -1; + return 0; + } else { + r->char_storage = state->undo_char_point; + state->undo_char_point = (short)(state->undo_char_point + insert_len); + return &state->undo_char[r->char_storage]; + } +} + +NK_API void +nk_textedit_undo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record u, *r; + if (s->undo_point == 0) + return; + + /* we need to do two things: apply the undo record, and create a redo record */ + u = s->undo_rec[s->undo_point-1]; + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = -1; + + r->insert_length = u.delete_length; + r->delete_length = u.insert_length; + r->where = u.where; + + if (u.delete_length) + { + /* if the undo record says to delete characters, then the redo record will + need to re-insert the characters that get deleted, so we need to store + them. + there are three cases: + - there's enough room to store the characters + - characters stored for *redoing* don't leave room for redo + - characters stored for *undoing* don't leave room for redo + if the last is true, we have to bail */ + if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) { + /* the undo records take up too much character space; there's no space + * to store the redo characters */ + r->insert_length = 0; + } else { + int i; + /* there's definitely room to store the characters eventually */ + while (s->undo_char_point + u.delete_length > s->redo_char_point) { + /* there's currently not enough room, so discard a redo record */ + nk_textedit_discard_redo(s); + /* should never happen: */ + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + } + + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = (short)(s->redo_char_point - u.delete_length); + s->redo_char_point = (short)(s->redo_char_point - u.delete_length); + + /* now save the characters */ + for (i=0; i < u.delete_length; ++i) + s->undo_char[r->char_storage + i] = + nk_str_rune_at(&state->string, u.where + i); + } + /* now we can carry out the deletion */ + nk_str_delete_runes(&state->string, u.where, u.delete_length); + } + + /* check type of recorded action: */ + if (u.insert_length) { + /* easy case: was a deletion, so we need to insert n characters */ + nk_str_insert_text_runes(&state->string, u.where, + &s->undo_char[u.char_storage], u.insert_length); + s->undo_char_point = (short)(s->undo_char_point - u.insert_length); + } + state->cursor = (short)(u.where + u.insert_length); + + s->undo_point--; + s->redo_point--; +} + +NK_API void +nk_textedit_redo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record *u, r; + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + + /* we need to do two things: apply the redo record, and create an undo record */ + u = &s->undo_rec[s->undo_point]; + r = s->undo_rec[s->redo_point]; + + /* we KNOW there must be room for the undo record, because the redo record + was derived from an undo record */ + u->delete_length = r.insert_length; + u->insert_length = r.delete_length; + u->where = r.where; + u->char_storage = -1; + + if (r.delete_length) { + /* the redo record requires us to delete characters, so the undo record + needs to store the characters */ + if (s->undo_char_point + u->insert_length > s->redo_char_point) { + u->insert_length = 0; + u->delete_length = 0; + } else { + int i; + u->char_storage = s->undo_char_point; + s->undo_char_point = (short)(s->undo_char_point + u->insert_length); + + /* now save the characters */ + for (i=0; i < u->insert_length; ++i) { + s->undo_char[u->char_storage + i] = + nk_str_rune_at(&state->string, u->where + i); + } + } + nk_str_delete_runes(&state->string, r.where, r.delete_length); + } + + if (r.insert_length) { + /* easy case: need to insert n characters */ + nk_str_insert_text_runes(&state->string, r.where, + &s->undo_char[r.char_storage], r.insert_length); + } + state->cursor = r.where + r.insert_length; + + s->undo_point++; + s->redo_point++; +} + +NK_INTERN void +nk_textedit_makeundo_insert(struct nk_text_edit *state, int where, int length) +{ + nk_textedit_createundo(&state->undo, where, 0, length); +} + +NK_INTERN void +nk_textedit_makeundo_delete(struct nk_text_edit *state, int where, int length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0); + if (p) { + for (i=0; i < length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} + +NK_INTERN void +nk_textedit_makeundo_replace(struct nk_text_edit *state, int where, + int old_length, int new_length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length); + if (p) { + for (i=0; i < old_length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} + +NK_INTERN void +nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, + nk_plugin_filter filter) +{ + /* reset the state to default */ + state->undo.undo_point = 0; + state->undo.undo_char_point = 0; + state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; + state->select_end = state->select_start = 0; + state->cursor = 0; + state->has_preferred_x = 0; + state->preferred_x = 0; + state->cursor_at_end_of_line = 0; + state->initialized = 1; + state->single_line = (unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE); + state->mode = NK_TEXT_EDIT_MODE_VIEW; + state->filter = filter; + state->scrollbar = nk_vec2(0,0); +} + +NK_API void +nk_textedit_init_fixed(struct nk_text_edit *state, void *memory, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(memory); + if (!state || !memory || !size) return; + NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_fixed(&state->string, memory, size); +} + +NK_API void +nk_textedit_init(struct nk_text_edit *state, struct nk_allocator *alloc, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(alloc); + if (!state || !alloc) return; + NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init(&state->string, alloc, size); +} + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_textedit_init_default(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_default(&state->string); +} +#endif + +NK_API void +nk_textedit_select_all(struct nk_text_edit *state) +{ + NK_ASSERT(state); + state->select_start = 0; + state->select_end = state->string.len; +} + +NK_API void +nk_textedit_free(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + nk_str_free(&state->string); +} + +/* =============================================================== + * + * TEXT WIDGET + * + * ===============================================================*/ +#define nk_widget_state_reset(s)\ + if ((*(s)) & NK_WIDGET_STATE_MODIFIED)\ + (*(s)) = NK_WIDGET_STATE_INACTIVE|NK_WIDGET_STATE_MODIFIED;\ + else (*(s)) = NK_WIDGET_STATE_INACTIVE; + +struct nk_text { + struct nk_vec2 padding; + struct nk_color background; + struct nk_color text; +}; + +NK_INTERN void +nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + nk_flags a, const struct nk_user_font *f) +{ + struct nk_rect label; + float text_width; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + b.h = NK_MAX(b.h, 2 * t->padding.y); + label.x = 0; label.w = 0; + label.y = b.y + t->padding.y; + label.h = NK_MIN(f->height, b.h - 2 * t->padding.y); + + text_width = f->width(f->userdata, f->height, (const char*)string, len); + text_width += (2.0f * t->padding.x); + + /* align in x-axis */ + if (a & NK_TEXT_ALIGN_LEFT) { + label.x = b.x + t->padding.x; + label.w = NK_MAX(0, b.w - 2 * t->padding.x); + } else if (a & NK_TEXT_ALIGN_CENTERED) { + label.w = NK_MAX(1, 2 * t->padding.x + (float)text_width); + label.x = (b.x + t->padding.x + ((b.w - 2 * t->padding.x) - label.w) / 2); + label.x = NK_MAX(b.x + t->padding.x, label.x); + label.w = NK_MIN(b.x + b.w, label.x + label.w); + if (label.w >= label.x) label.w -= label.x; + } else if (a & NK_TEXT_ALIGN_RIGHT) { + label.x = NK_MAX(b.x + t->padding.x, (b.x + b.w) - (2 * t->padding.x + (float)text_width)); + label.w = (float)text_width + 2 * t->padding.x; + } else return; + + /* align in y-axis */ + if (a & NK_TEXT_ALIGN_MIDDLE) { + label.y = b.y + b.h/2.0f - (float)f->height/2.0f; + label.h = NK_MAX(b.h/2.0f, b.h - (b.h/2.0f + f->height/2.0f)); + } else if (a & NK_TEXT_ALIGN_BOTTOM) { + label.y = b.y + b.h - f->height; + label.h = f->height; + } + nk_draw_text(o, label, (const char*)string, + len, f, t->background, t->text); +} + +NK_INTERN void +nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + const struct nk_user_font *f) +{ + float width; + int glyphs = 0; + int fitting = 0; + int done = 0; + struct nk_rect line; + struct nk_text text; + NK_INTERN nk_rune seperator[] = {' '}; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + text.padding = nk_vec2(0,0); + text.background = t->background; + text.text = t->text; + + b.w = NK_MAX(b.w, 2 * t->padding.x); + b.h = NK_MAX(b.h, 2 * t->padding.y); + b.h = b.h - 2 * t->padding.y; + + line.x = b.x + t->padding.x; + line.y = b.y + t->padding.y; + line.w = b.w - 2 * t->padding.x; + line.h = 2 * t->padding.y + f->height; + + fitting = nk_text_clamp(f, string, len, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); + while (done < len) { + if (!fitting || line.y + line.h >= (b.y + b.h)) break; + nk_widget_text(o, line, &string[done], fitting, &text, NK_TEXT_LEFT, f); + done += fitting; + line.y += f->height + 2 * t->padding.y; + fitting = nk_text_clamp(f, &string[done], len - done, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); + } +} + +/* =============================================================== + * + * BUTTON + * + * ===============================================================*/ +NK_INTERN void +nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, + struct nk_rect content, struct nk_color background, struct nk_color foreground, + float border_width, const struct nk_user_font *font) +{ + switch (type) { + case NK_SYMBOL_X: + case NK_SYMBOL_UNDERSCORE: + case NK_SYMBOL_PLUS: + case NK_SYMBOL_MINUS: { + /* single character text symbol */ + const char *X = (type == NK_SYMBOL_X) ? "x": + (type == NK_SYMBOL_UNDERSCORE) ? "_": + (type == NK_SYMBOL_PLUS) ? "+": "-"; + struct nk_text text; + text.padding = nk_vec2(0,0); + text.background = background; + text.text = foreground; + nk_widget_text(out, content, X, 1, &text, NK_TEXT_CENTERED, font); + } break; + case NK_SYMBOL_CIRCLE_SOLID: + case NK_SYMBOL_CIRCLE_OUTLINE: + case NK_SYMBOL_RECT_SOLID: + case NK_SYMBOL_RECT_OUTLINE: { + /* simple empty/filled shapes */ + if (type == NK_SYMBOL_RECT_SOLID || type == NK_SYMBOL_RECT_OUTLINE) { + nk_fill_rect(out, content, 0, foreground); + if (type == NK_SYMBOL_RECT_OUTLINE) + nk_fill_rect(out, nk_shrink_rect(content, border_width), 0, background); + } else { + nk_fill_circle(out, content, foreground); + if (type == NK_SYMBOL_CIRCLE_OUTLINE) + nk_fill_circle(out, nk_shrink_rect(content, 1), background); + } + } break; + case NK_SYMBOL_TRIANGLE_UP: + case NK_SYMBOL_TRIANGLE_DOWN: + case NK_SYMBOL_TRIANGLE_LEFT: + case NK_SYMBOL_TRIANGLE_RIGHT: { + enum nk_heading heading; + struct nk_vec2 points[3]; + heading = (type == NK_SYMBOL_TRIANGLE_RIGHT) ? NK_RIGHT : + (type == NK_SYMBOL_TRIANGLE_LEFT) ? NK_LEFT: + (type == NK_SYMBOL_TRIANGLE_UP) ? NK_UP: NK_DOWN; + nk_triangle_from_direction(points, content, 0, 0, heading); + nk_fill_triangle(out, points[0].x, points[0].y, points[1].x, points[1].y, + points[2].x, points[2].y, foreground); + } break; + default: + case NK_SYMBOL_NONE: + case NK_SYMBOL_MAX: break; + } +} + +NK_INTERN int +nk_button_behavior(nk_flags *state, struct nk_rect r, + const struct nk_input *i, enum nk_button_behavior behavior) +{ + int ret = 0; + nk_widget_state_reset(state); + if (!i) return 0; + if (nk_input_is_mouse_hovering_rect(i, r)) { + *state = NK_WIDGET_STATE_HOVERED; + if (nk_input_is_mouse_down(i, NK_BUTTON_LEFT)) + *state = NK_WIDGET_STATE_ACTIVE; + if (nk_input_has_mouse_click_in_rect(i, NK_BUTTON_LEFT, r)) { + ret = (behavior != NK_BUTTON_DEFAULT) ? + nk_input_is_mouse_down(i, NK_BUTTON_LEFT): +#ifdef NK_BUTTON_TRIGGER_ON_RELEASE + nk_input_is_mouse_released(i, NK_BUTTON_LEFT); +#else + nk_input_is_mouse_pressed(i, NK_BUTTON_LEFT); +#endif + } + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_LEFT; + return ret; +} + +NK_INTERN const struct nk_style_item* +nk_draw_button(struct nk_command_buffer *out, + const struct nk_rect *bounds, nk_flags state, + const struct nk_style_button *style) +{ + const struct nk_style_item *background; + if (state & NK_WIDGET_STATE_HOVER) + background = &style->hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + background = &style->active; + else background = &style->normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } + return background; +} + +NK_INTERN int +nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, + const struct nk_style_button *style, const struct nk_input *in, + enum nk_button_behavior behavior, struct nk_rect *content) +{ + struct nk_rect bounds; + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(out); + if (!out || !style) + return nk_false; + + /* calculate button content space */ + content->x = r.x + style->padding.x + style->border + style->rounding; + content->y = r.y + style->padding.y + style->border + style->rounding; + content->w = r.w - (2 * style->padding.x + style->border + style->rounding*2); + content->h = r.h - (2 * style->padding.y + style->border + style->rounding*2); + + /* execute button behavior */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + return nk_button_behavior(state, bounds, in, behavior); +} + +NK_INTERN void +nk_draw_button_text(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, + const struct nk_style_button *style, const char *txt, int len, + nk_flags text_alignment, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors/images */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVER) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *content, txt, len, &text, text_alignment, font); +} + +NK_INTERN int +nk_do_button_text(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + const char *string, int len, nk_flags align, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect content; + int ret = nk_false; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(string); + NK_ASSERT(font); + if (!out || !style || !font || !string) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text(out, &bounds, &content, *state, style, string, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, + enum nk_symbol_type type, const struct nk_user_font *font) +{ + struct nk_color sym, bg; + const struct nk_style_item *background; + + /* select correct colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + bg = background->data.color; + else bg = style->text_background; + + if (state & NK_WIDGET_STATE_HOVER) + sym = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + sym = style->text_active; + else sym = style->text_normal; + nk_draw_symbol(out, type, *content, bg, sym, 1, font); +} + +NK_INTERN int +nk_do_button_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !style || !font || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_symbol(out, &bounds, &content, *state, style, symbol, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, const struct nk_image *img) +{ + nk_draw_button(out, bounds, state, style); + nk_draw_image(out, *content, img, nk_white); +} + +NK_INTERN int +nk_do_button_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, enum nk_button_behavior b, + const struct nk_style_button *style, const struct nk_input *in) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, b, &content); + content.x += style->image_padding.x; + content.y += style->image_padding.y; + content.w -= 2 * style->image_padding.x; + content.h -= 2 * style->image_padding.y; + + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_image(out, &bounds, &content, *state, style, &img); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_text_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, + const char *str, int len, enum nk_symbol_type type, + const struct nk_user_font *font) +{ + struct nk_color sym; + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + + /* select correct text colors */ + if (state & NK_WIDGET_STATE_HOVER) { + sym = style->text_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + sym = style->text_active; + text.text = style->text_active; + } else { + sym = style->text_normal; + text.text = style->text_normal; + } + + text.padding = nk_vec2(0,0); + nk_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); +} + +NK_INTERN int +nk_do_button_text_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, const char *str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect tri = {0,0,0,0}; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + tri.y = content.y + (content.h/2) - font->height/2; + tri.w = font->height; tri.h = font->height; + if (align & NK_TEXT_ALIGN_LEFT) { + tri.x = (content.x + content.w) - (2 * style->padding.x + tri.w); + tri.x = NK_MAX(tri.x, 0); + } else tri.x = content.x + 2 * style->padding.x; + + /* draw button */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text_symbol(out, &bounds, &content, &tri, + *state, style, str, len, symbol, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +NK_INTERN void +nk_draw_button_text_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, + const char *str, int len, const struct nk_user_font *font, + const struct nk_image *img) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVER) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); + nk_draw_image(out, *image, img, nk_white); +} + +NK_INTERN int +nk_do_button_text_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, const char* str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect icon; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !font || !style || !str) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text_image(out, &bounds, &content, &icon, *state, style, str, len, font, &img); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} + +/* =============================================================== + * + * TOGGLE + * + * ===============================================================*/ +enum nk_toggle_type { + NK_TOGGLE_CHECK, + NK_TOGGLE_OPTION +}; + +NK_INTERN int +nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, + nk_flags *state, int active) +{ + nk_widget_state_reset(state); + if (nk_button_behavior(state, select, in, NK_BUTTON_DEFAULT)) { + *state = NK_WIDGET_STATE_ACTIVE; + active = !active; + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_LEFT; + return active; +} + +NK_INTERN void +nk_draw_checkbox(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; + + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *selector, 0, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*selector, style->border), 0, background->data.color); + } else nk_draw_image(out, *selector, &background->data.image, nk_white); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image, nk_white); + else nk_fill_rect(out, *cursors, 0, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} + +NK_INTERN void +nk_draw_option(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; + + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_circle(out, *selector, style->border_color); + nk_fill_circle(out, nk_shrink_rect(*selector, style->border), background->data.color); + } else nk_draw_image(out, *selector, &background->data.image, nk_white); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image, nk_white); + else nk_fill_circle(out, *cursors, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} + +NK_INTERN int +nk_do_toggle(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect r, + int *active, const char *str, int len, enum nk_toggle_type type, + const struct nk_style_toggle *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int was_active; + struct nk_rect bounds; + struct nk_rect select; + struct nk_rect cursor; + struct nk_rect label; + + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font || !active) + return 0; + + r.w = NK_MAX(r.w, font->height + 2 * style->padding.x); + r.h = NK_MAX(r.h, font->height + 2 * style->padding.y); + + /* add additional touch padding for touch screen devices */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + + /* calculate the selector space */ + select.w = font->height; + select.h = select.w; + select.y = r.y + r.h/2.0f - select.h/2.0f; + select.x = r.x; + + /* calculate the bounds of the cursor inside the selector */ + cursor.x = select.x + style->padding.x + style->border; + cursor.y = select.y + style->padding.y + style->border; + cursor.w = select.w - (2 * style->padding.x + 2 * style->border); + cursor.h = select.h - (2 * style->padding.y + 2 * style->border); + + /* label behind the selector */ + label.x = select.x + select.w + style->spacing; + label.y = select.y; + label.w = NK_MAX(r.x + r.w, label.x) - label.x; + label.h = select.w; + + /* update selector */ + was_active = *active; + *active = nk_toggle_behavior(in, bounds, state, *active); + + /* draw selector */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (type == NK_TOGGLE_CHECK) { + nk_draw_checkbox(out, *state, style, *active, &label, &select, &cursor, str, len, font); + } else { + nk_draw_option(out, *state, style, *active, &label, &select, &cursor, str, len, font); + } + if (style->draw_end) + style->draw_end(out, style->userdata); + return (was_active != *active); +} + +/* =============================================================== + * + * SELECTABLE + * + * ===============================================================*/ +NK_INTERN void +nk_draw_selectable(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_selectable *style, int active, + const struct nk_rect *bounds, const struct nk_rect *icon, const struct nk_image *img, + const char *string, int len, nk_flags align, const struct nk_user_font *font) +{ + const struct nk_style_item *background; + struct nk_text text; + text.padding = style->padding; + + /* select correct colors/images */ + if (!active) { + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->pressed; + text.text = style->text_pressed; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text.text = style->text_hover; + } else { + background = &style->normal; + text.text = style->text_normal; + } + } else { + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->pressed_active; + text.text = style->text_pressed_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover_active; + text.text = style->text_hover_active; + } else { + background = &style->normal_active; + text.text = style->text_normal_active; + } + } + + + /* draw selectable background and text */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + text.background = background->data.color; + } + if (img && icon) nk_draw_image(out, *icon, img, nk_white); + nk_widget_text(out, *bounds, string, len, &text, align, font); +} + +NK_INTERN int +nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, + const struct nk_style_selectable *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* remove padding */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + + /* update button */ + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, 0,0, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} + +NK_INTERN int +nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, + const struct nk_image *img, const struct nk_style_selectable *style, + const struct nk_input *in, const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + struct nk_rect icon; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* toggle behavior */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, &icon, img, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} + + +/* =============================================================== + * + * SLIDER + * + * ===============================================================*/ +NK_INTERN float +nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor, + struct nk_rect *visual_cursor, struct nk_input *in, + struct nk_rect bounds, float slider_min, float slider_max, float slider_value, + float slider_step, float slider_steps) +{ + int left_mouse_down; + int left_mouse_click_in_cursor; + + /* check if visual cursor is being dragged */ + nk_widget_state_reset(state); + left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, *visual_cursor, nk_true); + + if (left_mouse_down && left_mouse_click_in_cursor) + { + float ratio = 0; + const float d = in->mouse.pos.x - (visual_cursor->x+visual_cursor->w*0.5f); + const float pxstep = bounds.w / slider_steps; + + /* only update value if the next slider step is reached */ + *state = NK_WIDGET_STATE_ACTIVE; + if (NK_ABS(d) >= pxstep) { + const float steps = (float)((int)(NK_ABS(d) / pxstep)); + slider_value += (d > 0) ? (slider_step*steps) : -(slider_step*steps); + slider_value = NK_CLAMP(slider_min, slider_value, slider_max); + ratio = (slider_value - slider_min)/slider_step; + logical_cursor->x = bounds.x + (logical_cursor->w * ratio); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = logical_cursor->x; + } + } + + /* slider widget state */ + if (nk_input_is_mouse_hovering_rect(in, bounds)) + *state = NK_WIDGET_STATE_HOVERED; + if (*state & NK_WIDGET_STATE_HOVER && + !nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return slider_value; +} + +NK_INTERN void +nk_draw_slider(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_slider *style, const struct nk_rect *bounds, + const struct nk_rect *visual_cursor, float min, float value, float max) +{ + struct nk_rect fill; + struct nk_rect bar; + const struct nk_style_item *background; + + /* select correct slider images/colors */ + struct nk_color bar_color; + const struct nk_style_item *cursor; + + NK_UNUSED(min); + NK_UNUSED(max); + NK_UNUSED(value); + + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + bar_color = style->bar_active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + bar_color = style->bar_hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + bar_color = style->bar_normal; + cursor = &style->cursor_normal; + } + + /* calculate slider background bar */ + bar.x = bounds->x; + bar.y = (visual_cursor->y + visual_cursor->h/2) - bounds->h/12; + bar.w = bounds->w; + bar.h = bounds->h/6; + + /* filled background bar style */ + fill.w = (visual_cursor->x + (visual_cursor->w/2.0f)) - bar.x; + fill.x = bar.x; + fill.y = bar.y; + fill.h = bar.h; + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } + + /* draw slider bar */ + nk_fill_rect(out, bar, style->rounding, bar_color); + nk_fill_rect(out, fill, style->rounding, style->bar_filled); + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *visual_cursor, &cursor->data.image, nk_white); + else nk_fill_circle(out, *visual_cursor, cursor->data.color); +} + +NK_INTERN float +nk_do_slider(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + float min, float val, float max, float step, + const struct nk_style_slider *style, struct nk_input *in, + const struct nk_user_font *font) +{ + float slider_range; + float slider_min; + float slider_max; + float slider_value; + float slider_steps; + float cursor_offset; + + struct nk_rect visual_cursor; + struct nk_rect logical_cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) + return 0; + + /* remove padding from slider bounds */ + bounds.x = bounds.x + style->padding.x; + bounds.y = bounds.y + style->padding.y; + bounds.h = NK_MAX(bounds.h, 2*style->padding.y); + bounds.w = NK_MAX(bounds.w, 2*style->padding.x + style->cursor_size.x); + bounds.w -= 2 * style->padding.x; + bounds.h -= 2 * style->padding.y; + + /* optional buttons */ + if (style->show_buttons) { + nk_flags ws; + struct nk_rect button; + button.y = bounds.y; + button.w = bounds.h; + button.h = bounds.h; + + /* decrement button */ + button.x = bounds.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_DEFAULT, + &style->dec_button, in, font)) + val -= step; + + /* increment button */ + button.x = (bounds.x + bounds.w) - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_DEFAULT, + &style->inc_button, in, font)) + val += step; + + bounds.x = bounds.x + button.w + style->spacing.x; + bounds.w = bounds.w - (2*button.w + 2*style->spacing.x); + } + + /* remove one cursor size to support visual cursor */ + bounds.x += style->cursor_size.x*0.5f; + bounds.w -= style->cursor_size.x; + + /* make sure the provided values are correct */ + slider_max = NK_MAX(min, max); + slider_min = NK_MIN(min, max); + slider_value = NK_CLAMP(slider_min, val, slider_max); + slider_range = slider_max - slider_min; + slider_steps = slider_range / step; + cursor_offset = (slider_value - slider_min) / step; + + /* calculate cursor + Basically you have two cursors. One for visual representation and interaction + and one for updating the actual cursor value. */ + logical_cursor.h = bounds.h; + logical_cursor.w = bounds.w / slider_steps; + logical_cursor.x = bounds.x + (logical_cursor.w * cursor_offset); + logical_cursor.y = bounds.y; + + visual_cursor.h = style->cursor_size.y; + visual_cursor.w = style->cursor_size.x; + visual_cursor.y = (bounds.y + bounds.h*0.5f) - visual_cursor.h*0.5f; + visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; + + slider_value = nk_slider_behavior(state, &logical_cursor, &visual_cursor, + in, bounds, slider_min, slider_max, slider_value, step, slider_steps); + visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; + + /* draw slider */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_slider(out, *state, style, &bounds, &visual_cursor, slider_min, slider_value, slider_max); + if (style->draw_end) style->draw_end(out, style->userdata); + return slider_value; +} + +/* =============================================================== + * + * PROGRESSBAR + * + * ===============================================================*/ +NK_INTERN nk_size +nk_progress_behavior(nk_flags *state, const struct nk_input *in, + struct nk_rect r, nk_size max, nk_size value, int modifiable) +{ + nk_widget_state_reset(state); + if (in && modifiable && nk_input_is_mouse_hovering_rect(in, r)) { + int left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, r, nk_true); + + if (left_mouse_down && left_mouse_click_in_cursor) { + float ratio = NK_MAX(0, (float)(in->mouse.pos.x - r.x)) / (float)r.w; + value = (nk_size)NK_MAX(0,((float)max * ratio)); + *state = NK_WIDGET_STATE_ACTIVE; + } else *state = NK_WIDGET_STATE_HOVERED; + } + + /* set progressbar widget state */ + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_LEFT; + + if (!max) return value; + value = NK_MIN(value, max); + return value; +} + +NK_INTERN void +nk_draw_progress(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_progress *style, const struct nk_rect *bounds, + const struct nk_rect *scursor, nk_size value, nk_size max) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + NK_UNUSED(max); + NK_UNUSED(value); + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER){ + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } else nk_draw_image(out, *bounds, &background->data.image, nk_white); + + /* draw cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *scursor, style->rounding, cursor->data.color); + nk_stroke_rect(out, *scursor, style->rounding, style->border, style->border_color); + } else nk_draw_image(out, *scursor, &cursor->data.image, nk_white); +} + +NK_INTERN nk_size +nk_do_progress(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + nk_size value, nk_size max, int modifiable, + const struct nk_style_progress *style, const struct nk_input *in) +{ + float prog_scale; + nk_size prog_value; + struct nk_rect cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) return 0; + + /* calculate progressbar cursor */ + cursor.w = NK_MAX(bounds.w, 2 * style->padding.x + 2 * style->border); + cursor.h = NK_MAX(bounds.h, 2 * style->padding.y + 2 * style->border); + cursor = nk_pad_rect(bounds, nk_vec2(style->padding.x + style->border, style->padding.y + style->border)); + prog_scale = (float)value / (float)max; + cursor.w = (bounds.w - 2) * prog_scale; + + /* update progressbar */ + prog_value = NK_MIN(value, max); + prog_value = nk_progress_behavior(state, in, bounds, max, prog_value, modifiable); + + /* draw progressbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_progress(out, *state, style, &bounds, &cursor, value, max); + if (style->draw_end) style->draw_end(out, style->userdata); + return prog_value; +} + +/* =============================================================== + * + * SCROLLBAR + * + * ===============================================================*/ +NK_INTERN float +nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, + int has_scrolling, const struct nk_rect *scroll, + const struct nk_rect *cursor, const struct nk_rect *empty0, + const struct nk_rect *empty1, float scroll_offset, + float target, float scroll_step, enum nk_orientation o) +{ + nk_flags ws = 0; + int left_mouse_down; + int left_mouse_click_in_cursor; + float scroll_delta; + + nk_widget_state_reset(state); + if (!in) return scroll_offset; + + left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, *cursor, nk_true); + if (nk_input_is_mouse_hovering_rect(in, *scroll)) + *state = NK_WIDGET_STATE_HOVERED; + + scroll_delta = (o == NK_VERTICAL) ? in->mouse.scroll_delta.y: in->mouse.scroll_delta.x; + if (left_mouse_down && left_mouse_click_in_cursor) { + /* update cursor by mouse dragging */ + float pixel, delta; + *state = NK_WIDGET_STATE_ACTIVE; + if (o == NK_VERTICAL) { + float cursor_y; + pixel = in->mouse.delta.y; + delta = (pixel / scroll->h) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->h); + cursor_y = scroll->y + ((scroll_offset/target) * scroll->h); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = cursor_y + cursor->h/2.0f; + } else { + float cursor_x; + pixel = in->mouse.delta.x; + delta = (pixel / scroll->w) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->w); + cursor_x = scroll->x + ((scroll_offset/target) * scroll->w); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor_x + cursor->w/2.0f; + } + } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_UP) && o == NK_VERTICAL && has_scrolling)|| + nk_button_behavior(&ws, *empty0, in, NK_BUTTON_DEFAULT)) { + /* scroll page up by click on empty space or shortcut */ + if (o == NK_VERTICAL) + scroll_offset = NK_MAX(0, scroll_offset - scroll->h); + else scroll_offset = NK_MAX(0, scroll_offset - scroll->w); + } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_DOWN) && o == NK_VERTICAL && has_scrolling) || + nk_button_behavior(&ws, *empty1, in, NK_BUTTON_DEFAULT)) { + /* scroll page down by click on empty space or shortcut */ + if (o == NK_VERTICAL) + scroll_offset = NK_MIN(scroll_offset + scroll->h, target - scroll->h); + else scroll_offset = NK_MIN(scroll_offset + scroll->w, target - scroll->w); + } else if (has_scrolling) { + if ((scroll_delta < 0 || (scroll_delta > 0))) { + /* update cursor by mouse scrolling */ + scroll_offset = scroll_offset + scroll_step * (-scroll_delta); + if (o == NK_VERTICAL) + scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->h); + else scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->w); + } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_START)) { + /* update cursor to the beginning */ + if (o == NK_VERTICAL) scroll_offset = 0; + } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_END)) { + /* update cursor to the end */ + if (o == NK_VERTICAL) scroll_offset = target - scroll->h; + } + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *scroll)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, *scroll)) + *state |= NK_WIDGET_STATE_LEFT; + return scroll_offset; +} + +NK_INTERN void +nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_scrollbar *style, const struct nk_rect *bounds, + const struct nk_rect *scroll) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } else { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } + + /* draw cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *scroll, style->rounding_cursor, cursor->data.color); + nk_stroke_rect(out, *scroll, style->rounding_cursor, style->border_cursor, style->cursor_border_color); + } else nk_draw_image(out, *scroll, &cursor->data.image, nk_white); +} + +NK_INTERN float +nk_do_scrollbarv(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect empty_north; + struct nk_rect empty_south; + struct nk_rect cursor; + + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + NK_ASSERT(state); + if (!out || !style) return 0; + + scroll.w = NK_MAX(scroll.w, 1); + scroll.h = NK_MAX(scroll.h, 0); + if (target <= scroll.h) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_h; + struct nk_rect button; + + button.x = scroll.x; + button.w = scroll.w; + button.h = scroll.w; + + scroll_h = NK_MAX(scroll.h - 2 * button.h,0); + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.y = scroll.y; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.y = scroll.y + scroll.h - button.h; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.y = scroll.y + button.h; + scroll.h = scroll_h; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.h); + scroll_offset = NK_CLAMP(0, offset, target - scroll.h); + scroll_ratio = scroll.h / target; + scroll_off = scroll_offset / target; + + /* calculate scrollbar cursor bounds */ + cursor.h = NK_MAX((scroll_ratio * scroll.h) - (2*style->border + 2*style->padding.y), 0); + cursor.y = scroll.y + (scroll_off * scroll.h) + style->border + style->padding.y; + cursor.w = scroll.w - (2 * style->border + 2 * style->padding.x); + cursor.x = scroll.x + style->border + style->padding.x; + + /* calculate empty space around cursor */ + empty_north.x = scroll.x; + empty_north.y = scroll.y; + empty_north.w = scroll.w; + empty_north.h = NK_MAX(cursor.y - scroll.y, 0); + + empty_south.x = scroll.x; + empty_south.y = cursor.y + cursor.h; + empty_south.w = scroll.w; + empty_south.h = NK_MAX((scroll.y + scroll.h) - (cursor.y + cursor.h), 0); + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, + &empty_north, &empty_south, scroll_offset, target, scroll_step, NK_VERTICAL); + scroll_off = scroll_offset / target; + cursor.y = scroll.y + (scroll_off * scroll.h) + style->border_cursor + style->padding.y; + + /* draw scrollbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) style->draw_end(out, style->userdata); + return scroll_offset; +} + +NK_INTERN float +nk_do_scrollbarh(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect cursor; + struct nk_rect empty_west; + struct nk_rect empty_east; + + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + if (!out || !style) return 0; + + /* scrollbar background */ + scroll.h = NK_MAX(scroll.h, 1); + scroll.w = NK_MAX(scroll.w, 2 * scroll.h); + if (target <= scroll.w) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_w; + struct nk_rect button; + button.y = scroll.y; + button.w = scroll.h; + button.h = scroll.h; + + scroll_w = scroll.w - 2 * button.w; + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.x = scroll.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.x = scroll.x + scroll.w - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.x = scroll.x + button.w; + scroll.w = scroll_w; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.w); + scroll_offset = NK_CLAMP(0, offset, target - scroll.w); + scroll_ratio = scroll.w / target; + scroll_off = scroll_offset / target; + + /* calculate cursor bounds */ + cursor.w = (scroll_ratio * scroll.w) - (2*style->border + 2*style->padding.x); + cursor.x = scroll.x + (scroll_off * scroll.w) + style->border + style->padding.x; + cursor.h = scroll.h - (2 * style->border + 2 * style->padding.y); + cursor.y = scroll.y + style->border + style->padding.y; + + /* calculate empty space around cursor */ + empty_west.x = scroll.x; + empty_west.y = scroll.y; + empty_west.w = cursor.x - scroll.x; + empty_west.h = scroll.h; + + empty_east.x = cursor.x + cursor.w; + empty_east.y = scroll.y; + empty_east.w = (scroll.x + scroll.w) - (cursor.x + cursor.w); + empty_east.h = scroll.h; + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, + &empty_west, &empty_east, scroll_offset, target, scroll_step, NK_HORIZONTAL); + scroll_off = scroll_offset / target; + cursor.x = scroll.x + (scroll_off * scroll.w); + + /* draw scrollbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) style->draw_end(out, style->userdata); + return scroll_offset; +} + +/* =============================================================== + * + * FILTER + * + * ===============================================================*/ +NK_API int nk_filter_default(const struct nk_text_edit *box, nk_rune unicode) +{(void)unicode;NK_UNUSED(box);return nk_true;} + +NK_API int +nk_filter_ascii(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode > 128) return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_float(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-') + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_decimal(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '-') + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_hex(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && + (unicode < 'a' || unicode > 'f') && + (unicode < 'A' || unicode > 'F')) + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_oct(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode < '0' || unicode > '7') + return nk_false; + else return nk_true; +} + +NK_API int +nk_filter_binary(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode != '0' && unicode != '1') + return nk_false; + else return nk_true; +} + +/* =============================================================== + * + * EDIT + * + * ===============================================================*/ +NK_INTERN void +nk_edit_draw_text(struct nk_command_buffer *out, + const struct nk_style_edit *style, float pos_x, float pos_y, + float x_offset, const char *text, int byte_len, float row_height, + const struct nk_user_font *font, struct nk_color background, + struct nk_color foreground, int is_selected) +{ + NK_ASSERT(out); + NK_ASSERT(font); + NK_ASSERT(style); + if (!text || !byte_len || !out || !style) return; + + {int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + float line_width = 0; + float glyph_width; + const char *line = text; + float line_offset = 0; + int line_count = 0; + + struct nk_text txt; + txt.padding = nk_vec2(0,0); + txt.background = background; + txt.text = foreground; + + glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len); + if (!glyph_len) return; + while ((text_len < byte_len) && glyph_len) + { + if (unicode == '\n') { + /* new line sepeator so draw previous line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) /* selection needs to draw different background color */ + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_CENTERED, font); + + text_len++; + line_count++; + line_width = 0; + line = text + text_len; + line_offset += row_height; + glyph_len = nk_utf_decode(text + text_len, &unicode, (int)(byte_len-text_len)); + continue; + } + if (unicode == '\r') { + text_len++; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); + line_width += (float)glyph_width; + text_len += glyph_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + if (line_width > 0) { + /* draw last line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_LEFT, font); + }} +} + +NK_INTERN nk_flags +nk_do_edit(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter, + struct nk_text_edit *edit, const struct nk_style_edit *style, + struct nk_input *in, const struct nk_user_font *font) +{ + struct nk_rect area; + nk_flags ret = 0; + float row_height; + char prev_state = 0; + char is_hovered = 0; + char select_all = 0; + char cursor_follow = 0; + struct nk_rect old_clip; + struct nk_rect clip; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(style); + if (!state || !out || !style) + return ret; + + /* visible text area calculation */ + area.x = bounds.x + style->padding.x + style->border; + area.y = bounds.y + style->padding.y + style->border; + area.w = bounds.w - (2.0f * style->padding.x + 2 * style->border); + area.h = bounds.h - (2.0f * style->padding.y + 2 * style->border); + if (flags & NK_EDIT_MULTILINE) + area.w = NK_MAX(0, area.w - style->scrollbar_size.x); + row_height = (flags & NK_EDIT_MULTILINE)? font->height + style->row_padding: area.h; + + /* calculate clipping rectangle */ + old_clip = out->clip; + nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h); + + /* update edit state */ + prev_state = (char)edit->active; + is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds); + if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) { + edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, + bounds.x, bounds.y, bounds.w, bounds.h); + } + + /* (de)activate text editor */ + if (!prev_state && edit->active) { + const enum nk_text_edit_type type = (flags & NK_EDIT_MULTILINE) ? + NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE; + nk_textedit_clear_state(edit, type, filter); + if (flags & NK_EDIT_ALWAYS_INSERT_MODE) + edit->mode = NK_TEXT_EDIT_MODE_INSERT; + if (flags & NK_EDIT_AUTO_SELECT) + select_all = nk_true; + if (flags & NK_EDIT_GOTO_END_ON_ACTIVATE) { + edit->cursor = edit->string.len; + in = 0; + } + } else if (!edit->active) edit->mode = NK_TEXT_EDIT_MODE_VIEW; + if (flags & NK_EDIT_READ_ONLY) + edit->mode = NK_TEXT_EDIT_MODE_VIEW; + + ret = (edit->active) ? NK_EDIT_ACTIVE: NK_EDIT_INACTIVE; + if (prev_state != edit->active) + ret |= (edit->active) ? NK_EDIT_ACTIVATED: NK_EDIT_DEACTIVATED; + + /* handle user input */ + if (edit->active && in) + { + int shift_mod = in->keyboard.keys[NK_KEY_SHIFT].down; + const float mouse_x = (in->mouse.pos.x - area.x) + edit->scrollbar.x; + const float mouse_y = (in->mouse.pos.y - area.y) + edit->scrollbar.y; + + /* mouse click handler */ + is_hovered = (char)nk_input_is_mouse_hovering_rect(in, area); + if (select_all) { + nk_textedit_select_all(edit); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + in->mouse.buttons[NK_BUTTON_LEFT].clicked) { + nk_textedit_click(edit, mouse_x, mouse_y, font, row_height); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + (in->mouse.delta.x != 0.0f || in->mouse.delta.y != 0.0f)) { + nk_textedit_drag(edit, mouse_x, mouse_y, font, row_height); + cursor_follow = nk_true; + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_RIGHT].clicked && + in->mouse.buttons[NK_BUTTON_RIGHT].down) { + nk_textedit_key(edit, NK_KEY_TEXT_WORD_LEFT, nk_false, font, row_height); + nk_textedit_key(edit, NK_KEY_TEXT_WORD_RIGHT, nk_true, font, row_height); + cursor_follow = nk_true; + } + + {int i; /* keyboard input */ + int old_mode = edit->mode; + for (i = 0; i < NK_KEY_MAX; ++i) { + if (i == NK_KEY_ENTER || i == NK_KEY_TAB) continue; /* special case */ + if (nk_input_is_key_pressed(in, (enum nk_keys)i)) { + nk_textedit_key(edit, (enum nk_keys)i, shift_mod, font, row_height); + cursor_follow = nk_true; + } + } + if (old_mode != edit->mode) { + in->keyboard.text_len = 0; + }} + + /* text input */ + edit->filter = filter; + if (in->keyboard.text_len) { + nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len); + cursor_follow = nk_true; + in->keyboard.text_len = 0; + } + + /* enter key handler */ + if (nk_input_is_key_pressed(in, NK_KEY_ENTER)) { + cursor_follow = nk_true; + if (flags & NK_EDIT_CTRL_ENTER_NEWLINE && shift_mod) + nk_textedit_text(edit, "\n", 1); + else if (flags & NK_EDIT_SIG_ENTER) + ret |= NK_EDIT_COMMITED; + else nk_textedit_text(edit, "\n", 1); + } + + /* cut & copy handler */ + {int copy= nk_input_is_key_pressed(in, NK_KEY_COPY); + int cut = nk_input_is_key_pressed(in, NK_KEY_CUT); + if ((copy || cut) && (flags & NK_EDIT_CLIPBOARD)) + { + int glyph_len; + nk_rune unicode; + const char *text; + int b = edit->select_start; + int e = edit->select_end; + + int begin = NK_MIN(b, e); + int end = NK_MAX(b, e); + text = nk_str_at_const(&edit->string, begin, &unicode, &glyph_len); + if (edit->clip.copy) + edit->clip.copy(edit->clip.userdata, text, end - begin); + if (cut && !(flags & NK_EDIT_READ_ONLY)){ + nk_textedit_cut(edit); + cursor_follow = nk_true; + } + }} + + /* paste handler */ + {int paste = nk_input_is_key_pressed(in, NK_KEY_PASTE); + if (paste && (flags & NK_EDIT_CLIPBOARD) && edit->clip.paste) { + edit->clip.paste(edit->clip.userdata, edit); + cursor_follow = nk_true; + }} + + /* tab handler */ + {int tab = nk_input_is_key_pressed(in, NK_KEY_TAB); + if (tab && (flags & NK_EDIT_ALLOW_TAB)) { + nk_textedit_text(edit, " ", 4); + cursor_follow = nk_true; + }} + } + + /* set widget state */ + if (edit->active) + *state = NK_WIDGET_STATE_ACTIVE; + else nk_widget_state_reset(state); + + if (is_hovered) + *state |= NK_WIDGET_STATE_HOVERED; + + /* DRAW EDIT */ + {const char *text = nk_str_get_const(&edit->string); + int len = nk_str_len_char(&edit->string); + + {/* select background colors/images */ + const struct nk_style_item *background; + if (*state & NK_WIDGET_STATE_ACTIVED) + background = &style->active; + else if (*state & NK_WIDGET_STATE_HOVER) + background = &style->hover; + else background = &style->normal; + + /* draw background frame */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_stroke_rect(out, bounds, style->rounding, style->border, style->border_color); + nk_fill_rect(out, bounds, style->rounding, background->data.color); + } else nk_draw_image(out, bounds, &background->data.image, nk_white);} + + area.w = NK_MAX(0, area.w - style->cursor_size); + if (edit->active) + { + int total_lines = 1; + struct nk_vec2 text_size = nk_vec2(0,0); + + /* text pointer positions */ + const char *cursor_ptr = 0; + const char *select_begin_ptr = 0; + const char *select_end_ptr = 0; + + /* 2D pixel positions */ + struct nk_vec2 cursor_pos = nk_vec2(0,0); + struct nk_vec2 selection_offset_start = nk_vec2(0,0); + struct nk_vec2 selection_offset_end = nk_vec2(0,0); + + int selection_begin = NK_MIN(edit->select_start, edit->select_end); + int selection_end = NK_MAX(edit->select_start, edit->select_end); + + /* calculate total line count + total space + cursor/selection position */ + float line_width = 0.0f; + if (text && len) + { + /* utf8 encoding */ + float glyph_width; + int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + int glyphs = 0; + int row_begin = 0; + + glyph_len = nk_utf_decode(text, &unicode, len); + glyph_width = font->width(font->userdata, font->height, text, glyph_len); + line_width = 0; + + /* iterate all lines */ + while ((text_len < len) && glyph_len) + { + /* set cursor 2D position and line */ + if (!cursor_ptr && glyphs == edit->cursor) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + cursor_pos.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + cursor_pos.x = row_size.x; + cursor_ptr = text + text_len; + } + + /* set start selection 2D position and line */ + if (!select_begin_ptr && edit->select_start != edit->select_end && + glyphs == selection_begin) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_start.y = (float)(NK_MAX(total_lines-1,0)) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_start.x = row_size.x; + select_begin_ptr = text + text_len; + } + + /* set end selection 2D position and line */ + if (!select_end_ptr && edit->select_start != edit->select_end && + glyphs == selection_end) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_end.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_end.x = row_size.x; + select_end_ptr = text + text_len; + } + if (unicode == '\n') { + text_size.x = NK_MAX(text_size.x, line_width); + total_lines++; + line_width = 0; + text_len++; + glyphs++; + row_begin = text_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); + continue; + } + + glyphs++; + text_len += glyph_len; + line_width += (float)glyph_width; + + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + glyph_width = font->width(font->userdata, font->height, + text+text_len, glyph_len); + continue; + } + text_size.y = (float)total_lines * row_height; + + /* handle case when cursor is at end of text buffer */ + if (!cursor_ptr && edit->cursor == edit->string.len) { + cursor_pos.x = line_width; + cursor_pos.y = text_size.y - row_height; + } + } + { + /* scrollbar */ + if (cursor_follow) + { + /* update scrollbar to follow cursor */ + if (!(flags & NK_EDIT_NO_HORIZONTAL_SCROLL)) { + /* horizontal scroll */ + const float scroll_increment = area.w * 0.25f; + if (cursor_pos.x < edit->scrollbar.x) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - scroll_increment); + if (cursor_pos.x >= edit->scrollbar.x + area.w) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x); + } else edit->scrollbar.x = 0; + + if (flags & NK_EDIT_MULTILINE) { + /* vertical scroll */ + if (cursor_pos.y < edit->scrollbar.y) + edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height); + if (cursor_pos.y >= edit->scrollbar.y + area.h) + edit->scrollbar.y = edit->scrollbar.y + row_height; + } else edit->scrollbar.y = 0; + } + + /* scrollbar widget */ + if (flags & NK_EDIT_MULTILINE) + { + nk_flags ws; + struct nk_rect scroll; + float scroll_target; + float scroll_offset; + float scroll_step; + float scroll_inc; + + scroll = area; + scroll.x = (bounds.x + bounds.w - style->border) - style->scrollbar_size.x; + scroll.w = style->scrollbar_size.x; + + scroll_offset = edit->scrollbar.y; + scroll_step = scroll.h * 0.10f; + scroll_inc = scroll.h * 0.01f; + scroll_target = text_size.y; + edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &style->scrollbar, in, font); + } + } + + /* draw text */ + {struct nk_color background_color; + struct nk_color text_color; + struct nk_color sel_background_color; + struct nk_color sel_text_color; + struct nk_color cursor_color; + struct nk_color cursor_text_color; + const struct nk_style_item *background; + nk_push_scissor(out, clip); + + /* select correct colors to draw */ + if (*state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text_color = style->text_active; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_color = style->cursor_hover; + cursor_text_color = style->cursor_text_hover; + } else if (*state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text_color = style->text_hover; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_text_color = style->cursor_text_hover; + cursor_color = style->cursor_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + sel_text_color = style->selected_text_normal; + sel_background_color = style->selected_normal; + cursor_color = style->cursor_normal; + cursor_text_color = style->cursor_text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + + + if (edit->select_start == edit->select_end) { + /* no selection so just draw the complete text */ + const char *begin = nk_str_get_const(&edit->string); + int l = nk_str_len_char(&edit->string); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } else { + /* edit has selection so draw 1-3 text chunks */ + if (edit->select_start != edit->select_end && selection_begin > 0){ + /* draw unselected text before selection */ + const char *begin = nk_str_get_const(&edit->string); + NK_ASSERT(select_begin_ptr); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, (int)(select_begin_ptr - begin), + row_height, font, background_color, text_color, nk_false); + } + if (edit->select_start != edit->select_end) { + /* draw selected text */ + NK_ASSERT(select_begin_ptr); + if (!select_end_ptr) { + const char *begin = nk_str_get_const(&edit->string); + select_end_ptr = begin + nk_str_len_char(&edit->string); + } + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_start.y - edit->scrollbar.y, + selection_offset_start.x, + select_begin_ptr, (int)(select_end_ptr - select_begin_ptr), + row_height, font, sel_background_color, sel_text_color, nk_true); + } + if ((edit->select_start != edit->select_end && + selection_end < edit->string.len)) + { + /* draw unselected text after selected text */ + const char *begin = select_end_ptr; + const char *end = nk_str_get_const(&edit->string) + + nk_str_len_char(&edit->string); + NK_ASSERT(select_end_ptr); + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_end.y - edit->scrollbar.y, + selection_offset_end.x, + begin, (int)(end - begin), row_height, font, + background_color, text_color, nk_true); + } + } + + /* cursor */ + if (edit->select_start == edit->select_end) + { + if (edit->cursor >= nk_str_len(&edit->string) || + (cursor_ptr && *cursor_ptr == '\n')) { + /* draw cursor at end of line */ + struct nk_rect cursor; + cursor.w = style->cursor_size; + cursor.h = font->height; + cursor.x = area.x + cursor_pos.x - edit->scrollbar.x; + cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f; + cursor.y -= edit->scrollbar.y; + nk_fill_rect(out, cursor, 0, cursor_color); + } else { + /* draw cursor inside text */ + int glyph_len; + struct nk_rect label; + struct nk_text txt; + + nk_rune unicode; + NK_ASSERT(cursor_ptr); + glyph_len = nk_utf_decode(cursor_ptr, &unicode, 4); + + label.x = area.x + cursor_pos.x - edit->scrollbar.x; + label.y = area.y + cursor_pos.y - edit->scrollbar.y; + label.w = font->width(font->userdata, font->height, cursor_ptr, glyph_len); + label.h = row_height; + + txt.padding = nk_vec2(0,0); + txt.background = cursor_color;; + txt.text = cursor_text_color; + nk_fill_rect(out, label, 0, cursor_color); + nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font); + } + }} + } else { + /* not active so just draw text */ + int l = nk_str_len_char(&edit->string); + const char *begin = nk_str_get_const(&edit->string); + + const struct nk_style_item *background; + struct nk_color background_color; + struct nk_color text_color; + nk_push_scissor(out, clip); + if (*state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text_color = style->text_active; + } else if (*state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text_color = style->text_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } + nk_push_scissor(out, old_clip);} + return ret; +} + +/* =============================================================== + * + * PROPERTY + * + * ===============================================================*/ +enum nk_property_status { + NK_PROPERTY_DEFAULT, + NK_PROPERTY_EDIT, + NK_PROPERTY_DRAG +}; +enum nk_property_filter { + NK_FILTER_INT, + NK_FILTER_FLOAT +}; +enum nk_property_kind { + NK_PROPERTY_INT, + NK_PROPERTY_FLOAT, + NK_PROPERTY_DOUBLE +}; +union nk_property { + int i; + float f; + double d; +}; +struct nk_property_variant { + enum nk_property_kind kind; + union nk_property value; + union nk_property min_value; + union nk_property max_value; + union nk_property step; +}; + +NK_INTERN void +nk_drag_behavior(nk_flags *state, const struct nk_input *in, + struct nk_rect drag, struct nk_property_variant *variant, + float inc_per_pixel) +{ + int left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_cursor = in && + nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, drag, nk_true); + + nk_widget_state_reset(state); + if (nk_input_is_mouse_hovering_rect(in, drag)) + *state = NK_WIDGET_STATE_HOVERED; + + if (left_mouse_down && left_mouse_click_in_cursor) { + float delta, pixels; + pixels = in->mouse.delta.x; + delta = pixels * inc_per_pixel; + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = variant->value.i + (int)delta; + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); + break; + case NK_PROPERTY_FLOAT: + variant->value.f = variant->value.f + (float)delta; + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); + break; + case NK_PROPERTY_DOUBLE: + variant->value.d = variant->value.d + (double)delta; + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); + break; + } + *state = NK_WIDGET_STATE_ACTIVE; + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_LEFT; +} + +NK_INTERN void +nk_property_behavior(nk_flags *ws, const struct nk_input *in, + struct nk_rect property, struct nk_rect label, struct nk_rect edit, + struct nk_rect empty, int *state, struct nk_property_variant *variant, + float inc_per_pixel) +{ + if (in && *state == NK_PROPERTY_DEFAULT) { + if (nk_button_behavior(ws, edit, in, NK_BUTTON_DEFAULT)) + *state = NK_PROPERTY_EDIT; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, label, nk_true)) + *state = NK_PROPERTY_DRAG; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, empty, nk_true)) + *state = NK_PROPERTY_DRAG; + } + if (*state == NK_PROPERTY_DRAG) { + nk_drag_behavior(ws, in, property, variant, inc_per_pixel); + if (!(*ws & NK_WIDGET_STATE_ACTIVED)) *state = NK_PROPERTY_DEFAULT; + } +} + +NK_INTERN void +nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, + const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, + const char *name, int len, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background and text color */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text.text = style->label_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text.text = style->label_hover; + } else { + background = &style->normal; + text.text = style->label_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, background->data.color); + } + + /* draw label */ + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, name, len, &text, NK_TEXT_CENTERED, font); +} + +NK_INTERN void +nk_do_property(nk_flags *ws, + struct nk_command_buffer *out, struct nk_rect property, + const char *name, struct nk_property_variant *variant, + float inc_per_pixel, char *buffer, int *len, + int *state, int *cursor, int *select_begin, int *select_end, + const struct nk_style_property *style, + enum nk_property_filter filter, struct nk_input *in, + const struct nk_user_font *font, struct nk_text_edit *text_edit, + enum nk_button_behavior behavior) +{ + const nk_plugin_filter filters[] = { + nk_filter_decimal, + nk_filter_float + }; + int active, old; + int num_len, name_len; + char string[NK_MAX_NUMBER_BUFFER]; + float size; + + char *dst = 0; + int *length; + + struct nk_rect left; + struct nk_rect right; + struct nk_rect label; + struct nk_rect edit; + struct nk_rect empty; + + /* left decrement button */ + left.h = font->height/2; + left.w = left.h; + left.x = property.x + style->border + style->padding.x; + left.y = property.y + style->border + property.h/2.0f - left.h/2; + + /* text label */ + name_len = nk_strlen(name); + size = font->width(font->userdata, font->height, name, name_len); + label.x = left.x + left.w + style->padding.x; + label.w = (float)size + 2 * style->padding.x; + label.y = property.y + style->border + style->padding.y; + label.h = property.h - (2 * style->border + 2 * style->padding.y); + + /* right increment button */ + right.y = left.y; + right.w = left.w; + right.h = left.h; + right.x = property.x + property.w - (right.w + style->padding.x); + + /* edit */ + if (*state == NK_PROPERTY_EDIT) { + size = font->width(font->userdata, font->height, buffer, *len); + size += style->edit.cursor_size; + length = len; + dst = buffer; + } else { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + nk_itoa(string, variant->value.i); + num_len = nk_strlen(string); + break; + case NK_PROPERTY_FLOAT: + nk_dtoa(string, (double)variant->value.f); + num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); + break; + case NK_PROPERTY_DOUBLE: + nk_dtoa(string, variant->value.d); + num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); + break; + } + size = font->width(font->userdata, font->height, string, num_len); + dst = string; + length = &num_len; + } + + edit.w = (float)size + 2 * style->padding.x; + edit.w = NK_MIN(edit.w, right.x - (label.x + label.w)); + edit.x = right.x - (edit.w + style->padding.x); + edit.y = property.y + style->border; + edit.h = property.h - (2 * style->border); + + /* empty left space activator */ + empty.w = edit.x - (label.x + label.w); + empty.x = label.x + label.w; + empty.y = property.y; + empty.h = property.h; + + /* update property */ + old = (*state == NK_PROPERTY_EDIT); + nk_property_behavior(ws, in, property, label, edit, empty, state, variant, inc_per_pixel); + + /* draw property */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_property(out, style, &property, &label, *ws, name, name_len, font); + if (style->draw_end) style->draw_end(out, style->userdata); + + /* execute right button */ + if (nk_do_button_symbol(ws, out, left, style->sym_left, behavior, &style->dec_button, in, font)) { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i - variant->step.i, variant->max_value.i); break; + case NK_PROPERTY_FLOAT: + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f - variant->step.f, variant->max_value.f); break; + case NK_PROPERTY_DOUBLE: + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d - variant->step.d, variant->max_value.d); break; + } + } + /* execute left button */ + if (nk_do_button_symbol(ws, out, right, style->sym_right, behavior, &style->inc_button, in, font)) { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i + variant->step.i, variant->max_value.i); break; + case NK_PROPERTY_FLOAT: + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f + variant->step.f, variant->max_value.f); break; + case NK_PROPERTY_DOUBLE: + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d + variant->step.d, variant->max_value.d); break; + } + } + if (old != NK_PROPERTY_EDIT && (*state == NK_PROPERTY_EDIT)) { + /* property has been activated so setup buffer */ + NK_MEMCPY(buffer, dst, (nk_size)*length); + *cursor = nk_utf_len(buffer, *length); + *len = *length; + length = len; + dst = buffer; + active = 0; + } else active = (*state == NK_PROPERTY_EDIT); + + /* execute and run text edit field */ + nk_textedit_clear_state(text_edit, NK_TEXT_EDIT_SINGLE_LINE, filters[filter]); + text_edit->active = (unsigned char)active; + text_edit->string.len = *length; + text_edit->cursor = NK_CLAMP(0, *cursor, *length); + text_edit->select_start = NK_CLAMP(0,*select_begin, *length); + text_edit->select_end = NK_CLAMP(0,*select_end, *length); + text_edit->string.buffer.allocated = (nk_size)*length; + text_edit->string.buffer.memory.size = NK_MAX_NUMBER_BUFFER; + text_edit->string.buffer.memory.ptr = dst; + text_edit->string.buffer.size = NK_MAX_NUMBER_BUFFER; + text_edit->mode = NK_TEXT_EDIT_MODE_INSERT; + nk_do_edit(ws, out, edit, NK_EDIT_FIELD|NK_EDIT_AUTO_SELECT, + filters[filter], text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font); + + *length = text_edit->string.len; + *cursor = text_edit->cursor; + *select_begin = text_edit->select_start; + *select_end = text_edit->select_end; + if (text_edit->active && nk_input_is_key_pressed(in, NK_KEY_ENTER)) + text_edit->active = nk_false; + + if (active && !text_edit->active) { + /* property is now not active so convert edit text to value*/ + *state = NK_PROPERTY_DEFAULT; + buffer[*len] = '\0'; + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = nk_strtoi(buffer, 0); + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); + break; + case NK_PROPERTY_FLOAT: + nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); + variant->value.f = nk_strtof(buffer, 0); + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); + break; + case NK_PROPERTY_DOUBLE: + nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); + variant->value.d = nk_strtod(buffer, 0); + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); + break; + } + } +} +/* =============================================================== + * + * COLOR PICKER + * + * ===============================================================*/ +NK_INTERN int +nk_color_picker_behavior(nk_flags *state, + const struct nk_rect *bounds, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_color *color, const struct nk_input *in) +{ + float hsva[4]; + int value_changed = 0; + int hsv_changed = 0; + + NK_ASSERT(state); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + NK_ASSERT(color); + + /* color matrix */ + nk_color_hsva_fv(hsva, *color); + if (nk_button_behavior(state, *matrix, in, NK_BUTTON_REPEATER)) { + hsva[1] = NK_SATURATE((in->mouse.pos.x - matrix->x) / (matrix->w-1)); + hsva[2] = 1.0f - NK_SATURATE((in->mouse.pos.y - matrix->y) / (matrix->h-1)); + value_changed = hsv_changed = 1; + } + + /* hue bar */ + if (nk_button_behavior(state, *hue_bar, in, NK_BUTTON_REPEATER)) { + hsva[0] = NK_SATURATE((in->mouse.pos.y - hue_bar->y) / (hue_bar->h-1)); + value_changed = hsv_changed = 1; + } + + /* alpha bar */ + if (alpha_bar) { + if (nk_button_behavior(state, *alpha_bar, in, NK_BUTTON_REPEATER)) { + hsva[3] = 1.0f - NK_SATURATE((in->mouse.pos.y - alpha_bar->y) / (alpha_bar->h-1)); + value_changed = 1; + } + } + nk_widget_state_reset(state); + if (hsv_changed) { + *color = nk_hsva_fv(hsva); + *state = NK_WIDGET_STATE_ACTIVE; + } + if (value_changed) { + color->a = (nk_byte)(hsva[3] * 255.0f); + *state = NK_WIDGET_STATE_ACTIVE; + } + + /* set color picker widget state */ + if (nk_input_is_mouse_hovering_rect(in, *bounds)) + *state = NK_WIDGET_STATE_HOVERED; + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return value_changed; +} + +NK_INTERN void +nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_color color) +{ + NK_STORAGE const struct nk_color black = {0,0,0,255}; + NK_STORAGE const struct nk_color white = {255, 255, 255, 255}; + NK_STORAGE const struct nk_color black_trans = {0,0,0,0}; + + const float crosshair_size = 7.0f; + struct nk_color temp; + float hsva[4]; + float line_y; + int i; + + NK_ASSERT(o); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + + /* draw hue bar */ + nk_color_hsv_fv(hsva, color); + for (i = 0; i < 6; ++i) { + NK_GLOBAL const struct nk_color hue_colors[] = { + {255, 0, 0, 255}, + {255,255,0,255}, + {0,255,0,255}, + {0, 255,255,255}, + {0,0,255,255}, + {255, 0, 255, 255}, + {255, 0, 0, 255} + }; + nk_fill_rect_multi_color(o, + nk_rect(hue_bar->x, hue_bar->y + (float)i * (hue_bar->h/6.0f) + 0.5f, + hue_bar->w, (hue_bar->h/6.0f) + 0.5f), hue_colors[i], hue_colors[i], + hue_colors[i+1], hue_colors[i+1]); + } + line_y = (float)(int)(hue_bar->y + hsva[0] * matrix->h + 0.5f); + nk_stroke_line(o, hue_bar->x-1, line_y, hue_bar->x + hue_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); + + /* draw alpha bar */ + if (alpha_bar) { + float alpha = NK_SATURATE((float)color.a/255.0f); + line_y = (float)(int)(alpha_bar->y + (1.0f - alpha) * matrix->h + 0.5f); + + nk_fill_rect_multi_color(o, *alpha_bar, white, white, black, black); + nk_stroke_line(o, alpha_bar->x-1, line_y, alpha_bar->x + alpha_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); + } + + /* draw color matrix */ + temp = nk_hsv_f(hsva[0], 1.0f, 1.0f); + nk_fill_rect_multi_color(o, *matrix, white, temp, temp, white); + nk_fill_rect_multi_color(o, *matrix, black_trans, black_trans, black, black); + + /* draw cross-hair */ + {struct nk_vec2 p; float S = hsva[1]; float V = hsva[2]; + p.x = (float)(int)(matrix->x + S * matrix->w); + p.y = (float)(int)(matrix->y + (1.0f - V) * matrix->h); + nk_stroke_line(o, p.x - crosshair_size, p.y, p.x-2, p.y, 1.0f, white); + nk_stroke_line(o, p.x + crosshair_size + 1, p.y, p.x+3, p.y, 1.0f, white); + nk_stroke_line(o, p.x, p.y + crosshair_size + 1, p.x, p.y+3, 1.0f, white); + nk_stroke_line(o, p.x, p.y - crosshair_size, p.x, p.y-2, 1.0f, white);} +} + +NK_INTERN int +nk_do_color_picker(nk_flags *state, + struct nk_command_buffer *out, struct nk_color *color, + enum nk_color_format fmt, struct nk_rect bounds, + struct nk_vec2 padding, const struct nk_input *in, + const struct nk_user_font *font) +{ + int ret = 0; + struct nk_rect matrix; + struct nk_rect hue_bar; + struct nk_rect alpha_bar; + float bar_w; + + NK_ASSERT(out); + NK_ASSERT(color); + NK_ASSERT(state); + NK_ASSERT(font); + if (!out || !color || !state || !font) + return ret; + + bar_w = font->height; + bounds.x += padding.x; + bounds.y += padding.x; + bounds.w -= 2 * padding.x; + bounds.h -= 2 * padding.y; + + matrix.x = bounds.x; + matrix.y = bounds.y; + matrix.h = bounds.h; + matrix.w = bounds.w - (3 * padding.x + 2 * bar_w); + + hue_bar.w = bar_w; + hue_bar.y = bounds.y; + hue_bar.h = matrix.h; + hue_bar.x = matrix.x + matrix.w + padding.x; + + alpha_bar.x = hue_bar.x + hue_bar.w + padding.x; + alpha_bar.y = bounds.y; + alpha_bar.w = bar_w; + alpha_bar.h = matrix.h; + + ret = nk_color_picker_behavior(state, &bounds, &matrix, &hue_bar, + (fmt == NK_RGBA) ? &alpha_bar:0, color, in); + nk_draw_color_picker(out, &matrix, &hue_bar, (fmt == NK_RGBA) ? &alpha_bar:0, *color); + return ret; +} + +/* ============================================================== + * + * STYLE + * + * ===============================================================*/ +NK_API void nk_style_default(struct nk_context *ctx){nk_style_from_table(ctx, 0);} +#define NK_COLOR_MAP(NK_COLOR)\ + NK_COLOR(NK_COLOR_TEXT, 175,175,175,255) \ + NK_COLOR(NK_COLOR_WINDOW, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_HEADER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BORDER, 65, 65, 65, 255) \ + NK_COLOR(NK_COLOR_BUTTON, 50, 50, 50, 255) \ + NK_COLOR(NK_COLOR_BUTTON_HOVER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BUTTON_ACTIVE, 35, 35, 35, 255) \ + NK_COLOR(NK_COLOR_TOGGLE, 100,100,100,255) \ + NK_COLOR(NK_COLOR_TOGGLE_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_TOGGLE_CURSOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT_ACTIVE, 35, 35, 35,255) \ + NK_COLOR(NK_COLOR_SLIDER, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_ACTIVE, 150,150,150,255) \ + NK_COLOR(NK_COLOR_PROPERTY, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT_CURSOR, 175,175,175,255) \ + NK_COLOR(NK_COLOR_COMBO, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART, 120,120,120,255) \ + NK_COLOR(NK_COLOR_CHART_COLOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART_COLOR_HIGHLIGHT,255, 0, 0, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_HOVER,120,120,120,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_ACTIVE,150,150,150,255) \ + NK_COLOR(NK_COLOR_TAB_HEADER, 40, 40, 40,255) + +NK_GLOBAL const struct nk_color +nk_default_color_style[NK_COLOR_COUNT] = { +#define NK_COLOR(a,b,c,d,e) {b,c,d,e}, + NK_COLOR_MAP(NK_COLOR) +#undef NK_COLOR +}; + +NK_GLOBAL const char *nk_color_names[NK_COLOR_COUNT] = { +#define NK_COLOR(a,b,c,d,e) #a, + NK_COLOR_MAP(NK_COLOR) +#undef NK_COLOR +}; + +NK_API const char *nk_style_get_color_by_name(enum nk_style_colors c) +{return nk_color_names[c];} + +NK_API struct nk_style_item nk_style_item_image(struct nk_image img) +{struct nk_style_item i; i.type = NK_STYLE_ITEM_IMAGE; i.data.image = img; return i;} + +NK_API struct nk_style_item nk_style_item_color(struct nk_color col) +{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = col; return i;} + +NK_API struct nk_style_item nk_style_item_hide(void) +{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = nk_rgba(0,0,0,0); return i;} + +NK_API void +nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) +{ + struct nk_style *style; + struct nk_style_text *text; + struct nk_style_button *button; + struct nk_style_toggle *toggle; + struct nk_style_selectable *select; + struct nk_style_slider *slider; + struct nk_style_progress *prog; + struct nk_style_scrollbar *scroll; + struct nk_style_edit *edit; + struct nk_style_property *property; + struct nk_style_combo *combo; + struct nk_style_chart *chart; + struct nk_style_tab *tab; + struct nk_style_window *win; + + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + table = (!table) ? nk_default_color_style: table; + + /* default text */ + text = &style->text; + text->color = table[NK_COLOR_TEXT]; + text->padding = nk_vec2(0,0); + + /* default button */ + button = &style->button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_BUTTON]); + button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); + button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); + button->border_color = table[NK_COLOR_BORDER]; + button->text_background = table[NK_COLOR_BUTTON]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->image_padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f, 0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 4.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* contextual button */ + button = &style->contextual_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); + button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); + button->border_color = table[NK_COLOR_WINDOW]; + button->text_background = table[NK_COLOR_WINDOW]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* menu button */ + button = &style->menu_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->border_color = table[NK_COLOR_WINDOW]; + button->text_background = table[NK_COLOR_WINDOW]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 1.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* checkbox toggle */ + toggle = &style->checkbox; + nk_zero_struct(*toggle); + toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); + toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->userdata = nk_handle_ptr(0); + toggle->text_background = table[NK_COLOR_WINDOW]; + toggle->text_normal = table[NK_COLOR_TEXT]; + toggle->text_hover = table[NK_COLOR_TEXT]; + toggle->text_active = table[NK_COLOR_TEXT]; + toggle->padding = nk_vec2(2.0f, 2.0f); + toggle->touch_padding = nk_vec2(0,0); + toggle->border_color = nk_rgba(0,0,0,0); + toggle->border = 0.0f; + toggle->spacing = 4; + + /* option toggle */ + toggle = &style->option; + nk_zero_struct(*toggle); + toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); + toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); + toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); + toggle->userdata = nk_handle_ptr(0); + toggle->text_background = table[NK_COLOR_WINDOW]; + toggle->text_normal = table[NK_COLOR_TEXT]; + toggle->text_hover = table[NK_COLOR_TEXT]; + toggle->text_active = table[NK_COLOR_TEXT]; + toggle->padding = nk_vec2(3.0f, 3.0f); + toggle->touch_padding = nk_vec2(0,0); + toggle->border_color = nk_rgba(0,0,0,0); + toggle->border = 0.0f; + toggle->spacing = 4; + + /* selectable */ + select = &style->selectable; + nk_zero_struct(*select); + select->normal = nk_style_item_color(table[NK_COLOR_SELECT]); + select->hover = nk_style_item_color(table[NK_COLOR_SELECT]); + select->pressed = nk_style_item_color(table[NK_COLOR_SELECT]); + select->normal_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->hover_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->pressed_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); + select->text_normal = table[NK_COLOR_TEXT]; + select->text_hover = table[NK_COLOR_TEXT]; + select->text_pressed = table[NK_COLOR_TEXT]; + select->text_normal_active = table[NK_COLOR_TEXT]; + select->text_hover_active = table[NK_COLOR_TEXT]; + select->text_pressed_active = table[NK_COLOR_TEXT]; + select->padding = nk_vec2(2.0f,2.0f); + select->touch_padding = nk_vec2(0,0); + select->userdata = nk_handle_ptr(0); + select->rounding = 0.0f; + select->draw_begin = 0; + select->draw_end = 0; + + /* slider */ + slider = &style->slider; + nk_zero_struct(*slider); + slider->normal = nk_style_item_hide(); + slider->hover = nk_style_item_hide(); + slider->active = nk_style_item_hide(); + slider->bar_normal = table[NK_COLOR_SLIDER]; + slider->bar_hover = table[NK_COLOR_SLIDER]; + slider->bar_active = table[NK_COLOR_SLIDER]; + slider->bar_filled = table[NK_COLOR_SLIDER_CURSOR]; + slider->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); + slider->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); + slider->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); + slider->inc_symbol = NK_SYMBOL_TRIANGLE_RIGHT; + slider->dec_symbol = NK_SYMBOL_TRIANGLE_LEFT; + slider->cursor_size = nk_vec2(16,16); + slider->padding = nk_vec2(2,2); + slider->spacing = nk_vec2(2,2); + slider->userdata = nk_handle_ptr(0); + slider->show_buttons = nk_false; + slider->bar_height = 8; + slider->rounding = 0; + slider->draw_begin = 0; + slider->draw_end = 0; + + /* slider buttons */ + button = &style->slider.inc_button; + button->normal = nk_style_item_color(nk_rgb(40,40,40)); + button->hover = nk_style_item_color(nk_rgb(42,42,42)); + button->active = nk_style_item_color(nk_rgb(44,44,44)); + button->border_color = nk_rgb(65,65,65); + button->text_background = nk_rgb(40,40,40); + button->text_normal = nk_rgb(175,175,175); + button->text_hover = nk_rgb(175,175,175); + button->text_active = nk_rgb(175,175,175); + button->padding = nk_vec2(8.0f,8.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->slider.dec_button = style->slider.inc_button; + + /* progressbar */ + prog = &style->progress; + nk_zero_struct(*prog); + prog->normal = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->hover = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->active = nk_style_item_color(table[NK_COLOR_SLIDER]); + prog->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); + prog->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); + prog->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); + prog->border_color = nk_rgba(0,0,0,0); + prog->cursor_border_color = nk_rgba(0,0,0,0); + prog->userdata = nk_handle_ptr(0); + prog->padding = nk_vec2(4,4); + prog->rounding = 0; + prog->border = 0; + prog->cursor_rounding = 0; + prog->cursor_border = 0; + prog->draw_begin = 0; + prog->draw_end = 0; + + /* scrollbars */ + scroll = &style->scrollh; + nk_zero_struct(*scroll); + scroll->normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->active = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); + scroll->cursor_normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR]); + scroll->cursor_hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_HOVER]); + scroll->cursor_active = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE]); + scroll->dec_symbol = NK_SYMBOL_CIRCLE_SOLID; + scroll->inc_symbol = NK_SYMBOL_CIRCLE_SOLID; + scroll->userdata = nk_handle_ptr(0); + scroll->border_color = table[NK_COLOR_SCROLLBAR]; + scroll->cursor_border_color = table[NK_COLOR_SCROLLBAR]; + scroll->padding = nk_vec2(0,0); + scroll->show_buttons = nk_false; + scroll->border = 0; + scroll->rounding = 0; + scroll->border_cursor = 0; + scroll->rounding_cursor = 0; + scroll->draw_begin = 0; + scroll->draw_end = 0; + style->scrollv = style->scrollh; + + /* scrollbars buttons */ + button = &style->scrollh.inc_button; + button->normal = nk_style_item_color(nk_rgb(40,40,40)); + button->hover = nk_style_item_color(nk_rgb(42,42,42)); + button->active = nk_style_item_color(nk_rgb(44,44,44)); + button->border_color = nk_rgb(65,65,65); + button->text_background = nk_rgb(40,40,40); + button->text_normal = nk_rgb(175,175,175); + button->text_hover = nk_rgb(175,175,175); + button->text_active = nk_rgb(175,175,175); + button->padding = nk_vec2(4.0f,4.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 1.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->scrollh.dec_button = style->scrollh.inc_button; + style->scrollv.inc_button = style->scrollh.inc_button; + style->scrollv.dec_button = style->scrollh.inc_button; + + /* edit */ + edit = &style->edit; + nk_zero_struct(*edit); + edit->normal = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->hover = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->active = nk_style_item_color(table[NK_COLOR_EDIT]); + edit->cursor_normal = table[NK_COLOR_TEXT]; + edit->cursor_hover = table[NK_COLOR_TEXT]; + edit->cursor_text_normal= table[NK_COLOR_EDIT]; + edit->cursor_text_hover = table[NK_COLOR_EDIT]; + edit->border_color = table[NK_COLOR_BORDER]; + edit->text_normal = table[NK_COLOR_TEXT]; + edit->text_hover = table[NK_COLOR_TEXT]; + edit->text_active = table[NK_COLOR_TEXT]; + edit->selected_normal = table[NK_COLOR_TEXT]; + edit->selected_hover = table[NK_COLOR_TEXT]; + edit->selected_text_normal = table[NK_COLOR_EDIT]; + edit->selected_text_hover = table[NK_COLOR_EDIT]; + edit->scrollbar_size = nk_vec2(10,10); + edit->scrollbar = style->scrollv; + edit->padding = nk_vec2(4,4); + edit->row_padding = 2; + edit->cursor_size = 4; + edit->border = 1; + edit->rounding = 0; + + /* property */ + property = &style->property; + nk_zero_struct(*property); + property->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + property->border_color = table[NK_COLOR_BORDER]; + property->label_normal = table[NK_COLOR_TEXT]; + property->label_hover = table[NK_COLOR_TEXT]; + property->label_active = table[NK_COLOR_TEXT]; + property->sym_left = NK_SYMBOL_TRIANGLE_LEFT; + property->sym_right = NK_SYMBOL_TRIANGLE_RIGHT; + property->userdata = nk_handle_ptr(0); + property->padding = nk_vec2(4,4); + property->border = 1; + property->rounding = 10; + property->draw_begin = 0; + property->draw_end = 0; + + /* property buttons */ + button = &style->property.dec_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_PROPERTY]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->property.inc_button = style->property.dec_button; + + /* property edit */ + edit = &style->property.edit; + nk_zero_struct(*edit); + edit->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); + edit->border_color = nk_rgba(0,0,0,0); + edit->cursor_normal = table[NK_COLOR_TEXT]; + edit->cursor_hover = table[NK_COLOR_TEXT]; + edit->cursor_text_normal= table[NK_COLOR_EDIT]; + edit->cursor_text_hover = table[NK_COLOR_EDIT]; + edit->text_normal = table[NK_COLOR_TEXT]; + edit->text_hover = table[NK_COLOR_TEXT]; + edit->text_active = table[NK_COLOR_TEXT]; + edit->selected_normal = table[NK_COLOR_TEXT]; + edit->selected_hover = table[NK_COLOR_TEXT]; + edit->selected_text_normal = table[NK_COLOR_EDIT]; + edit->selected_text_hover = table[NK_COLOR_EDIT]; + edit->padding = nk_vec2(0,0); + edit->cursor_size = 8; + edit->border = 0; + edit->rounding = 0; + + /* chart */ + chart = &style->chart; + nk_zero_struct(*chart); + chart->background = nk_style_item_color(table[NK_COLOR_CHART]); + chart->border_color = table[NK_COLOR_BORDER]; + chart->selected_color = table[NK_COLOR_CHART_COLOR_HIGHLIGHT]; + chart->color = table[NK_COLOR_CHART_COLOR]; + chart->padding = nk_vec2(4,4); + chart->border = 0; + chart->rounding = 0; + + /* combo */ + combo = &style->combo; + combo->normal = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->hover = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->active = nk_style_item_color(table[NK_COLOR_COMBO]); + combo->border_color = table[NK_COLOR_BORDER]; + combo->label_normal = table[NK_COLOR_TEXT]; + combo->label_hover = table[NK_COLOR_TEXT]; + combo->label_active = table[NK_COLOR_TEXT]; + combo->sym_normal = NK_SYMBOL_TRIANGLE_DOWN; + combo->sym_hover = NK_SYMBOL_TRIANGLE_DOWN; + combo->sym_active = NK_SYMBOL_TRIANGLE_DOWN; + combo->content_padding = nk_vec2(4,4); + combo->button_padding = nk_vec2(0,4); + combo->spacing = nk_vec2(4,0); + combo->border = 1; + combo->rounding = 0; + + /* combo button */ + button = &style->combo.button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_COMBO]); + button->hover = nk_style_item_color(table[NK_COLOR_COMBO]); + button->active = nk_style_item_color(table[NK_COLOR_COMBO]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_COMBO]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* tab */ + tab = &style->tab; + tab->background = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + tab->border_color = table[NK_COLOR_BORDER]; + tab->text = table[NK_COLOR_TEXT]; + tab->sym_minimize = NK_SYMBOL_TRIANGLE_RIGHT; + tab->sym_maximize = NK_SYMBOL_TRIANGLE_DOWN; + tab->padding = nk_vec2(4,4); + tab->spacing = nk_vec2(4,4); + tab->indent = 10.0f; + tab->border = 1; + tab->rounding = 0; + + /* tab button */ + button = &style->tab.tab_minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_TAB_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->tab.tab_maximize_button =*button; + + /* node button */ + button = &style->tab.node_minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_TAB_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(2.0f,2.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + style->tab.node_maximize_button =*button; + + /* window header */ + win = &style->window; + win->header.align = NK_HEADER_RIGHT; + win->header.close_symbol = NK_SYMBOL_X; + win->header.minimize_symbol = NK_SYMBOL_MINUS; + win->header.maximize_symbol = NK_SYMBOL_PLUS; + win->header.normal = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.hover = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.active = nk_style_item_color(table[NK_COLOR_HEADER]); + win->header.label_normal = table[NK_COLOR_TEXT]; + win->header.label_hover = table[NK_COLOR_TEXT]; + win->header.label_active = table[NK_COLOR_TEXT]; + win->header.label_padding = nk_vec2(4,4); + win->header.padding = nk_vec2(4,4); + win->header.spacing = nk_vec2(0,0); + + /* window header close button */ + button = &style->window.header.close_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* window header minimize button */ + button = &style->window.header.minimize_button; + nk_zero_struct(*button); + button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); + button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); + button->active = nk_style_item_color(table[NK_COLOR_HEADER]); + button->border_color = nk_rgba(0,0,0,0); + button->text_background = table[NK_COLOR_HEADER]; + button->text_normal = table[NK_COLOR_TEXT]; + button->text_hover = table[NK_COLOR_TEXT]; + button->text_active = table[NK_COLOR_TEXT]; + button->padding = nk_vec2(0.0f,0.0f); + button->touch_padding = nk_vec2(0.0f,0.0f); + button->userdata = nk_handle_ptr(0); + button->text_alignment = NK_TEXT_CENTERED; + button->border = 0.0f; + button->rounding = 0.0f; + button->draw_begin = 0; + button->draw_end = 0; + + /* window */ + win->background = table[NK_COLOR_WINDOW]; + win->fixed_background = nk_style_item_color(table[NK_COLOR_WINDOW]); + win->border_color = table[NK_COLOR_BORDER]; + win->popup_border_color = table[NK_COLOR_BORDER]; + win->combo_border_color = table[NK_COLOR_BORDER]; + win->contextual_border_color = table[NK_COLOR_BORDER]; + win->menu_border_color = table[NK_COLOR_BORDER]; + win->group_border_color = table[NK_COLOR_BORDER]; + win->tooltip_border_color = table[NK_COLOR_BORDER]; + win->scaler = nk_style_item_color(table[NK_COLOR_TEXT]); + + win->rounding = 0.0f; + win->spacing = nk_vec2(4,4); + win->scrollbar_size = nk_vec2(10,10); + win->min_size = nk_vec2(64,64); + + win->combo_border = 1.0f; + win->contextual_border = 1.0f; + win->menu_border = 1.0f; + win->group_border = 1.0f; + win->tooltip_border = 1.0f; + win->popup_border = 1.0f; + win->border = 2.0f; + win->min_row_height_padding = 8; + + win->padding = nk_vec2(4,4); + win->group_padding = nk_vec2(4,4); + win->popup_padding = nk_vec2(4,4); + win->combo_padding = nk_vec2(4,4); + win->contextual_padding = nk_vec2(4,4); + win->menu_padding = nk_vec2(4,4); + win->tooltip_padding = nk_vec2(4,4); +} + +NK_API void +nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_style *style; + NK_ASSERT(ctx); + + if (!ctx) return; + style = &ctx->style; + style->font = font; + ctx->stacks.fonts.head = 0; + if (ctx->current) + nk_layout_reset_min_row_height(ctx); +} + +NK_API int +nk_style_push_font(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_config_stack_user_font *font_stack; + struct nk_config_stack_user_font_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + font_stack = &ctx->stacks.fonts; + NK_ASSERT(font_stack->head < (int)NK_LEN(font_stack->elements)); + if (font_stack->head >= (int)NK_LEN(font_stack->elements)) + return 0; + + element = &font_stack->elements[font_stack->head++]; + element->address = &ctx->style.font; + element->old_value = ctx->style.font; + ctx->style.font = font; + return 1; +} + +NK_API int +nk_style_pop_font(struct nk_context *ctx) +{ + struct nk_config_stack_user_font *font_stack; + struct nk_config_stack_user_font_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + font_stack = &ctx->stacks.fonts; + NK_ASSERT(font_stack->head > 0); + if (font_stack->head < 1) + return 0; + + element = &font_stack->elements[--font_stack->head]; + *element->address = element->old_value; + return 1; +} + +#define NK_STYLE_PUSH_IMPLEMENATION(prefix, type, stack) \ +nk_style_push_##type(struct nk_context *ctx, prefix##_##type *address, prefix##_##type value)\ +{\ + struct nk_config_stack_##type * type_stack;\ + struct nk_config_stack_##type##_element *element;\ + NK_ASSERT(ctx);\ + if (!ctx) return 0;\ + type_stack = &ctx->stacks.stack;\ + NK_ASSERT(type_stack->head < (int)NK_LEN(type_stack->elements));\ + if (type_stack->head >= (int)NK_LEN(type_stack->elements))\ + return 0;\ + element = &type_stack->elements[type_stack->head++];\ + element->address = address;\ + element->old_value = *address;\ + *address = value;\ + return 1;\ +} + +#define NK_STYLE_POP_IMPLEMENATION(type, stack) \ +nk_style_pop_##type(struct nk_context *ctx)\ +{\ + struct nk_config_stack_##type *type_stack;\ + struct nk_config_stack_##type##_element *element;\ + NK_ASSERT(ctx);\ + if (!ctx) return 0;\ + type_stack = &ctx->stacks.stack;\ + NK_ASSERT(type_stack->head > 0);\ + if (type_stack->head < 1)\ + return 0;\ + element = &type_stack->elements[--type_stack->head];\ + *element->address = element->old_value;\ + return 1;\ +} + +NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk, style_item, style_items) +NK_API int NK_STYLE_PUSH_IMPLEMENATION(nk,float, floats) +NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk, vec2, vectors) +NK_API int NK_STYLE_PUSH_IMPLEMENATION(nk,flags, flags) +NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk,color, colors) + +NK_API int NK_STYLE_POP_IMPLEMENATION(style_item, style_items) +NK_API int NK_STYLE_POP_IMPLEMENATION(float,floats) +NK_API int NK_STYLE_POP_IMPLEMENATION(vec2, vectors) +NK_API int NK_STYLE_POP_IMPLEMENATION(flags,flags) +NK_API int NK_STYLE_POP_IMPLEMENATION(color,colors) + +NK_API int +nk_style_set_cursor(struct nk_context *ctx, enum nk_style_cursor c) +{ + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return 0; + style = &ctx->style; + if (style->cursors[c]) { + style->cursor_active = style->cursors[c]; + return 1; + } + return 0; +} + +NK_API void +nk_style_show_cursor(struct nk_context *ctx) +{ + ctx->style.cursor_visible = nk_true; +} + +NK_API void +nk_style_hide_cursor(struct nk_context *ctx) +{ + ctx->style.cursor_visible = nk_false; +} + +NK_API void +nk_style_load_cursor(struct nk_context *ctx, enum nk_style_cursor cursor, + const struct nk_cursor *c) +{ + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + style->cursors[cursor] = c; +} + +NK_API void +nk_style_load_all_cursors(struct nk_context *ctx, struct nk_cursor *cursors) +{ + int i = 0; + struct nk_style *style; + NK_ASSERT(ctx); + if (!ctx) return; + style = &ctx->style; + for (i = 0; i < NK_CURSOR_COUNT; ++i) + style->cursors[i] = &cursors[i]; + style->cursor_visible = nk_true; +} + +/* =============================================================== + * + * POOL + * + * ===============================================================*/ +NK_INTERN void +nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, + unsigned int capacity) +{ + nk_zero(pool, sizeof(*pool)); + pool->alloc = *alloc; + pool->capacity = capacity; + pool->type = NK_BUFFER_DYNAMIC; + pool->pages = 0; +} + +NK_INTERN void +nk_pool_free(struct nk_pool *pool) +{ + struct nk_page *iter = pool->pages; + if (!pool) return; + if (pool->type == NK_BUFFER_FIXED) return; + while (iter) { + struct nk_page *next = iter->next; + pool->alloc.free(pool->alloc.userdata, iter); + iter = next; + } +} + +NK_INTERN void +nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size) +{ + nk_zero(pool, sizeof(*pool)); + NK_ASSERT(size >= sizeof(struct nk_page)); + if (size < sizeof(struct nk_page)) return; + pool->capacity = (unsigned)(size - sizeof(struct nk_page)) / sizeof(struct nk_page_element); + pool->pages = (struct nk_page*)memory; + pool->type = NK_BUFFER_FIXED; + pool->size = size; +} + +NK_INTERN struct nk_page_element* +nk_pool_alloc(struct nk_pool *pool) +{ + if (!pool->pages || pool->pages->size >= pool->capacity) { + /* allocate new page */ + struct nk_page *page; + if (pool->type == NK_BUFFER_FIXED) { + if (!pool->pages) { + NK_ASSERT(pool->pages); + return 0; + } + NK_ASSERT(pool->pages->size < pool->capacity); + return 0; + } else { + nk_size size = sizeof(struct nk_page); + size += NK_POOL_DEFAULT_CAPACITY * sizeof(union nk_page_data); + page = (struct nk_page*)pool->alloc.alloc(pool->alloc.userdata,0, size); + page->next = pool->pages; + pool->pages = page; + page->size = 0; + } + } + return &pool->pages->win[pool->pages->size++]; +} + +/* =============================================================== + * + * CONTEXT + * + * ===============================================================*/ +NK_INTERN void* nk_create_window(struct nk_context *ctx); +NK_INTERN void nk_remove_window(struct nk_context*, struct nk_window*); +NK_INTERN void nk_free_window(struct nk_context *ctx, struct nk_window *win); +NK_INTERN void nk_free_table(struct nk_context *ctx, struct nk_table *tbl); +NK_INTERN void nk_remove_table(struct nk_window *win, struct nk_table *tbl); +NK_INTERN void* nk_create_panel(struct nk_context *ctx); +NK_INTERN void nk_free_panel(struct nk_context*, struct nk_panel *pan); + +NK_INTERN void +nk_setup(struct nk_context *ctx, const struct nk_user_font *font) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_zero_struct(*ctx); + nk_style_default(ctx); + ctx->seq = 1; + if (font) ctx->style.font = font; +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_draw_list_init(&ctx->draw_list); +#endif +} + +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API int +nk_init_default(struct nk_context *ctx, const struct nk_user_font *font) +{ + struct nk_allocator alloc; + alloc.userdata.ptr = 0; + alloc.alloc = nk_malloc; + alloc.free = nk_mfree; + return nk_init(ctx, &alloc, font); +} +#endif + +NK_API int +nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, + const struct nk_user_font *font) +{ + NK_ASSERT(memory); + if (!memory) return 0; + nk_setup(ctx, font); + nk_buffer_init_fixed(&ctx->memory, memory, size); + ctx->use_pool = nk_false; + return 1; +} + +NK_API int +nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, + struct nk_buffer *pool, const struct nk_user_font *font) +{ + NK_ASSERT(cmds); + NK_ASSERT(pool); + if (!cmds || !pool) return 0; + + nk_setup(ctx, font); + ctx->memory = *cmds; + if (pool->type == NK_BUFFER_FIXED) { + /* take memory from buffer and alloc fixed pool */ + nk_pool_init_fixed(&ctx->pool, pool->memory.ptr, pool->memory.size); + } else { + /* create dynamic pool from buffer allocator */ + struct nk_allocator *alloc = &pool->pool; + nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); + } + ctx->use_pool = nk_true; + return 1; +} + +NK_API int +nk_init(struct nk_context *ctx, struct nk_allocator *alloc, + const struct nk_user_font *font) +{ + NK_ASSERT(alloc); + if (!alloc) return 0; + nk_setup(ctx, font); + nk_buffer_init(&ctx->memory, alloc, NK_DEFAULT_COMMAND_BUFFER_SIZE); + nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); + ctx->use_pool = nk_true; + return 1; +} + +#ifdef NK_INCLUDE_COMMAND_USERDATA +NK_API void +nk_set_user_data(struct nk_context *ctx, nk_handle handle) +{ + if (!ctx) return; + ctx->userdata = handle; + if (ctx->current) + ctx->current->buffer.userdata = handle; +} +#endif + +NK_API void +nk_free(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_buffer_free(&ctx->memory); + if (ctx->use_pool) + nk_pool_free(&ctx->pool); + + nk_zero(&ctx->input, sizeof(ctx->input)); + nk_zero(&ctx->style, sizeof(ctx->style)); + nk_zero(&ctx->memory, sizeof(ctx->memory)); + + ctx->seq = 0; + ctx->build = 0; + ctx->begin = 0; + ctx->end = 0; + ctx->active = 0; + ctx->current = 0; + ctx->freelist = 0; + ctx->count = 0; +} + +NK_API void +nk_clear(struct nk_context *ctx) +{ + struct nk_window *iter; + struct nk_window *next; + NK_ASSERT(ctx); + + if (!ctx) return; + if (ctx->use_pool) + nk_buffer_clear(&ctx->memory); + else nk_buffer_reset(&ctx->memory, NK_BUFFER_FRONT); + + ctx->build = 0; + ctx->memory.calls = 0; + ctx->last_widget_state = 0; + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW]; + NK_MEMSET(&ctx->overlay, 0, sizeof(ctx->overlay)); +#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + nk_draw_list_clear(&ctx->draw_list); +#endif + + /* garbage collector */ + iter = ctx->begin; + while (iter) { + /* make sure minimized windows do not get removed */ + if ((iter->flags & NK_WINDOW_MINIMIZED) && + !(iter->flags & NK_WINDOW_CLOSED)) { + iter = iter->next; + continue; + } + /* remove hotness from hidden or closed windows*/ + if (((iter->flags & NK_WINDOW_HIDDEN) || + (iter->flags & NK_WINDOW_CLOSED)) && + iter == ctx->active) + ctx->active = iter->next; + + /* free unused popup windows */ + if (iter->popup.win && iter->popup.win->seq != ctx->seq) { + nk_free_window(ctx, iter->popup.win); + iter->popup.win = 0; + } + /* remove unused window state tables */ + {struct nk_table *n, *it = iter->tables; + while (it) { + n = it->next; + if (it->seq != ctx->seq) { + nk_remove_table(iter, it); + nk_zero(it, sizeof(union nk_page_data)); + nk_free_table(ctx, it); + if (it == iter->tables) + iter->tables = n; + } + it = n; + }} + /* window itself is not used anymore so free */ + if (iter->seq != ctx->seq || iter->flags & NK_WINDOW_CLOSED) { + next = iter->next; + nk_remove_window(ctx, iter); + nk_free_window(ctx, iter); + iter = next; + } else iter = iter->next; + } + ctx->seq++; +} + +/* ---------------------------------------------------------------- + * + * BUFFERING + * + * ---------------------------------------------------------------*/ +NK_INTERN void +nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) +{ + NK_ASSERT(ctx); + NK_ASSERT(buffer); + if (!ctx || !buffer) return; + buffer->begin = ctx->memory.allocated; + buffer->end = buffer->begin; + buffer->last = buffer->begin; + buffer->clip = nk_null_rect; +} + +NK_INTERN void +nk_start(struct nk_context *ctx, struct nk_window *win) +{ + NK_ASSERT(ctx); + NK_ASSERT(win); + nk_start_buffer(ctx, &win->buffer); +} + +NK_INTERN void +nk_start_popup(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + + /* save buffer fill state for popup */ + buf = &win->popup.buf; + buf->begin = win->buffer.end; + buf->end = win->buffer.end; + buf->parent = win->buffer.last; + buf->last = buf->begin; + buf->active = nk_true; +} + +NK_INTERN void +nk_finish_popup(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + + buf = &win->popup.buf; + buf->last = win->buffer.last; + buf->end = win->buffer.end; +} + +NK_INTERN void +nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) +{ + NK_ASSERT(ctx); + NK_ASSERT(buffer); + if (!ctx || !buffer) return; + buffer->end = ctx->memory.allocated; +} + +NK_INTERN void +nk_finish(struct nk_context *ctx, struct nk_window *win) +{ + struct nk_popup_buffer *buf; + struct nk_command *parent_last; + void *memory; + + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!ctx || !win) return; + nk_finish_buffer(ctx, &win->buffer); + if (!win->popup.buf.active) return; + + buf = &win->popup.buf; + memory = ctx->memory.memory.ptr; + parent_last = nk_ptr_add(struct nk_command, memory, buf->parent); + parent_last->next = buf->end; +} + +NK_INTERN void +nk_build(struct nk_context *ctx) +{ + struct nk_window *iter = 0; + struct nk_command *cmd = 0; + nk_byte *buffer = 0; + + /* draw cursor overlay */ + if (!ctx->style.cursor_active) + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW]; + if (ctx->style.cursor_active && !ctx->input.mouse.grabbed && ctx->style.cursor_visible) { + struct nk_rect mouse_bounds; + const struct nk_cursor *cursor = ctx->style.cursor_active; + nk_command_buffer_init(&ctx->overlay, &ctx->memory, NK_CLIPPING_OFF); + nk_start_buffer(ctx, &ctx->overlay); + + mouse_bounds.x = ctx->input.mouse.pos.x - cursor->offset.x; + mouse_bounds.y = ctx->input.mouse.pos.y - cursor->offset.y; + mouse_bounds.w = cursor->size.x; + mouse_bounds.h = cursor->size.y; + + nk_draw_image(&ctx->overlay, mouse_bounds, &cursor->img, nk_white); + nk_finish_buffer(ctx, &ctx->overlay); + } + /* build one big draw command list out of all window buffers */ + iter = ctx->begin; + buffer = (nk_byte*)ctx->memory.memory.ptr; + while (iter != 0) { + struct nk_window *next = iter->next; + if (iter->buffer.last == iter->buffer.begin || (iter->flags & NK_WINDOW_HIDDEN)|| + iter->seq != ctx->seq) + goto cont; + + cmd = nk_ptr_add(struct nk_command, buffer, iter->buffer.last); + while (next && ((next->buffer.last == next->buffer.begin) || + (next->flags & NK_WINDOW_HIDDEN))) + next = next->next; /* skip empty command buffers */ + + if (next) cmd->next = next->buffer.begin; + cont: iter = next; + } + /* append all popup draw commands into lists */ + iter = ctx->begin; + while (iter != 0) { + struct nk_window *next = iter->next; + struct nk_popup_buffer *buf; + if (!iter->popup.buf.active) + goto skip; + + buf = &iter->popup.buf; + cmd->next = buf->begin; + cmd = nk_ptr_add(struct nk_command, buffer, buf->last); + buf->active = nk_false; + skip: iter = next; + } + /* append overlay commands */ + if (cmd) { + if (ctx->overlay.end != ctx->overlay.begin) + cmd->next = ctx->overlay.begin; + else cmd->next = ctx->memory.allocated; + } +} + +NK_API const struct nk_command* +nk__begin(struct nk_context *ctx) +{ + struct nk_window *iter; + nk_byte *buffer; + NK_ASSERT(ctx); + if (!ctx) return 0; + if (!ctx->count) return 0; + + buffer = (nk_byte*)ctx->memory.memory.ptr; + if (!ctx->build) { + nk_build(ctx); + ctx->build = nk_true; + } + iter = ctx->begin; + while (iter && ((iter->buffer.begin == iter->buffer.end) || (iter->flags & NK_WINDOW_HIDDEN))) + iter = iter->next; + if (!iter) return 0; + return nk_ptr_add_const(struct nk_command, buffer, iter->buffer.begin); +} + +NK_API const struct nk_command* +nk__next(struct nk_context *ctx, const struct nk_command *cmd) +{ + nk_byte *buffer; + const struct nk_command *next; + NK_ASSERT(ctx); + if (!ctx || !cmd || !ctx->count) return 0; + if (cmd->next >= ctx->memory.allocated) return 0; + buffer = (nk_byte*)ctx->memory.memory.ptr; + next = nk_ptr_add_const(struct nk_command, buffer, cmd->next); + return next; +} + +/* ---------------------------------------------------------------- + * + * PANEL + * + * ---------------------------------------------------------------*/ +static int +nk_panel_has_header(nk_flags flags, const char *title) +{ + int active = 0; + active = (flags & (NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE)); + active = active || (flags & NK_WINDOW_TITLE); + active = active && !(flags & NK_WINDOW_HIDDEN) && title; + return active; +} + +NK_INTERN struct nk_vec2 +nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type) +{ + switch (type) { + default: + case NK_PANEL_WINDOW: return style->window.padding; + case NK_PANEL_GROUP: return style->window.group_padding; + case NK_PANEL_POPUP: return style->window.popup_padding; + case NK_PANEL_CONTEXTUAL: return style->window.contextual_padding; + case NK_PANEL_COMBO: return style->window.combo_padding; + case NK_PANEL_MENU: return style->window.menu_padding; + case NK_PANEL_TOOLTIP: return style->window.menu_padding; + } +} + +NK_INTERN float +nk_panel_get_border(const struct nk_style *style, nk_flags flags, + enum nk_panel_type type) +{ + if (flags & NK_WINDOW_BORDER) { + switch (type) { + default: + case NK_PANEL_WINDOW: return style->window.border; + case NK_PANEL_GROUP: return style->window.group_border; + case NK_PANEL_POPUP: return style->window.popup_border; + case NK_PANEL_CONTEXTUAL: return style->window.contextual_border; + case NK_PANEL_COMBO: return style->window.combo_border; + case NK_PANEL_MENU: return style->window.menu_border; + case NK_PANEL_TOOLTIP: return style->window.menu_border; + }} else return 0; +} + +NK_INTERN struct nk_color +nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type) +{ + switch (type) { + default: + case NK_PANEL_WINDOW: return style->window.border_color; + case NK_PANEL_GROUP: return style->window.group_border_color; + case NK_PANEL_POPUP: return style->window.popup_border_color; + case NK_PANEL_CONTEXTUAL: return style->window.contextual_border_color; + case NK_PANEL_COMBO: return style->window.combo_border_color; + case NK_PANEL_MENU: return style->window.menu_border_color; + case NK_PANEL_TOOLTIP: return style->window.menu_border_color; + } +} + +NK_INTERN int +nk_panel_is_sub(enum nk_panel_type type) +{ + return (type & NK_PANEL_SET_SUB)?1:0; +} + +NK_INTERN int +nk_panel_is_nonblock(enum nk_panel_type type) +{ + return (type & NK_PANEL_SET_NONBLOCK)?1:0; +} + +NK_INTERN int +nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type) +{ + struct nk_input *in; + struct nk_window *win; + struct nk_panel *layout; + struct nk_command_buffer *out; + const struct nk_style *style; + const struct nk_user_font *font; + + struct nk_vec2 scrollbar_size; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + nk_zero(ctx->current->layout, sizeof(*ctx->current->layout)); + if ((ctx->current->flags & NK_WINDOW_HIDDEN) || (ctx->current->flags & NK_WINDOW_CLOSED)) { + nk_zero(ctx->current->layout, sizeof(struct nk_panel)); + ctx->current->layout->type = panel_type; + return 0; + } + /* pull state into local stack */ + style = &ctx->style; + font = style->font; + win = ctx->current; + layout = win->layout; + out = &win->buffer; + in = (win->flags & NK_WINDOW_NO_INPUT) ? 0: &ctx->input; +#ifdef NK_INCLUDE_COMMAND_USERDATA + win->buffer.userdata = ctx->userdata; +#endif + /* pull style configuration into local stack */ + scrollbar_size = style->window.scrollbar_size; + panel_padding = nk_panel_get_padding(style, panel_type); + + /* window movement */ + if ((win->flags & NK_WINDOW_MOVABLE) && !(win->flags & NK_WINDOW_ROM)) { + int left_mouse_down; + int left_mouse_click_in_cursor; + + /* calculate draggable window space */ + struct nk_rect header; + header.x = win->bounds.x; + header.y = win->bounds.y; + header.w = win->bounds.w; + if (nk_panel_has_header(win->flags, title)) { + header.h = font->height + 2.0f * style->window.header.padding.y; + header.h += 2.0f * style->window.header.label_padding.y; + } else header.h = panel_padding.y; + + /* window movement by dragging */ + left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, header, nk_true); + if (left_mouse_down && left_mouse_click_in_cursor) { + win->bounds.x = win->bounds.x + in->mouse.delta.x; + win->bounds.y = win->bounds.y + in->mouse.delta.y; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x += in->mouse.delta.x; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y += in->mouse.delta.y; + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_MOVE]; + } + } + + /* setup panel */ + layout->type = panel_type; + layout->flags = win->flags; + layout->bounds = win->bounds; + layout->bounds.x += panel_padding.x; + layout->bounds.w -= 2*panel_padding.x; + if (win->flags & NK_WINDOW_BORDER) { + layout->border = nk_panel_get_border(style, win->flags, panel_type); + layout->bounds = nk_shrink_rect(layout->bounds, layout->border); + } else layout->border = 0; + layout->at_y = layout->bounds.y; + layout->at_x = layout->bounds.x; + layout->max_x = 0; + layout->header_height = 0; + layout->footer_height = 0; + nk_layout_reset_min_row_height(ctx); + layout->row.index = 0; + layout->row.columns = 0; + layout->row.ratio = 0; + layout->row.item_width = 0; + layout->row.tree_depth = 0; + layout->row.height = panel_padding.y; + layout->has_scrolling = nk_true; + if (!(win->flags & NK_WINDOW_NO_SCROLLBAR)) + layout->bounds.w -= scrollbar_size.x; + if (!nk_panel_is_nonblock(panel_type)) { + layout->footer_height = 0; + if (!(win->flags & NK_WINDOW_NO_SCROLLBAR) || win->flags & NK_WINDOW_SCALABLE) + layout->footer_height = scrollbar_size.y; + layout->bounds.h -= layout->footer_height; + } + + /* panel header */ + if (nk_panel_has_header(win->flags, title)) + { + struct nk_text text; + struct nk_rect header; + const struct nk_style_item *background = 0; + + /* calculate header bounds */ + header.x = win->bounds.x; + header.y = win->bounds.y; + header.w = win->bounds.w; + header.h = font->height + 2.0f * style->window.header.padding.y; + header.h += (2.0f * style->window.header.label_padding.y); + + /* shrink panel by header */ + layout->header_height = header.h; + layout->bounds.y += header.h; + layout->bounds.h -= header.h; + layout->at_y += header.h; + + /* select correct header background and text color */ + if (ctx->active == win) { + background = &style->window.header.active; + text.text = style->window.header.label_active; + } else if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) { + background = &style->window.header.hover; + text.text = style->window.header.label_hover; + } else { + background = &style->window.header.normal; + text.text = style->window.header.label_normal; + } + + /* draw header background */ + header.h += 1.0f; + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(out, header, 0, background->data.color); + } + + /* window close button */ + {struct nk_rect button; + button.y = header.y + style->window.header.padding.y; + button.h = header.h - 2 * style->window.header.padding.y; + button.w = button.h; + if (win->flags & NK_WINDOW_CLOSABLE) { + nk_flags ws = 0; + if (style->window.header.align == NK_HEADER_RIGHT) { + button.x = (header.w + header.x) - (button.w + style->window.header.padding.x); + header.w -= button.w + style->window.header.spacing.x + style->window.header.padding.x; + } else { + button.x = header.x + style->window.header.padding.x; + header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; + } + + if (nk_do_button_symbol(&ws, &win->buffer, button, + style->window.header.close_symbol, NK_BUTTON_DEFAULT, + &style->window.header.close_button, in, style->font) && !(win->flags & NK_WINDOW_ROM)) + { + layout->flags |= NK_WINDOW_HIDDEN; + layout->flags &= (nk_flags)~NK_WINDOW_MINIMIZED; + } + } + + /* window minimize button */ + if (win->flags & NK_WINDOW_MINIMIZABLE) { + nk_flags ws = 0; + if (style->window.header.align == NK_HEADER_RIGHT) { + button.x = (header.w + header.x) - button.w; + if (!(win->flags & NK_WINDOW_CLOSABLE)) { + button.x -= style->window.header.padding.x; + header.w -= style->window.header.padding.x; + } + header.w -= button.w + style->window.header.spacing.x; + } else { + button.x = header.x; + header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; + } + if (nk_do_button_symbol(&ws, &win->buffer, button, (layout->flags & NK_WINDOW_MINIMIZED)? + style->window.header.maximize_symbol: style->window.header.minimize_symbol, + NK_BUTTON_DEFAULT, &style->window.header.minimize_button, in, style->font) && !(win->flags & NK_WINDOW_ROM)) + layout->flags = (layout->flags & NK_WINDOW_MINIMIZED) ? + layout->flags & (nk_flags)~NK_WINDOW_MINIMIZED: + layout->flags | NK_WINDOW_MINIMIZED; + }} + + {/* window header title */ + int text_len = nk_strlen(title); + struct nk_rect label = {0,0,0,0}; + float t = font->width(font->userdata, font->height, title, text_len); + text.padding = nk_vec2(0,0); + + label.x = header.x + style->window.header.padding.x; + label.x += style->window.header.label_padding.x; + label.y = header.y + style->window.header.label_padding.y; + label.h = font->height + 2 * style->window.header.label_padding.y; + label.w = t + 2 * style->window.header.spacing.x; + label.w = NK_CLAMP(0, label.w, header.x + header.w - label.x); + nk_widget_text(out, label,(const char*)title, text_len, &text, NK_TEXT_LEFT, font);} + } + + /* draw window background */ + if (!(layout->flags & NK_WINDOW_MINIMIZED) && !(layout->flags & NK_WINDOW_DYNAMIC)) { + struct nk_rect body; + body.x = win->bounds.x; + body.w = win->bounds.w; + body.y = (win->bounds.y + layout->header_height); + body.h = (win->bounds.h - layout->header_height); + if (style->window.fixed_background.type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, body, &style->window.fixed_background.data.image, nk_white); + else nk_fill_rect(out, body, 0, style->window.fixed_background.data.color); + } + + /* set clipping rectangle */ + {struct nk_rect clip; + layout->clip = layout->bounds; + nk_unify(&clip, &win->buffer.clip, layout->clip.x, layout->clip.y, + layout->clip.x + layout->clip.w, layout->clip.y + layout->clip.h); + nk_push_scissor(out, clip); + layout->clip = clip;} + return !(layout->flags & NK_WINDOW_HIDDEN) && !(layout->flags & NK_WINDOW_MINIMIZED); +} + +NK_INTERN void +nk_panel_end(struct nk_context *ctx) +{ + struct nk_input *in; + struct nk_window *window; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + + struct nk_vec2 scrollbar_size; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + window = ctx->current; + layout = window->layout; + style = &ctx->style; + out = &window->buffer; + in = (layout->flags & NK_WINDOW_ROM || layout->flags & NK_WINDOW_NO_INPUT) ? 0 :&ctx->input; + if (!nk_panel_is_sub(layout->type)) + nk_push_scissor(out, nk_null_rect); + + /* cache configuration data */ + scrollbar_size = style->window.scrollbar_size; + panel_padding = nk_panel_get_padding(style, layout->type); + + /* update the current cursor Y-position to point over the last added widget */ + layout->at_y += layout->row.height; + + /* dynamic panels */ + if (layout->flags & NK_WINDOW_DYNAMIC && !(layout->flags & NK_WINDOW_MINIMIZED)) + { + /* update panel height to fit dynamic growth */ + struct nk_rect empty_space; + if (layout->at_y < (layout->bounds.y + layout->bounds.h)) + layout->bounds.h = layout->at_y - layout->bounds.y; + + /* fill top empty space */ + empty_space.x = window->bounds.x; + empty_space.y = layout->bounds.y; + empty_space.h = panel_padding.y; + empty_space.w = window->bounds.w; + nk_fill_rect(out, empty_space, 0, style->window.background); + + /* fill left empty space */ + empty_space.x = window->bounds.x; + empty_space.y = layout->bounds.y; + empty_space.w = panel_padding.x + layout->border; + empty_space.h = layout->bounds.h; + nk_fill_rect(out, empty_space, 0, style->window.background); + + /* fill right empty space */ + empty_space.x = layout->bounds.x + layout->bounds.w - layout->border; + empty_space.y = layout->bounds.y; + empty_space.w = panel_padding.x + layout->border; + empty_space.h = layout->bounds.h; + if (*layout->offset_y == 0 && !(layout->flags & NK_WINDOW_NO_SCROLLBAR)) + empty_space.w += scrollbar_size.x; + nk_fill_rect(out, empty_space, 0, style->window.background); + + /* fill bottom empty space */ + if (*layout->offset_x != 0 && !(layout->flags & NK_WINDOW_NO_SCROLLBAR)) { + empty_space.x = window->bounds.x; + empty_space.y = layout->bounds.y + layout->bounds.h; + empty_space.w = window->bounds.w; + empty_space.h = scrollbar_size.y; + nk_fill_rect(out, empty_space, 0, style->window.background); + } + } + + /* scrollbars */ + if (!(layout->flags & NK_WINDOW_NO_SCROLLBAR) && + !(layout->flags & NK_WINDOW_MINIMIZED) && + window->scrollbar_hiding_timer < NK_SCROLLBAR_HIDING_TIMEOUT) + { + struct nk_rect scroll; + int scroll_has_scrolling; + float scroll_target; + float scroll_offset; + float scroll_step; + float scroll_inc; + + /* mouse wheel scrolling */ + if (nk_panel_is_sub(layout->type)) + { + /* sub-window mouse wheel scrolling */ + struct nk_window *root_window = window; + struct nk_panel *root_panel = window->layout; + while (root_panel->parent) + root_panel = root_panel->parent; + while (root_window->parent) + root_window = root_window->parent; + + /* only allow scrolling if parent window is active */ + scroll_has_scrolling = 0; + if ((root_window == ctx->active) && layout->has_scrolling) { + /* and panel is being hovered and inside clip rect*/ + if (nk_input_is_mouse_hovering_rect(in, layout->bounds) && + NK_INTERSECT(layout->bounds.x, layout->bounds.y, layout->bounds.w, layout->bounds.h, + root_panel->clip.x, root_panel->clip.y, root_panel->clip.w, root_panel->clip.h)) + { + /* deactivate all parent scrolling */ + root_panel = window->layout; + while (root_panel->parent) { + root_panel->has_scrolling = nk_false; + root_panel = root_panel->parent; + } + root_panel->has_scrolling = nk_false; + scroll_has_scrolling = nk_true; + } + } + } else if (!nk_panel_is_sub(layout->type)) { + /* window mouse wheel scrolling */ + scroll_has_scrolling = (window == ctx->active) && layout->has_scrolling; + if (in && (in->mouse.scroll_delta.y > 0 || in->mouse.scroll_delta.x > 0) && scroll_has_scrolling) + window->scrolled = nk_true; + else window->scrolled = nk_false; + } else scroll_has_scrolling = nk_false; + + { + /* vertical scrollbar */ + nk_flags state = 0; + scroll.x = layout->bounds.x + layout->bounds.w + panel_padding.x; + scroll.y = layout->bounds.y; + scroll.w = scrollbar_size.x; + scroll.h = layout->bounds.h; + + scroll_offset = (float)*layout->offset_y; + scroll_step = scroll.h * 0.10f; + scroll_inc = scroll.h * 0.01f; + scroll_target = (float)(int)(layout->at_y - scroll.y); + scroll_offset = nk_do_scrollbarv(&state, out, scroll, scroll_has_scrolling, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &ctx->style.scrollv, in, style->font); + *layout->offset_y = (nk_uint)scroll_offset; + if (in && scroll_has_scrolling) + in->mouse.scroll_delta.y = 0; + } + { + /* horizontal scrollbar */ + nk_flags state = 0; + scroll.x = layout->bounds.x; + scroll.y = layout->bounds.y + layout->bounds.h; + scroll.w = layout->bounds.w; + scroll.h = scrollbar_size.y; + + scroll_offset = (float)*layout->offset_x; + scroll_target = (float)(int)(layout->max_x - scroll.x); + scroll_step = layout->max_x * 0.05f; + scroll_inc = layout->max_x * 0.005f; + scroll_offset = nk_do_scrollbarh(&state, out, scroll, scroll_has_scrolling, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &ctx->style.scrollh, in, style->font); + *layout->offset_x = (nk_uint)scroll_offset; + } + } + + /* hide scroll if no user input */ + if (window->flags & NK_WINDOW_SCROLL_AUTO_HIDE) { + int has_input = ctx->input.mouse.delta.x != 0 || ctx->input.mouse.delta.y != 0 || ctx->input.mouse.scroll_delta.y != 0; + int is_window_hovered = nk_window_is_hovered(ctx); + int any_item_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED); + if ((!has_input && is_window_hovered) || (!is_window_hovered && !any_item_active)) + window->scrollbar_hiding_timer += ctx->delta_time_seconds; + else window->scrollbar_hiding_timer = 0; + } else window->scrollbar_hiding_timer = 0; + + /* window border */ + if (layout->flags & NK_WINDOW_BORDER) + { + struct nk_color border_color = nk_panel_get_border_color(style, layout->type); + const float padding_y = (layout->flags & NK_WINDOW_MINIMIZED) ? + style->window.border + window->bounds.y + layout->header_height: + (layout->flags & NK_WINDOW_DYNAMIC)? + layout->bounds.y + layout->bounds.h + layout->footer_height: + window->bounds.y + window->bounds.h; + + /* draw border top */ + nk_stroke_line(out,window->bounds.x,window->bounds.y, + window->bounds.x + window->bounds.w, window->bounds.y, + layout->border, border_color); + + /* draw bottom border */ + nk_stroke_line(out, window->bounds.x, padding_y, + window->bounds.x + window->bounds.w, padding_y, layout->border, border_color); + + /* draw left border */ + nk_stroke_line(out, window->bounds.x + layout->border*0.5f, + window->bounds.y, window->bounds.x + layout->border*0.5f, + padding_y, layout->border, border_color); + + /* draw right border */ + nk_stroke_line(out, window->bounds.x + window->bounds.w - layout->border*0.5f, + window->bounds.y, window->bounds.x + window->bounds.w - layout->border*0.5f, + padding_y, layout->border, border_color); + } + + /* scaler */ + if ((layout->flags & NK_WINDOW_SCALABLE) && in && !(layout->flags & NK_WINDOW_MINIMIZED)) + { + /* calculate scaler bounds */ + struct nk_rect scaler; + scaler.w = scrollbar_size.x; + scaler.h = scrollbar_size.y; + scaler.y = layout->bounds.y + layout->bounds.h; + if (layout->flags & NK_WINDOW_SCALE_LEFT) + scaler.x = layout->bounds.x - panel_padding.x * 0.5f; + else scaler.x = layout->bounds.x + layout->bounds.w + panel_padding.x; + if (layout->flags & NK_WINDOW_NO_SCROLLBAR) + scaler.x -= scaler.w; + + /* draw scaler */ + {const struct nk_style_item *item = &style->window.scaler; + if (item->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, scaler, &item->data.image, nk_white); + else { + if (layout->flags & NK_WINDOW_SCALE_LEFT) { + nk_fill_triangle(out, scaler.x, scaler.y, scaler.x, + scaler.y + scaler.h, scaler.x + scaler.w, + scaler.y + scaler.h, item->data.color); + } else { + nk_fill_triangle(out, scaler.x + scaler.w, scaler.y, scaler.x + scaler.w, + scaler.y + scaler.h, scaler.x, scaler.y + scaler.h, item->data.color); + } + }} + + /* do window scaling */ + if (!(window->flags & NK_WINDOW_ROM)) { + struct nk_vec2 window_size = style->window.min_size; + int left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_scaler = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, scaler, nk_true); + + if (left_mouse_down && left_mouse_click_in_scaler) { + float delta_x = in->mouse.delta.x; + if (layout->flags & NK_WINDOW_SCALE_LEFT) { + delta_x = -delta_x; + window->bounds.x += in->mouse.delta.x; + } + /* dragging in x-direction */ + if (window->bounds.w + delta_x >= window_size.x) { + if ((delta_x < 0) || (delta_x > 0 && in->mouse.pos.x >= scaler.x)) { + window->bounds.w = window->bounds.w + delta_x; + scaler.x += in->mouse.delta.x; + } + } + /* dragging in y-direction (only possible if static window) */ + if (!(layout->flags & NK_WINDOW_DYNAMIC)) { + if (window_size.y < window->bounds.h + in->mouse.delta.y) { + if ((in->mouse.delta.y < 0) || (in->mouse.delta.y > 0 && in->mouse.pos.y >= scaler.y)) { + window->bounds.h = window->bounds.h + in->mouse.delta.y; + scaler.y += in->mouse.delta.y; + } + } + } + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT]; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = scaler.x + scaler.w/2.0f; + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = scaler.y + scaler.h/2.0f; + } + } + } + if (!nk_panel_is_sub(layout->type)) { + /* window is hidden so clear command buffer */ + if (layout->flags & NK_WINDOW_HIDDEN) + nk_command_buffer_reset(&window->buffer); + /* window is visible and not tab */ + else nk_finish(ctx, window); + } + + /* NK_WINDOW_REMOVE_ROM flag was set so remove NK_WINDOW_ROM */ + if (layout->flags & NK_WINDOW_REMOVE_ROM) { + layout->flags &= ~(nk_flags)NK_WINDOW_ROM; + layout->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; + } + window->flags = layout->flags; + + /* property garbage collector */ + if (window->property.active && window->property.old != window->property.seq && + window->property.active == window->property.prev) { + nk_zero(&window->property, sizeof(window->property)); + } else { + window->property.old = window->property.seq; + window->property.prev = window->property.active; + window->property.seq = 0; + } + /* edit garbage collector */ + if (window->edit.active && window->edit.old != window->edit.seq && + window->edit.active == window->edit.prev) { + nk_zero(&window->edit, sizeof(window->edit)); + } else { + window->edit.old = window->edit.seq; + window->edit.prev = window->edit.active; + window->edit.seq = 0; + } + /* contextual garbage collector */ + if (window->popup.active_con && window->popup.con_old != window->popup.con_count) { + window->popup.con_count = 0; + window->popup.con_old = 0; + window->popup.active_con = 0; + } else { + window->popup.con_old = window->popup.con_count; + window->popup.con_count = 0; + } + window->popup.combo_count = 0; + /* helper to make sure you have a 'nk_tree_push' for every 'nk_tree_pop' */ + NK_ASSERT(!layout->row.tree_depth); +} + +/* ---------------------------------------------------------------- + * + * PAGE ELEMENT + * + * ---------------------------------------------------------------*/ +NK_INTERN struct nk_page_element* +nk_create_page_element(struct nk_context *ctx) +{ + struct nk_page_element *elem; + if (ctx->freelist) { + /* unlink page element from free list */ + elem = ctx->freelist; + ctx->freelist = elem->next; + } else if (ctx->use_pool) { + /* allocate page element from memory pool */ + elem = nk_pool_alloc(&ctx->pool); + NK_ASSERT(elem); + if (!elem) return 0; + } else { + /* allocate new page element from back of fixed size memory buffer */ + NK_STORAGE const nk_size size = sizeof(struct nk_page_element); + NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_page_element); + elem = (struct nk_page_element*)nk_buffer_alloc(&ctx->memory, NK_BUFFER_BACK, size, align); + NK_ASSERT(elem); + if (!elem) return 0; + } + nk_zero_struct(*elem); + elem->next = 0; + elem->prev = 0; + return elem; +} + +NK_INTERN void +nk_link_page_element_into_freelist(struct nk_context *ctx, + struct nk_page_element *elem) +{ + /* link table into freelist */ + if (!ctx->freelist) { + ctx->freelist = elem; + } else { + elem->next = ctx->freelist; + ctx->freelist = elem; + } +} + +NK_INTERN void +nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem) +{ + /* we have a pool so just add to free list */ + if (ctx->use_pool) { + nk_link_page_element_into_freelist(ctx, elem); + return; + } + /* if possible remove last element from back of fixed memory buffer */ + {void *elem_end = (void*)(elem + 1); + void *buffer_end = (nk_byte*)ctx->memory.memory.ptr + ctx->memory.size; + if (elem_end == buffer_end) + ctx->memory.size -= sizeof(struct nk_page_element); + else nk_link_page_element_into_freelist(ctx, elem);} +} + +/* ---------------------------------------------------------------- + * + * PANEL + * + * ---------------------------------------------------------------*/ +NK_INTERN void* +nk_create_panel(struct nk_context *ctx) +{ + struct nk_page_element *elem; + elem = nk_create_page_element(ctx); + if (!elem) return 0; + nk_zero_struct(*elem); + return &elem->data.pan; +} + +NK_INTERN void +nk_free_panel(struct nk_context *ctx, struct nk_panel *pan) +{ + union nk_page_data *pd = NK_CONTAINER_OF(pan, union nk_page_data, pan); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe); +} + +/* ---------------------------------------------------------------- + * + * TABLES + * + * ---------------------------------------------------------------*/ +NK_INTERN struct nk_table* +nk_create_table(struct nk_context *ctx) +{ + struct nk_page_element *elem; + elem = nk_create_page_element(ctx); + if (!elem) return 0; + nk_zero_struct(*elem); + return &elem->data.tbl; +} + +NK_INTERN void +nk_free_table(struct nk_context *ctx, struct nk_table *tbl) +{ + union nk_page_data *pd = NK_CONTAINER_OF(tbl, union nk_page_data, tbl); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe); +} + +NK_INTERN void +nk_push_table(struct nk_window *win, struct nk_table *tbl) +{ + if (!win->tables) { + win->tables = tbl; + tbl->next = 0; + tbl->prev = 0; + tbl->size = 0; + win->table_count = 1; + return; + } + win->tables->prev = tbl; + tbl->next = win->tables; + tbl->prev = 0; + tbl->size = 0; + win->tables = tbl; + win->table_count++; +} + +NK_INTERN void +nk_remove_table(struct nk_window *win, struct nk_table *tbl) +{ + if (win->tables == tbl) + win->tables = tbl->next; + if (tbl->next) + tbl->next->prev = tbl->prev; + if (tbl->prev) + tbl->prev->next = tbl->next; + tbl->next = 0; + tbl->prev = 0; +} + +NK_INTERN nk_uint* +nk_add_value(struct nk_context *ctx, struct nk_window *win, + nk_hash name, nk_uint value) +{ + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!win || !ctx) return 0; + if (!win->tables || win->tables->size >= NK_VALUE_PAGE_CAPACITY) { + struct nk_table *tbl = nk_create_table(ctx); + NK_ASSERT(tbl); + if (!tbl) return 0; + nk_push_table(win, tbl); + } + win->tables->seq = win->seq; + win->tables->keys[win->tables->size] = name; + win->tables->values[win->tables->size] = value; + return &win->tables->values[win->tables->size++]; +} + +NK_INTERN nk_uint* +nk_find_value(struct nk_window *win, nk_hash name) +{ + struct nk_table *iter = win->tables; + while (iter) { + unsigned int i = 0; + unsigned int size = iter->size; + for (i = 0; i < size; ++i) { + if (iter->keys[i] == name) { + iter->seq = win->seq; + return &iter->values[i]; + } + } size = NK_VALUE_PAGE_CAPACITY; + iter = iter->next; + } + return 0; +} + +/* ---------------------------------------------------------------- + * + * WINDOW + * + * ---------------------------------------------------------------*/ +NK_INTERN void* +nk_create_window(struct nk_context *ctx) +{ + struct nk_page_element *elem; + elem = nk_create_page_element(ctx); + if (!elem) return 0; + elem->data.win.seq = ctx->seq; + return &elem->data.win; +} + +NK_INTERN void +nk_free_window(struct nk_context *ctx, struct nk_window *win) +{ + /* unlink windows from list */ + struct nk_table *it = win->tables; + if (win->popup.win) { + nk_free_window(ctx, win->popup.win); + win->popup.win = 0; + } + win->next = 0; + win->prev = 0; + + while (it) { + /*free window state tables */ + struct nk_table *n = it->next; + nk_remove_table(win, it); + nk_free_table(ctx, it); + if (it == win->tables) + win->tables = n; + it = n; + } + + /* link windows into freelist */ + {union nk_page_data *pd = NK_CONTAINER_OF(win, union nk_page_data, win); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe);} +} + +NK_INTERN struct nk_window* +nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name) +{ + struct nk_window *iter; + iter = ctx->begin; + while (iter) { + NK_ASSERT(iter != iter->next); + if (iter->name == hash) { + int max_len = nk_strlen(iter->name_string); + if (!nk_stricmpn(iter->name_string, name, max_len)) + return iter; + } + iter = iter->next; + } + return 0; +} + +enum nk_window_insert_location { + NK_INSERT_BACK, /* inserts window into the back of list (front of screen) */ + NK_INSERT_FRONT /* inserts window into the front of list (back of screen) */ +}; +NK_INTERN void +nk_insert_window(struct nk_context *ctx, struct nk_window *win, + enum nk_window_insert_location loc) +{ + const struct nk_window *iter; + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!win || !ctx) return; + + iter = ctx->begin; + while (iter) { + NK_ASSERT(iter != iter->next); + NK_ASSERT(iter != win); + if (iter == win) return; + iter = iter->next; + } + + if (!ctx->begin) { + win->next = 0; + win->prev = 0; + ctx->begin = win; + ctx->end = win; + ctx->count = 1; + return; + } + if (loc == NK_INSERT_BACK) { + struct nk_window *end; + end = ctx->end; + end->flags |= NK_WINDOW_ROM; + end->next = win; + win->prev = ctx->end; + win->next = 0; + ctx->end = win; + ctx->active = ctx->end; + ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; + } else { + ctx->end->flags |= NK_WINDOW_ROM; + ctx->begin->prev = win; + win->next = ctx->begin; + win->prev = 0; + ctx->begin = win; + ctx->begin->flags &= ~(nk_flags)NK_WINDOW_ROM; + } + ctx->count++; +} + +NK_INTERN void +nk_remove_window(struct nk_context *ctx, struct nk_window *win) +{ + if (win == ctx->begin || win == ctx->end) { + if (win == ctx->begin) { + ctx->begin = win->next; + if (win->next) + win->next->prev = 0; + } + if (win == ctx->end) { + ctx->end = win->prev; + if (win->prev) + win->prev->next = 0; + } + } else { + if (win->next) + win->next->prev = win->prev; + if (win->prev) + win->prev->next = win->next; + } + if (win == ctx->active || !ctx->active) { + ctx->active = ctx->end; + if (ctx->end) + ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; + } + win->next = 0; + win->prev = 0; + ctx->count--; +} + +NK_API int +nk_begin(struct nk_context *ctx, const char *title, + struct nk_rect bounds, nk_flags flags) +{ + return nk_begin_titled(ctx, title, title, bounds, flags); +} + +NK_API int +nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, + struct nk_rect bounds, nk_flags flags) +{ + struct nk_window *win; + struct nk_style *style; + nk_hash title_hash; + int title_len; + int ret = 0; + + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(title); + NK_ASSERT(ctx->style.font && ctx->style.font->width && "if this triggers you forgot to add a font"); + NK_ASSERT(!ctx->current && "if this triggers you missed a `nk_end` call"); + if (!ctx || ctx->current || !title || !name) + return 0; + + /* find or create window */ + style = &ctx->style; + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) { + /* create new window */ + nk_size name_length = (nk_size)nk_strlen(name); + win = (struct nk_window*)nk_create_window(ctx); + NK_ASSERT(win); + if (!win) return 0; + + if (flags & NK_WINDOW_BACKGROUND) + nk_insert_window(ctx, win, NK_INSERT_FRONT); + else nk_insert_window(ctx, win, NK_INSERT_BACK); + nk_command_buffer_init(&win->buffer, &ctx->memory, NK_CLIPPING_ON); + + win->flags = flags; + win->bounds = bounds; + win->name = title_hash; + name_length = NK_MIN(name_length, NK_WINDOW_MAX_NAME-1); + NK_MEMCPY(win->name_string, name, name_length); + win->name_string[name_length] = 0; + win->popup.win = 0; + if (!ctx->active) + ctx->active = win; + } else { + /* update window */ + win->flags &= ~(nk_flags)(NK_WINDOW_PRIVATE-1); + win->flags |= flags; + if (!(win->flags & (NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE))) + win->bounds = bounds; + /* If this assert triggers you either: + * + * I.) Have more than one window with the same name or + * II.) You forgot to actually draw the window. + * More specific you did not call `nk_clear` (nk_clear will be + * automatically called for you if you are using one of the + * provided demo backends). */ + NK_ASSERT(win->seq != ctx->seq); + win->seq = ctx->seq; + if (!ctx->active && !(win->flags & NK_WINDOW_HIDDEN)) + ctx->active = win; + } + if (win->flags & NK_WINDOW_HIDDEN) { + ctx->current = win; + win->layout = 0; + return 0; + } + + /* window overlapping */ + if (!(win->flags & NK_WINDOW_HIDDEN) && !(win->flags & NK_WINDOW_NO_INPUT)) + { + int inpanel, ishovered; + const struct nk_window *iter = win; + float h = ctx->style.font->height + 2.0f * style->window.header.padding.y + + (2.0f * style->window.header.label_padding.y); + struct nk_rect win_bounds = (!(win->flags & NK_WINDOW_MINIMIZED))? + win->bounds: nk_rect(win->bounds.x, win->bounds.y, win->bounds.w, h); + + /* activate window if hovered and no other window is overlapping this window */ + nk_start(ctx, win); + inpanel = nk_input_has_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_LEFT, win_bounds, nk_true); + inpanel = inpanel && ctx->input.mouse.buttons[NK_BUTTON_LEFT].clicked; + ishovered = nk_input_is_mouse_hovering_rect(&ctx->input, win_bounds); + if ((win != ctx->active) && ishovered && !ctx->input.mouse.buttons[NK_BUTTON_LEFT].down) { + iter = win->next; + while (iter) { + struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))? + iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h); + if (NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, + iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) && + (!(iter->flags & NK_WINDOW_HIDDEN) || !(iter->flags & NK_WINDOW_BACKGROUND))) + break; + + if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && + NK_INTERSECT(win->bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, + iter->popup.win->bounds.x, iter->popup.win->bounds.y, + iter->popup.win->bounds.w, iter->popup.win->bounds.h)) + break; + iter = iter->next; + } + } + + /* activate window if clicked */ + if (iter && inpanel && (win != ctx->end) && !(iter->flags & NK_WINDOW_BACKGROUND)) { + iter = win->next; + while (iter) { + /* try to find a panel with higher priority in the same position */ + struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))? + iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h); + if (NK_INBOX(ctx->input.mouse.pos.x, ctx->input.mouse.pos.y, + iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) && + !(iter->flags & NK_WINDOW_HIDDEN)) + break; + if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && + NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, + iter->popup.win->bounds.x, iter->popup.win->bounds.y, + iter->popup.win->bounds.w, iter->popup.win->bounds.h)) + break; + iter = iter->next; + } + } + + if (!iter && ctx->end != win) { + if (!(win->flags & NK_WINDOW_BACKGROUND)) { + /* current window is active in that position so transfer to top + * at the highest priority in stack */ + nk_remove_window(ctx, win); + nk_insert_window(ctx, win, NK_INSERT_BACK); + } + win->flags &= ~(nk_flags)NK_WINDOW_ROM; + ctx->active = win; + } + if (ctx->end != win && !(win->flags & NK_WINDOW_BACKGROUND)) + win->flags |= NK_WINDOW_ROM; + } + + win->layout = (struct nk_panel*)nk_create_panel(ctx); + ctx->current = win; + ret = nk_panel_begin(ctx, title, NK_PANEL_WINDOW); + win->layout->offset_x = &win->scrollbar.x; + win->layout->offset_y = &win->scrollbar.y; + return ret; +} + +NK_API void +nk_end(struct nk_context *ctx) +{ + struct nk_panel *layout; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current && "if this triggers you forgot to call `nk_begin`"); + if (!ctx || !ctx->current) + return; + + layout = ctx->current->layout; + if (!layout || (layout->type == NK_PANEL_WINDOW && (ctx->current->flags & NK_WINDOW_HIDDEN))) { + ctx->current = 0; + return; + } + nk_panel_end(ctx); + nk_free_panel(ctx, ctx->current->layout); + ctx->current = 0; +} + +NK_API struct nk_rect +nk_window_get_bounds(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_rect(0,0,0,0); + return ctx->current->bounds; +} + +NK_API struct nk_vec2 +nk_window_get_position(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->bounds.x, ctx->current->bounds.y); +} + +NK_API struct nk_vec2 +nk_window_get_size(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->bounds.w, ctx->current->bounds.h); +} + +NK_API float +nk_window_get_width(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->bounds.w; +} + +NK_API float +nk_window_get_height(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->bounds.h; +} + +NK_API struct nk_rect +nk_window_get_content_region(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return nk_rect(0,0,0,0); + return ctx->current->layout->clip; +} + +NK_API struct nk_vec2 +nk_window_get_content_region_min(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.x, ctx->current->layout->clip.y); +} + +NK_API struct nk_vec2 +nk_window_get_content_region_max(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.x + ctx->current->layout->clip.w, + ctx->current->layout->clip.y + ctx->current->layout->clip.h); +} + +NK_API struct nk_vec2 +nk_window_get_content_region_size(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return nk_vec2(0,0); + return nk_vec2(ctx->current->layout->clip.w, ctx->current->layout->clip.h); +} + +NK_API struct nk_command_buffer* +nk_window_get_canvas(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return 0; + return &ctx->current->buffer; +} + +NK_API struct nk_panel* +nk_window_get_panel(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return ctx->current->layout; +} + +NK_API int +nk_window_has_focus(const struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current) return 0; + return ctx->current == ctx->active; +} + +NK_API int +nk_window_is_hovered(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return 0; + return nk_input_is_mouse_hovering_rect(&ctx->input, ctx->current->bounds); +} + +NK_API int +nk_window_is_any_hovered(struct nk_context *ctx) +{ + struct nk_window *iter; + NK_ASSERT(ctx); + if (!ctx) return 0; + iter = ctx->begin; + while (iter) { + /* check if window is being hovered */ + if (iter->flags & NK_WINDOW_MINIMIZED) { + struct nk_rect header = iter->bounds; + header.h = ctx->style.font->height + 2 * ctx->style.window.header.padding.y; + if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) + return 1; + } else if (nk_input_is_mouse_hovering_rect(&ctx->input, iter->bounds)) { + return 1; + } + /* check if window popup is being hovered */ + if (iter->popup.active && iter->popup.win && nk_input_is_mouse_hovering_rect(&ctx->input, iter->popup.win->bounds)) + return 1; + iter = iter->next; + } + return 0; +} + +NK_API int +nk_item_is_any_active(struct nk_context *ctx) +{ + int any_hovered = nk_window_is_any_hovered(ctx); + int any_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED); + return any_hovered || any_active; +} + +NK_API int +nk_window_is_collapsed(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 0; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 0; + return win->flags & NK_WINDOW_MINIMIZED; +} + +NK_API int +nk_window_is_closed(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 1; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 1; + return (win->flags & NK_WINDOW_CLOSED); +} + +NK_API int +nk_window_is_hidden(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 1; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 1; + return (win->flags & NK_WINDOW_HIDDEN); +} + +NK_API int +nk_window_is_active(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return 0; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return 0; + return win == ctx->active; +} + +NK_API struct nk_window* +nk_window_find(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + return nk_find_window(ctx, title_hash, name); +} + +NK_API void +nk_window_close(struct nk_context *ctx, const char *name) +{ + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + win = nk_window_find(ctx, name); + if (!win) return; + NK_ASSERT(ctx->current != win && "You cannot close a currently active window"); + if (ctx->current == win) return; + win->flags |= NK_WINDOW_HIDDEN; + win->flags |= NK_WINDOW_CLOSED; +} + +NK_API void +nk_window_set_bounds(struct nk_context *ctx, struct nk_rect bounds) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->bounds = bounds; +} + +NK_API void +nk_window_set_position(struct nk_context *ctx, struct nk_vec2 pos) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->bounds.x = pos.x; + ctx->current->bounds.y = pos.y; +} + +NK_API void +nk_window_set_size(struct nk_context *ctx, struct nk_vec2 size) +{ + NK_ASSERT(ctx); NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->bounds.w = size.x; + ctx->current->bounds.h = size.y; +} + +NK_API void +nk_window_collapse(struct nk_context *ctx, const char *name, + enum nk_collapse_states c) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return; + if (c == NK_MINIMIZED) + win->flags |= NK_WINDOW_MINIMIZED; + else win->flags &= ~(nk_flags)NK_WINDOW_MINIMIZED; +} + +NK_API void +nk_window_collapse_if(struct nk_context *ctx, const char *name, + enum nk_collapse_states c, int cond) +{ + NK_ASSERT(ctx); + if (!ctx || !cond) return; + nk_window_collapse(ctx, name, c); +} + +NK_API void +nk_window_show(struct nk_context *ctx, const char *name, enum nk_show_states s) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (!win) return; + if (s == NK_HIDDEN) { + win->flags |= NK_WINDOW_HIDDEN; + } else win->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; +} + +NK_API void +nk_window_show_if(struct nk_context *ctx, const char *name, + enum nk_show_states s, int cond) +{ + NK_ASSERT(ctx); + if (!ctx || !cond) return; + nk_window_show(ctx, name, s); +} + +NK_API void +nk_window_set_focus(struct nk_context *ctx, const char *name) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + NK_ASSERT(ctx); + if (!ctx) return; + + title_len = (int)nk_strlen(name); + title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); + win = nk_find_window(ctx, title_hash, name); + if (win && ctx->end != win) { + nk_remove_window(ctx, win); + nk_insert_window(ctx, win, NK_INSERT_BACK); + } + ctx->active = win; +} + +/*---------------------------------------------------------------- + * + * MENUBAR + * + * --------------------------------------------------------------*/ +NK_API void +nk_menubar_begin(struct nk_context *ctx) +{ + struct nk_panel *layout; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + layout = ctx->current->layout; + NK_ASSERT(layout->at_y == layout->bounds.y); + /* if this assert triggers you allocated space between nk_begin and nk_menubar_begin. + If you want a menubar the first nuklear function after `nk_begin` has to be a + `nk_menubar_begin` call. Inside the menubar you then have to allocate space for + widgets (also supports multiple rows). + Example: + if (nk_begin(...)) { + nk_menubar_begin(...); + nk_layout_xxxx(...); + nk_button(...); + nk_layout_xxxx(...); + nk_button(...); + nk_menubar_end(...); + } + nk_end(...); + */ + if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) + return; + + layout->menu.x = layout->at_x; + layout->menu.y = layout->at_y + layout->row.height; + layout->menu.w = layout->bounds.w; + layout->menu.offset.x = *layout->offset_x; + layout->menu.offset.y = *layout->offset_y; + *layout->offset_y = 0; +} + +NK_API void +nk_menubar_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_command_buffer *out; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + out = &win->buffer; + layout = win->layout; + if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) + return; + + layout->menu.h = layout->at_y - layout->menu.y; + layout->bounds.y += layout->menu.h + ctx->style.window.spacing.y + layout->row.height; + layout->bounds.h -= layout->menu.h + ctx->style.window.spacing.y + layout->row.height; + + *layout->offset_x = layout->menu.offset.x; + *layout->offset_y = layout->menu.offset.y; + layout->at_y = layout->bounds.y - layout->row.height; + + layout->clip.y = layout->bounds.y; + layout->clip.h = layout->bounds.h; + nk_push_scissor(out, layout->clip); +} +/* ------------------------------------------------------------- + * + * LAYOUT + * + * --------------------------------------------------------------*/ +NK_API void +nk_layout_set_min_row_height(struct nk_context *ctx, float height) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.min_height = height; +} + +NK_API void +nk_layout_reset_min_row_height(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.min_height = ctx->style.font->height; + layout->row.min_height += ctx->style.text.padding.y*2; + layout->row.min_height += ctx->style.window.min_row_height_padding*2; +} + +NK_INTERN float +nk_layout_row_calculate_usable_space(const struct nk_style *style, enum nk_panel_type type, + float total_space, int columns) +{ + float panel_padding; + float panel_spacing; + float panel_space; + + struct nk_vec2 spacing; + struct nk_vec2 padding; + + spacing = style->window.spacing; + padding = nk_panel_get_padding(style, type); + + /* calculate the usable panel space */ + panel_padding = 2 * padding.x; + panel_spacing = (float)NK_MAX(columns - 1, 0) * spacing.x; + panel_space = total_space - panel_padding - panel_spacing; + return panel_space; +} + +NK_INTERN void +nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, + float height, int cols) +{ + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + + struct nk_vec2 item_spacing; + struct nk_color color; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* prefetch some configuration data */ + layout = win->layout; + style = &ctx->style; + out = &win->buffer; + color = style->window.background; + item_spacing = style->window.spacing; + + /* if one of these triggers you forgot to add an `if` condition around either + a window, group, popup, combobox or contextual menu `begin` and `end` block. + Example: + if (nk_begin(...) {...} nk_end(...); or + if (nk_group_begin(...) { nk_group_end(...);} */ + NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED)); + NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN)); + NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED)); + + /* update the current row and set the current row layout */ + layout->row.index = 0; + layout->at_y += layout->row.height; + layout->row.columns = cols; + if (height == 0.0f) + layout->row.height = NK_MAX(height, layout->row.min_height) + item_spacing.y; + else layout->row.height = height + item_spacing.y; + + layout->row.item_offset = 0; + if (layout->flags & NK_WINDOW_DYNAMIC) { + /* draw background for dynamic panels */ + struct nk_rect background; + background.x = win->bounds.x; + background.w = win->bounds.w; + background.y = layout->at_y - 1.0f; + background.h = layout->row.height + 1.0f; + nk_fill_rect(out, background, 0, color); + } +} + +NK_INTERN void +nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int cols, int width) +{ + /* update the current row and set the current row layout */ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + nk_panel_layout(ctx, win, height, cols); + if (fmt == NK_DYNAMIC) + win->layout->row.type = NK_LAYOUT_DYNAMIC_FIXED; + else win->layout->row.type = NK_LAYOUT_STATIC_FIXED; + + win->layout->row.ratio = 0; + win->layout->row.filled = 0; + win->layout->row.item_offset = 0; + win->layout->row.item_width = (float)width; +} + +NK_API float +nk_layout_ratio_from_pixel(struct nk_context *ctx, float pixel_width) +{ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(pixel_width); + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + win = ctx->current; + return NK_CLAMP(0.0f, pixel_width/win->bounds.x, 1.0f); +} + +NK_API void +nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols) +{ + nk_row_layout(ctx, NK_DYNAMIC, height, cols, 0); +} + +NK_API void +nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols) +{ + nk_row_layout(ctx, NK_STATIC, height, cols, item_width); +} + +NK_API void +nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, + float row_height, int cols) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, row_height, cols); + if (fmt == NK_DYNAMIC) + layout->row.type = NK_LAYOUT_DYNAMIC_ROW; + else layout->row.type = NK_LAYOUT_STATIC_ROW; + + layout->row.ratio = 0; + layout->row.filled = 0; + layout->row.item_width = 0; + layout->row.item_offset = 0; + layout->row.columns = cols; +} + +NK_API void +nk_layout_row_push(struct nk_context *ctx, float ratio_or_width) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_STATIC_ROW || layout->row.type == NK_LAYOUT_DYNAMIC_ROW); + if (layout->row.type != NK_LAYOUT_STATIC_ROW && layout->row.type != NK_LAYOUT_DYNAMIC_ROW) + return; + + if (layout->row.type == NK_LAYOUT_DYNAMIC_ROW) { + float ratio = ratio_or_width; + if ((ratio + layout->row.filled) > 1.0f) return; + if (ratio > 0.0f) + layout->row.item_width = NK_SATURATE(ratio); + else layout->row.item_width = 1.0f - layout->row.filled; + } else layout->row.item_width = ratio_or_width; +} + +NK_API void +nk_layout_row_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_STATIC_ROW || layout->row.type == NK_LAYOUT_DYNAMIC_ROW); + if (layout->row.type != NK_LAYOUT_STATIC_ROW && layout->row.type != NK_LAYOUT_DYNAMIC_ROW) + return; + layout->row.item_width = 0; + layout->row.item_offset = 0; +} + +NK_API void +nk_layout_row(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int cols, const float *ratio) +{ + int i; + int n_undef = 0; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, cols); + if (fmt == NK_DYNAMIC) { + /* calculate width of undefined widget ratios */ + float r = 0; + layout->row.ratio = ratio; + for (i = 0; i < cols; ++i) { + if (ratio[i] < 0.0f) + n_undef++; + else r += ratio[i]; + } + r = NK_SATURATE(1.0f - r); + layout->row.type = NK_LAYOUT_DYNAMIC; + layout->row.item_width = (r > 0 && n_undef > 0) ? (r / (float)n_undef):0; + } else { + layout->row.ratio = ratio; + layout->row.type = NK_LAYOUT_STATIC; + layout->row.item_width = 0; + layout->row.item_offset = 0; + } + layout->row.item_offset = 0; + layout->row.filled = 0; +} + +NK_API void +nk_layout_row_template_begin(struct nk_context *ctx, float height) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, 1); + layout->row.type = NK_LAYOUT_TEMPLATE; + layout->row.columns = 0; + layout->row.ratio = 0; + layout->row.item_width = 0; + layout->row.item_height = 0; + layout->row.item_offset = 0; + layout->row.filled = 0; + layout->row.item.x = 0; + layout->row.item.y = 0; + layout->row.item.w = 0; + layout->row.item.h = 0; +} + +NK_API void +nk_layout_row_template_push_dynamic(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); + NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); + if (layout->row.type != NK_LAYOUT_TEMPLATE) return; + if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; + layout->row.templates[layout->row.columns++] = -1.0f; +} + +NK_API void +nk_layout_row_template_push_variable(struct nk_context *ctx, float min_width) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); + NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); + if (layout->row.type != NK_LAYOUT_TEMPLATE) return; + if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; + layout->row.templates[layout->row.columns++] = -min_width; +} + +NK_API void +nk_layout_row_template_push_static(struct nk_context *ctx, float width) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); + NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); + if (layout->row.type != NK_LAYOUT_TEMPLATE) return; + if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; + layout->row.templates[layout->row.columns++] = width; +} + +NK_API void +nk_layout_row_template_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + int i = 0; + int variable_count = 0; + int min_variable_count = 0; + float min_fixed_width = 0.0f; + float total_fixed_width = 0.0f; + float max_variable_width = 0.0f; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); + if (layout->row.type != NK_LAYOUT_TEMPLATE) return; + for (i = 0; i < layout->row.columns; ++i) { + float width = layout->row.templates[i]; + if (width >= 0.0f) { + total_fixed_width += width; + min_fixed_width += width; + } else if (width < -1.0f) { + width = -width; + total_fixed_width += width; + max_variable_width = NK_MAX(max_variable_width, width); + variable_count++; + } else { + min_variable_count++; + variable_count++; + } + } + if (variable_count) { + float space = nk_layout_row_calculate_usable_space(&ctx->style, layout->type, + layout->bounds.w, layout->row.columns); + float var_width = (NK_MAX(space-min_fixed_width,0.0f)) / (float)variable_count; + int enough_space = var_width >= max_variable_width; + if (!enough_space) + var_width = (NK_MAX(space-total_fixed_width,0)) / (float)min_variable_count; + for (i = 0; i < layout->row.columns; ++i) { + float *width = &layout->row.templates[i]; + *width = (*width >= 0.0f)? *width: (*width < -1.0f && !enough_space)? -(*width): var_width; + } + } +} + +NK_API void +nk_layout_space_begin(struct nk_context *ctx, enum nk_layout_format fmt, + float height, int widget_count) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + nk_panel_layout(ctx, win, height, widget_count); + if (fmt == NK_STATIC) + layout->row.type = NK_LAYOUT_STATIC_FREE; + else layout->row.type = NK_LAYOUT_DYNAMIC_FREE; + + layout->row.ratio = 0; + layout->row.filled = 0; + layout->row.item_width = 0; + layout->row.item_offset = 0; +} + +NK_API void +nk_layout_space_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item_width = 0; + layout->row.item_height = 0; + layout->row.item_offset = 0; + nk_zero(&layout->row.item, sizeof(layout->row.item)); +} + +NK_API void +nk_layout_space_push(struct nk_context *ctx, struct nk_rect rect) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->row.item = rect; +} + +NK_API struct nk_rect +nk_layout_space_bounds(struct nk_context *ctx) +{ + struct nk_rect ret; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x = layout->clip.x; + ret.y = layout->clip.y; + ret.w = layout->clip.w; + ret.h = layout->row.height; + return ret; +} + +NK_API struct nk_rect +nk_layout_widget_bounds(struct nk_context *ctx) +{ + struct nk_rect ret; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x = layout->at_x; + ret.y = layout->at_y; + ret.w = layout->bounds.w - NK_MAX(layout->at_x - layout->bounds.x,0); + ret.h = layout->row.height; + return ret; +} + +NK_API struct nk_vec2 +nk_layout_space_to_screen(struct nk_context *ctx, struct nk_vec2 ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += layout->at_x - (float)*layout->offset_x; + ret.y += layout->at_y - (float)*layout->offset_y; + return ret; +} + +NK_API struct nk_vec2 +nk_layout_space_to_local(struct nk_context *ctx, struct nk_vec2 ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += -layout->at_x + (float)*layout->offset_x; + ret.y += -layout->at_y + (float)*layout->offset_y; + return ret; +} + +NK_API struct nk_rect +nk_layout_space_rect_to_screen(struct nk_context *ctx, struct nk_rect ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += layout->at_x - (float)*layout->offset_x; + ret.y += layout->at_y - (float)*layout->offset_y; + return ret; +} + +NK_API struct nk_rect +nk_layout_space_rect_to_local(struct nk_context *ctx, struct nk_rect ret) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + win = ctx->current; + layout = win->layout; + + ret.x += -layout->at_x + (float)*layout->offset_x; + ret.y += -layout->at_y + (float)*layout->offset_y; + return ret; +} + +NK_INTERN void +nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win) +{ + struct nk_panel *layout = win->layout; + struct nk_vec2 spacing = ctx->style.window.spacing; + const float row_height = layout->row.height - spacing.y; + nk_panel_layout(ctx, win, row_height, layout->row.columns); +} + +NK_INTERN void +nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, + struct nk_window *win, int modify) +{ + struct nk_panel *layout; + const struct nk_style *style; + + struct nk_vec2 spacing; + struct nk_vec2 padding; + + float item_offset = 0; + float item_width = 0; + float item_spacing = 0; + float panel_space = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + NK_ASSERT(bounds); + + spacing = style->window.spacing; + padding = nk_panel_get_padding(style, layout->type); + panel_space = nk_layout_row_calculate_usable_space(&ctx->style, layout->type, + layout->bounds.w, layout->row.columns); + + /* calculate the width of one item inside the current layout space */ + switch (layout->row.type) { + case NK_LAYOUT_DYNAMIC_FIXED: { + /* scaling fixed size widgets item width */ + item_width = NK_MAX(1.0f,panel_space-1.0f) / (float)layout->row.columns; + item_offset = (float)layout->row.index * item_width; + item_spacing = (float)layout->row.index * spacing.x; + } break; + case NK_LAYOUT_DYNAMIC_ROW: { + /* scaling single ratio widget width */ + item_width = layout->row.item_width * panel_space; + item_offset = layout->row.item_offset; + item_spacing = 0; + + if (modify) { + layout->row.item_offset += item_width + spacing.x; + layout->row.filled += layout->row.item_width; + layout->row.index = 0; + } + } break; + case NK_LAYOUT_DYNAMIC_FREE: { + /* panel width depended free widget placing */ + bounds->x = layout->at_x + (layout->bounds.w * layout->row.item.x); + bounds->x -= (float)*layout->offset_x; + bounds->y = layout->at_y + (layout->row.height * layout->row.item.y); + bounds->y -= (float)*layout->offset_y; + bounds->w = layout->bounds.w * layout->row.item.w; + bounds->h = layout->row.height * layout->row.item.h; + return; + } break; + case NK_LAYOUT_DYNAMIC: { + /* scaling arrays of panel width ratios for every widget */ + float ratio; + NK_ASSERT(layout->row.ratio); + ratio = (layout->row.ratio[layout->row.index] < 0) ? + layout->row.item_width : layout->row.ratio[layout->row.index]; + + item_spacing = (float)layout->row.index * spacing.x; + item_width = (ratio * panel_space); + item_offset = layout->row.item_offset; + + if (modify) { + layout->row.item_offset += item_width; + layout->row.filled += ratio; + } + } break; + case NK_LAYOUT_STATIC_FIXED: { + /* non-scaling fixed widgets item width */ + item_width = layout->row.item_width; + item_offset = (float)layout->row.index * item_width; + item_spacing = (float)layout->row.index * spacing.x; + } break; + case NK_LAYOUT_STATIC_ROW: { + /* scaling single ratio widget width */ + item_width = layout->row.item_width; + item_offset = layout->row.item_offset; + item_spacing = (float)layout->row.index * spacing.x; + if (modify) layout->row.item_offset += item_width; + } break; + case NK_LAYOUT_STATIC_FREE: { + /* free widget placing */ + bounds->x = layout->at_x + layout->row.item.x; + bounds->w = layout->row.item.w; + if (((bounds->x + bounds->w) > layout->max_x) && modify) + layout->max_x = (bounds->x + bounds->w); + bounds->x -= (float)*layout->offset_x; + bounds->y = layout->at_y + layout->row.item.y; + bounds->y -= (float)*layout->offset_y; + bounds->h = layout->row.item.h; + return; + } break; + case NK_LAYOUT_STATIC: { + /* non-scaling array of panel pixel width for every widget */ + item_spacing = (float)layout->row.index * spacing.x; + item_width = layout->row.ratio[layout->row.index]; + item_offset = layout->row.item_offset; + if (modify) layout->row.item_offset += item_width; + } break; + case NK_LAYOUT_TEMPLATE: { + /* stretchy row layout with combined dynamic/static widget width*/ + NK_ASSERT(layout->row.index < layout->row.columns); + NK_ASSERT(layout->row.index < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); + item_width = layout->row.templates[layout->row.index]; + item_offset = layout->row.item_offset; + item_spacing = (float)layout->row.index * spacing.x; + if (modify) layout->row.item_offset += item_width; + } break; + default: NK_ASSERT(0); break; + }; + + /* set the bounds of the newly allocated widget */ + bounds->w = item_width; + bounds->h = layout->row.height - spacing.y; + bounds->y = layout->at_y - (float)*layout->offset_y; + bounds->x = layout->at_x + item_offset + item_spacing + padding.x; + if (((bounds->x + bounds->w) > layout->max_x) && modify) + layout->max_x = bounds->x + bounds->w; + bounds->x -= (float)*layout->offset_x; +} + +NK_INTERN void +nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* check if the end of the row has been hit and begin new row if so */ + win = ctx->current; + layout = win->layout; + if (layout->row.index >= layout->row.columns) + nk_panel_alloc_row(ctx, win); + + /* calculate widget position and size */ + nk_layout_widget_space(bounds, ctx, win, nk_true); + layout->row.index++; +} + +NK_INTERN void +nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx) +{ + float y; + int index; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + y = layout->at_y; + index = layout->row.index; + if (layout->row.index >= layout->row.columns) { + layout->at_y += layout->row.height; + layout->row.index = 0; + } + nk_layout_widget_space(bounds, ctx, win, nk_false); + layout->at_y = y; + layout->row.index = index; +} + +NK_INTERN int +nk_tree_state_base(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image *img, const char *title, enum nk_collapse_states *state) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + const struct nk_input *in; + const struct nk_style_button *button; + enum nk_symbol_type symbol; + float row_height; + + struct nk_vec2 item_spacing; + struct nk_rect header = {0,0,0,0}; + struct nk_rect sym = {0,0,0,0}; + struct nk_text text; + + nk_flags ws = 0; + enum nk_widget_layout_states widget_state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* cache some data */ + win = ctx->current; + layout = win->layout; + out = &win->buffer; + style = &ctx->style; + item_spacing = style->window.spacing; + + /* calculate header bounds and draw background */ + row_height = style->font->height + 2 * style->tab.padding.y; + nk_layout_set_min_row_height(ctx, row_height); + nk_layout_row_dynamic(ctx, row_height, 1); + nk_layout_reset_min_row_height(ctx); + + widget_state = nk_widget(&header, ctx); + if (type == NK_TREE_TAB) { + const struct nk_style_item *background = &style->tab.background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, header, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, header, 0, style->tab.border_color); + nk_fill_rect(out, nk_shrink_rect(header, style->tab.border), + style->tab.rounding, background->data.color); + } + } else text.background = style->window.background; + + /* update node state */ + in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0; + in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0; + if (nk_button_behavior(&ws, header, in, NK_BUTTON_DEFAULT)) + *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED; + + /* select correct button style */ + if (*state == NK_MAXIMIZED) { + symbol = style->tab.sym_maximize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_maximize_button; + else button = &style->tab.node_maximize_button; + } else { + symbol = style->tab.sym_minimize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_minimize_button; + else button = &style->tab.node_minimize_button; + } + + {/* draw triangle button */ + sym.w = sym.h = style->font->height; + sym.y = header.y + style->tab.padding.y; + sym.x = header.x + style->tab.padding.x; + nk_do_button_symbol(&ws, &win->buffer, sym, symbol, NK_BUTTON_DEFAULT, + button, 0, style->font); + + if (img) { + /* draw optional image icon */ + sym.x = sym.x + sym.w + 4 * item_spacing.x; + nk_draw_image(&win->buffer, sym, img, nk_white); + sym.w = style->font->height + style->tab.spacing.x;} + } + + {/* draw label */ + struct nk_rect label; + header.w = NK_MAX(header.w, sym.w + item_spacing.x); + label.x = sym.x + sym.w + item_spacing.x; + label.y = sym.y; + label.w = header.w - (sym.w + item_spacing.y + style->tab.indent); + label.h = style->font->height; + text.text = style->tab.text; + text.padding = nk_vec2(0,0); + nk_widget_text(out, label, title, nk_strlen(title), &text, + NK_TEXT_LEFT, style->font);} + + /* increase x-axis cursor widget position pointer */ + if (*state == NK_MAXIMIZED) { + layout->at_x = header.x + (float)*layout->offset_x + style->tab.indent; + layout->bounds.w = NK_MAX(layout->bounds.w, style->tab.indent); + layout->bounds.w -= (style->tab.indent + style->window.padding.x); + layout->row.tree_depth++; + return nk_true; + } else return nk_false; +} + +NK_INTERN int +nk_tree_base(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image *img, const char *title, enum nk_collapse_states initial_state, + const char *hash, int len, int line) +{ + struct nk_window *win = ctx->current; + int title_len = 0; + nk_hash tree_hash = 0; + nk_uint *state = 0; + + /* retrieve tree state from internal widget state tables */ + if (!hash) { + title_len = (int)nk_strlen(title); + tree_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line); + } else tree_hash = nk_murmur_hash(hash, len, (nk_hash)line); + state = nk_find_value(win, tree_hash); + if (!state) { + state = nk_add_value(ctx, win, tree_hash, 0); + *state = initial_state; + } + return nk_tree_state_base(ctx, type, img, title, (enum nk_collapse_states*)state); +} + +NK_API int +nk_tree_state_push(struct nk_context *ctx, enum nk_tree_type type, + const char *title, enum nk_collapse_states *state) +{return nk_tree_state_base(ctx, type, 0, title, state);} + +NK_API int +nk_tree_state_image_push(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image img, const char *title, enum nk_collapse_states *state) +{return nk_tree_state_base(ctx, type, &img, title, state);} + +NK_API void +nk_tree_state_pop(struct nk_context *ctx) +{ + struct nk_window *win = 0; + struct nk_panel *layout = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + layout->at_x -= ctx->style.tab.indent + ctx->style.window.padding.x; + layout->bounds.w += ctx->style.tab.indent + ctx->style.window.padding.x; + NK_ASSERT(layout->row.tree_depth); + layout->row.tree_depth--; +} + +NK_API int +nk_tree_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + const char *title, enum nk_collapse_states initial_state, + const char *hash, int len, int line) +{return nk_tree_base(ctx, type, 0, title, initial_state, hash, len, line);} + +NK_API int +nk_tree_image_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image img, const char *title, enum nk_collapse_states initial_state, + const char *hash, int len,int seed) +{return nk_tree_base(ctx, type, &img, title, initial_state, hash, len, seed);} + +NK_API void +nk_tree_pop(struct nk_context *ctx) +{nk_tree_state_pop(ctx);} + +/*---------------------------------------------------------------- + * + * WIDGETS + * + * --------------------------------------------------------------*/ +NK_API struct nk_rect +nk_widget_bounds(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_rect(0,0,0,0); + nk_layout_peek(&bounds, ctx); + return bounds; +} + +NK_API struct nk_vec2 +nk_widget_position(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_vec2(0,0); + + nk_layout_peek(&bounds, ctx); + return nk_vec2(bounds.x, bounds.y); +} + +NK_API struct nk_vec2 +nk_widget_size(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return nk_vec2(0,0); + + nk_layout_peek(&bounds, ctx); + return nk_vec2(bounds.w, bounds.h); +} + +NK_API float +nk_widget_width(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + return bounds.w; +} + +NK_API float +nk_widget_height(struct nk_context *ctx) +{ + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return 0; + + nk_layout_peek(&bounds, ctx); + return bounds.h; +} + +NK_API int +nk_widget_is_hovered(struct nk_context *ctx) +{ + struct nk_rect c, v; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current || ctx->active != ctx->current) + return 0; + + c = ctx->current->layout->clip; + c.x = (float)((int)c.x); + c.y = (float)((int)c.y); + c.w = (float)((int)c.w); + c.h = (float)((int)c.h); + + nk_layout_peek(&bounds, ctx); + nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h); + if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h)) + return 0; + return nk_input_is_mouse_hovering_rect(&ctx->input, bounds); +} + +NK_API int +nk_widget_is_mouse_clicked(struct nk_context *ctx, enum nk_buttons btn) +{ + struct nk_rect c, v; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current || ctx->active != ctx->current) + return 0; + + c = ctx->current->layout->clip; + c.x = (float)((int)c.x); + c.y = (float)((int)c.y); + c.w = (float)((int)c.w); + c.h = (float)((int)c.h); + + nk_layout_peek(&bounds, ctx); + nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h); + if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h)) + return 0; + return nk_input_mouse_clicked(&ctx->input, btn, bounds); +} + +NK_API int +nk_widget_has_mouse_click_down(struct nk_context *ctx, enum nk_buttons btn, int down) +{ + struct nk_rect c, v; + struct nk_rect bounds; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current || ctx->active != ctx->current) + return 0; + + c = ctx->current->layout->clip; + c.x = (float)((int)c.x); + c.y = (float)((int)c.y); + c.w = (float)((int)c.w); + c.h = (float)((int)c.h); + + nk_layout_peek(&bounds, ctx); + nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h); + if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h)) + return 0; + return nk_input_has_mouse_click_down_in_rect(&ctx->input, btn, bounds, down); +} + +NK_API enum nk_widget_layout_states +nk_widget(struct nk_rect *bounds, const struct nk_context *ctx) +{ + struct nk_rect c, v; + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return NK_WIDGET_INVALID; + + /* allocate space and check if the widget needs to be updated and drawn */ + nk_panel_alloc_space(bounds, ctx); + win = ctx->current; + layout = win->layout; + in = &ctx->input; + c = layout->clip; + + /* if one of these triggers you forgot to add an `if` condition around either + a window, group, popup, combobox or contextual menu `begin` and `end` block. + Example: + if (nk_begin(...) {...} nk_end(...); or + if (nk_group_begin(...) { nk_group_end(...);} */ + NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED)); + NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN)); + NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED)); + + /* need to convert to int here to remove floating point errors */ + bounds->x = (float)((int)bounds->x); + bounds->y = (float)((int)bounds->y); + bounds->w = (float)((int)bounds->w); + bounds->h = (float)((int)bounds->h); + + c.x = (float)((int)c.x); + c.y = (float)((int)c.y); + c.w = (float)((int)c.w); + c.h = (float)((int)c.h); + + nk_unify(&v, &c, bounds->x, bounds->y, bounds->x + bounds->w, bounds->y + bounds->h); + if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds->x, bounds->y, bounds->w, bounds->h)) + return NK_WIDGET_INVALID; + if (!NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, v.x, v.y, v.w, v.h)) + return NK_WIDGET_ROM; + return NK_WIDGET_VALID; +} + +NK_API enum nk_widget_layout_states +nk_widget_fitting(struct nk_rect *bounds, struct nk_context *ctx, + struct nk_vec2 item_padding) +{ + /* update the bounds to stand without padding */ + struct nk_window *win; + struct nk_style *style; + struct nk_panel *layout; + enum nk_widget_layout_states state; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return NK_WIDGET_INVALID; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(bounds, ctx); + + panel_padding = nk_panel_get_padding(style, layout->type); + if (layout->row.index == 1) { + bounds->w += panel_padding.x; + bounds->x -= panel_padding.x; + } else bounds->x -= item_padding.x; + + if (layout->row.index == layout->row.columns) + bounds->w += panel_padding.x; + else bounds->w += item_padding.x; + return state; +} + +/*---------------------------------------------------------------- + * + * MISC + * + * --------------------------------------------------------------*/ +NK_API void +nk_spacing(struct nk_context *ctx, int cols) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_rect none; + int i, index, rows; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + /* spacing over row boundaries */ + win = ctx->current; + layout = win->layout; + index = (layout->row.index + cols) % layout->row.columns; + rows = (layout->row.index + cols) / layout->row.columns; + if (rows) { + for (i = 0; i < rows; ++i) + nk_panel_alloc_row(ctx, win); + cols = index; + } + /* non table layout need to allocate space */ + if (layout->row.type != NK_LAYOUT_DYNAMIC_FIXED && + layout->row.type != NK_LAYOUT_STATIC_FIXED) { + for (i = 0; i < cols; ++i) + nk_panel_alloc_space(&none, ctx); + } + layout->row.index = index; +} + +/*---------------------------------------------------------------- + * + * TEXT + * + * --------------------------------------------------------------*/ +NK_API void +nk_text_colored(struct nk_context *ctx, const char *str, int len, + nk_flags alignment, struct nk_color color) +{ + struct nk_window *win; + const struct nk_style *style; + + struct nk_vec2 item_padding; + struct nk_rect bounds; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + style = &ctx->style; + nk_panel_alloc_space(&bounds, ctx); + item_padding = style->text.padding; + + text.padding.x = item_padding.x; + text.padding.y = item_padding.y; + text.background = style->window.background; + text.text = color; + nk_widget_text(&win->buffer, bounds, str, len, &text, alignment, style->font); +} + +NK_API void +nk_text_wrap_colored(struct nk_context *ctx, const char *str, + int len, struct nk_color color) +{ + struct nk_window *win; + const struct nk_style *style; + + struct nk_vec2 item_padding; + struct nk_rect bounds; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + style = &ctx->style; + nk_panel_alloc_space(&bounds, ctx); + item_padding = style->text.padding; + + text.padding.x = item_padding.x; + text.padding.y = item_padding.y; + text.background = style->window.background; + text.text = color; + nk_widget_text_wrap(&win->buffer, bounds, str, len, &text, style->font); +} + +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_API void +nk_labelf_colored(struct nk_context *ctx, nk_flags flags, + struct nk_color color, const char *fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_colored(ctx, buf, flags, color); + va_end(args); +} + +NK_API void +nk_labelf_colored_wrap(struct nk_context *ctx, struct nk_color color, + const char *fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_colored_wrap(ctx, buf, color); + va_end(args); +} + +NK_API void +nk_labelf(struct nk_context *ctx, nk_flags flags, const char *fmt, ...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label(ctx, buf, flags); + va_end(args); +} + +NK_API void +nk_labelf_wrap(struct nk_context *ctx, const char *fmt,...) +{ + char buf[256]; + va_list args; + va_start(args, fmt); + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_wrap(ctx, buf); + va_end(args); +} + +NK_API void +nk_value_bool(struct nk_context *ctx, const char *prefix, int value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, ((value) ? "true": "false"));} + +NK_API void +nk_value_int(struct nk_context *ctx, const char *prefix, int value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %d", prefix, value);} + +NK_API void +nk_value_uint(struct nk_context *ctx, const char *prefix, unsigned int value) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %u", prefix, value);} + +NK_API void +nk_value_float(struct nk_context *ctx, const char *prefix, float value) +{ + double double_value = (double)value; + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %.3f", prefix, double_value); +} + +NK_API void +nk_value_color_byte(struct nk_context *ctx, const char *p, struct nk_color c) +{nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%d, %d, %d, %d)", p, c.r, c.g, c.b, c.a);} + +NK_API void +nk_value_color_float(struct nk_context *ctx, const char *p, struct nk_color color) +{ + double c[4]; nk_color_dv(c, color); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%.2f, %.2f, %.2f, %.2f)", + p, c[0], c[1], c[2], c[3]); +} + +NK_API void +nk_value_color_hex(struct nk_context *ctx, const char *prefix, struct nk_color color) +{ + char hex[16]; + nk_color_hex_rgba(hex, color); + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, hex); +} +#endif + +NK_API void +nk_text(struct nk_context *ctx, const char *str, int len, nk_flags alignment) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_text_colored(ctx, str, len, alignment, ctx->style.text.color); +} + +NK_API void +nk_text_wrap(struct nk_context *ctx, const char *str, int len) +{ + NK_ASSERT(ctx); + if (!ctx) return; + nk_text_wrap_colored(ctx, str, len, ctx->style.text.color); +} + +NK_API void +nk_label(struct nk_context *ctx, const char *str, nk_flags alignment) +{nk_text(ctx, str, nk_strlen(str), alignment);} + +NK_API void +nk_label_colored(struct nk_context *ctx, const char *str, nk_flags align, + struct nk_color color) +{nk_text_colored(ctx, str, nk_strlen(str), align, color);} + +NK_API void +nk_label_wrap(struct nk_context *ctx, const char *str) +{nk_text_wrap(ctx, str, nk_strlen(str));} + +NK_API void +nk_label_colored_wrap(struct nk_context *ctx, const char *str, struct nk_color color) +{nk_text_wrap_colored(ctx, str, nk_strlen(str), color);} + +NK_API void +nk_image(struct nk_context *ctx, struct nk_image img) +{ + struct nk_window *win; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + if (!nk_widget(&bounds, ctx)) return; + nk_draw_image(&win->buffer, bounds, &img, nk_white); +} + +/*---------------------------------------------------------------- + * + * BUTTON + * + * --------------------------------------------------------------*/ +NK_API void +nk_button_set_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) +{ + NK_ASSERT(ctx); + if (!ctx) return; + ctx->button_behavior = behavior; +} + +NK_API int +nk_button_push_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) +{ + struct nk_config_stack_button_behavior *button_stack; + struct nk_config_stack_button_behavior_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + button_stack = &ctx->stacks.button_behaviors; + NK_ASSERT(button_stack->head < (int)NK_LEN(button_stack->elements)); + if (button_stack->head >= (int)NK_LEN(button_stack->elements)) + return 0; + + element = &button_stack->elements[button_stack->head++]; + element->address = &ctx->button_behavior; + element->old_value = ctx->button_behavior; + ctx->button_behavior = behavior; + return 1; +} + +NK_API int +nk_button_pop_behavior(struct nk_context *ctx) +{ + struct nk_config_stack_button_behavior *button_stack; + struct nk_config_stack_button_behavior_element *element; + + NK_ASSERT(ctx); + if (!ctx) return 0; + + button_stack = &ctx->stacks.button_behaviors; + NK_ASSERT(button_stack->head > 0); + if (button_stack->head < 1) + return 0; + + element = &button_stack->elements[--button_stack->head]; + *element->address = element->old_value; + return 1; +} + +NK_API int +nk_button_text_styled(struct nk_context *ctx, + const struct nk_style_button *style, const char *title, int len) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(style); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!style || !ctx || !ctx->current || !ctx->current->layout) return 0; + + win = ctx->current; + layout = win->layout; + state = nk_widget(&bounds, ctx); + + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, + title, len, style->text_alignment, ctx->button_behavior, + style, in, ctx->style.font); +} + +NK_API int +nk_button_text(struct nk_context *ctx, const char *title, int len) +{ + NK_ASSERT(ctx); + if (!ctx) return 0; + return nk_button_text_styled(ctx, &ctx->style.button, title, len); +} + +NK_API int nk_button_label_styled(struct nk_context *ctx, + const struct nk_style_button *style, const char *title) +{return nk_button_text_styled(ctx, style, title, nk_strlen(title));} + +NK_API int nk_button_label(struct nk_context *ctx, const char *title) +{return nk_button_text(ctx, title, nk_strlen(title));} + +NK_API int +nk_button_color(struct nk_context *ctx, struct nk_color color) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + struct nk_style_button button; + + int ret = 0; + struct nk_rect bounds; + struct nk_rect content; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + button = ctx->style.button; + button.normal = nk_style_item_color(color); + button.hover = nk_style_item_color(color); + button.active = nk_style_item_color(color); + ret = nk_do_button(&ctx->last_widget_state, &win->buffer, bounds, + &button, in, ctx->button_behavior, &content); + nk_draw_button(&win->buffer, &bounds, ctx->last_widget_state, &button); + return ret; +} + +NK_API int +nk_button_symbol_styled(struct nk_context *ctx, + const struct nk_style_button *style, enum nk_symbol_type symbol) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, ctx->button_behavior, style, in, ctx->style.font); +} + +NK_API int +nk_button_symbol(struct nk_context *ctx, enum nk_symbol_type symbol) +{ + NK_ASSERT(ctx); + if (!ctx) return 0; + return nk_button_symbol_styled(ctx, &ctx->style.button, symbol); +} + +NK_API int +nk_button_image_styled(struct nk_context *ctx, const struct nk_style_button *style, + struct nk_image img) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_image(&ctx->last_widget_state, &win->buffer, bounds, + img, ctx->button_behavior, style, in); +} + +NK_API int +nk_button_image(struct nk_context *ctx, struct nk_image img) +{ + NK_ASSERT(ctx); + if (!ctx) return 0; + return nk_button_image_styled(ctx, &ctx->style.button, img); +} + +NK_API int +nk_button_symbol_text_styled(struct nk_context *ctx, + const struct nk_style_button *style, enum nk_symbol_type symbol, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, text, len, align, ctx->button_behavior, + style, ctx->style.font, in); +} + +NK_API int +nk_button_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, + const char* text, int len, nk_flags align) +{ + NK_ASSERT(ctx); + if (!ctx) return 0; + return nk_button_symbol_text_styled(ctx, &ctx->style.button, symbol, text, len, align); +} + +NK_API int nk_button_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *label, nk_flags align) +{return nk_button_symbol_text(ctx, symbol, label, nk_strlen(label), align);} + +NK_API int nk_button_symbol_label_styled(struct nk_context *ctx, + const struct nk_style_button *style, enum nk_symbol_type symbol, + const char *title, nk_flags align) +{return nk_button_symbol_text_styled(ctx, style, symbol, title, nk_strlen(title), align);} + +NK_API int +nk_button_image_text_styled(struct nk_context *ctx, + const struct nk_style_button *style, struct nk_image img, const char *text, + int len, nk_flags align) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, + bounds, img, text, len, align, ctx->button_behavior, + style, ctx->style.font, in); +} + +NK_API int +nk_button_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{return nk_button_image_text_styled(ctx, &ctx->style.button,img, text, len, align);} + + +NK_API int nk_button_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{return nk_button_image_text(ctx, img, label, nk_strlen(label), align);} + +NK_API int nk_button_image_label_styled(struct nk_context *ctx, + const struct nk_style_button *style, struct nk_image img, + const char *label, nk_flags text_alignment) +{return nk_button_image_text_styled(ctx, style, img, label, nk_strlen(label), text_alignment);} + +/*---------------------------------------------------------------- + * + * SELECTABLE + * + * --------------------------------------------------------------*/ +NK_API int +nk_selectable_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, int *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, &style->selectable, in, style->font); +} + +NK_API int +nk_selectable_image_text(struct nk_context *ctx, struct nk_image img, + const char *str, int len, nk_flags align, int *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable_image(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, &img, &style->selectable, in, style->font); +} + +NK_API int nk_select_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, int value) +{nk_selectable_text(ctx, str, len, align, &value);return value;} + +NK_API int nk_selectable_label(struct nk_context *ctx, const char *str, nk_flags align, int *value) +{return nk_selectable_text(ctx, str, nk_strlen(str), align, value);} + +NK_API int nk_selectable_image_label(struct nk_context *ctx,struct nk_image img, + const char *str, nk_flags align, int *value) +{return nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, value);} + +NK_API int nk_select_label(struct nk_context *ctx, const char *str, nk_flags align, int value) +{nk_selectable_text(ctx, str, nk_strlen(str), align, &value);return value;} + +NK_API int nk_select_image_label(struct nk_context *ctx, struct nk_image img, + const char *str, nk_flags align, int value) +{nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, &value);return value;} + +NK_API int nk_select_image_text(struct nk_context *ctx, struct nk_image img, + const char *str, int len, nk_flags align, int value) +{nk_selectable_image_text(ctx, img, str, len, align, &value);return value;} + +/*---------------------------------------------------------------- + * + * CHECKBOX + * + * --------------------------------------------------------------*/ +NK_API int +nk_check_text(struct nk_context *ctx, const char *text, int len, int active) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return active; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return active; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &active, + text, len, NK_TOGGLE_CHECK, &style->checkbox, in, style->font); + return active; +} + +NK_API unsigned int +nk_check_flags_text(struct nk_context *ctx, const char *text, int len, + unsigned int flags, unsigned int value) +{ + int old_active; + NK_ASSERT(ctx); + NK_ASSERT(text); + if (!ctx || !text) return flags; + old_active = (int)((flags & value) & value); + if (nk_check_text(ctx, text, len, old_active)) + flags |= value; + else flags &= ~value; + return flags; +} + +NK_API int +nk_checkbox_text(struct nk_context *ctx, const char *text, int len, int *active) +{ + int old_val; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(active); + if (!ctx || !text || !active) return 0; + old_val = *active; + *active = nk_check_text(ctx, text, len, *active); + return old_val != *active; +} + +NK_API int +nk_checkbox_flags_text(struct nk_context *ctx, const char *text, int len, + unsigned int *flags, unsigned int value) +{ + int active; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(flags); + if (!ctx || !text || !flags) return 0; + + active = (int)((*flags & value) & value); + if (nk_checkbox_text(ctx, text, len, &active)) { + if (active) *flags |= value; + else *flags &= ~value; + return 1; + } + return 0; +} + +NK_API int nk_check_label(struct nk_context *ctx, const char *label, int active) +{return nk_check_text(ctx, label, nk_strlen(label), active);} + +NK_API unsigned int nk_check_flags_label(struct nk_context *ctx, const char *label, + unsigned int flags, unsigned int value) +{return nk_check_flags_text(ctx, label, nk_strlen(label), flags, value);} + +NK_API int nk_checkbox_label(struct nk_context *ctx, const char *label, int *active) +{return nk_checkbox_text(ctx, label, nk_strlen(label), active);} + +NK_API int nk_checkbox_flags_label(struct nk_context *ctx, const char *label, + unsigned int *flags, unsigned int value) +{return nk_checkbox_flags_text(ctx, label, nk_strlen(label), flags, value);} + +/*---------------------------------------------------------------- + * + * OPTION + * + * --------------------------------------------------------------*/ +NK_API int +nk_option_text(struct nk_context *ctx, const char *text, int len, int is_active) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return is_active; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return state; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active, + text, len, NK_TOGGLE_OPTION, &style->option, in, style->font); + return is_active; +} + +NK_API int +nk_radio_text(struct nk_context *ctx, const char *text, int len, int *active) +{ + int old_value; + NK_ASSERT(ctx); + NK_ASSERT(text); + NK_ASSERT(active); + if (!ctx || !text || !active) return 0; + old_value = *active; + *active = nk_option_text(ctx, text, len, old_value); + return old_value != *active; +} + +NK_API int +nk_option_label(struct nk_context *ctx, const char *label, int active) +{return nk_option_text(ctx, label, nk_strlen(label), active);} + +NK_API int +nk_radio_label(struct nk_context *ctx, const char *label, int *active) +{return nk_radio_text(ctx, label, nk_strlen(label), active);} + +/*---------------------------------------------------------------- + * + * SLIDER + * + * --------------------------------------------------------------*/ +NK_API int +nk_slider_float(struct nk_context *ctx, float min_value, float *value, float max_value, + float value_step) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_input *in; + const struct nk_style *style; + + int ret = 0; + float old_value; + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(value); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return ret; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + + state = nk_widget(&bounds, ctx); + if (!state) return ret; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + old_value = *value; + *value = nk_do_slider(&ctx->last_widget_state, &win->buffer, bounds, min_value, + old_value, max_value, value_step, &style->slider, in, style->font); + return (old_value > *value || old_value < *value); +} + +NK_API float +nk_slide_float(struct nk_context *ctx, float min, float val, float max, float step) +{ + nk_slider_float(ctx, min, &val, max, step); return val; +} + +NK_API int +nk_slide_int(struct nk_context *ctx, int min, int val, int max, int step) +{ + float value = (float)val; + nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); + return (int)value; +} + +NK_API int +nk_slider_int(struct nk_context *ctx, int min, int *val, int max, int step) +{ + int ret; + float value = (float)*val; + ret = nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); + *val = (int)value; + return ret; +} + +/*---------------------------------------------------------------- + * + * PROGRESSBAR + * + * --------------------------------------------------------------*/ +NK_API int +nk_progress(struct nk_context *ctx, nk_size *cur, nk_size max, int is_modifyable) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + const struct nk_input *in; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + nk_size old_value; + + NK_ASSERT(ctx); + NK_ASSERT(cur); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !cur) + return 0; + + win = ctx->current; + style = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + old_value = *cur; + *cur = nk_do_progress(&ctx->last_widget_state, &win->buffer, bounds, + *cur, max, is_modifyable, &style->progress, in); + return (*cur != old_value); +} + +NK_API nk_size nk_prog(struct nk_context *ctx, nk_size cur, nk_size max, int modifyable) +{nk_progress(ctx, &cur, max, modifyable);return cur;} + +/*---------------------------------------------------------------- + * + * EDIT + * + * --------------------------------------------------------------*/ +NK_API void +nk_edit_focus(struct nk_context *ctx, nk_flags flags) +{ + nk_hash hash; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + + win = ctx->current; + hash = win->edit.seq; + win->edit.active = nk_true; + win->edit.name = hash; + if (flags & NK_EDIT_ALWAYS_INSERT_MODE) + win->edit.mode = NK_TEXT_EDIT_MODE_INSERT; +} + +NK_API void +nk_edit_unfocus(struct nk_context *ctx) +{ + struct nk_window *win; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + + win = ctx->current; + win->edit.active = nk_false; + win->edit.name = 0; +} + +NK_API nk_flags +nk_edit_string(struct nk_context *ctx, nk_flags flags, + char *memory, int *len, int max, nk_plugin_filter filter) +{ + nk_hash hash; + nk_flags state; + struct nk_text_edit *edit; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(memory); + NK_ASSERT(len); + if (!ctx || !memory || !len) + return 0; + + filter = (!filter) ? nk_filter_default: filter; + win = ctx->current; + hash = win->edit.seq; + edit = &ctx->text_edit; + nk_textedit_clear_state(&ctx->text_edit, (flags & NK_EDIT_MULTILINE)? + NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE, filter); + + if (win->edit.active && hash == win->edit.name) { + if (flags & NK_EDIT_NO_CURSOR) + edit->cursor = nk_utf_len(memory, *len); + else edit->cursor = win->edit.cursor; + if (!(flags & NK_EDIT_SELECTABLE)) { + edit->select_start = win->edit.cursor; + edit->select_end = win->edit.cursor; + } else { + edit->select_start = win->edit.sel_start; + edit->select_end = win->edit.sel_end; + } + edit->mode = win->edit.mode; + edit->scrollbar.x = (float)win->edit.scrollbar.x; + edit->scrollbar.y = (float)win->edit.scrollbar.y; + edit->active = nk_true; + } else edit->active = nk_false; + + max = NK_MAX(1, max); + *len = NK_MIN(*len, max-1); + nk_str_init_fixed(&edit->string, memory, (nk_size)max); + edit->string.buffer.allocated = (nk_size)*len; + edit->string.len = nk_utf_len(memory, *len); + state = nk_edit_buffer(ctx, flags, edit, filter); + *len = (int)edit->string.buffer.allocated; + + if (edit->active) { + win->edit.cursor = edit->cursor; + win->edit.sel_start = edit->select_start; + win->edit.sel_end = edit->select_end; + win->edit.mode = edit->mode; + win->edit.scrollbar.x = (nk_ushort)edit->scrollbar.x; + win->edit.scrollbar.y = (nk_ushort)edit->scrollbar.y; + } + return state; +} + +NK_API nk_flags +nk_edit_buffer(struct nk_context *ctx, nk_flags flags, + struct nk_text_edit *edit, nk_plugin_filter filter) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + nk_flags ret_flags = 0; + unsigned char prev_state; + nk_hash hash; + + /* make sure correct values */ + NK_ASSERT(ctx); + NK_ASSERT(edit); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget(&bounds, ctx); + if (!state) return state; + in = (win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + + /* check if edit is currently hot item */ + hash = win->edit.seq++; + if (win->edit.active && hash == win->edit.name) { + if (flags & NK_EDIT_NO_CURSOR) + edit->cursor = edit->string.len; + if (!(flags & NK_EDIT_SELECTABLE)) { + edit->select_start = edit->cursor; + edit->select_end = edit->cursor; + } + if (flags & NK_EDIT_CLIPBOARD) + edit->clip = ctx->clip; + } + + filter = (!filter) ? nk_filter_default: filter; + prev_state = (unsigned char)edit->active; + in = (flags & NK_EDIT_READ_ONLY) ? 0: in; + ret_flags = nk_do_edit(&ctx->last_widget_state, &win->buffer, bounds, flags, + filter, edit, &style->edit, in, style->font); + + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_TEXT]; + if (edit->active && prev_state != edit->active) { + /* current edit is now hot */ + win->edit.active = nk_true; + win->edit.name = hash; + } else if (prev_state && !edit->active) { + /* current edit is now cold */ + win->edit.active = nk_false; + } + return ret_flags; +} + +NK_API nk_flags +nk_edit_string_zero_terminated(struct nk_context *ctx, nk_flags flags, + char *buffer, int max, nk_plugin_filter filter) +{ + nk_flags result; + int len = nk_strlen(buffer); + result = nk_edit_string(ctx, flags, buffer, &len, max, filter); + buffer[NK_MIN(NK_MAX(max-1,0), len)] = '\0'; + return result; +} + +/*---------------------------------------------------------------- + * + * PROPERTY + * + * --------------------------------------------------------------*/ +NK_INTERN struct nk_property_variant +nk_property_variant_int(int value, int min_value, int max_value, int step) +{ + struct nk_property_variant result; + result.kind = NK_PROPERTY_INT; + result.value.i = value; + result.min_value.i = min_value; + result.max_value.i = max_value; + result.step.i = step; + return result; +} + +NK_INTERN struct nk_property_variant +nk_property_variant_float(float value, float min_value, float max_value, float step) +{ + struct nk_property_variant result; + result.kind = NK_PROPERTY_FLOAT; + result.value.f = value; + result.min_value.f = min_value; + result.max_value.f = max_value; + result.step.f = step; + return result; +} + +NK_INTERN struct nk_property_variant +nk_property_variant_double(double value, double min_value, double max_value, + double step) +{ + struct nk_property_variant result; + result.kind = NK_PROPERTY_DOUBLE; + result.value.d = value; + result.min_value.d = min_value; + result.max_value.d = max_value; + result.step.d = step; + return result; +} + +NK_INTERN void +nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant, + float inc_per_pixel, const enum nk_property_filter filter) +{ + struct nk_window *win; + struct nk_panel *layout; + struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states s; + + int *state = 0; + nk_hash hash = 0; + char *buffer = 0; + int *len = 0; + int *cursor = 0; + int *select_begin = 0; + int *select_end = 0; + int old_state; + + char dummy_buffer[NK_MAX_NUMBER_BUFFER]; + int dummy_state = NK_PROPERTY_DEFAULT; + int dummy_length = 0; + int dummy_cursor = 0; + int dummy_select_begin = 0; + int dummy_select_end = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + s = nk_widget(&bounds, ctx); + if (!s) return; + + /* calculate hash from name */ + if (name[0] == '#') { + hash = nk_murmur_hash(name, (int)nk_strlen(name), win->property.seq++); + name++; /* special number hash */ + } else hash = nk_murmur_hash(name, (int)nk_strlen(name), 42); + + /* check if property is currently hot item */ + if (win->property.active && hash == win->property.name) { + buffer = win->property.buffer; + len = &win->property.length; + cursor = &win->property.cursor; + state = &win->property.state; + select_begin = &win->property.select_start; + select_end = &win->property.select_end; + } else { + buffer = dummy_buffer; + len = &dummy_length; + cursor = &dummy_cursor; + state = &dummy_state; + select_begin = &dummy_select_begin; + select_end = &dummy_select_end; + } + + /* execute property widget */ + old_state = *state; + ctx->text_edit.clip = ctx->clip; + in = ((s == NK_WIDGET_ROM && !win->property.active) || + layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + nk_do_property(&ctx->last_widget_state, &win->buffer, bounds, name, + variant, inc_per_pixel, buffer, len, state, cursor, select_begin, + select_end, &style->property, filter, in, style->font, &ctx->text_edit, + ctx->button_behavior); + + if (in && *state != NK_PROPERTY_DEFAULT && !win->property.active) { + /* current property is now hot */ + win->property.active = 1; + NK_MEMCPY(win->property.buffer, buffer, (nk_size)*len); + win->property.length = *len; + win->property.cursor = *cursor; + win->property.state = *state; + win->property.name = hash; + win->property.select_start = *select_begin; + win->property.select_end = *select_end; + if (*state == NK_PROPERTY_DRAG) { + ctx->input.mouse.grab = nk_true; + ctx->input.mouse.grabbed = nk_true; + } + } + /* check if previously active property is now inactive */ + if (*state == NK_PROPERTY_DEFAULT && old_state != NK_PROPERTY_DEFAULT) { + if (old_state == NK_PROPERTY_DRAG) { + ctx->input.mouse.grab = nk_false; + ctx->input.mouse.grabbed = nk_false; + ctx->input.mouse.ungrab = nk_true; + } + win->property.select_start = 0; + win->property.select_end = 0; + win->property.active = 0; + } +} + +NK_API void +nk_property_int(struct nk_context *ctx, const char *name, + int min, int *val, int max, int step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + + if (!ctx || !ctx->current || !name || !val) return; + variant = nk_property_variant_int(*val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT); + *val = variant.value.i; +} + +NK_API void +nk_property_float(struct nk_context *ctx, const char *name, + float min, float *val, float max, float step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + + if (!ctx || !ctx->current || !name || !val) return; + variant = nk_property_variant_float(*val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + *val = variant.value.f; +} + +NK_API void +nk_property_double(struct nk_context *ctx, const char *name, + double min, double *val, double max, double step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + NK_ASSERT(val); + + if (!ctx || !ctx->current || !name || !val) return; + variant = nk_property_variant_double(*val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + *val = variant.value.d; +} + +NK_API int +nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, + int max, int step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + + if (!ctx || !ctx->current || !name) return val; + variant = nk_property_variant_int(val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT); + val = variant.value.i; + return val; +} + +NK_API float +nk_propertyf(struct nk_context *ctx, const char *name, float min, + float val, float max, float step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + + if (!ctx || !ctx->current || !name) return val; + variant = nk_property_variant_float(val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + val = variant.value.f; + return val; +} + +NK_API double +nk_propertyd(struct nk_context *ctx, const char *name, double min, + double val, double max, double step, float inc_per_pixel) +{ + struct nk_property_variant variant; + NK_ASSERT(ctx); + NK_ASSERT(name); + + if (!ctx || !ctx->current || !name) return val; + variant = nk_property_variant_double(val, min, max, step); + nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); + val = variant.value.d; + return val; +} + +/*---------------------------------------------------------------- + * + * COLOR PICKER + * + * --------------------------------------------------------------*/ +NK_API int +nk_color_pick(struct nk_context * ctx, struct nk_color *color, + enum nk_color_format fmt) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *config; + const struct nk_input *in; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(color); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !color) + return 0; + + win = ctx->current; + config = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_color_picker(&ctx->last_widget_state, &win->buffer, color, fmt, bounds, + nk_vec2(0,0), in, config->font); +} + +NK_API struct nk_color +nk_color_picker(struct nk_context *ctx, struct nk_color color, + enum nk_color_format fmt) +{ + nk_color_pick(ctx, &color, fmt); + return color; +} + +/* ------------------------------------------------------------- + * + * CHART + * + * --------------------------------------------------------------*/ +NK_API int +nk_chart_begin_colored(struct nk_context *ctx, enum nk_chart_type type, + struct nk_color color, struct nk_color highlight, + int count, float min_value, float max_value) +{ + struct nk_window *win; + struct nk_chart *chart; + const struct nk_style *config; + const struct nk_style_chart *style; + + const struct nk_style_item *background; + struct nk_rect bounds = {0, 0, 0, 0}; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + + if (!ctx || !ctx->current || !ctx->current->layout) return 0; + if (!nk_widget(&bounds, ctx)) { + chart = &ctx->current->layout->chart; + nk_zero(chart, sizeof(*chart)); + return 0; + } + + win = ctx->current; + config = &ctx->style; + chart = &win->layout->chart; + style = &config->chart; + + /* setup basic generic chart */ + nk_zero(chart, sizeof(*chart)); + chart->x = bounds.x + style->padding.x; + chart->y = bounds.y + style->padding.y; + chart->w = bounds.w - 2 * style->padding.x; + chart->h = bounds.h - 2 * style->padding.y; + chart->w = NK_MAX(chart->w, 2 * style->padding.x); + chart->h = NK_MAX(chart->h, 2 * style->padding.y); + + /* add first slot into chart */ + {struct nk_chart_slot *slot = &chart->slots[chart->slot++]; + slot->type = type; + slot->count = count; + slot->color = color; + slot->highlight = highlight; + slot->min = NK_MIN(min_value, max_value); + slot->max = NK_MAX(min_value, max_value); + slot->range = slot->max - slot->min;} + + /* draw chart background */ + background = &style->background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(&win->buffer, bounds, style->rounding, style->border_color); + nk_fill_rect(&win->buffer, nk_shrink_rect(bounds, style->border), + style->rounding, style->background.data.color); + } + return 1; +} + +NK_API int +nk_chart_begin(struct nk_context *ctx, const enum nk_chart_type type, + int count, float min_value, float max_value) +{return nk_chart_begin_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value);} + +NK_API void +nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type type, + struct nk_color color, struct nk_color highlight, + int count, float min_value, float max_value) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(ctx->current->layout->chart.slot < NK_CHART_MAX_SLOT); + if (!ctx || !ctx->current || !ctx->current->layout) return; + if (ctx->current->layout->chart.slot >= NK_CHART_MAX_SLOT) return; + + /* add another slot into the graph */ + {struct nk_chart *chart = &ctx->current->layout->chart; + struct nk_chart_slot *slot = &chart->slots[chart->slot++]; + slot->type = type; + slot->count = count; + slot->color = color; + slot->highlight = highlight; + slot->min = NK_MIN(min_value, max_value); + slot->max = NK_MAX(min_value, max_value); + slot->range = slot->max - slot->min;} +} + +NK_API void +nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type type, + int count, float min_value, float max_value) +{nk_chart_add_slot_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value);} + +NK_INTERN nk_flags +nk_chart_push_line(struct nk_context *ctx, struct nk_window *win, + struct nk_chart *g, float value, int slot) +{ + struct nk_panel *layout = win->layout; + const struct nk_input *i = &ctx->input; + struct nk_command_buffer *out = &win->buffer; + + nk_flags ret = 0; + struct nk_vec2 cur; + struct nk_rect bounds; + struct nk_color color; + float step; + float range; + float ratio; + + NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); + step = g->w / (float)g->slots[slot].count; + range = g->slots[slot].max - g->slots[slot].min; + ratio = (value - g->slots[slot].min) / range; + + if (g->slots[slot].index == 0) { + /* first data point does not have a connection */ + g->slots[slot].last.x = g->x; + g->slots[slot].last.y = (g->y + g->h) - ratio * (float)g->h; + + bounds.x = g->slots[slot].last.x - 2; + bounds.y = g->slots[slot].last.y - 2; + bounds.w = bounds.h = 4; + + color = g->slots[slot].color; + if (!(layout->flags & NK_WINDOW_ROM) && + NK_INBOX(i->mouse.pos.x,i->mouse.pos.y, g->slots[slot].last.x-3, g->slots[slot].last.y-3, 6, 6)){ + ret = nk_input_is_mouse_hovering_rect(i, bounds) ? NK_CHART_HOVERING : 0; + ret |= (i->mouse.buttons[NK_BUTTON_LEFT].down && + i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = g->slots[slot].highlight; + } + nk_fill_rect(out, bounds, 0, color); + g->slots[slot].index += 1; + return ret; + } + + /* draw a line between the last data point and the new one */ + color = g->slots[slot].color; + cur.x = g->x + (float)(step * (float)g->slots[slot].index); + cur.y = (g->y + g->h) - (ratio * (float)g->h); + nk_stroke_line(out, g->slots[slot].last.x, g->slots[slot].last.y, cur.x, cur.y, 1.0f, color); + + bounds.x = cur.x - 3; + bounds.y = cur.y - 3; + bounds.w = bounds.h = 6; + + /* user selection of current data point */ + if (!(layout->flags & NK_WINDOW_ROM)) { + if (nk_input_is_mouse_hovering_rect(i, bounds)) { + ret = NK_CHART_HOVERING; + ret |= (!i->mouse.buttons[NK_BUTTON_LEFT].down && + i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = g->slots[slot].highlight; + } + } + nk_fill_rect(out, nk_rect(cur.x - 2, cur.y - 2, 4, 4), 0, color); + + /* save current data point position */ + g->slots[slot].last.x = cur.x; + g->slots[slot].last.y = cur.y; + g->slots[slot].index += 1; + return ret; +} + +NK_INTERN nk_flags +nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win, + struct nk_chart *chart, float value, int slot) +{ + struct nk_command_buffer *out = &win->buffer; + const struct nk_input *in = &ctx->input; + struct nk_panel *layout = win->layout; + + float ratio; + nk_flags ret = 0; + struct nk_color color; + struct nk_rect item = {0,0,0,0}; + + NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); + if (chart->slots[slot].index >= chart->slots[slot].count) + return nk_false; + if (chart->slots[slot].count) { + float padding = (float)(chart->slots[slot].count-1); + item.w = (chart->w - padding) / (float)(chart->slots[slot].count); + } + + /* calculate bounds of current bar chart entry */ + color = chart->slots[slot].color;; + item.h = chart->h * NK_ABS((value/chart->slots[slot].range)); + if (value >= 0) { + ratio = (value + NK_ABS(chart->slots[slot].min)) / NK_ABS(chart->slots[slot].range); + item.y = (chart->y + chart->h) - chart->h * ratio; + } else { + ratio = (value - chart->slots[slot].max) / chart->slots[slot].range; + item.y = chart->y + (chart->h * NK_ABS(ratio)) - item.h; + } + item.x = chart->x + ((float)chart->slots[slot].index * item.w); + item.x = item.x + ((float)chart->slots[slot].index); + + /* user chart bar selection */ + if (!(layout->flags & NK_WINDOW_ROM) && + NK_INBOX(in->mouse.pos.x,in->mouse.pos.y,item.x,item.y,item.w,item.h)) { + ret = NK_CHART_HOVERING; + ret |= (!in->mouse.buttons[NK_BUTTON_LEFT].down && + in->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; + color = chart->slots[slot].highlight; + } + nk_fill_rect(out, item, 0, color); + chart->slots[slot].index += 1; + return ret; +} + +NK_API nk_flags +nk_chart_push_slot(struct nk_context *ctx, float value, int slot) +{ + nk_flags flags; + struct nk_window *win; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); + NK_ASSERT(slot < ctx->current->layout->chart.slot); + if (!ctx || !ctx->current || slot >= NK_CHART_MAX_SLOT) return nk_false; + if (slot >= ctx->current->layout->chart.slot) return nk_false; + + win = ctx->current; + if (win->layout->chart.slot < slot) return nk_false; + switch (win->layout->chart.slots[slot].type) { + case NK_CHART_LINES: + flags = nk_chart_push_line(ctx, win, &win->layout->chart, value, slot); break; + case NK_CHART_COLUMN: + flags = nk_chart_push_column(ctx, win, &win->layout->chart, value, slot); break; + default: + case NK_CHART_MAX: + flags = 0; + } + return flags; +} + +NK_API nk_flags +nk_chart_push(struct nk_context *ctx, float value) +{return nk_chart_push_slot(ctx, value, 0);} + +NK_API void +nk_chart_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_chart *chart; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + + win = ctx->current; + chart = &win->layout->chart; + NK_MEMSET(chart, 0, sizeof(*chart)); + return; +} + +NK_API void +nk_plot(struct nk_context *ctx, enum nk_chart_type type, const float *values, + int count, int offset) +{ + int i = 0; + float min_value; + float max_value; + + NK_ASSERT(ctx); + NK_ASSERT(values); + if (!ctx || !values || !count) return; + + min_value = values[offset]; + max_value = values[offset]; + for (i = 0; i < count; ++i) { + min_value = NK_MIN(values[i + offset], min_value); + max_value = NK_MAX(values[i + offset], max_value); + } + + if (nk_chart_begin(ctx, type, count, min_value, max_value)) { + for (i = 0; i < count; ++i) + nk_chart_push(ctx, values[i + offset]); + nk_chart_end(ctx); + } +} + +NK_API void +nk_plot_function(struct nk_context *ctx, enum nk_chart_type type, void *userdata, + float(*value_getter)(void* user, int index), int count, int offset) +{ + int i = 0; + float min_value; + float max_value; + + NK_ASSERT(ctx); + NK_ASSERT(value_getter); + if (!ctx || !value_getter || !count) return; + + max_value = min_value = value_getter(userdata, offset); + for (i = 0; i < count; ++i) { + float value = value_getter(userdata, i + offset); + min_value = NK_MIN(value, min_value); + max_value = NK_MAX(value, max_value); + } + + if (nk_chart_begin(ctx, type, count, min_value, max_value)) { + for (i = 0; i < count; ++i) + nk_chart_push(ctx, value_getter(userdata, i + offset)); + nk_chart_end(ctx); + } +} + +/* ------------------------------------------------------------- + * + * GROUP + * + * --------------------------------------------------------------*/ +NK_API int +nk_group_scrolled_offset_begin(struct nk_context *ctx, + nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags) +{ + struct nk_rect bounds; + struct nk_window panel; + struct nk_window *win; + + win = ctx->current; + nk_panel_alloc_space(&bounds, ctx); + {const struct nk_rect *c = &win->layout->clip; + if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h) && + !(flags & NK_WINDOW_MOVABLE)) { + return 0; + }} + if (win->flags & NK_WINDOW_ROM) + flags |= NK_WINDOW_ROM; + + /* initialize a fake window to create the panel from */ + nk_zero(&panel, sizeof(panel)); + panel.bounds = bounds; + panel.flags = flags; + panel.scrollbar.x = *x_offset; + panel.scrollbar.y = *y_offset; + panel.buffer = win->buffer; + panel.layout = (struct nk_panel*)nk_create_panel(ctx); + ctx->current = &panel; + nk_panel_begin(ctx, (flags & NK_WINDOW_TITLE) ? title: 0, NK_PANEL_GROUP); + + win->buffer = panel.buffer; + win->buffer.clip = panel.layout->clip; + panel.layout->offset_x = x_offset; + panel.layout->offset_y = y_offset; + panel.layout->parent = win->layout; + win->layout = panel.layout; + + ctx->current = win; + if ((panel.layout->flags & NK_WINDOW_CLOSED) || + (panel.layout->flags & NK_WINDOW_MINIMIZED)) + { + nk_flags f = panel.layout->flags; + nk_group_scrolled_end(ctx); + if (f & NK_WINDOW_CLOSED) + return NK_WINDOW_CLOSED; + if (f & NK_WINDOW_MINIMIZED) + return NK_WINDOW_MINIMIZED; + } + return 1; +} + +NK_API void +nk_group_scrolled_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *parent; + struct nk_panel *g; + + struct nk_rect clip; + struct nk_window pan; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + + /* make sure nk_group_begin was called correctly */ + NK_ASSERT(ctx->current); + win = ctx->current; + NK_ASSERT(win->layout); + g = win->layout; + NK_ASSERT(g->parent); + parent = g->parent; + + /* dummy window */ + nk_zero_struct(pan); + panel_padding = nk_panel_get_padding(&ctx->style, NK_PANEL_GROUP); + pan.bounds.y = g->bounds.y - (g->header_height + g->menu.h); + pan.bounds.x = g->bounds.x - panel_padding.x; + pan.bounds.w = g->bounds.w + 2 * panel_padding.x; + pan.bounds.h = g->bounds.h + g->header_height + g->menu.h; + if (g->flags & NK_WINDOW_BORDER) { + pan.bounds.x -= g->border; + pan.bounds.y -= g->border; + pan.bounds.w += 2*g->border; + pan.bounds.h += 2*g->border; + } + if (!(g->flags & NK_WINDOW_NO_SCROLLBAR)) { + pan.bounds.w += ctx->style.window.scrollbar_size.x; + pan.bounds.h += ctx->style.window.scrollbar_size.y; + } + pan.scrollbar.x = *g->offset_x; + pan.scrollbar.y = *g->offset_y; + pan.flags = g->flags; + pan.buffer = win->buffer; + pan.layout = g; + pan.parent = win; + ctx->current = &pan; + + /* make sure group has correct clipping rectangle */ + nk_unify(&clip, &parent->clip, pan.bounds.x, pan.bounds.y, + pan.bounds.x + pan.bounds.w, pan.bounds.y + pan.bounds.h + panel_padding.x); + nk_push_scissor(&pan.buffer, clip); + nk_end(ctx); + + win->buffer = pan.buffer; + nk_push_scissor(&win->buffer, parent->clip); + ctx->current = win; + win->layout = parent; + g->bounds = pan.bounds; + return; +} + +NK_API int +nk_group_scrolled_begin(struct nk_context *ctx, + struct nk_scroll *scroll, const char *title, nk_flags flags) +{return nk_group_scrolled_offset_begin(ctx, &scroll->x, &scroll->y, title, flags);} + +NK_API int +nk_group_begin(struct nk_context *ctx, const char *title, nk_flags flags) +{ + int title_len; + nk_hash title_hash; + struct nk_window *win; + nk_uint *x_offset; + nk_uint *y_offset; + + NK_ASSERT(ctx); + NK_ASSERT(title); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !title) + return 0; + + /* find persistent group scrollbar value */ + win = ctx->current; + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP); + x_offset = nk_find_value(win, title_hash); + if (!x_offset) { + x_offset = nk_add_value(ctx, win, title_hash, 0); + y_offset = nk_add_value(ctx, win, title_hash+1, 0); + + NK_ASSERT(x_offset); + NK_ASSERT(y_offset); + if (!x_offset || !y_offset) return 0; + *x_offset = *y_offset = 0; + } else y_offset = nk_find_value(win, title_hash+1); + return nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); +} + +NK_API void +nk_group_end(struct nk_context *ctx) +{nk_group_scrolled_end(ctx);} + +NK_API int +nk_list_view_begin(struct nk_context *ctx, struct nk_list_view *view, + const char *title, nk_flags flags, int row_height, int row_count) +{ + int title_len; + nk_hash title_hash; + nk_uint *x_offset; + nk_uint *y_offset; + + int result; + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_vec2 item_spacing; + + NK_ASSERT(ctx); + NK_ASSERT(view); + NK_ASSERT(title); + if (!ctx || !view || !title) return 0; + + win = ctx->current; + style = &ctx->style; + item_spacing = style->window.spacing; + row_height += NK_MAX(0, (int)item_spacing.y); + + /* find persistent list view scrollbar offset */ + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP); + x_offset = nk_find_value(win, title_hash); + if (!x_offset) { + x_offset = nk_add_value(ctx, win, title_hash, 0); + y_offset = nk_add_value(ctx, win, title_hash+1, 0); + + NK_ASSERT(x_offset); + NK_ASSERT(y_offset); + if (!x_offset || !y_offset) return 0; + *x_offset = *y_offset = 0; + } else y_offset = nk_find_value(win, title_hash+1); + view->scroll_value = *y_offset; + view->scroll_pointer = y_offset; + + *y_offset = 0; + result = nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); + win = ctx->current; + layout = win->layout; + + view->total_height = row_height * NK_MAX(row_count,1); + view->begin = (int)NK_MAX(((float)view->scroll_value / (float)row_height), 0.0f); + view->count = (int)NK_MAX(nk_iceilf((layout->clip.h)/(float)row_height), 0); + view->end = view->begin + view->count; + view->ctx = ctx; + return result; +} + +NK_API void +nk_list_view_end(struct nk_list_view *view) +{ + struct nk_context *ctx; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(view); + NK_ASSERT(view->ctx); + NK_ASSERT(view->scroll_pointer); + if (!view || !view->ctx) return; + + ctx = view->ctx; + win = ctx->current; + layout = win->layout; + layout->at_y = layout->bounds.y + (float)view->total_height; + *view->scroll_pointer = *view->scroll_pointer + view->scroll_value; + nk_group_end(view->ctx); +} + +/* -------------------------------------------------------------- + * + * POPUP + * + * --------------------------------------------------------------*/ +NK_API int +nk_popup_begin(struct nk_context *ctx, enum nk_popup_type type, + const char *title, nk_flags flags, struct nk_rect rect) +{ + struct nk_window *popup; + struct nk_window *win; + struct nk_panel *panel; + + int title_len; + nk_hash title_hash; + nk_size allocated; + + NK_ASSERT(ctx); + NK_ASSERT(title); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + panel = win->layout; + NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP) && "popups are not allowed to have popups"); + (void)panel; + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_POPUP); + + popup = win->popup.win; + if (!popup) { + popup = (struct nk_window*)nk_create_window(ctx); + popup->parent = win; + win->popup.win = popup; + win->popup.active = 0; + win->popup.type = NK_PANEL_POPUP; + } + + /* make sure we have correct popup */ + if (win->popup.name != title_hash) { + if (!win->popup.active) { + nk_zero(popup, sizeof(*popup)); + win->popup.name = title_hash; + win->popup.active = 1; + win->popup.type = NK_PANEL_POPUP; + } else return 0; + } + + /* popup position is local to window */ + ctx->current = popup; + rect.x += win->layout->clip.x; + rect.y += win->layout->clip.y; + + /* setup popup data */ + popup->parent = win; + popup->bounds = rect; + popup->seq = ctx->seq; + popup->layout = (struct nk_panel*)nk_create_panel(ctx); + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER; + if (type == NK_POPUP_DYNAMIC) + popup->flags |= NK_WINDOW_DYNAMIC; + + popup->buffer = win->buffer; + nk_start_popup(ctx, win); + allocated = ctx->memory.allocated; + nk_push_scissor(&popup->buffer, nk_null_rect); + + if (nk_panel_begin(ctx, title, NK_PANEL_POPUP)) { + /* popup is running therefore invalidate parent panels */ + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_ROM; + root->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.active = 1; + popup->layout->offset_x = &popup->scrollbar.x; + popup->layout->offset_y = &popup->scrollbar.y; + popup->layout->parent = win->layout; + return 1; + } else { + /* popup was closed/is invalid so cleanup */ + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.buf.active = 0; + win->popup.active = 0; + ctx->memory.allocated = allocated; + ctx->current = win; + nk_free_panel(ctx, popup->layout); + popup->layout = 0; + return 0; + } +} + +NK_INTERN int +nk_nonblock_begin(struct nk_context *ctx, + nk_flags flags, struct nk_rect body, struct nk_rect header, + enum nk_panel_type panel_type) +{ + struct nk_window *popup; + struct nk_window *win; + struct nk_panel *panel; + int is_active = nk_true; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* popups cannot have popups */ + win = ctx->current; + panel = win->layout; + NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP)); + (void)panel; + popup = win->popup.win; + if (!popup) { + /* create window for nonblocking popup */ + popup = (struct nk_window*)nk_create_window(ctx); + popup->parent = win; + win->popup.win = popup; + win->popup.type = panel_type; + nk_command_buffer_init(&popup->buffer, &ctx->memory, NK_CLIPPING_ON); + } else { + /* close the popup if user pressed outside or in the header */ + int pressed, in_body, in_header; + pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); + in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); + in_header = nk_input_is_mouse_hovering_rect(&ctx->input, header); + if (pressed && (!in_body || in_header)) + is_active = nk_false; + } + win->popup.header = header; + + if (!is_active) { + /* remove read only mode from all parent panels */ + struct nk_panel *root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + return is_active; + } + + popup->bounds = body; + popup->parent = win; + popup->layout = (struct nk_panel*)nk_create_panel(ctx); + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER; + popup->flags |= NK_WINDOW_DYNAMIC; + popup->seq = ctx->seq; + win->popup.active = 1; + NK_ASSERT(popup->layout); + + nk_start_popup(ctx, win); + popup->buffer = win->buffer; + nk_push_scissor(&popup->buffer, nk_null_rect); + ctx->current = popup; + + nk_panel_begin(ctx, 0, panel_type); + win->buffer = popup->buffer; + popup->layout->parent = win->layout; + popup->layout->offset_x = &popup->scrollbar.x; + popup->layout->offset_y = &popup->scrollbar.y; + + /* set read only mode to all parent panels */ + {struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_ROM; + root = root->parent; + }} + return is_active; +} + +NK_API void +nk_popup_close(struct nk_context *ctx) +{ + struct nk_window *popup; + NK_ASSERT(ctx); + if (!ctx || !ctx->current) return; + + popup = ctx->current; + NK_ASSERT(popup->parent); + NK_ASSERT(popup->layout->type & NK_PANEL_SET_POPUP); + popup->flags |= NK_WINDOW_HIDDEN; +} + +NK_API void +nk_popup_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_window *popup; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + popup = ctx->current; + if (!popup->parent) return; + win = popup->parent; + if (popup->flags & NK_WINDOW_HIDDEN) { + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.active = 0; + } + nk_push_scissor(&popup->buffer, nk_null_rect); + nk_end(ctx); + + win->buffer = popup->buffer; + nk_finish_popup(ctx, win); + ctx->current = win; + nk_push_scissor(&win->buffer, win->layout->clip); +} +/* ------------------------------------------------------------- + * + * TOOLTIP + * + * -------------------------------------------------------------- */ +NK_API int +nk_tooltip_begin(struct nk_context *ctx, float width) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect bounds; + int ret; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* make sure that no nonblocking popup is currently active */ + win = ctx->current; + in = &ctx->input; + if (win->popup.win && (win->popup.type & NK_PANEL_SET_NONBLOCK)) + return 0; + + bounds.w = width; + bounds.h = nk_null_rect.h; + bounds.x = (in->mouse.pos.x + 1) - win->layout->clip.x; + bounds.y = (in->mouse.pos.y + 1) - win->layout->clip.y; + + ret = nk_popup_begin(ctx, NK_POPUP_DYNAMIC, + "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER, bounds); + if (ret) win->layout->flags &= ~(nk_flags)NK_WINDOW_ROM; + win->popup.type = NK_PANEL_TOOLTIP; + ctx->current->layout->type = NK_PANEL_TOOLTIP; + return ret; +} + +NK_API void +nk_tooltip_end(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->seq--; + nk_popup_close(ctx); + nk_popup_end(ctx); +} + +NK_API void +nk_tooltip(struct nk_context *ctx, const char *text) +{ + const struct nk_style *style; + struct nk_vec2 padding; + + int text_len; + float text_width; + float text_height; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(text); + if (!ctx || !ctx->current || !ctx->current->layout || !text) + return; + + /* fetch configuration data */ + style = &ctx->style; + padding = style->window.padding; + + /* calculate size of the text and tooltip */ + text_len = nk_strlen(text); + text_width = style->font->width(style->font->userdata, + style->font->height, text, text_len); + text_width += (4 * padding.x); + text_height = (style->font->height + 2 * padding.y); + + /* execute tooltip and fill with text */ + if (nk_tooltip_begin(ctx, (float)text_width)) { + nk_layout_row_dynamic(ctx, (float)text_height, 1); + nk_text(ctx, text, text_len, NK_TEXT_LEFT); + nk_tooltip_end(ctx); + } +} +/* ------------------------------------------------------------- + * + * CONTEXTUAL + * + * -------------------------------------------------------------- */ +NK_API int +nk_contextual_begin(struct nk_context *ctx, nk_flags flags, struct nk_vec2 size, + struct nk_rect trigger_bounds) +{ + struct nk_window *win; + struct nk_window *popup; + struct nk_rect body; + + NK_STORAGE const struct nk_rect null_rect = {0,0,0,0}; + int is_clicked = 0; + int is_active = 0; + int is_open = 0; + int ret = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + ++win->popup.con_count; + + /* check if currently active contextual is active */ + popup = win->popup.win; + is_open = (popup && win->popup.type == NK_PANEL_CONTEXTUAL); + is_clicked = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, trigger_bounds); + if (win->popup.active_con && win->popup.con_count != win->popup.active_con) + return 0; + if ((is_clicked && is_open && !is_active) || (!is_open && !is_active && !is_clicked)) + return 0; + + /* calculate contextual position on click */ + win->popup.active_con = win->popup.con_count; + if (is_clicked) { + body.x = ctx->input.mouse.pos.x; + body.y = ctx->input.mouse.pos.y; + } else { + body.x = popup->bounds.x; + body.y = popup->bounds.y; + } + body.w = size.x; + body.h = size.y; + + /* start nonblocking contextual popup */ + ret = nk_nonblock_begin(ctx, flags|NK_WINDOW_NO_SCROLLBAR, body, + null_rect, NK_PANEL_CONTEXTUAL); + if (ret) win->popup.type = NK_PANEL_CONTEXTUAL; + else { + win->popup.active_con = 0; + if (win->popup.win) + win->popup.win->flags = 0; + } + return ret; +} + +NK_API int +nk_contextual_item_text(struct nk_context *ctx, const char *text, int len, + nk_flags alignment) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, + text, len, alignment, NK_BUTTON_DEFAULT, &style->contextual_button, in, style->font)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} + +NK_API int nk_contextual_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{return nk_contextual_item_text(ctx, label, nk_strlen(label), align);} + +NK_API int +nk_contextual_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds, + img, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)){ + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} + +NK_API int nk_contextual_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{return nk_contextual_item_image_text(ctx, img, label, nk_strlen(label), align);} + +NK_API int +nk_contextual_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} + +NK_API int nk_contextual_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, nk_flags align) +{return nk_contextual_item_symbol_text(ctx, symbol, text, nk_strlen(text), align);} + +NK_API void +nk_contextual_close(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + nk_popup_close(ctx); +} + +NK_API void +nk_contextual_end(struct nk_context *ctx) +{ + struct nk_window *popup; + struct nk_panel *panel; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + + popup = ctx->current; + panel = popup->layout; + NK_ASSERT(popup->parent); + NK_ASSERT(panel->type & NK_PANEL_SET_POPUP); + if (panel->flags & NK_WINDOW_DYNAMIC) { + /* Close behavior + This is a bit of a hack solution since we do not know before we end our popup + how big it will be. We therefore do not directly know when a + click outside the non-blocking popup must close it at that direct frame. + Instead it will be closed in the next frame.*/ + struct nk_rect body = {0,0,0,0}; + if (panel->at_y < (panel->bounds.y + panel->bounds.h)) { + struct nk_vec2 padding = nk_panel_get_padding(&ctx->style, panel->type); + body = panel->bounds; + body.y = (panel->at_y + panel->footer_height + panel->border + padding.y + panel->row.height); + body.h = (panel->bounds.y + panel->bounds.h) - body.y; + } + {int pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); + int in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); + if (pressed && in_body) + popup->flags |= NK_WINDOW_HIDDEN; + } + } + if (popup->flags & NK_WINDOW_HIDDEN) + popup->seq = 0; + nk_popup_end(ctx); + return; +} +/* ------------------------------------------------------------- + * + * COMBO + * + * --------------------------------------------------------------*/ +NK_INTERN int +nk_combo_begin(struct nk_context *ctx, struct nk_window *win, + struct nk_vec2 size, int is_clicked, struct nk_rect header) +{ + struct nk_window *popup; + int is_open = 0; + int is_active = 0; + struct nk_rect body; + nk_hash hash; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + popup = win->popup.win; + body.x = header.x; + body.w = size.x; + body.y = header.y + header.h-ctx->style.window.combo_border; + body.h = size.y; + + hash = win->popup.combo_count++; + is_open = (popup) ? nk_true:nk_false; + is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_COMBO); + if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || + (!is_open && !is_active && !is_clicked)) return 0; + if (!nk_nonblock_begin(ctx, 0, body, + (is_clicked && is_open)?nk_rect(0,0,0,0):header, NK_PANEL_COMBO)) return 0; + + win->popup.type = NK_PANEL_COMBO; + win->popup.name = hash; + return 1; +} + +NK_API int +nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len, + struct nk_vec2 size) +{ + const struct nk_input *in; + struct nk_window *win; + struct nk_style *style; + + enum nk_widget_layout_states s; + int is_clicked = nk_false; + struct nk_rect header; + const struct nk_style_item *background; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(selected); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !selected) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + /* print currently selected text item */ + struct nk_rect label; + struct nk_rect button; + struct nk_rect content; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw selected label */ + text.padding = nk_vec2(0,0); + label.x = header.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = button.x - (style->combo.content_padding.x + style->combo.spacing.x) - label.x;; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, + NK_TEXT_LEFT, ctx->style.font); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} + +NK_API int nk_combo_begin_label(struct nk_context *ctx, const char *selected, struct nk_vec2 size) +{return nk_combo_begin_text(ctx, selected, nk_strlen(selected), size);} + +NK_API int +nk_combo_begin_color(struct nk_context *ctx, struct nk_color color, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) + background = &style->combo.active; + else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + background = &style->combo.hover; + else background = &style->combo.normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, header, &background->data.image,nk_white); + } else { + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect bounds; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw color */ + bounds.h = header.h - 4 * style->combo.content_padding.y; + bounds.y = header.y + 2 * style->combo.content_padding.y; + bounds.x = header.x + 2 * style->combo.content_padding.x; + bounds.w = (button.x - (style->combo.content_padding.x + style->combo.spacing.x)) - bounds.x; + nk_fill_rect(&win->buffer, bounds, 0, color); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} + +NK_API int +nk_combo_begin_symbol(struct nk_context *ctx, enum nk_symbol_type symbol, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_color sym_background; + struct nk_color symbol_color; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + symbol_color = style->combo.symbol_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + symbol_color = style->combo.symbol_hover; + } else { + background = &style->combo.normal; + symbol_color = style->combo.symbol_hover; + } + + if (background->type == NK_STYLE_ITEM_IMAGE) { + sym_background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + sym_background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect bounds = {0,0,0,0}; + struct nk_rect content; + struct nk_rect button; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw symbol */ + bounds.h = header.h - 2 * style->combo.content_padding.y; + bounds.y = header.y + style->combo.content_padding.y; + bounds.x = header.x + style->combo.content_padding.x; + bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; + nk_draw_symbol(&win->buffer, symbol, bounds, sym_background, symbol_color, + 1.0f, style->font); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} + +NK_API int +nk_combo_begin_symbol_text(struct nk_context *ctx, const char *selected, int len, + enum nk_symbol_type symbol, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_color symbol_color; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (!s) return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + symbol_color = style->combo.symbol_active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + symbol_color = style->combo.symbol_hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + symbol_color = style->combo.symbol_normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect label; + struct nk_rect image; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + + /* draw symbol */ + image.x = header.x + style->combo.content_padding.x; + image.y = header.y + style->combo.content_padding.y; + image.h = header.h - 2 * style->combo.content_padding.y; + image.w = image.h; + nk_draw_symbol(&win->buffer, symbol, image, text.background, symbol_color, + 1.0f, style->font); + + /* draw label */ + text.padding = nk_vec2(0,0); + label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = (button.x - style->combo.content_padding.x) - label.x; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} + +NK_API int +nk_combo_begin_image(struct nk_context *ctx, struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + const struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (s == NK_WIDGET_INVALID) + return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) + background = &style->combo.active; + else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + background = &style->combo.hover; + else background = &style->combo.normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect bounds = {0,0,0,0}; + struct nk_rect content; + struct nk_rect button; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + + /* draw image */ + bounds.h = header.h - 2 * style->combo.content_padding.y; + bounds.y = header.y + style->combo.content_padding.y; + bounds.x = header.x + style->combo.content_padding.x; + bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; + nk_draw_image(&win->buffer, bounds, &img, nk_white); + + /* draw open/close button */ + nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} + +NK_API int +nk_combo_begin_image_text(struct nk_context *ctx, const char *selected, int len, + struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_style *style; + struct nk_input *in; + + struct nk_rect header; + int is_clicked = nk_false; + enum nk_widget_layout_states s; + const struct nk_style_item *background; + struct nk_text text; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + s = nk_widget(&header, ctx); + if (!s) return 0; + + in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; + if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) + is_clicked = nk_true; + + /* draw combo box header background and border */ + if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { + background = &style->combo.active; + text.text = style->combo.label_active; + } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { + background = &style->combo.hover; + text.text = style->combo.label_hover; + } else { + background = &style->combo.normal; + text.text = style->combo.label_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) { + text.background = nk_rgba(0,0,0,0); + nk_draw_image(&win->buffer, header, &background->data.image, nk_white); + } else { + text.background = background->data.color; + nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); + nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); + } + { + struct nk_rect content; + struct nk_rect button; + struct nk_rect label; + struct nk_rect image; + + enum nk_symbol_type sym; + if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) + sym = style->combo.sym_hover; + else if (is_clicked) + sym = style->combo.sym_active; + else sym = style->combo.sym_normal; + + /* calculate button */ + button.w = header.h - 2 * style->combo.button_padding.y; + button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; + button.y = header.y + style->combo.button_padding.y; + button.h = button.w; + + content.x = button.x + style->combo.button.padding.x; + content.y = button.y + style->combo.button.padding.y; + content.w = button.w - 2 * style->combo.button.padding.x; + content.h = button.h - 2 * style->combo.button.padding.y; + nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, + &ctx->style.combo.button, sym, style->font); + + /* draw image */ + image.x = header.x + style->combo.content_padding.x; + image.y = header.y + style->combo.content_padding.y; + image.h = header.h - 2 * style->combo.content_padding.y; + image.w = image.h; + nk_draw_image(&win->buffer, image, &img, nk_white); + + /* draw label */ + text.padding = nk_vec2(0,0); + label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; + label.y = header.y + style->combo.content_padding.y; + label.w = (button.x - style->combo.content_padding.x) - label.x; + label.h = header.h - 2 * style->combo.content_padding.y; + nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font); + } + return nk_combo_begin(ctx, win, size, is_clicked, header); +} + +NK_API int nk_combo_begin_symbol_label(struct nk_context *ctx, + const char *selected, enum nk_symbol_type type, struct nk_vec2 size) +{return nk_combo_begin_symbol_text(ctx, selected, nk_strlen(selected), type, size);} + +NK_API int nk_combo_begin_image_label(struct nk_context *ctx, + const char *selected, struct nk_image img, struct nk_vec2 size) +{return nk_combo_begin_image_text(ctx, selected, nk_strlen(selected), img, size);} + +NK_API int nk_combo_item_text(struct nk_context *ctx, const char *text, int len,nk_flags align) +{return nk_contextual_item_text(ctx, text, len, align);} + +NK_API int nk_combo_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{return nk_contextual_item_label(ctx, label, align);} + +NK_API int nk_combo_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text, + int len, nk_flags alignment) +{return nk_contextual_item_image_text(ctx, img, text, len, alignment);} + +NK_API int nk_combo_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *text, nk_flags alignment) +{return nk_contextual_item_image_label(ctx, img, text, alignment);} + +NK_API int nk_combo_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *text, int len, nk_flags alignment) +{return nk_contextual_item_symbol_text(ctx, sym, text, len, alignment);} + +NK_API int nk_combo_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *label, nk_flags alignment) +{return nk_contextual_item_symbol_label(ctx, sym, label, alignment);} + +NK_API void nk_combo_end(struct nk_context *ctx) +{nk_contextual_end(ctx);} + +NK_API void nk_combo_close(struct nk_context *ctx) +{nk_contextual_close(ctx);} + +NK_API int +nk_combo(struct nk_context *ctx, const char **items, int count, + int selected, int item_height, struct nk_vec2 size) +{ + int i = 0; + int max_height; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + + NK_ASSERT(ctx); + NK_ASSERT(items); + NK_ASSERT(ctx->current); + if (!ctx || !items ||!count) + return selected; + + item_spacing = ctx->style.window.spacing; + window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); + max_height = count * item_height + count * (int)item_spacing.y; + max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; + size.y = NK_MIN(size.y, (float)max_height); + if (nk_combo_begin_label(ctx, items[selected], size)) { + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT)) + selected = i; + } + nk_combo_end(ctx); + } + return selected; +} + +NK_API int +nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separator, + int separator, int selected, int count, int item_height, struct nk_vec2 size) +{ + int i; + int max_height; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + const char *current_item; + const char *iter; + int length = 0; + + NK_ASSERT(ctx); + NK_ASSERT(items_separated_by_separator); + if (!ctx || !items_separated_by_separator) + return selected; + + /* calculate popup window */ + item_spacing = ctx->style.window.spacing; + window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); + max_height = count * item_height + count * (int)item_spacing.y; + max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; + size.y = NK_MIN(size.y, (float)max_height); + + /* find selected item */ + current_item = items_separated_by_separator; + for (i = 0; i < count; ++i) { + iter = current_item; + while (*iter && *iter != separator) iter++; + length = (int)(iter - current_item); + if (i == selected) break; + current_item = iter + 1; + } + + if (nk_combo_begin_text(ctx, current_item, length, size)) { + current_item = items_separated_by_separator; + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + iter = current_item; + while (*iter && *iter != separator) iter++; + length = (int)(iter - current_item); + if (nk_combo_item_text(ctx, current_item, length, NK_TEXT_LEFT)) + selected = i; + current_item = current_item + length + 1; + } + nk_combo_end(ctx); + } + return selected; +} + +NK_API int +nk_combo_string(struct nk_context *ctx, const char *items_separated_by_zeros, + int selected, int count, int item_height, struct nk_vec2 size) +{return nk_combo_separator(ctx, items_separated_by_zeros, '\0', selected, count, item_height, size);} + +NK_API int +nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const char**), + void *userdata, int selected, int count, int item_height, struct nk_vec2 size) +{ + int i; + int max_height; + struct nk_vec2 item_spacing; + struct nk_vec2 window_padding; + const char *item; + + NK_ASSERT(ctx); + NK_ASSERT(item_getter); + if (!ctx || !item_getter) + return selected; + + /* calculate popup window */ + item_spacing = ctx->style.window.spacing; + window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); + max_height = count * item_height + count * (int)item_spacing.y; + max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; + size.y = NK_MIN(size.y, (float)max_height); + + item_getter(userdata, selected, &item); + if (nk_combo_begin_label(ctx, item, size)) { + nk_layout_row_dynamic(ctx, (float)item_height, 1); + for (i = 0; i < count; ++i) { + item_getter(userdata, i, &item); + if (nk_combo_item_label(ctx, item, NK_TEXT_LEFT)) + selected = i; + } + nk_combo_end(ctx); + } + return selected; +} + +NK_API void nk_combobox(struct nk_context *ctx, const char **items, int count, + int *selected, int item_height, struct nk_vec2 size) +{*selected = nk_combo(ctx, items, count, *selected, item_height, size);} + +NK_API void nk_combobox_string(struct nk_context *ctx, const char *items_separated_by_zeros, + int *selected, int count, int item_height, struct nk_vec2 size) +{*selected = nk_combo_string(ctx, items_separated_by_zeros, *selected, count, item_height, size);} + +NK_API void nk_combobox_separator(struct nk_context *ctx, const char *items_separated_by_separator, + int separator,int *selected, int count, int item_height, struct nk_vec2 size) +{*selected = nk_combo_separator(ctx, items_separated_by_separator, separator, + *selected, count, item_height, size);} + +NK_API void nk_combobox_callback(struct nk_context *ctx, + void(*item_getter)(void* data, int id, const char **out_text), + void *userdata, int *selected, int count, int item_height, struct nk_vec2 size) +{*selected = nk_combo_callback(ctx, item_getter, userdata, *selected, count, item_height, size);} + +/* + * ------------------------------------------------------------- + * + * MENU + * + * -------------------------------------------------------------- + */ +NK_INTERN int +nk_menu_begin(struct nk_context *ctx, struct nk_window *win, + const char *id, int is_clicked, struct nk_rect header, struct nk_vec2 size) +{ + int is_open = 0; + int is_active = 0; + struct nk_rect body; + struct nk_window *popup; + nk_hash hash = nk_murmur_hash(id, (int)nk_strlen(id), NK_PANEL_MENU); + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + body.x = header.x; + body.w = size.x; + body.y = header.y + header.h; + body.h = size.y; + + popup = win->popup.win; + is_open = popup ? nk_true : nk_false; + is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_MENU); + if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || + (!is_open && !is_active && !is_clicked)) return 0; + if (!nk_nonblock_begin(ctx, NK_WINDOW_NO_SCROLLBAR, body, header, NK_PANEL_MENU)) + return 0; + + win->popup.type = NK_PANEL_MENU; + win->popup.name = hash; + return 1; +} + +NK_API int +nk_menu_begin_text(struct nk_context *ctx, const char *title, int len, + nk_flags align, struct nk_vec2 size) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header, + title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, title, is_clicked, header, size); +} + +NK_API int nk_menu_begin_label(struct nk_context *ctx, + const char *text, nk_flags align, struct nk_vec2 size) +{return nk_menu_begin_text(ctx, text, nk_strlen(text), align, size);} + +NK_API int +nk_menu_begin_image(struct nk_context *ctx, const char *id, struct nk_image img, + struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_image(&ctx->last_widget_state, &win->buffer, header, + img, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, id, is_clicked, header, size); +} + +NK_API int +nk_menu_begin_symbol(struct nk_context *ctx, const char *id, + enum nk_symbol_type sym, struct nk_vec2 size) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, header, + sym, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, id, is_clicked, header, size); +} + +NK_API int +nk_menu_begin_image_text(struct nk_context *ctx, const char *title, int len, + nk_flags align, struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, + header, img, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + ctx->style.font, in)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, title, is_clicked, header, size); +} + +NK_API int nk_menu_begin_image_label(struct nk_context *ctx, + const char *title, nk_flags align, struct nk_image img, struct nk_vec2 size) +{return nk_menu_begin_image_text(ctx, title, nk_strlen(title), align, img, size);} + +NK_API int +nk_menu_begin_symbol_text(struct nk_context *ctx, const char *title, int len, + nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, + header, sym, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + ctx->style.font, in)) is_clicked = nk_true; + return nk_menu_begin(ctx, win, title, is_clicked, header, size); +} + +NK_API int nk_menu_begin_symbol_label(struct nk_context *ctx, + const char *title, nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size ) +{return nk_menu_begin_symbol_text(ctx, title, nk_strlen(title), align,sym,size);} + +NK_API int nk_menu_item_text(struct nk_context *ctx, const char *title, int len, nk_flags align) +{return nk_contextual_item_text(ctx, title, len, align);} + +NK_API int nk_menu_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{return nk_contextual_item_label(ctx, label, align);} + +NK_API int nk_menu_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{return nk_contextual_item_image_label(ctx, img, label, align);} + +NK_API int nk_menu_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{return nk_contextual_item_image_text(ctx, img, text, len, align);} + +NK_API int nk_menu_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *text, int len, nk_flags align) +{return nk_contextual_item_symbol_text(ctx, sym, text, len, align);} + +NK_API int nk_menu_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *label, nk_flags align) +{return nk_contextual_item_symbol_label(ctx, sym, label, align);} + +NK_API void nk_menu_close(struct nk_context *ctx) +{nk_contextual_close(ctx);} + +NK_API void +nk_menu_end(struct nk_context *ctx) +{nk_contextual_end(ctx);} + +#endif diff --git a/raylib/external/glfw/deps/nuklear_glfw_gl2.h b/raylib/external/glfw/deps/nuklear_glfw_gl2.h new file mode 100644 index 0000000..965af5f --- /dev/null +++ b/raylib/external/glfw/deps/nuklear_glfw_gl2.h @@ -0,0 +1,372 @@ +/* + * Nuklear - v1.32.0 - public domain + * no warrenty implied; use at your own risk. + * authored from 2015-2017 by Micha Mettke + */ +/* + * ============================================================== + * + * API + * + * =============================================================== + */ +#ifndef NK_GLFW_GL2_H_ +#define NK_GLFW_GL2_H_ + +#include + +enum nk_glfw_init_state{ + NK_GLFW3_DEFAULT = 0, + NK_GLFW3_INSTALL_CALLBACKS +}; +NK_API struct nk_context* nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state); +NK_API void nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas); +NK_API void nk_glfw3_font_stash_end(void); + +NK_API void nk_glfw3_new_frame(void); +NK_API void nk_glfw3_render(enum nk_anti_aliasing); +NK_API void nk_glfw3_shutdown(void); + +NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint); +NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff); + +#endif + +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_GLFW_GL2_IMPLEMENTATION + +#ifndef NK_GLFW_TEXT_MAX +#define NK_GLFW_TEXT_MAX 256 +#endif +#ifndef NK_GLFW_DOUBLE_CLICK_LO +#define NK_GLFW_DOUBLE_CLICK_LO 0.02 +#endif +#ifndef NK_GLFW_DOUBLE_CLICK_HI +#define NK_GLFW_DOUBLE_CLICK_HI 0.2 +#endif + +struct nk_glfw_device { + struct nk_buffer cmds; + struct nk_draw_null_texture null; + GLuint font_tex; +}; + +struct nk_glfw_vertex { + float position[2]; + float uv[2]; + nk_byte col[4]; +}; + +static struct nk_glfw { + GLFWwindow *win; + int width, height; + int display_width, display_height; + struct nk_glfw_device ogl; + struct nk_context ctx; + struct nk_font_atlas atlas; + struct nk_vec2 fb_scale; + unsigned int text[NK_GLFW_TEXT_MAX]; + int text_len; + struct nk_vec2 scroll; + double last_button_click; +} glfw; + +NK_INTERN void +nk_glfw3_device_upload_atlas(const void *image, int width, int height) +{ + struct nk_glfw_device *dev = &glfw.ogl; + glGenTextures(1, &dev->font_tex); + glBindTexture(GL_TEXTURE_2D, dev->font_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); +} + +NK_API void +nk_glfw3_render(enum nk_anti_aliasing AA) +{ + /* setup global state */ + struct nk_glfw_device *dev = &glfw.ogl; + glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + /* setup viewport/project */ + glViewport(0,0,(GLsizei)glfw.display_width,(GLsizei)glfw.display_height); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, glfw.width, glfw.height, 0.0f, -1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + { + GLsizei vs = sizeof(struct nk_glfw_vertex); + size_t vp = offsetof(struct nk_glfw_vertex, position); + size_t vt = offsetof(struct nk_glfw_vertex, uv); + size_t vc = offsetof(struct nk_glfw_vertex, col); + + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + const nk_draw_index *offset = NULL; + struct nk_buffer vbuf, ebuf; + + /* fill convert configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_glfw_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); + config.null = dev->null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* convert shapes into vertexes */ + nk_buffer_init_default(&vbuf); + nk_buffer_init_default(&ebuf); + nk_convert(&glfw.ctx, &dev->cmds, &vbuf, &ebuf, &config); + + /* setup vertex buffer pointer */ + {const void *vertices = nk_buffer_memory_const(&vbuf); + glVertexPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vp)); + glTexCoordPointer(2, GL_FLOAT, vs, (const void*)((const nk_byte*)vertices + vt)); + glColorPointer(4, GL_UNSIGNED_BYTE, vs, (const void*)((const nk_byte*)vertices + vc));} + + /* iterate over and execute each draw command */ + offset = (const nk_draw_index*)nk_buffer_memory_const(&ebuf); + nk_draw_foreach(cmd, &glfw.ctx, &dev->cmds) + { + if (!cmd->elem_count) continue; + glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); + glScissor( + (GLint)(cmd->clip_rect.x * glfw.fb_scale.x), + (GLint)((glfw.height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * glfw.fb_scale.y), + (GLint)(cmd->clip_rect.w * glfw.fb_scale.x), + (GLint)(cmd->clip_rect.h * glfw.fb_scale.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); + offset += cmd->elem_count; + } + nk_clear(&glfw.ctx); + nk_buffer_free(&vbuf); + nk_buffer_free(&ebuf); + } + + /* default OpenGL state */ + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); +} + +NK_API void +nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) +{ + (void)win; + if (glfw.text_len < NK_GLFW_TEXT_MAX) + glfw.text[glfw.text_len++] = codepoint; +} + +NK_API void +nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff) +{ + (void)win; (void)xoff; + glfw.scroll.x += (float)xoff; + glfw.scroll.y += (float)yoff; +} + +NK_API void +nk_glfw3_mouse_button_callback(GLFWwindow* window, int button, int action, int mods) +{ + double x, y; + if (button != GLFW_MOUSE_BUTTON_LEFT) return; + glfwGetCursorPos(window, &x, &y); + if (action == GLFW_PRESS) { + double dt = glfwGetTime() - glfw.last_button_click; + if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI) + nk_input_button(&glfw.ctx, NK_BUTTON_DOUBLE, (int)x, (int)y, nk_true); + glfw.last_button_click = glfwGetTime(); + } else nk_input_button(&glfw.ctx, NK_BUTTON_DOUBLE, (int)x, (int)y, nk_false); +} + +NK_INTERN void +nk_glfw3_clipbard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + const char *text = glfwGetClipboardString(glfw.win); + if (text) nk_textedit_paste(edit, text, nk_strlen(text)); + (void)usr; +} + +NK_INTERN void +nk_glfw3_clipbard_copy(nk_handle usr, const char *text, int len) +{ + char *str = 0; + (void)usr; + if (!len) return; + str = (char*)malloc((size_t)len+1); + if (!str) return; + NK_MEMCPY(str, text, (size_t)len); + str[len] = '\0'; + glfwSetClipboardString(glfw.win, str); + free(str); +} + +NK_API struct nk_context* +nk_glfw3_init(GLFWwindow *win, enum nk_glfw_init_state init_state) +{ + glfw.win = win; + if (init_state == NK_GLFW3_INSTALL_CALLBACKS) { + glfwSetScrollCallback(win, nk_gflw3_scroll_callback); + glfwSetCharCallback(win, nk_glfw3_char_callback); + glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback); + } + nk_init_default(&glfw.ctx, 0); + glfw.ctx.clip.copy = nk_glfw3_clipbard_copy; + glfw.ctx.clip.paste = nk_glfw3_clipbard_paste; + glfw.ctx.clip.userdata = nk_handle_ptr(0); + nk_buffer_init_default(&glfw.ogl.cmds); + return &glfw.ctx; +} + +NK_API void +nk_glfw3_font_stash_begin(struct nk_font_atlas **atlas) +{ + nk_font_atlas_init_default(&glfw.atlas); + nk_font_atlas_begin(&glfw.atlas); + *atlas = &glfw.atlas; +} + +NK_API void +nk_glfw3_font_stash_end(void) +{ + const void *image; int w, h; + image = nk_font_atlas_bake(&glfw.atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + nk_glfw3_device_upload_atlas(image, w, h); + nk_font_atlas_end(&glfw.atlas, nk_handle_id((int)glfw.ogl.font_tex), &glfw.ogl.null); + if (glfw.atlas.default_font) + nk_style_set_font(&glfw.ctx, &glfw.atlas.default_font->handle); +} + +NK_API void +nk_glfw3_new_frame(void) +{ + int i; + double x, y; + struct nk_context *ctx = &glfw.ctx; + struct GLFWwindow *win = glfw.win; + + glfwGetWindowSize(win, &glfw.width, &glfw.height); + glfwGetFramebufferSize(win, &glfw.display_width, &glfw.display_height); + glfw.fb_scale.x = (float)glfw.display_width/(float)glfw.width; + glfw.fb_scale.y = (float)glfw.display_height/(float)glfw.height; + + nk_input_begin(ctx); + for (i = 0; i < glfw.text_len; ++i) + nk_input_unicode(ctx, glfw.text[i]); + + /* optional grabbing behavior */ + if (ctx->input.mouse.grab) + glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + else if (ctx->input.mouse.ungrab) + glfwSetInputMode(glfw.win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS|| + glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS); + + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || + glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { + nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); + } else { + nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); + nk_input_key(ctx, NK_KEY_COPY, 0); + nk_input_key(ctx, NK_KEY_PASTE, 0); + nk_input_key(ctx, NK_KEY_CUT, 0); + nk_input_key(ctx, NK_KEY_SHIFT, 0); + } + + glfwGetCursorPos(win, &x, &y); + nk_input_motion(ctx, (int)x, (int)y); + if (ctx->input.mouse.grabbed) { + glfwSetCursorPos(glfw.win, ctx->input.mouse.prev.x, ctx->input.mouse.prev.y); + ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; + ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; + } + + nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); + nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); + nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); + nk_input_scroll(ctx, glfw.scroll); + nk_input_end(&glfw.ctx); + glfw.text_len = 0; + glfw.scroll = nk_vec2(0,0); +} + +NK_API +void nk_glfw3_shutdown(void) +{ + struct nk_glfw_device *dev = &glfw.ogl; + nk_font_atlas_clear(&glfw.atlas); + nk_free(&glfw.ctx); + glDeleteTextures(1, &dev->font_tex); + nk_buffer_free(&dev->cmds); + NK_MEMSET(&glfw, 0, sizeof(glfw)); +} + +#endif diff --git a/raylib/external/glfw/deps/stb_image_write.h b/raylib/external/glfw/deps/stb_image_write.h new file mode 100644 index 0000000..4319c0d --- /dev/null +++ b/raylib/external/glfw/deps/stb_image_write.h @@ -0,0 +1,1048 @@ +/* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +extern int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_tga_with_rle = 1; +#else +int stbi_write_tga_with_rle = 1; +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 1: + s->func(s->context,d,1); + break; + case 2: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson +#ifndef STBI_WRITE_NO_STDIO + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + return 1; + } +} + +int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = j ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ diff --git a/raylib/external/glfw/deps/tinycthread.c b/raylib/external/glfw/deps/tinycthread.c index ddb86d9..f9cea2e 100644 --- a/raylib/external/glfw/deps/tinycthread.c +++ b/raylib/external/glfw/deps/tinycthread.c @@ -21,7 +21,7 @@ freely, subject to the following restrictions: distribution. */ -/* 2013-01-06 Camilla Berglund +/* 2013-01-06 Camilla Löwy * * Added casts from time_t to DWORD to avoid warnings on VC++. * Fixed time retrieval on POSIX systems. diff --git a/raylib/external/glfw/deps/tinycthread.h b/raylib/external/glfw/deps/tinycthread.h index 6da30d7..42958c3 100644 --- a/raylib/external/glfw/deps/tinycthread.h +++ b/raylib/external/glfw/deps/tinycthread.h @@ -123,7 +123,9 @@ typedef int _tthread_clockid_t; /* Emulate clock_gettime */ int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts); #define clock_gettime _tthread_clock_gettime -#define CLOCK_REALTIME 0 +#ifndef CLOCK_REALTIME + #define CLOCK_REALTIME 0 +#endif #endif diff --git a/raylib/external/glfw/deps/vs2008/stdint.h b/raylib/external/glfw/deps/vs2008/stdint.h new file mode 100644 index 0000000..d02608a --- /dev/null +++ b/raylib/external/glfw/deps/vs2008/stdint.h @@ -0,0 +1,247 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/raylib/external/glfw/deps/vulkan/vk_platform.h b/raylib/external/glfw/deps/vulkan/vk_platform.h index 5d0fc76..0fa62ee 100644 --- a/raylib/external/glfw/deps/vulkan/vk_platform.h +++ b/raylib/external/glfw/deps/vulkan/vk_platform.h @@ -51,13 +51,13 @@ extern "C" #define VKAPI_ATTR #define VKAPI_CALL __stdcall #define VKAPI_PTR VKAPI_CALL -#elif defined(__ANDROID__) && defined(__ARM_EABI__) && !defined(__ARM_ARCH_7A__) - // Android does not support Vulkan in native code using the "armeabi" ABI. - #error "Vulkan requires the 'armeabi-v7a' or 'armeabi-v7a-hard' ABI on 32-bit ARM CPUs" -#elif defined(__ANDROID__) && defined(__ARM_ARCH_7A__) - // On Android/ARMv7a, Vulkan functions use the armeabi-v7a-hard calling - // convention, even if the application's native code is compiled with the - // armeabi-v7a calling convention. +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 + #error "Vulkan isn't supported for the 'armeabi' NDK ABI" +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) + // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" + // calling convention, i.e. float parameters are passed in registers. This + // is true even if the rest of the application passes floats on the stack, + // as it does by default when compiling for the armeabi-v7a NDK ABI. #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) #define VKAPI_CALL #define VKAPI_PTR VKAPI_ATTR diff --git a/raylib/external/glfw/deps/vulkan/vulkan.h b/raylib/external/glfw/deps/vulkan/vulkan.h index eb8343e..81dedf7 100644 --- a/raylib/external/glfw/deps/vulkan/vulkan.h +++ b/raylib/external/glfw/deps/vulkan/vulkan.h @@ -6,7 +6,7 @@ extern "C" { #endif /* -** Copyright (c) 2015-2016 The Khronos Group Inc. +** Copyright (c) 2015-2017 The Khronos Group Inc. ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ extern "C" { #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) // Version of this file -#define VK_HEADER_VERSION 11 +#define VK_HEADER_VERSION 39 #define VK_NULL_HANDLE 0 @@ -53,11 +53,13 @@ extern "C" { #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; -#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; #else #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; #endif +#endif @@ -135,6 +137,7 @@ typedef enum VkResult { VK_ERROR_INCOMPATIBLE_DRIVER = -9, VK_ERROR_TOO_MANY_OBJECTS = -10, VK_ERROR_FORMAT_NOT_SUPPORTED = -11, + VK_ERROR_FRAGMENTED_POOL = -12, VK_ERROR_SURFACE_LOST_KHR = -1000000000, VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, VK_SUBOPTIMAL_KHR = 1000001003, @@ -142,9 +145,10 @@ typedef enum VkResult { VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, VK_ERROR_INVALID_SHADER_NV = -1000012000, - VK_RESULT_BEGIN_RANGE = VK_ERROR_FORMAT_NOT_SUPPORTED, + VK_ERROR_OUT_OF_POOL_MEMORY_KHR = -1000069000, + VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL, VK_RESULT_END_RANGE = VK_INCOMPLETE, - VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FORMAT_NOT_SUPPORTED + 1), + VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1), VK_RESULT_MAX_ENUM = 0x7FFFFFFF } VkResult; @@ -210,6 +214,40 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000, + VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, + VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001, + VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002, + VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000, + VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001, + VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002, + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000, + VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001, + VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000, + VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001, + VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = 1000059000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = 1000059001, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = 1000059002, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = 1000059004, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000059005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006, + VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008, + VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000, + VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN = 1000062000, + VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000, + VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001, + VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002, + VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003, + VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004, + VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005, + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000, + VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000, + VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001, + VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002, + VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT = 1000091003, VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO, VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1), @@ -422,6 +460,14 @@ typedef enum VkFormat { VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, + VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, + VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, + VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, + VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003, + VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004, + VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, + VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED, VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK, VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1), @@ -810,6 +856,8 @@ typedef enum VkFormatFeatureFlagBits { VK_FORMAT_FEATURE_BLIT_DST_BIT = 0x00000800, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000, + VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = 0x00004000, + VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = 0x00008000, VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFormatFeatureFlagBits; typedef VkFlags VkFormatFeatureFlags; @@ -833,6 +881,7 @@ typedef enum VkImageCreateFlagBits { VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010, + VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020, VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageCreateFlagBits; typedef VkFlags VkImageCreateFlags; @@ -894,6 +943,7 @@ typedef enum VkPipelineStageFlagBits { VK_PIPELINE_STAGE_HOST_BIT = 0x00004000, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000, + VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000, VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkPipelineStageFlagBits; typedef VkFlags VkPipelineStageFlags; @@ -1068,6 +1118,8 @@ typedef enum VkAccessFlagBits { VK_ACCESS_HOST_WRITE_BIT = 0x00004000, VK_ACCESS_MEMORY_READ_BIT = 0x00008000, VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000, + VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000, + VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000, VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkAccessFlagBits; typedef VkFlags VkAccessFlags; @@ -2343,7 +2395,7 @@ typedef void (VKAPI_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkIm typedef void (VKAPI_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter); typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions); typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions); -typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData); +typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData); typedef void (VKAPI_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); typedef void (VKAPI_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); typedef void (VKAPI_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); @@ -3028,7 +3080,7 @@ VKAPI_ATTR void VKAPI_CALL vkCmdUpdateBuffer( VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, - const uint32_t* pData); + const void* pData); VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer( VkCommandBuffer commandBuffer, @@ -3168,13 +3220,26 @@ VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) #define VK_KHR_SURFACE_SPEC_VERSION 25 #define VK_KHR_SURFACE_EXTENSION_NAME "VK_KHR_surface" +#define VK_COLORSPACE_SRGB_NONLINEAR_KHR VK_COLOR_SPACE_SRGB_NONLINEAR_KHR typedef enum VkColorSpaceKHR { - VK_COLORSPACE_SRGB_NONLINEAR_KHR = 0, - VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLORSPACE_SRGB_NONLINEAR_KHR, - VK_COLOR_SPACE_END_RANGE_KHR = VK_COLORSPACE_SRGB_NONLINEAR_KHR, - VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLORSPACE_SRGB_NONLINEAR_KHR - VK_COLORSPACE_SRGB_NONLINEAR_KHR + 1), + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0, + VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT = 1000104001, + VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104002, + VK_COLOR_SPACE_SCRGB_LINEAR_EXT = 1000104003, + VK_COLOR_SPACE_SCRGB_NONLINEAR_EXT = 1000104004, + VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = 1000104005, + VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104006, + VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104007, + VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104008, + VK_COLOR_SPACE_BT2020_LINEAR_EXT = 1000104009, + VK_COLOR_SPACE_BT2020_NONLINEAR_EXT = 1000104010, + VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT = 1000104011, + VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012, + VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_END_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + 1), VK_COLOR_SPACE_MAX_ENUM_KHR = 0x7FFFFFFF } VkColorSpaceKHR; @@ -3707,10 +3772,140 @@ VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWin32PresentationSupportKHR( #define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge" +#define VK_KHR_get_physical_device_properties2 1 +#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1 +#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2" + +typedef struct VkPhysicalDeviceFeatures2KHR { + VkStructureType sType; + void* pNext; + VkPhysicalDeviceFeatures features; +} VkPhysicalDeviceFeatures2KHR; + +typedef struct VkPhysicalDeviceProperties2KHR { + VkStructureType sType; + void* pNext; + VkPhysicalDeviceProperties properties; +} VkPhysicalDeviceProperties2KHR; + +typedef struct VkFormatProperties2KHR { + VkStructureType sType; + void* pNext; + VkFormatProperties formatProperties; +} VkFormatProperties2KHR; + +typedef struct VkImageFormatProperties2KHR { + VkStructureType sType; + void* pNext; + VkImageFormatProperties imageFormatProperties; +} VkImageFormatProperties2KHR; + +typedef struct VkPhysicalDeviceImageFormatInfo2KHR { + VkStructureType sType; + const void* pNext; + VkFormat format; + VkImageType type; + VkImageTiling tiling; + VkImageUsageFlags usage; + VkImageCreateFlags flags; +} VkPhysicalDeviceImageFormatInfo2KHR; + +typedef struct VkQueueFamilyProperties2KHR { + VkStructureType sType; + void* pNext; + VkQueueFamilyProperties queueFamilyProperties; +} VkQueueFamilyProperties2KHR; + +typedef struct VkPhysicalDeviceMemoryProperties2KHR { + VkStructureType sType; + void* pNext; + VkPhysicalDeviceMemoryProperties memoryProperties; +} VkPhysicalDeviceMemoryProperties2KHR; + +typedef struct VkSparseImageFormatProperties2KHR { + VkStructureType sType; + void* pNext; + VkSparseImageFormatProperties properties; +} VkSparseImageFormatProperties2KHR; + +typedef struct VkPhysicalDeviceSparseImageFormatInfo2KHR { + VkStructureType sType; + const void* pNext; + VkFormat format; + VkImageType type; + VkSampleCountFlagBits samples; + VkImageUsageFlags usage; + VkImageTiling tiling; +} VkPhysicalDeviceSparseImageFormatInfo2KHR; + + +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures2KHR( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2KHR* pFeatures); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties2KHR( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties2KHR* pProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties2KHR( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkFormatProperties2KHR* pFormatProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2KHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, + VkImageFormatProperties2KHR* pImageFormatProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2KHR( + VkPhysicalDevice physicalDevice, + uint32_t* pQueueFamilyPropertyCount, + VkQueueFamilyProperties2KHR* pQueueFamilyProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2KHR( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, + uint32_t* pPropertyCount, + VkSparseImageFormatProperties2KHR* pProperties); +#endif + +#define VK_KHR_shader_draw_parameters 1 +#define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1 +#define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters" + + +#define VK_KHR_maintenance1 1 +#define VK_KHR_MAINTENANCE1_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1" + +typedef VkFlags VkCommandPoolTrimFlagsKHR; + +typedef void (VKAPI_PTR *PFN_vkTrimCommandPoolKHR)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlagsKHR flags); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkTrimCommandPoolKHR( + VkDevice device, + VkCommandPool commandPool, + VkCommandPoolTrimFlagsKHR flags); +#endif + #define VK_EXT_debug_report 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) -#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 2 +#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 4 #define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" #define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT @@ -3745,9 +3940,13 @@ typedef enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, + VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31, + VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32, VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, - VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, - VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1), + VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1), VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportObjectTypeEXT; @@ -3828,6 +4027,735 @@ VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( #define VK_IMG_FILTER_CUBIC_EXTENSION_NAME "VK_IMG_filter_cubic" +#define VK_AMD_rasterization_order 1 +#define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION 1 +#define VK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME "VK_AMD_rasterization_order" + + +typedef enum VkRasterizationOrderAMD { + VK_RASTERIZATION_ORDER_STRICT_AMD = 0, + VK_RASTERIZATION_ORDER_RELAXED_AMD = 1, + VK_RASTERIZATION_ORDER_BEGIN_RANGE_AMD = VK_RASTERIZATION_ORDER_STRICT_AMD, + VK_RASTERIZATION_ORDER_END_RANGE_AMD = VK_RASTERIZATION_ORDER_RELAXED_AMD, + VK_RASTERIZATION_ORDER_RANGE_SIZE_AMD = (VK_RASTERIZATION_ORDER_RELAXED_AMD - VK_RASTERIZATION_ORDER_STRICT_AMD + 1), + VK_RASTERIZATION_ORDER_MAX_ENUM_AMD = 0x7FFFFFFF +} VkRasterizationOrderAMD; + +typedef struct VkPipelineRasterizationStateRasterizationOrderAMD { + VkStructureType sType; + const void* pNext; + VkRasterizationOrderAMD rasterizationOrder; +} VkPipelineRasterizationStateRasterizationOrderAMD; + + + +#define VK_AMD_shader_trinary_minmax 1 +#define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1 +#define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax" + + +#define VK_AMD_shader_explicit_vertex_parameter 1 +#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1 +#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter" + + +#define VK_EXT_debug_marker 1 +#define VK_EXT_DEBUG_MARKER_SPEC_VERSION 3 +#define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker" + +typedef struct VkDebugMarkerObjectNameInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportObjectTypeEXT objectType; + uint64_t object; + const char* pObjectName; +} VkDebugMarkerObjectNameInfoEXT; + +typedef struct VkDebugMarkerObjectTagInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportObjectTypeEXT objectType; + uint64_t object; + uint64_t tagName; + size_t tagSize; + const void* pTag; +} VkDebugMarkerObjectTagInfoEXT; + +typedef struct VkDebugMarkerMarkerInfoEXT { + VkStructureType sType; + const void* pNext; + const char* pMarkerName; + float color[4]; +} VkDebugMarkerMarkerInfoEXT; + + +typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, VkDebugMarkerObjectTagInfoEXT* pTagInfo); +typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, VkDebugMarkerObjectNameInfoEXT* pNameInfo); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerEndEXT)(VkCommandBuffer commandBuffer); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT( + VkDevice device, + VkDebugMarkerObjectTagInfoEXT* pTagInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT( + VkDevice device, + VkDebugMarkerObjectNameInfoEXT* pNameInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT( + VkCommandBuffer commandBuffer, + VkDebugMarkerMarkerInfoEXT* pMarkerInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT( + VkCommandBuffer commandBuffer); + +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT( + VkCommandBuffer commandBuffer, + VkDebugMarkerMarkerInfoEXT* pMarkerInfo); +#endif + +#define VK_AMD_gcn_shader 1 +#define VK_AMD_GCN_SHADER_SPEC_VERSION 1 +#define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader" + + +#define VK_NV_dedicated_allocation 1 +#define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1 +#define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation" + +typedef struct VkDedicatedAllocationImageCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 dedicatedAllocation; +} VkDedicatedAllocationImageCreateInfoNV; + +typedef struct VkDedicatedAllocationBufferCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 dedicatedAllocation; +} VkDedicatedAllocationBufferCreateInfoNV; + +typedef struct VkDedicatedAllocationMemoryAllocateInfoNV { + VkStructureType sType; + const void* pNext; + VkImage image; + VkBuffer buffer; +} VkDedicatedAllocationMemoryAllocateInfoNV; + + + +#define VK_AMD_draw_indirect_count 1 +#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1 +#define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count" + +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountAMD( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); +#endif + +#define VK_AMD_negative_viewport_height 1 +#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1 +#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height" + + +#define VK_AMD_gpu_shader_half_float 1 +#define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1 +#define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float" + + +#define VK_AMD_shader_ballot 1 +#define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1 +#define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot" + + +#define VK_IMG_format_pvrtc 1 +#define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1 +#define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc" + + +#define VK_NV_external_memory_capabilities 1 +#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities" + + +typedef enum VkExternalMemoryHandleTypeFlagBitsNV { + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkExternalMemoryHandleTypeFlagBitsNV; +typedef VkFlags VkExternalMemoryHandleTypeFlagsNV; + +typedef enum VkExternalMemoryFeatureFlagBitsNV { + VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001, + VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002, + VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004, + VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkExternalMemoryFeatureFlagBitsNV; +typedef VkFlags VkExternalMemoryFeatureFlagsNV; + +typedef struct VkExternalImageFormatPropertiesNV { + VkImageFormatProperties imageFormatProperties; + VkExternalMemoryFeatureFlagsNV externalMemoryFeatures; + VkExternalMemoryHandleTypeFlagsNV exportFromImportedHandleTypes; + VkExternalMemoryHandleTypeFlagsNV compatibleHandleTypes; +} VkExternalImageFormatPropertiesNV; + + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceExternalImageFormatPropertiesNV( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags, + VkExternalMemoryHandleTypeFlagsNV externalHandleType, + VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); +#endif + +#define VK_NV_external_memory 1 +#define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory" + +typedef struct VkExternalMemoryImageCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagsNV handleTypes; +} VkExternalMemoryImageCreateInfoNV; + +typedef struct VkExportMemoryAllocateInfoNV { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagsNV handleTypes; +} VkExportMemoryAllocateInfoNV; + + + +#ifdef VK_USE_PLATFORM_WIN32_KHR +#define VK_NV_external_memory_win32 1 +#define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32" + +typedef struct VkImportMemoryWin32HandleInfoNV { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagsNV handleType; + HANDLE handle; +} VkImportMemoryWin32HandleInfoNV; + +typedef struct VkExportMemoryWin32HandleInfoNV { + VkStructureType sType; + const void* pNext; + const SECURITY_ATTRIBUTES* pAttributes; + DWORD dwAccess; +} VkExportMemoryWin32HandleInfoNV; + + +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV( + VkDevice device, + VkDeviceMemory memory, + VkExternalMemoryHandleTypeFlagsNV handleType, + HANDLE* pHandle); +#endif +#endif /* VK_USE_PLATFORM_WIN32_KHR */ + +#ifdef VK_USE_PLATFORM_WIN32_KHR +#define VK_NV_win32_keyed_mutex 1 +#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1 +#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex" + +typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV { + VkStructureType sType; + const void* pNext; + uint32_t acquireCount; + const VkDeviceMemory* pAcquireSyncs; + const uint64_t* pAcquireKeys; + const uint32_t* pAcquireTimeoutMilliseconds; + uint32_t releaseCount; + const VkDeviceMemory* pReleaseSyncs; + const uint64_t* pReleaseKeys; +} VkWin32KeyedMutexAcquireReleaseInfoNV; + + +#endif /* VK_USE_PLATFORM_WIN32_KHR */ + +#define VK_EXT_validation_flags 1 +#define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1 +#define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags" + + +typedef enum VkValidationCheckEXT { + VK_VALIDATION_CHECK_ALL_EXT = 0, + VK_VALIDATION_CHECK_BEGIN_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT, + VK_VALIDATION_CHECK_END_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT, + VK_VALIDATION_CHECK_RANGE_SIZE_EXT = (VK_VALIDATION_CHECK_ALL_EXT - VK_VALIDATION_CHECK_ALL_EXT + 1), + VK_VALIDATION_CHECK_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationCheckEXT; + +typedef struct VkValidationFlagsEXT { + VkStructureType sType; + const void* pNext; + uint32_t disabledValidationCheckCount; + VkValidationCheckEXT* pDisabledValidationChecks; +} VkValidationFlagsEXT; + + + +#ifdef VK_USE_PLATFORM_VI_NN +#define VK_NN_vi_surface 1 +#define VK_NN_VI_SURFACE_SPEC_VERSION 1 +#define VK_NN_VI_SURFACE_EXTENSION_NAME "VK_NN_vi_surface" + +typedef VkFlags VkViSurfaceCreateFlagsNN; + +typedef struct VkViSurfaceCreateInfoNN { + VkStructureType sType; + const void* pNext; + VkViSurfaceCreateFlagsNN flags; + void* window; +} VkViSurfaceCreateInfoNN; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateViSurfaceNN)(VkInstance instance, const VkViSurfaceCreateInfoNN* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateViSurfaceNN( + VkInstance instance, + const VkViSurfaceCreateInfoNN* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif +#endif /* VK_USE_PLATFORM_VI_NN */ + +#define VK_EXT_shader_subgroup_ballot 1 +#define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1 +#define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot" + + +#define VK_EXT_shader_subgroup_vote 1 +#define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1 +#define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote" + + +#define VK_NVX_device_generated_commands 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX) + +#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1 +#define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands" + + +typedef enum VkIndirectCommandsTokenTypeNVX { + VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX = 0, + VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX = 1, + VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX = 2, + VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX = 3, + VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX = 4, + VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX = 5, + VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX = 6, + VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX = 7, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_BEGIN_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_END_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_RANGE_SIZE_NVX = (VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX - VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX + 1), + VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF +} VkIndirectCommandsTokenTypeNVX; + +typedef enum VkObjectEntryTypeNVX { + VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX = 0, + VK_OBJECT_ENTRY_PIPELINE_NVX = 1, + VK_OBJECT_ENTRY_INDEX_BUFFER_NVX = 2, + VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX = 3, + VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX = 4, + VK_OBJECT_ENTRY_TYPE_BEGIN_RANGE_NVX = VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX, + VK_OBJECT_ENTRY_TYPE_END_RANGE_NVX = VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX, + VK_OBJECT_ENTRY_TYPE_RANGE_SIZE_NVX = (VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX - VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX + 1), + VK_OBJECT_ENTRY_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF +} VkObjectEntryTypeNVX; + + +typedef enum VkIndirectCommandsLayoutUsageFlagBitsNVX { + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF +} VkIndirectCommandsLayoutUsageFlagBitsNVX; +typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNVX; + +typedef enum VkObjectEntryUsageFlagBitsNVX { + VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001, + VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002, + VK_OBJECT_ENTRY_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF +} VkObjectEntryUsageFlagBitsNVX; +typedef VkFlags VkObjectEntryUsageFlagsNVX; + +typedef struct VkDeviceGeneratedCommandsFeaturesNVX { + VkStructureType sType; + const void* pNext; + VkBool32 computeBindingPointSupport; +} VkDeviceGeneratedCommandsFeaturesNVX; + +typedef struct VkDeviceGeneratedCommandsLimitsNVX { + VkStructureType sType; + const void* pNext; + uint32_t maxIndirectCommandsLayoutTokenCount; + uint32_t maxObjectEntryCounts; + uint32_t minSequenceCountBufferOffsetAlignment; + uint32_t minSequenceIndexBufferOffsetAlignment; + uint32_t minCommandsTokenBufferOffsetAlignment; +} VkDeviceGeneratedCommandsLimitsNVX; + +typedef struct VkIndirectCommandsTokenNVX { + VkIndirectCommandsTokenTypeNVX tokenType; + VkBuffer buffer; + VkDeviceSize offset; +} VkIndirectCommandsTokenNVX; + +typedef struct VkIndirectCommandsLayoutTokenNVX { + VkIndirectCommandsTokenTypeNVX tokenType; + uint32_t bindingUnit; + uint32_t dynamicCount; + uint32_t divisor; +} VkIndirectCommandsLayoutTokenNVX; + +typedef struct VkIndirectCommandsLayoutCreateInfoNVX { + VkStructureType sType; + const void* pNext; + VkPipelineBindPoint pipelineBindPoint; + VkIndirectCommandsLayoutUsageFlagsNVX flags; + uint32_t tokenCount; + const VkIndirectCommandsLayoutTokenNVX* pTokens; +} VkIndirectCommandsLayoutCreateInfoNVX; + +typedef struct VkCmdProcessCommandsInfoNVX { + VkStructureType sType; + const void* pNext; + VkObjectTableNVX objectTable; + VkIndirectCommandsLayoutNVX indirectCommandsLayout; + uint32_t indirectCommandsTokenCount; + const VkIndirectCommandsTokenNVX* pIndirectCommandsTokens; + uint32_t maxSequencesCount; + VkCommandBuffer targetCommandBuffer; + VkBuffer sequencesCountBuffer; + VkDeviceSize sequencesCountOffset; + VkBuffer sequencesIndexBuffer; + VkDeviceSize sequencesIndexOffset; +} VkCmdProcessCommandsInfoNVX; + +typedef struct VkCmdReserveSpaceForCommandsInfoNVX { + VkStructureType sType; + const void* pNext; + VkObjectTableNVX objectTable; + VkIndirectCommandsLayoutNVX indirectCommandsLayout; + uint32_t maxSequencesCount; +} VkCmdReserveSpaceForCommandsInfoNVX; + +typedef struct VkObjectTableCreateInfoNVX { + VkStructureType sType; + const void* pNext; + uint32_t objectCount; + const VkObjectEntryTypeNVX* pObjectEntryTypes; + const uint32_t* pObjectEntryCounts; + const VkObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags; + uint32_t maxUniformBuffersPerDescriptor; + uint32_t maxStorageBuffersPerDescriptor; + uint32_t maxStorageImagesPerDescriptor; + uint32_t maxSampledImagesPerDescriptor; + uint32_t maxPipelineLayouts; +} VkObjectTableCreateInfoNVX; + +typedef struct VkObjectTableEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; +} VkObjectTableEntryNVX; + +typedef struct VkObjectTablePipelineEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkPipeline pipeline; +} VkObjectTablePipelineEntryNVX; + +typedef struct VkObjectTableDescriptorSetEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkPipelineLayout pipelineLayout; + VkDescriptorSet descriptorSet; +} VkObjectTableDescriptorSetEntryNVX; + +typedef struct VkObjectTableVertexBufferEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkBuffer buffer; +} VkObjectTableVertexBufferEntryNVX; + +typedef struct VkObjectTableIndexBufferEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkBuffer buffer; + VkIndexType indexType; +} VkObjectTableIndexBufferEntryNVX; + +typedef struct VkObjectTablePushConstantEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkPipelineLayout pipelineLayout; + VkShaderStageFlags stageFlags; +} VkObjectTablePushConstantEntryNVX; + + +typedef void (VKAPI_PTR *PFN_vkCmdProcessCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); +typedef void (VKAPI_PTR *PFN_vkCmdReserveSpaceForCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNVX)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); +typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNVX)(VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateObjectTableNVX)(VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable); +typedef void (VKAPI_PTR *PFN_vkDestroyObjectTableNVX)(VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkRegisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices); +typedef VkResult (VKAPI_PTR *PFN_vkUnregisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX)(VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdProcessCommandsNVX( + VkCommandBuffer commandBuffer, + const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdReserveSpaceForCommandsNVX( + VkCommandBuffer commandBuffer, + const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNVX( + VkDevice device, + const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); + +VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNVX( + VkDevice device, + VkIndirectCommandsLayoutNVX indirectCommandsLayout, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateObjectTableNVX( + VkDevice device, + const VkObjectTableCreateInfoNVX* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkObjectTableNVX* pObjectTable); + +VKAPI_ATTR void VKAPI_CALL vkDestroyObjectTableNVX( + VkDevice device, + VkObjectTableNVX objectTable, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkRegisterObjectsNVX( + VkDevice device, + VkObjectTableNVX objectTable, + uint32_t objectCount, + const VkObjectTableEntryNVX* const* ppObjectTableEntries, + const uint32_t* pObjectIndices); + +VKAPI_ATTR VkResult VKAPI_CALL vkUnregisterObjectsNVX( + VkDevice device, + VkObjectTableNVX objectTable, + uint32_t objectCount, + const VkObjectEntryTypeNVX* pObjectEntryTypes, + const uint32_t* pObjectIndices); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( + VkPhysicalDevice physicalDevice, + VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, + VkDeviceGeneratedCommandsLimitsNVX* pLimits); +#endif + +#define VK_EXT_direct_mode_display 1 +#define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1 +#define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display" + +typedef VkResult (VKAPI_PTR *PFN_vkReleaseDisplayEXT)(VkPhysicalDevice physicalDevice, VkDisplayKHR display); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkReleaseDisplayEXT( + VkPhysicalDevice physicalDevice, + VkDisplayKHR display); +#endif + +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT +#define VK_EXT_acquire_xlib_display 1 +#include + +#define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1 +#define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display" + +typedef VkResult (VKAPI_PTR *PFN_vkAcquireXlibDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, VkDisplayKHR display); +typedef VkResult (VKAPI_PTR *PFN_vkGetRandROutputDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, RROutput rrOutput, VkDisplayKHR* pDisplay); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireXlibDisplayEXT( + VkPhysicalDevice physicalDevice, + Display* dpy, + VkDisplayKHR display); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetRandROutputDisplayEXT( + VkPhysicalDevice physicalDevice, + Display* dpy, + RROutput rrOutput, + VkDisplayKHR* pDisplay); +#endif +#endif /* VK_USE_PLATFORM_XLIB_XRANDR_EXT */ + +#define VK_EXT_display_surface_counter 1 +#define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1 +#define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter" + + +typedef enum VkSurfaceCounterFlagBitsEXT { + VK_SURFACE_COUNTER_VBLANK_EXT = 0x00000001, + VK_SURFACE_COUNTER_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkSurfaceCounterFlagBitsEXT; +typedef VkFlags VkSurfaceCounterFlagsEXT; + +typedef struct VkSurfaceCapabilities2EXT { + VkStructureType sType; + void* pNext; + uint32_t minImageCount; + uint32_t maxImageCount; + VkExtent2D currentExtent; + VkExtent2D minImageExtent; + VkExtent2D maxImageExtent; + uint32_t maxImageArrayLayers; + VkSurfaceTransformFlagsKHR supportedTransforms; + VkSurfaceTransformFlagBitsKHR currentTransform; + VkCompositeAlphaFlagsKHR supportedCompositeAlpha; + VkImageUsageFlags supportedUsageFlags; + VkSurfaceCounterFlagsEXT supportedSurfaceCounters; +} VkSurfaceCapabilities2EXT; + + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilities2EXT* pSurfaceCapabilities); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2EXT( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + VkSurfaceCapabilities2EXT* pSurfaceCapabilities); +#endif + +#define VK_EXT_display_control 1 +#define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1 +#define VK_EXT_DISPLAY_CONTROL_EXTENSION_NAME "VK_EXT_display_control" + + +typedef enum VkDisplayPowerStateEXT { + VK_DISPLAY_POWER_STATE_OFF_EXT = 0, + VK_DISPLAY_POWER_STATE_SUSPEND_EXT = 1, + VK_DISPLAY_POWER_STATE_ON_EXT = 2, + VK_DISPLAY_POWER_STATE_BEGIN_RANGE_EXT = VK_DISPLAY_POWER_STATE_OFF_EXT, + VK_DISPLAY_POWER_STATE_END_RANGE_EXT = VK_DISPLAY_POWER_STATE_ON_EXT, + VK_DISPLAY_POWER_STATE_RANGE_SIZE_EXT = (VK_DISPLAY_POWER_STATE_ON_EXT - VK_DISPLAY_POWER_STATE_OFF_EXT + 1), + VK_DISPLAY_POWER_STATE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDisplayPowerStateEXT; + +typedef enum VkDeviceEventTypeEXT { + VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT = 0, + VK_DEVICE_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT, + VK_DEVICE_EVENT_TYPE_END_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT, + VK_DEVICE_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT - VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT + 1), + VK_DEVICE_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDeviceEventTypeEXT; + +typedef enum VkDisplayEventTypeEXT { + VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT = 0, + VK_DISPLAY_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT, + VK_DISPLAY_EVENT_TYPE_END_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT, + VK_DISPLAY_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT - VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT + 1), + VK_DISPLAY_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDisplayEventTypeEXT; + +typedef struct VkDisplayPowerInfoEXT { + VkStructureType sType; + const void* pNext; + VkDisplayPowerStateEXT powerState; +} VkDisplayPowerInfoEXT; + +typedef struct VkDeviceEventInfoEXT { + VkStructureType sType; + const void* pNext; + VkDeviceEventTypeEXT deviceEvent; +} VkDeviceEventInfoEXT; + +typedef struct VkDisplayEventInfoEXT { + VkStructureType sType; + const void* pNext; + VkDisplayEventTypeEXT displayEvent; +} VkDisplayEventInfoEXT; + +typedef struct VkSwapchainCounterCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkSurfaceCounterFlagsEXT surfaceCounters; +} VkSwapchainCounterCreateInfoEXT; + + +typedef VkResult (VKAPI_PTR *PFN_vkDisplayPowerControlEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayPowerInfoEXT* pDisplayPowerInfo); +typedef VkResult (VKAPI_PTR *PFN_vkRegisterDeviceEventEXT)(VkDevice device, const VkDeviceEventInfoEXT* pDeviceEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); +typedef VkResult (VKAPI_PTR *PFN_vkRegisterDisplayEventEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT* pDisplayEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainCounterEXT)(VkDevice device, VkSwapchainKHR swapchain, VkSurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkDisplayPowerControlEXT( + VkDevice device, + VkDisplayKHR display, + const VkDisplayPowerInfoEXT* pDisplayPowerInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDeviceEventEXT( + VkDevice device, + const VkDeviceEventInfoEXT* pDeviceEventInfo, + const VkAllocationCallbacks* pAllocator, + VkFence* pFence); + +VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDisplayEventEXT( + VkDevice device, + VkDisplayKHR display, + const VkDisplayEventInfoEXT* pDisplayEventInfo, + const VkAllocationCallbacks* pAllocator, + VkFence* pFence); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainCounterEXT( + VkDevice device, + VkSwapchainKHR swapchain, + VkSurfaceCounterFlagBitsEXT counter, + uint64_t* pCounterValue); +#endif + +#define VK_EXT_swapchain_colorspace 1 +#define VK_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION 1 +#define VK_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace" + + #ifdef __cplusplus } #endif diff --git a/raylib/external/glfw/include/GLFW/glfw3.h b/raylib/external/glfw/include/GLFW/glfw3.h index 95caa95..9c02401 100644 --- a/raylib/external/glfw/include/GLFW/glfw3.h +++ b/raylib/external/glfw/include/GLFW/glfw3.h @@ -1,9 +1,9 @@ /************************************************************************* - * GLFW 3.2 - www.glfw.org + * GLFW 3.3 - www.glfw.org * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Berglund + * Copyright (c) 2006-2016 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -47,32 +47,38 @@ extern "C" { * For more information about how to use this file, see @ref build_include. */ /*! @defgroup context Context reference + * @brief Functions and types related to OpenGL and OpenGL ES contexts. * * This is the reference documentation for OpenGL and OpenGL ES context related * functions. For more task-oriented information, see the @ref context_guide. */ /*! @defgroup vulkan Vulkan reference + * @brief Functions and types related to Vulkan. * * This is the reference documentation for Vulkan related functions and types. * For more task-oriented information, see the @ref vulkan_guide. */ /*! @defgroup init Initialization, version and error reference + * @brief Functions and types related to initialization and error handling. * * This is the reference documentation for initialization and termination of * the library, version management and error handling. For more task-oriented * information, see the @ref intro_guide. */ /*! @defgroup input Input reference + * @brief Functions and types related to input handling. * * This is the reference documentation for input related functions and types. * For more task-oriented information, see the @ref input_guide. */ /*! @defgroup monitor Monitor reference + * @brief Functions and types related to monitors. * * This is the reference documentation for monitor related functions and types. * For more task-oriented information, see the @ref monitor_guide. */ /*! @defgroup window Window reference + * @brief Functions and types related to windows. * * This is the reference documentation for window related functions and types, * including creation, deletion and event polling. For more task-oriented @@ -99,6 +105,7 @@ extern "C" { #else #define APIENTRY #endif + #define GLFW_APIENTRY_DEFINED #endif /* APIENTRY */ /* Some Windows OpenGL headers need this. @@ -116,67 +123,97 @@ extern "C" { #endif /* CALLBACK */ /* Include because most Windows GLU headers need wchar_t and - * the OS X OpenGL header blocks the definition of ptrdiff_t by glext.h. + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. * Include it unconditionally to avoid surprising side-effects. */ #include /* Include because it is needed by Vulkan and related functions. + * Include it unconditionally to avoid surprising side-effects. */ #include -/* Include the chosen client API headers. +/* Include the chosen OpenGL or OpenGL ES headers. */ -#if defined(__APPLE__) - #if defined(GLFW_INCLUDE_GLCOREARB) +#if defined(GLFW_INCLUDE_ES1) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES2) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES3) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES31) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_ES32) + + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + +#elif defined(GLFW_INCLUDE_GLCOREARB) + + #if defined(__APPLE__) + #include #if defined(GLFW_INCLUDE_GLEXT) #include - #endif - #elif !defined(GLFW_INCLUDE_NONE) + #endif /*GLFW_INCLUDE_GLEXT*/ + + #else /*__APPLE__*/ + + #include + + #endif /*__APPLE__*/ + +#elif !defined(GLFW_INCLUDE_NONE) + + #if defined(__APPLE__) + #if !defined(GLFW_INCLUDE_GLEXT) #define GL_GLEXT_LEGACY #endif #include - #endif - #if defined(GLFW_INCLUDE_GLU) - #include - #endif -#else - #if defined(GLFW_INCLUDE_GLCOREARB) - #include - #elif defined(GLFW_INCLUDE_ES1) - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include + #if defined(GLFW_INCLUDE_GLU) + #include #endif - #elif defined(GLFW_INCLUDE_ES2) - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - #elif defined(GLFW_INCLUDE_ES3) - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - #elif defined(GLFW_INCLUDE_ES31) - #include - #if defined(GLFW_INCLUDE_GLEXT) - #include - #endif - #elif defined(GLFW_INCLUDE_VULKAN) - #include - #elif !defined(GLFW_INCLUDE_NONE) + + #else /*__APPLE__*/ + #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif - #endif - #if defined(GLFW_INCLUDE_GLU) - #include - #endif -#endif + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #endif /*__APPLE__*/ + +#endif /* OpenGL and OpenGL ES headers */ + +#if defined(GLFW_INCLUDE_VULKAN) + #include +#endif /* Vulkan header */ #if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) /* GLFW_DLL must be defined by applications that are linking against the DLL @@ -222,14 +259,14 @@ extern "C" { * backward-compatible. * @ingroup init */ -#define GLFW_VERSION_MINOR 2 +#define GLFW_VERSION_MINOR 3 /*! @brief The revision number of the GLFW library. * * This is incremented when a bug fix release is made that does not contain any * API changes. * @ingroup init */ -#define GLFW_VERSION_REVISION 1 +#define GLFW_VERSION_REVISION 0 /*! @} */ /*! @name Boolean values @@ -237,14 +274,14 @@ extern "C" { /*! @brief One. * * One. Seriously. You don't _need_ to use this symbol in your code. It's - * just semantic sugar for the number 1. You can use `1` or `true` or `_True` + * semantic sugar for the number 1. You can also use `1` or `true` or `_True` * or `GL_TRUE` or whatever you want. */ #define GLFW_TRUE 1 /*! @brief Zero. * * Zero. Seriously. You don't _need_ to use this symbol in your code. It's - * just just semantic sugar for the number 0. You can use `0` or `false` or + * semantic sugar for the number 0. You can also use `0` or `false` or * `_False` or `GL_FALSE` or whatever you want. */ #define GLFW_FALSE 0 @@ -275,7 +312,25 @@ extern "C" { #define GLFW_REPEAT 2 /*! @} */ +/*! @defgroup hat_state Joystick hat states + * + * See [joystick hat input](@ref joystick_hat) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_HAT_CENTERED 0 +#define GLFW_HAT_UP 1 +#define GLFW_HAT_RIGHT 2 +#define GLFW_HAT_DOWN 4 +#define GLFW_HAT_LEFT 8 +#define GLFW_HAT_RIGHT_UP (GLFW_HAT_RIGHT | GLFW_HAT_UP) +#define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) +#define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) +#define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) +/*! @} */ + /*! @defgroup keys Keyboard keys + * @brief Keyboard key IDs. * * See [key input](@ref input_key) for how these are used. * @@ -430,6 +485,7 @@ extern "C" { /*! @} */ /*! @defgroup mods Modifier key flags + * @brief Modifier key flags. * * See [key input](@ref input_key) for how these are used. * @@ -452,6 +508,7 @@ extern "C" { /*! @} */ /*! @defgroup buttons Mouse buttons + * @brief Mouse button IDs. * * See [mouse button input](@ref input_mouse_button) for how these are used. * @@ -472,6 +529,7 @@ extern "C" { /*! @} */ /*! @defgroup joysticks Joysticks + * @brief Joystick IDs. * * See [joystick input](@ref joystick) for how these are used. * @@ -496,12 +554,66 @@ extern "C" { #define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 /*! @} */ +/*! @defgroup gamepad_buttons Gamepad buttons + * @brief Gamepad buttons. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_BUTTON_A 0 +#define GLFW_GAMEPAD_BUTTON_B 1 +#define GLFW_GAMEPAD_BUTTON_X 2 +#define GLFW_GAMEPAD_BUTTON_Y 3 +#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4 +#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5 +#define GLFW_GAMEPAD_BUTTON_BACK 6 +#define GLFW_GAMEPAD_BUTTON_START 7 +#define GLFW_GAMEPAD_BUTTON_GUIDE 8 +#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9 +#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10 +#define GLFW_GAMEPAD_BUTTON_DPAD_UP 11 +#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12 +#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13 +#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14 +#define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT + +#define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A +#define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B +#define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X +#define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y +/*! @} */ + +/*! @defgroup gamepad_axes Gamepad axes + * @brief Gamepad axes. + * + * See @ref gamepad for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_GAMEPAD_AXIS_LEFT_X 0 +#define GLFW_GAMEPAD_AXIS_LEFT_Y 1 +#define GLFW_GAMEPAD_AXIS_RIGHT_X 2 +#define GLFW_GAMEPAD_AXIS_RIGHT_Y 3 +#define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4 +#define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5 +#define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER +/*! @} */ + /*! @defgroup errors Error codes + * @brief Error codes. * * See [error handling](@ref error_handling) for how these are used. * * @ingroup init * @{ */ +/*! @brief No error has occurred. + * + * No error has occurred. + * + * @analysis Yay. + */ +#define GLFW_NO_ERROR 0 /*! @brief GLFW has not been initialized. * * This occurs if a GLFW function was called that must not be called unless the @@ -524,8 +636,7 @@ extern "C" { /*! @brief One of the arguments to the function was an invalid enum value. * * One of the arguments to the function was an invalid enum value, for example - * requesting [GLFW_RED_BITS](@ref window_hints_fb) with @ref - * glfwGetWindowAttrib. + * requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib. * * @analysis Application programmer error. Fix the offending call. */ @@ -560,7 +671,7 @@ extern "C" { * @par * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only * supports OpenGL ES via EGL, while Nvidia and Intel only support it via - * a WGL or GLX extension. OS X does not provide OpenGL ES at all. The Mesa + * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary * driver. Older graphics drivers do not support Vulkan. */ @@ -622,44 +733,221 @@ extern "C" { #define GLFW_NO_WINDOW_CONTEXT 0x0001000A /*! @} */ +/*! @addtogroup window + * @{ */ +/*! @brief Input focus window hint and attribute + * + * Input focus [window hint](@ref GLFW_FOCUSED_hint) or + * [window attribute](@ref GLFW_FOCUSED_attrib). + */ #define GLFW_FOCUSED 0x00020001 +/*! @brief Window iconification window attribute + * + * Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib). + */ #define GLFW_ICONIFIED 0x00020002 +/*! @brief Window resize-ability window hint and attribute + * + * Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and + * [window attribute](@ref GLFW_RESIZABLE_attrib). + */ #define GLFW_RESIZABLE 0x00020003 +/*! @brief Window visibility window hint and attribute + * + * Window visibility [window hint](@ref GLFW_VISIBLE_hint) and + * [window attribute](@ref GLFW_VISIBLE_attrib). + */ #define GLFW_VISIBLE 0x00020004 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_DECORATED_hint) and + * [window attribute](@ref GLFW_DECORATED_attrib). + */ #define GLFW_DECORATED 0x00020005 +/*! @brief Window auto-iconification window hint and attribute + * + * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and + * [window attribute](@ref GLFW_AUTO_ICONIFY_attrib). + */ #define GLFW_AUTO_ICONIFY 0x00020006 +/*! @brief Window decoration window hint and attribute + * + * Window decoration [window hint](@ref GLFW_FLOATING_hint) and + * [window attribute](@ref GLFW_FLOATING_attrib). + */ #define GLFW_FLOATING 0x00020007 +/*! @brief Window maximization window hint and attribute + * + * Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and + * [window attribute](@ref GLFW_MAXIMIZED_attrib). + */ #define GLFW_MAXIMIZED 0x00020008 +/*! @brief Cursor centering window hint + * + * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). + */ +#define GLFW_CENTER_CURSOR 0x00020009 +/*! @brief Window framebuffer transparency hint and attribute + * + * Window framebuffer transparency + * [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and + * [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib). + */ +#define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). + */ #define GLFW_RED_BITS 0x00021001 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS). + */ #define GLFW_GREEN_BITS 0x00021002 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS). + */ #define GLFW_BLUE_BITS 0x00021003 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS). + */ #define GLFW_ALPHA_BITS 0x00021004 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS). + */ #define GLFW_DEPTH_BITS 0x00021005 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS). + */ #define GLFW_STENCIL_BITS 0x00021006 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS). + */ #define GLFW_ACCUM_RED_BITS 0x00021007 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS). + */ #define GLFW_ACCUM_GREEN_BITS 0x00021008 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS). + */ #define GLFW_ACCUM_BLUE_BITS 0x00021009 +/*! @brief Framebuffer bit depth hint. + * + * Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS). + */ #define GLFW_ACCUM_ALPHA_BITS 0x0002100A +/*! @brief Framebuffer auxiliary buffer hint. + * + * Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS). + */ #define GLFW_AUX_BUFFERS 0x0002100B +/*! @brief OpenGL stereoscopic rendering hint. + * + * OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO). + */ #define GLFW_STEREO 0x0002100C +/*! @brief Framebuffer MSAA samples hint. + * + * Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES). + */ #define GLFW_SAMPLES 0x0002100D +/*! @brief Framebuffer sRGB hint. + * + * Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE). + */ #define GLFW_SRGB_CAPABLE 0x0002100E +/*! @brief Monitor refresh rate hint. + * + * Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE). + */ #define GLFW_REFRESH_RATE 0x0002100F +/*! @brief Framebuffer double buffering hint. + * + * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). + */ #define GLFW_DOUBLEBUFFER 0x00021010 +/*! @brief Context client API hint and attribute. + * + * Context client API [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CLIENT_API 0x00022001 +/*! @brief Context client API major version hint and attribute. + * + * Context client API major version [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 +/*! @brief Context client API minor version hint and attribute. + * + * Context client API minor version [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_VERSION_MINOR 0x00022003 +/*! @brief Context client API revision number hint and attribute. + * + * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_REVISION 0x00022004 +/*! @brief Context robustness hint and attribute. + * + * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_ROBUSTNESS 0x00022005 +/*! @brief OpenGL forward-compatibility hint and attribute. + * + * OpenGL forward-compatibility [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 +/*! @brief OpenGL debug context hint and attribute. + * + * OpenGL debug context [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 +/*! @brief OpenGL profile hint and attribute. + * + * OpenGL profile [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_OPENGL_PROFILE 0x00022008 +/*! @brief Context flush-on-release hint and attribute. + * + * Context flush-on-release [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 +/*! @brief Context error suppression hint and attribute. + * + * Context error suppression [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_NO_ERROR 0x0002200A +/*! @brief Context creation API hint and attribute. + * + * Context creation API [hint](@ref GLFW_CLIENT_API_hint) and + * [attribute](@ref GLFW_CLIENT_API_attrib). + */ #define GLFW_CONTEXT_CREATION_API 0x0002200B +#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 +#define GLFW_COCOA_FRAME_AUTOSAVE 0x00023002 +#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 +/*! @} */ + #define GLFW_NO_API 0 #define GLFW_OPENGL_API 0x00030001 #define GLFW_OPENGL_ES_API 0x00030002 @@ -686,8 +974,10 @@ extern "C" { #define GLFW_NATIVE_CONTEXT_API 0x00036001 #define GLFW_EGL_CONTEXT_API 0x00036002 +#define GLFW_OSMESA_CONTEXT_API 0x00036003 /*! @defgroup shapes Standard cursor shapes + * @brief Standard system cursor shapes. * * See [standard cursor creation](@ref cursor_standard) for how these are used. * @@ -729,6 +1019,17 @@ extern "C" { #define GLFW_CONNECTED 0x00040001 #define GLFW_DISCONNECTED 0x00040002 +/*! @addtogroup init + * @{ */ +#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 + +#define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 +#define GLFW_COCOA_MENUBAR 0x00051002 + +#define GLFW_X11_WM_CLASS_NAME 0x00052001 +#define GLFW_X11_WM_CLASS_CLASS 0x00052002 +/*! @} */ + #define GLFW_DONT_CARE -1 @@ -742,7 +1043,7 @@ extern "C" { * without forcing a cast from a regular pointer. * * @sa @ref context_glext - * @sa glfwGetProcAddress + * @sa @ref glfwGetProcAddress * * @since Added in version 3.0. @@ -756,7 +1057,7 @@ typedef void (*GLFWglproc)(void); * without forcing a cast from a regular pointer. * * @sa @ref vulkan_proc - * @sa glfwGetInstanceProcAddress + * @sa @ref glfwGetInstanceProcAddress * * @since Added in version 3.2. * @@ -808,7 +1109,7 @@ typedef struct GLFWcursor GLFWcursor; * @param[in] description A UTF-8 encoded string describing the error. * * @sa @ref error_handling - * @sa glfwSetErrorCallback + * @sa @ref glfwSetErrorCallback * * @since Added in version 3.0. * @@ -827,7 +1128,7 @@ typedef void (* GLFWerrorfun)(int,const char*); * upper-left corner of the client area of the window. * * @sa @ref window_pos - * @sa glfwSetWindowPosCallback + * @sa @ref glfwSetWindowPosCallback * * @since Added in version 3.0. * @@ -844,7 +1145,7 @@ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); * @param[in] height The new height, in screen coordinates, of the window. * * @sa @ref window_size - * @sa glfwSetWindowSizeCallback + * @sa @ref glfwSetWindowSizeCallback * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -860,7 +1161,7 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); * @param[in] window The window that the user attempted to close. * * @sa @ref window_close - * @sa glfwSetWindowCloseCallback + * @sa @ref glfwSetWindowCloseCallback * * @since Added in version 2.5. * @glfw3 Added window handle parameter. @@ -876,7 +1177,7 @@ typedef void (* GLFWwindowclosefun)(GLFWwindow*); * @param[in] window The window whose content needs to be refreshed. * * @sa @ref window_refresh - * @sa glfwSetWindowRefreshCallback + * @sa @ref glfwSetWindowRefreshCallback * * @since Added in version 2.5. * @glfw3 Added window handle parameter. @@ -894,7 +1195,7 @@ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); * `GLFW_FALSE` if it lost it. * * @sa @ref window_focus - * @sa glfwSetWindowFocusCallback + * @sa @ref glfwSetWindowFocusCallback * * @since Added in version 3.0. * @@ -912,7 +1213,7 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); * `GLFW_FALSE` if it was restored. * * @sa @ref window_iconify - * @sa glfwSetWindowIconifyCallback + * @sa @ref glfwSetWindowIconifyCallback * * @since Added in version 3.0. * @@ -920,6 +1221,24 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); */ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); +/*! @brief The function signature for window maximize/restore callbacks. + * + * This is the function signature for window maximize/restore callback + * functions. + * + * @param[in] window The window that was maximized or restored. + * @param[in] iconified `GLFW_TRUE` if the window was maximized, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_maximize + * @sa glfwSetWindowMaximizeCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); + /*! @brief The function signature for framebuffer resize callbacks. * * This is the function signature for framebuffer resize callback @@ -930,7 +1249,7 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); * @param[in] height The new height, in pixels, of the framebuffer. * * @sa @ref window_fbsize - * @sa glfwSetFramebufferSizeCallback + * @sa @ref glfwSetFramebufferSizeCallback * * @since Added in version 3.0. * @@ -950,7 +1269,7 @@ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); * held down. * * @sa @ref input_mouse_button - * @sa glfwSetMouseButtonCallback + * @sa @ref glfwSetMouseButtonCallback * * @since Added in version 1.0. * @glfw3 Added window handle and modifier mask parameters. @@ -970,7 +1289,7 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); * client area. * * @sa @ref cursor_pos - * @sa glfwSetCursorPosCallback + * @sa @ref glfwSetCursorPosCallback * * @since Added in version 3.0. Replaces `GLFWmouseposfun`. * @@ -987,7 +1306,7 @@ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); * area, or `GLFW_FALSE` if it left it. * * @sa @ref cursor_enter - * @sa glfwSetCursorEnterCallback + * @sa @ref glfwSetCursorEnterCallback * * @since Added in version 3.0. * @@ -1004,7 +1323,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); * @param[in] yoffset The scroll offset along the y-axis. * * @sa @ref scrolling - * @sa glfwSetScrollCallback + * @sa @ref glfwSetScrollCallback * * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. * @@ -1024,7 +1343,7 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); * held down. * * @sa @ref input_key - * @sa glfwSetKeyCallback + * @sa @ref glfwSetKeyCallback * * @since Added in version 1.0. * @glfw3 Added window handle, scancode and modifier mask parameters. @@ -1041,7 +1360,7 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); * @param[in] codepoint The Unicode code point of the character. * * @sa @ref input_char - * @sa glfwSetCharCallback + * @sa @ref glfwSetCharCallback * * @since Added in version 2.4. * @glfw3 Added window handle parameter. @@ -1063,7 +1382,9 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); * held down. * * @sa @ref input_char - * @sa glfwSetCharModsCallback + * @sa @ref glfwSetCharModsCallback + * + * @deprecated Scheduled for removal in version 4.0. * * @since Added in version 3.1. * @@ -1080,7 +1401,7 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); * @param[in] paths The UTF-8 encoded file and/or directory path names. * * @sa @ref path_drop - * @sa glfwSetDropCallback + * @sa @ref glfwSetDropCallback * * @since Added in version 3.1. * @@ -1096,7 +1417,7 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. * * @sa @ref monitor_event - * @sa glfwSetMonitorCallback + * @sa @ref glfwSetMonitorCallback * * @since Added in version 3.0. * @@ -1109,11 +1430,11 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); * This is the function signature for joystick configuration callback * functions. * - * @param[in] joy The joystick that was connected or disconnected. + * @param[in] jid The joystick that was connected or disconnected. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. * * @sa @ref joystick_event - * @sa glfwSetJoystickCallback + * @sa @ref glfwSetJoystickCallback * * @since Added in version 3.2. * @@ -1126,7 +1447,8 @@ typedef void (* GLFWjoystickfun)(int,int); * This describes a single video mode. * * @sa @ref monitor_modes - * @sa glfwGetVideoMode glfwGetVideoModes + * @sa @ref glfwGetVideoMode + * @sa @ref glfwGetVideoModes * * @since Added in version 1.0. * @glfw3 Added refresh rate member. @@ -1160,7 +1482,8 @@ typedef struct GLFWvidmode * This describes the gamma ramp for a monitor. * * @sa @ref monitor_gamma - * @sa glfwGetGammaRamp glfwSetGammaRamp + * @sa @ref glfwGetGammaRamp + * @sa @ref glfwSetGammaRamp * * @since Added in version 3.0. * @@ -1183,6 +1506,9 @@ typedef struct GLFWgammaramp } GLFWgammaramp; /*! @brief Image data. + * + * This describes a single 2D image. See the documentation for each related + * function what the expected pixel format is. * * @sa @ref cursor_custom * @sa @ref window_icon @@ -1203,6 +1529,27 @@ typedef struct GLFWimage unsigned char* pixels; } GLFWimage; +/*! @brief Gamepad input state + * + * This describes the input state of a gamepad. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + */ +typedef struct GLFWgamepadstate +{ + /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS` + * or `GLFW_RELEASE`. + */ + unsigned char buttons[15]; + /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0 + * to 1.0 inclusive. + */ + float axes[6]; +} GLFWgamepadstate; + /************************************************************************* * GLFW API functions @@ -1226,15 +1573,15 @@ typedef struct GLFWimage * * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * - * @remark @osx This function will change the current directory of the + * @remark @macos This function will change the current directory of the * application to the `Contents/Resources` subdirectory of the application's - * bundle, if present. This can be disabled with a - * [compile-time option](@ref compile_options_osx). + * bundle, if present. This can be disabled with the @ref + * GLFW_COCOA_CHDIR_RESOURCES init hint. * * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init - * @sa glfwTerminate + * @sa @ref glfwTerminate * * @since Added in version 1.0. * @@ -1266,7 +1613,7 @@ GLFWAPI int glfwInit(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init - * @sa glfwInit + * @sa @ref glfwInit * * @since Added in version 1.0. * @@ -1274,6 +1621,74 @@ GLFWAPI int glfwInit(void); */ GLFWAPI void glfwTerminate(void); +/*! @brief Sets the specified init hint to the desired value. + * + * This function sets hints for the next initialization of GLFW. Only integer + * type hints can be set with this function. + * + * The values you set hints to are never reset by GLFW, but they only take + * effect during initialization. Once GLFW has been initialized, any values + * you set will be ignored until the library is terminated and initialized + * again. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [init hint](@ref init_hints) to set. + * @param[in] value The new value of the init hint. + * + * @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref + * GLFW_INVALID_VALUE. + * + * @remarks This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa init_hints + * @sa glfwInit + * @sa glfwInitHintString + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI void glfwInitHint(int hint, int value); + +/*! @brief Sets the specified init hint to the desired value. + * + * This function sets hints for the next initialization of GLFW. Only string + * type hints can be set with this function. + * + * The values you set hints to are never reset by GLFW, but they only take + * effect during initialization. Once GLFW has been initialized, any values + * you set will be ignored until the library is terminated and initialized + * again. + * + * Some hints are platform specific. These may be set on any platform but they + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. + * + * @param[in] hint The [init hint](@ref init_hints) to set. + * @param[in] value The new value of the init hint. + * + * @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref + * GLFW_INVALID_VALUE. + * + * @remarks This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa init_hints + * @sa glfwInit + * @sa glfwInitHint + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI void glfwInitHintString(int hint, const char* value); + /*! @brief Retrieves the version of the GLFW library. * * This function retrieves the major, minor and revision numbers of the GLFW @@ -1293,7 +1708,7 @@ GLFWAPI void glfwTerminate(void); * @thread_safety This function may be called from any thread. * * @sa @ref intro_version - * @sa glfwGetVersionString + * @sa @ref glfwGetVersionString * * @since Added in version 1.0. * @@ -1324,7 +1739,7 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); * @thread_safety This function may be called from any thread. * * @sa @ref intro_version - * @sa glfwGetVersion + * @sa @ref glfwGetVersion * * @since Added in version 3.0. * @@ -1332,11 +1747,46 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); */ GLFWAPI const char* glfwGetVersionString(void); +/*! @brief Returns and clears the last error for the calling thread. + * + * This function returns and clears the [error code](@ref errors) of the last + * error that occurred on the calling thread, and optionally a UTF-8 encoded + * human-readable description of it. If no error has occurred since the last + * call, it returns @ref GLFW_NO_ERROR (zero) and the description pointer is + * set to `NULL`. + * + * @param[in] description Where to store the error description pointer, or `NULL`. + * @return The last error code for the calling thread, or @ref GLFW_NO_ERROR + * (zero). + * + * @errors None. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * next error occurs or the library is terminated. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI int glfwGetError(const char** description); + /*! @brief Sets the error callback. * * This function sets the error callback, which is called with an error code * and a human-readable description each time a GLFW error occurs. * + * The error code is set before the callback is called. Calling @ref + * glfwGetError from the error callback will return the same value as the error + * code argument. + * * The error callback is called on the thread where the error occurred. If you * are using GLFW from multiple threads, your error callback needs to be * written accordingly. @@ -1359,6 +1809,7 @@ GLFWAPI const char* glfwGetVersionString(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref error_handling + * @sa @ref glfwGetError * * @since Added in version 3.0. * @@ -1387,7 +1838,7 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); * * @sa @ref monitor_monitors * @sa @ref monitor_event - * @sa glfwGetPrimaryMonitor + * @sa @ref glfwGetPrimaryMonitor * * @since Added in version 3.0. * @@ -1411,7 +1862,7 @@ GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); * glfwGetMonitors. * * @sa @ref monitor_monitors - * @sa glfwGetMonitors + * @sa @ref glfwGetMonitors * * @since Added in version 3.0. * @@ -1478,6 +1929,36 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); */ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); +/*! @brief Retrieves the content scale for the specified monitor. + * + * This function retrieves the content scale for the specified monitor. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. If you scale all pixel dimensions by this scale then your + * content should appear at an appropriate size. This is especially important + * for text and any UI elements. + * + * The content scale may depend on both the monitor resolution and pixel + * density and on user settings. It may be very different from the raw DPI + * calculated from the physical size and current resolution. + * + * @param[in] monitor The monitor to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale); + /*! @brief Returns the name of the specified monitor. * * This function returns a human-readable name, encoded as UTF-8, of the @@ -1551,7 +2032,7 @@ GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_modes - * @sa glfwGetVideoMode + * @sa @ref glfwGetVideoMode * * @since Added in version 1.0. * @glfw3 Changed to return an array of modes for a specific monitor. @@ -1580,7 +2061,7 @@ GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_modes - * @sa glfwGetVideoModes + * @sa @ref glfwGetVideoModes * * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. * @@ -1594,12 +2075,23 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); * and then calls @ref glfwSetGammaRamp with it. The value must be a finite * number greater than zero. * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. + * * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] gamma The desired exponent. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * + * @remark @wayland Gamma handling is a priviledged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_gamma @@ -1621,6 +2113,10 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Gamma handling is a priviledged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while + * returning `NULL`. + * * @pointer_lifetime The returned structure and its arrays are allocated and * freed by GLFW. You should not free them yourself. They are valid until the * specified monitor is disconnected, this function is called again for that @@ -1642,6 +2138,14 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * original gamma ramp for that monitor is saved by GLFW the first time this * function is called and is restored by @ref glfwTerminate. * + * The software controlled gamma ramp is applied _in addition_ to the hardware + * gamma correction, which today is usually an approximation of sRGB gamma. + * This means that setting a perfectly linear ramp, or gamma 1.0, will produce + * the default (usually sRGB-like) behavior. + * + * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref + * GLFW_SRGB_CAPABLE hint. + * * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] ramp The gamma ramp to use. * @@ -1653,6 +2157,9 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * * @remark @win32 The gamma ramp size must be 256. * + * @remark @wayland Gamma handling is a priviledged protocol, this function + * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. + * * @pointer_lifetime The specified gamma ramp is copied before this function * returns. * @@ -1676,7 +2183,7 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hints - * @sa glfwWindowHint + * @sa @ref glfwWindowHint * * @since Added in version 3.0. * @@ -1704,7 +2211,7 @@ GLFWAPI void glfwDefaultWindowHints(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hints - * @sa glfwDefaultWindowHints + * @sa @ref glfwDefaultWindowHints * * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. * @@ -1744,12 +2251,12 @@ GLFWAPI void glfwWindowHint(int hint, int value); * or _borderless full screen_ windows, see @ref window_windowed_full_screen. * * Once you have created the window, you can switch it between windowed and - * full screen mode with @ref glfwSetWindowMonitor. If the window has an - * OpenGL or OpenGL ES context, it will be unaffected. + * full screen mode with @ref glfwSetWindowMonitor. This will not affect its + * OpenGL or OpenGL ES context. * * By default, newly created windows use the placement recommended by the * window system. To create the window at a specific position, make it - * initially invisible using the [GLFW_VISIBLE](@ref window_hints_wnd) window + * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) * it. * @@ -1785,33 +2292,47 @@ GLFWAPI void glfwWindowHint(int hint, int value); * * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it * will be set as the initial icon for the window. If no such icon is present, - * the `IDI_WINLOGO` icon will be used instead. To set a different icon, see - * @ref glfwSetWindowIcon. + * the `IDI_APPLICATION` icon will be used instead. To set a different icon, + * see @ref glfwSetWindowIcon. * * @remark @win32 The context to share resources with must not be current on * any other thread. * - * @remark @osx The GLFW window has no icon, as it is not a document + * @remark @macos The OS only supports forward-compatible core profile contexts + * for OpenGL versions 3.2 and later. Before creating an OpenGL context of + * version 3.2 or later you must set the + * [GLFW_OPENGL_FORWARD_COMPAT](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) and + * [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) hints accordingly. + * OpenGL 3.0 and 3.1 contexts are not supported at all on macOS. + * + * @remark @macos The GLFW window has no icon, as it is not a document * window, but the dock icon will be the same as the application bundle's icon. * For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * - * @remark @osx The first time a window is created the menu bar is populated - * with common commands like Hide, Quit and About. The About entry opens - * a minimal about dialog with information from the application's bundle. The - * menu bar can be disabled with a - * [compile-time option](@ref compile_options_osx). + * @remark @macos The first time a window is created the menu bar is created. + * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu + * bar. Otherwise a minimal menu bar is created manually with common commands + * like Hide, Quit and About. The About entry opens a minimal about dialog + * with information from the application's bundle. Menu bar creation can be + * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint. * - * @remark @osx On OS X 10.10 and later the window frame will not be rendered - * at full resolution on Retina displays unless the `NSHighResolutionCapable` - * key is enabled in the application bundle's `Info.plist`. For more - * information, see + * @remark @macos On OS X 10.10 and later the window frame will not be rendered + * at full resolution on Retina displays unless the + * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) + * hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the + * application bundle's `Info.plist`. For more information, see * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) * in the Mac Developer Library. The GLFW test and example programs use * a custom `Info.plist` template for this, which can be found as * `CMake/MacOSXBundleInfo.plist.in` in the source tree. * + * @remark @macos When activating frame autosaving with + * [GLFW_COCOA_FRAME_AUTOSAVE](@ref GLFW_COCOA_FRAME_AUTOSAVE_hint), the + * specified window size may be overriden by a previously saved size and + * position. + * * @remark @x11 Some window managers will not respect the placement of * initially hidden windows. * @@ -1820,12 +2341,30 @@ GLFWAPI void glfwWindowHint(int hint, int value); * query the final size, position or other attributes directly after window * creation. * - * @reentrancy This function must not be called from a callback. + * @remark @x11 The name and class of the `WM_CLASS` window property will by + * default be set to the window title passed to this function. Set the @ref + * GLFW_X11_WM_CLASS_NAME and @ref GLFW_X11_WM_CLASS_CLASS init hints before + * initialization to override this. + * + * @remark @wayland The window frame is currently unimplemented, as if + * [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`. + * A compositor can still emit close, resize or maximize events, using for + * example a keybind mechanism. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size or refresh rate. + * + * @remark @wayland The wl_shell protocol does not support window + * icons, the window will inherit the one defined in the application's + * desktop file, so this function emits @ref GLFW_PLATFORM_ERROR. + * + * @remark @wayland Screensaver inhibition requires the idle-inhibit protocol + * to be implemented in the user's compositor. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_creation - * @sa glfwDestroyWindow + * @sa @ref glfwDestroyWindow * * @since Added in version 3.0. Replaces `glfwOpenWindow`. * @@ -1854,7 +2393,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, G * @thread_safety This function must only be called from the main thread. * * @sa @ref window_creation - * @sa glfwCreateWindow + * @sa @ref glfwCreateWindow * * @since Added in version 3.0. Replaces `glfwCloseWindow`. * @@ -1915,7 +2454,7 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @osx The window title will not be updated until the next time you + * @remark @macos The window title will not be updated until the next time you * process events. * * @thread_safety This function must only be called from the main thread. @@ -1936,6 +2475,10 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * selected. If no images are specified, the window reverts to its default * icon. * + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. + * * The desired image sizes varies depending on platform and system settings. * The selected images will be rescaled as needed. Good sizes include 16x16, * 32x32 and 48x48. @@ -1952,12 +2495,16 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * @pointer_lifetime The specified image data is copied before this function * returns. * - * @remark @osx The GLFW window has no icon, as it is not a document + * @remark @macos The GLFW window has no icon, as it is not a document * window, so this function does nothing. The dock icon will be the same as * the application bundle's icon. For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * + * @remark @wayland The wl_shell protocol does not support icons, the window + * will inherit the one defined in the application's desktop file, so this + * function emits @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_icon @@ -1985,10 +2532,14 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland There is no way for an application to retrieve the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos - * @sa glfwSetWindowPos + * @sa @ref glfwSetWindowPos * * @since Added in version 3.0. * @@ -2015,10 +2566,14 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland There is no way for an application to set the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos - * @sa glfwGetWindowPos + * @sa @ref glfwGetWindowPos * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -2048,7 +2603,7 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size - * @sa glfwSetWindowSize + * @sa @ref glfwSetWindowSize * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -2086,10 +2641,13 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); * @remark If you set size limits and an aspect ratio that conflict, the * results are undefined. * + * @remark @wayland The size limits will not be applied until the window is + * actually resized, either by the user or by the compositor. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_sizelimits - * @sa glfwSetWindowAspectRatio + * @sa @ref glfwSetWindowAspectRatio * * @since Added in version 3.2. * @@ -2126,10 +2684,13 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe * @remark If you set size limits and an aspect ratio that conflict, the * results are undefined. * + * @remark @wayland The aspect ratio will not be applied until the window is + * actually resized, either by the user or by the compositor. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_sizelimits - * @sa glfwSetWindowSizeLimits + * @sa @ref glfwSetWindowSizeLimits * * @since Added in version 3.2. * @@ -2162,11 +2723,14 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size - * @sa glfwGetWindowSize - * @sa glfwSetWindowMonitor + * @sa @ref glfwGetWindowSize + * @sa @ref glfwSetWindowMonitor * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -2196,7 +2760,7 @@ GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_fbsize - * @sa glfwSetFramebufferSizeCallback + * @sa @ref glfwSetFramebufferSizeCallback * * @since Added in version 3.0. * @@ -2231,6 +2795,10 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland The window frame is currently unimplemented, as if + * [GLFW_DECORATED](@ref GLFW_DECORATED_hint) was always set to `GLFW_FALSE`, + * so the returned values will always be zero. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size @@ -2241,6 +2809,92 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) */ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); +/*! @brief Retrieves the content scale for the specified window. + * + * This function retrieves the content scale for the specified window. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. If you scale all pixel dimensions by this scale then your + * content should appear at an appropriate size. This is especially important + * for text and any UI elements. + * + * On systems where each monitors can have its own content scale, the window + * content scale will depend on which monitor the system considers the window + * to be on. + * + * @param[in] window The window to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwGetMonitorContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale); + +/*! @brief Returns the opacity of the whole window. + * + * This function returns the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. If the system + * does not support whole window transparency, this function always returns one. + * + * The initial opacity value for newly created windows is one. + * + * @param[in] window The window to query. + * @return The opacity value of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwSetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window); + +/*! @brief Sets the opacity of the whole window. + * + * This function sets the opacity of the window, including any decorations. + * + * The opacity (or alpha) value is a positive finite number between zero and + * one, where zero is fully transparent and one is fully opaque. + * + * The initial opacity value for newly created windows is one. + * + * A window created with framebuffer transparency may not use whole window + * transparency. The results of doing this are undefined. + * + * @param[in] window The window to set the opacity for. + * @param[in] opacity The desired opacity of the specified window. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_transparency + * @sa @ref glfwGetWindowOpacity + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); + /*! @brief Iconifies the specified window. * * This function iconifies (minimizes) the specified window if it was @@ -2255,11 +2909,14 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland There is no concept of iconification in wl_shell, this + * function will always emit @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify - * @sa glfwRestoreWindow - * @sa glfwMaximizeWindow + * @sa @ref glfwRestoreWindow + * @sa @ref glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. @@ -2285,8 +2942,8 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify - * @sa glfwIconifyWindow - * @sa glfwMaximizeWindow + * @sa @ref glfwIconifyWindow + * @sa @ref glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. @@ -2311,8 +2968,8 @@ GLFWAPI void glfwRestoreWindow(GLFWwindow* window); * This function may only be called from the main thread. * * @sa @ref window_iconify - * @sa glfwIconifyWindow - * @sa glfwRestoreWindow + * @sa @ref glfwIconifyWindow + * @sa @ref glfwRestoreWindow * * @since Added in GLFW 3.2. * @@ -2334,7 +2991,7 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide - * @sa glfwHideWindow + * @sa @ref glfwHideWindow * * @since Added in version 3.0. * @@ -2356,7 +3013,7 @@ GLFWAPI void glfwShowWindow(GLFWwindow* window); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide - * @sa glfwShowWindow + * @sa @ref glfwShowWindow * * @since Added in version 3.0. * @@ -2370,21 +3027,28 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window); * The window should already be visible and not iconified. * * By default, both windowed and full screen mode windows are focused when - * initially created. Set the [GLFW_FOCUSED](@ref window_hints_wnd) to disable - * this behavior. + * initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to + * disable this behavior. * * __Do not use this function__ to steal focus from other applications unless * you are certain that is what the user wants. Focus stealing can be * extremely disruptive. * + * For a less disruptive way of getting the user's attention, see + * [attention requests](@ref window_attention). + * * @param[in] window The window to give input focus. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland It is not possible for an application to bring its windows + * to front, this function will always emit @ref GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_focus + * @sa @ref window_attention * * @since Added in version 3.2. * @@ -2392,6 +3056,33 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window); */ GLFWAPI void glfwFocusWindow(GLFWwindow* window); +/*! @brief Requests user attention to the specified window. + * + * This function requests user attention to the specified window. On + * platforms where this is not supported, attention is requested to the + * application as a whole. + * + * Once the user has given attention, usually by focusing the window or + * application, the system will end the request automatically. + * + * @param[in] window The window to request attention to. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @macos Attention is requested to the application as a whole, not the + * specific window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attention + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window); + /*! @brief Returns the monitor that the window uses for full screen mode. * * This function returns the handle of the monitor that the specified window is @@ -2406,7 +3097,7 @@ GLFWAPI void glfwFocusWindow(GLFWwindow* window); * @thread_safety This function must only be called from the main thread. * * @sa @ref window_monitor - * @sa glfwSetWindowMonitor + * @sa @ref glfwSetWindowMonitor * * @since Added in version 3.0. * @@ -2432,7 +3123,7 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * * When a window transitions from full screen to windowed mode, this function * restores any previous window settings such as whether it is decorated, - * floating, resizable, has size or aspect ratio limits, etc.. + * floating, resizable, has size or aspect ratio limits, etc. * * @param[in] window The window whose monitor, size or video mode to set. * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. @@ -2450,12 +3141,22 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise + * affected by any resizing or mode switching, although you may need to update + * your viewport if the framebuffer size has changed. + * + * @remark @wayland The desired window position is ignored, as there is no way + * for an application to set this property. + * + * @remark @wayland Setting the window to full screen will not attempt to + * change the mode, no matter what the requested size or refresh rate. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_monitor * @sa @ref window_full_screen - * @sa glfwGetWindowMonitor - * @sa glfwSetWindowSize + * @sa @ref glfwGetWindowMonitor + * @sa @ref glfwSetWindowSize * * @since Added in version 3.2. * @@ -2488,6 +3189,7 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs + * @sa @ref glfwSetWindowAttrib * * @since Added in version 3.0. Replaces `glfwGetWindowParam` and * `glfwGetGLVersion`. @@ -2496,6 +3198,42 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int */ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); +/*! @brief Sets an attribute of the specified window. + * + * This function sets the value of an attribute of the specified window. + * + * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), + * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), + * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib) and + * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib). + * + * Some of these attributes are ignored for full screen windows. The new + * value will take effect if the window is later made windowed. + * + * Some of these attributes are ignored for windowed mode windows. The new + * value will take effect if the window is later made full screen. + * + * @param[in] window The window to set the attribute for. + * @param[in] attrib A supported window attribute. + * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark Calling @ref glfwGetWindowAttrib will always return the latest + * value, even if that value is ignored by the current mode of the window. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * @sa @ref glfwGetWindowAttrib + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value); + /*! @brief Sets the user pointer of the specified window. * * This function sets the user-defined pointer of the specified window. The @@ -2511,7 +3249,7 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); * synchronized. * * @sa @ref window_userptr - * @sa glfwGetWindowUserPointer + * @sa @ref glfwGetWindowUserPointer * * @since Added in version 3.0. * @@ -2532,7 +3270,7 @@ GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); * synchronized. * * @sa @ref window_userptr - * @sa glfwSetWindowUserPointer + * @sa @ref glfwSetWindowUserPointer * * @since Added in version 3.0. * @@ -2543,8 +3281,9 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); /*! @brief Sets the position callback for the specified window. * * This function sets the position callback of the specified window, which is - * called when the window is moved. The callback is provided with the screen - * position of the upper-left corner of the client area of the window. + * called when the window is moved. The callback is provided with the + * position, in screen coordinates, of the upper-left corner of the client area + * of the window. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -2554,6 +3293,9 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark @wayland This callback will never be called, as there is no way for + * an application to know its global position. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos @@ -2608,8 +3350,8 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @osx Selecting Quit from the application menu will trigger the close - * callback for all windows. + * @remark @macos Selecting Quit from the application menu will trigger the + * close callback for all windows. * * @thread_safety This function must only be called from the main thread. * @@ -2628,9 +3370,9 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * called when the client area of the window needs to be redrawn, for example * if the window has been exposed after having been covered by another window. * - * On compositing window systems such as Aero, Compiz or Aqua, where the window - * contents are saved off-screen, this callback may be called only very - * infrequently or never at all. + * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where + * the window contents are saved off-screen, this callback may be called only + * very infrequently or never at all. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -2692,6 +3434,9 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark @wayland The wl_shell protocol has no concept of iconification, + * this callback will never be called. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify @@ -2702,6 +3447,29 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi */ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun cbfun); +/*! @brief Sets the maximize callback for the specified window. + * + * This function sets the maximization callback of the specified window, which + * is called when the window is maximized or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_maximize + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun cbfun); + /*! @brief Sets the framebuffer resize callback for the specified window. * * This function sets the framebuffer resize callback of the specified window, @@ -2737,9 +3505,12 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * [window refresh callback](@ref window_refresh) to redraw the contents of * your window when necessary during such operations. * - * On some platforms, certain events are sent directly to the application - * without going through the event queue, causing callbacks to be called - * outside of a call to one of the event processing functions. + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. * * Event processing is not required for joystick input to work. * @@ -2751,8 +3522,8 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * @thread_safety This function must only be called from the main thread. * * @sa @ref events - * @sa glfwWaitEvents - * @sa glfwWaitEventsTimeout + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout * * @since Added in version 1.0. * @@ -2779,8 +3550,12 @@ GLFWAPI void glfwPollEvents(void); * [window refresh callback](@ref window_refresh) to redraw the contents of * your window when necessary during such operations. * - * On some platforms, certain callbacks may be called outside of a call to one - * of the event processing functions. + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. * * If no windows exist, this function returns immediately. For synchronization * of threads in applications that do not create windows, use your threading @@ -2796,8 +3571,8 @@ GLFWAPI void glfwPollEvents(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref events - * @sa glfwPollEvents - * @sa glfwWaitEventsTimeout + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEventsTimeout * * @since Added in version 2.5. * @@ -2826,8 +3601,12 @@ GLFWAPI void glfwWaitEvents(void); * [window refresh callback](@ref window_refresh) to redraw the contents of * your window when necessary during such operations. * - * On some platforms, certain callbacks may be called outside of a call to one - * of the event processing functions. + * Do not assume that callbacks you set will _only_ be called in response to + * event processing functions like this one. While it is necessary to poll for + * events, window systems that require GLFW to register callbacks of its own + * can pass events to GLFW in response to many window system function calls. + * GLFW will pass those events on to the application callbacks before + * returning. * * If no windows exist, this function returns immediately. For synchronization * of threads in applications that do not create windows, use your threading @@ -2842,8 +3621,8 @@ GLFWAPI void glfwWaitEvents(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref events - * @sa glfwPollEvents - * @sa glfwWaitEvents + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEvents * * @since Added in version 3.2. * @@ -2866,8 +3645,8 @@ GLFWAPI void glfwWaitEventsTimeout(double timeout); * @thread_safety This function may be called from any thread. * * @sa @ref events - * @sa glfwWaitEvents - * @sa glfwWaitEventsTimeout + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout * * @since Added in version 3.1. * @@ -2878,8 +3657,8 @@ GLFWAPI void glfwPostEmptyEvent(void); /*! @brief Returns the value of an input option for the specified window. * * This function returns the value of an input option for the specified window. - * The mode must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or - * `GLFW_STICKY_MOUSE_BUTTONS`. + * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS or + * @ref GLFW_STICKY_MOUSE_BUTTONS. * * @param[in] window The window to query. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or @@ -2890,7 +3669,7 @@ GLFWAPI void glfwPostEmptyEvent(void); * * @thread_safety This function must only be called from the main thread. * - * @sa glfwSetInputMode + * @sa @ref glfwSetInputMode * * @since Added in version 3.0. * @@ -2901,8 +3680,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); /*! @brief Sets an input option for the specified window. * * This function sets an input mode option for the specified window. The mode - * must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or - * `GLFW_STICKY_MOUSE_BUTTONS`. + * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS or + * @ref GLFW_STICKY_MOUSE_BUTTONS. * * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor * modes: @@ -2938,7 +3717,7 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * * @thread_safety This function must only be called from the main thread. * - * @sa glfwGetInputMode + * @sa @ref glfwGetInputMode * * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. * @@ -2946,17 +3725,22 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); -/*! @brief Returns the localized name of the specified printable key. +/*! @brief Returns the layout-specific name of the specified printable key. * - * This function returns the localized name of the specified printable key. - * This is intended for displaying key bindings to the user. + * This function returns the name of the specified printable key, encoded as + * UTF-8. This is typically the character that key would produce without any + * modifier keys, intended for displaying key bindings to the user. For dead + * keys, it is typically the diacritic it would add to a character. * - * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used instead, otherwise - * the scancode is ignored. If a non-printable key or (if the key is - * `GLFW_KEY_UNKNOWN`) a scancode that maps to a non-printable key is - * specified, this function returns `NULL`. + * __Do not use this function__ for [text input](@ref input_char). You will + * break text input for many languages even if it happens to work for yours. * - * This behavior allows you to pass in the arguments passed to the + * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used to identify the key, + * otherwise the scancode is ignored. If you specify a non-printable key, or + * `GLFW_KEY_UNKNOWN` and a scancode that maps to a non-printable key, this + * function returns `NULL` but does not emit an error. + * + * This behavior allows you to always pass in the arguments in the * [key callback](@ref input_key) without modification. * * The printable keys are: @@ -2982,9 +3766,13 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); * - `GLFW_KEY_KP_ADD` * - `GLFW_KEY_KP_EQUAL` * + * Names for printable keys depend on keyboard layout, while names for + * non-printable keys are the same across layouts but depend on the application + * language and should be localized along with other user interface text. + * * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. * @param[in] scancode The scancode of the key to query. - * @return The localized name of the key, or `NULL`. + * @return The UTF-8 encoded, layout-specific name of the key, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -3003,6 +3791,30 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); */ GLFWAPI const char* glfwGetKeyName(int key, int scancode); +/*! @brief Returns the platform-specific scancode of the specified key. + * + * This function returns the platform-specific scancode of the specified key. + * + * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this + * method will return `-1`. + * + * @param[in] key Any [named key](@ref keys). + * @return The platform-specific scancode for the key, or `-1` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref input_key + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetKeyScancode(int key); + /*! @brief Returns the last reported state of a keyboard key for the specified * window. * @@ -3011,7 +3823,7 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode); * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to * the key callback. * - * If the `GLFW_STICKY_KEYS` input mode is enabled, this function returns + * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns * `GLFW_PRESS` the first time you call it for a key that was pressed, even if * that key has already been released. * @@ -3050,7 +3862,7 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); * to the specified window. The returned state is one of `GLFW_PRESS` or * `GLFW_RELEASE`. * - * If the `GLFW_STICKY_MOUSE_BUTTONS` input mode is enabled, this function + * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function * `GLFW_PRESS` the first time you call it for a mouse button that was pressed, * even if that mouse button has already been released. * @@ -3102,7 +3914,7 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos - * @sa glfwSetCursorPos + * @sa @ref glfwSetCursorPos * * @since Added in version 3.0. Replaces `glfwGetMousePos`. * @@ -3136,10 +3948,13 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland This function will only work when the cursor mode is + * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos - * @sa glfwGetCursorPos + * @sa @ref glfwGetCursorPos * * @since Added in version 3.0. Replaces `glfwSetMousePos`. * @@ -3154,8 +3969,8 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * Any remaining cursors are destroyed by @ref glfwTerminate. * * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight - * bits per channel. They are arranged canonically as packed sequential rows, - * starting from the top-left corner. + * bits per channel with the red channel first. They are arranged canonically + * as packed sequential rows, starting from the top-left corner. * * The cursor hotspot is specified in pixels, relative to the upper-left corner * of the cursor image. Like all other coordinate systems in GLFW, the X-axis @@ -3173,13 +3988,11 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * @pointer_lifetime The specified image data is copied before this function * returns. * - * @reentrancy This function must not be called from a callback. - * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object - * @sa glfwDestroyCursor - * @sa glfwCreateStandardCursor + * @sa @ref glfwDestroyCursor + * @sa @ref glfwCreateStandardCursor * * @since Added in version 3.1. * @@ -3199,12 +4012,10 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * - * @reentrancy This function must not be called from a callback. - * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object - * @sa glfwCreateCursor + * @sa @ref glfwCreateCursor * * @since Added in version 3.1. * @@ -3218,6 +4029,9 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); * glfwCreateCursor. Any remaining cursors will be destroyed by @ref * glfwTerminate. * + * If the specified cursor is current for any window, that window will be + * reverted to the default cursor. This does not affect the cursor mode. + * * @param[in] cursor The cursor object to destroy. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref @@ -3228,7 +4042,7 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object - * @sa glfwCreateCursor + * @sa @ref glfwCreateCursor * * @since Added in version 3.1. * @@ -3320,7 +4134,7 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * * The character callback behaves as system text input normally does and will * not be called if modifier keys are held down that would prevent normal text - * input on that platform, for example a Super (Command) key on OS X or Alt key + * input on that platform, for example a Super (Command) key on macOS or Alt key * on Windows. There is a * [character with modifiers callback](@ref glfwSetCharModsCallback) that * receives these events. @@ -3365,6 +4179,8 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * @return The previously set callback, or `NULL` if no callback was set or an * [error](@ref error_handling) occurred. * + * @deprecated Scheduled for removal in version 4.0. + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3501,6 +4317,8 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * + * @remark @wayland File drop is currently unimplemented. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref path_drop @@ -3515,7 +4333,11 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); * * This function returns whether the specified joystick is present. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * There is no need to call this function before other functions that accept + * a joystick ID, as they all check for presence before performing any other + * work. + * + * @param[in] jid The [joystick](@ref joysticks) to query. * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref @@ -3529,18 +4351,18 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); * * @ingroup input */ -GLFWAPI int glfwJoystickPresent(int joy); +GLFWAPI int glfwJoystickPresent(int jid); /*! @brief Returns the values of all axes of the specified joystick. * * This function returns the values of all axes of the specified joystick. * Each element in the array is a value between -1.0 and 1.0. * - * Querying a joystick slot with no device present is not an error, but will - * cause this function to return `NULL`. Call @ref glfwJoystickPresent to - * check device presence. + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of axis values in the returned * array. This is set to zero if the joystick is not present or an error * occurred. @@ -3552,8 +4374,7 @@ GLFWAPI int glfwJoystickPresent(int joy); * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is - * disconnected, this function is called again for that joystick or the library - * is terminated. + * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * @@ -3563,18 +4384,25 @@ GLFWAPI int glfwJoystickPresent(int joy); * * @ingroup input */ -GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); /*! @brief Returns the state of all buttons of the specified joystick. * * This function returns the state of all buttons of the specified joystick. * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. * - * Querying a joystick slot with no device present is not an error, but will - * cause this function to return `NULL`. Call @ref glfwJoystickPresent to - * check device presence. + * For backward compatibility with earlier versions that did not have @ref + * glfwGetJoystickHats, the button array also includes all hats, each + * represented as four buttons. The hats are in the same order as returned by + * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and + * _left_. To disable these extra buttons, set the @ref + * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of button states in the returned * array. This is set to zero if the joystick is not present or an error * occurred. @@ -3586,19 +4414,75 @@ GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_button + * + * @since Added in version 2.2. + * @glfw3 Changed to return a dynamic array. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); + +/*! @brief Returns the state of all hats of the specified joystick. + * + * This function returns the state of all hats of the specified joystick. + * Each element in the array is one of the following values: + * + * Name | Value + * --------------------- | -------------------------------- + * `GLFW_HAT_CENTERED` | 0 + * `GLFW_HAT_UP` | 1 + * `GLFW_HAT_RIGHT` | 2 + * `GLFW_HAT_DOWN` | 4 + * `GLFW_HAT_LEFT` | 8 + * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` + * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` + * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` + * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` + * + * The diagonal directions are bitwise combinations of the primary (up, right, + * down and left) directions and you can test for these individually by ANDing + * it with the corresponding direction. + * + * @code + * if (hats[2] & GLFW_HAT_RIGHT) + * { + * // State of hat 2 could be right-up, right or right-down + * } + * @endcode + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of hat states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of hat states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is * disconnected, this function is called again for that joystick or the library * is terminated. * * @thread_safety This function must only be called from the main thread. * - * @sa @ref joystick_button + * @sa @ref joystick_hat * - * @since Added in version 2.2. - * @glfw3 Changed to return a dynamic array. + * @since Added in version 3.3. * * @ingroup input */ -GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); /*! @brief Returns the name of the specified joystick. * @@ -3606,11 +4490,11 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * The returned string is allocated and freed by GLFW. You should not free it * yourself. * - * Querying a joystick slot with no device present is not an error, but will - * cause this function to return `NULL`. Call @ref glfwJoystickPresent to - * check device presence. + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. * - * @param[in] joy The [joystick](@ref joysticks) to query. + * @param[in] jid The [joystick](@ref joysticks) to query. * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick * is not present or an [error](@ref error_handling) occurred. * @@ -3619,8 +4503,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is - * disconnected, this function is called again for that joystick or the library - * is terminated. + * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * @@ -3630,7 +4513,76 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * * @ingroup input */ -GLFWAPI const char* glfwGetJoystickName(int joy); +GLFWAPI const char* glfwGetJoystickName(int jid); + +/*! @brief Returns the SDL comaptible GUID of the specified joystick. + * + * This function returns the SDL compatible GUID, as a UTF-8 encoded + * hexadecimal string, of the specified joystick. The returned string is + * allocated and freed by GLFW. You should not free it yourself. + * + * The GUID is what connects a joystick to a gamepad mapping. A connected + * joystick will always have a GUID even if there is no gamepad mapping + * assigned to it. + * + * If the specified joystick is not present this function will return `NULL` + * but will not generate an error. This can be used instead of first calling + * @ref glfwJoystickPresent. + * + * The GUID uses the format introduced in SDL 2.0.5. This GUID tries to + * uniquely identify the make and model of a joystick but does not identify + * a specific unit, e.g. all wired Xbox 360 controllers will have the same + * GUID on that platform. The GUID for a unit may vary between platforms + * depending on what hardware information the platform specific APIs provide. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded GUID of the joystick, or `NULL` if the joystick + * is not present or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickGUID(int jid); + +/*! @brief Returns whether the specified joystick has a gamepad mapping. + * + * This function returns whether the specified joystick is both present and has + * a gamepad mapping. + * + * If the specified joystick is present but does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check if a joystick is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return `GLFW_TRUE` if a joystick is both present and has a gamepad mapping, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwGetGamepadState + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickIsGamepad(int jid); /*! @brief Sets the joystick configuration callback. * @@ -3638,6 +4590,12 @@ GLFWAPI const char* glfwGetJoystickName(int joy); * currently set callback. This is called when a joystick is connected to or * disconnected from the system. * + * For joystick connection and disconnection events to be delivered on all + * platforms, you need to call one of the [event processing](@ref events) + * functions. Joystick disconnection may also be detected and the callback + * called by joystick functions. The function will then return whatever it + * returns if the joystick is not present. + * * @param[in] cbfun The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the @@ -3655,24 +4613,126 @@ GLFWAPI const char* glfwGetJoystickName(int joy); */ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); +/*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. + * + * This function parses the specified ASCII encoded string and updates the + * internal list with any gamepad mappings it finds. This string may + * contain either a single gamepad mapping or many mappings separated by + * newlines. The parser supports the full format of the `gamecontrollerdb.txt` + * source file including empty lines and comments. + * + * See @ref gamepad_mapping for a description of the format. + * + * If there is already a gamepad mapping for a given GUID in the internal list, + * it will be replaced by the one passed to this function. If the library is + * terminated and re-initialized the internal list will revert to the built-in + * default. + * + * @param[in] string The string containing the gamepad mappings. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * @sa @ref glfwGetGamepadName + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwUpdateGamepadMappings(const char* string); + +/*! @brief Returns the human-readable gamepad name for the specified joystick. + * + * This function returns the human-readable name of the gamepad from the + * gamepad mapping assigned to the specified joystick. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `NULL` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded name of the gamepad, or `NULL` if the + * joystick is not present, does not have a mapping or an + * [error](@ref error_handling) occurred. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, the gamepad mappings are updated or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref gamepad + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetGamepadName(int jid); + +/*! @brief Retrieves the state of the specified joystick remapped as a gamepad. + * + * This function retrives the state of the specified joystick remapped to + * an Xbox-like gamepad. + * + * If the specified joystick is not present or does not have a gamepad mapping + * this function will return `GLFW_FALSE` but will not generate an error. Call + * @ref glfwJoystickPresent to check whether it is present regardless of + * whether it has a mapping. + * + * The Guide button may not be available for input as it is often hooked by the + * system or the Steam client. + * + * Not all devices have all the buttons or axes provided by @ref + * GLFWgamepadstate. Unavailable buttons and axes will always report + * `GLFW_RELEASE` and 1.0 respectively. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] state The gamepad input state of the joystick. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if no joystick is + * connected, it has no gamepad mapping or an [error](@ref error_handling) + * occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @sa @ref gamepad + * @sa @ref glfwUpdateGamepadMappings + * @sa @ref glfwJoystickIsGamepad + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); + /*! @brief Sets the clipboard to the specified string. * * This function sets the system clipboard to the specified, UTF-8 encoded * string. * - * @param[in] window The window that will own the clipboard contents. + * @param[in] window Deprecated. Any valid window or `NULL`. * @param[in] string A UTF-8 encoded string. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Clipboard is currently unimplemented. + * * @pointer_lifetime The specified string is copied before this function * returns. * * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard - * @sa glfwGetClipboardString + * @sa @ref glfwGetClipboardString * * @since Added in version 3.0. * @@ -3687,13 +4747,15 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * if its contents cannot be converted, `NULL` is returned and a @ref * GLFW_FORMAT_UNAVAILABLE error is generated. * - * @param[in] window The window that will request the clipboard contents. + * @param[in] window Deprecated. Any valid window or `NULL`. * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Clipboard is currently unimplemented. + * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the next call to @ref * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library @@ -3702,7 +4764,7 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard - * @sa glfwSetClipboardString + * @sa @ref glfwSetClipboardString * * @since Added in version 3.0. * @@ -3778,7 +4840,7 @@ GLFWAPI void glfwSetTime(double time); * @thread_safety This function may be called from any thread. * * @sa @ref time - * @sa glfwGetTimerFrequency + * @sa @ref glfwGetTimerFrequency * * @since Added in version 3.2. * @@ -3798,7 +4860,7 @@ GLFWAPI uint64_t glfwGetTimerValue(void); * @thread_safety This function may be called from any thread. * * @sa @ref time - * @sa glfwGetTimerValue + * @sa @ref glfwGetTimerValue * * @since Added in version 3.2. * @@ -3810,14 +4872,18 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void); * thread. * * This function makes the OpenGL or OpenGL ES context of the specified window - * current on the calling thread. A context can only be made current on + * current on the calling thread. A context must only be made current on * a single thread at a time and each thread can have only a single current * context at a time. * + * When moving a context between threads, you must make it non-current on the + * old thread before making it current on the new one. + * * By default, making a context non-current implicitly forces a pipeline flush. * On machines that support `GL_KHR_context_flush_control`, you can control * whether a context performs this flush by setting the - * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref window_hints_ctx) window hint. + * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) + * hint. * * The specified window must have an OpenGL or OpenGL ES context. Specifying * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT @@ -3832,7 +4898,7 @@ GLFWAPI uint64_t glfwGetTimerFrequency(void); * @thread_safety This function may be called from any thread. * * @sa @ref context_current - * @sa glfwGetCurrentContext + * @sa @ref glfwGetCurrentContext * * @since Added in version 3.0. * @@ -3853,7 +4919,7 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); * @thread_safety This function may be called from any thread. * * @sa @ref context_current - * @sa glfwMakeContextCurrent + * @sa @ref glfwMakeContextCurrent * * @since Added in version 3.0. * @@ -3886,7 +4952,7 @@ GLFWAPI GLFWwindow* glfwGetCurrentContext(void); * @thread_safety This function may be called from any thread. * * @sa @ref buffer_swap - * @sa glfwSwapInterval + * @sa @ref glfwSwapInterval * * @since Added in version 1.0. * @glfw3 Added window handle parameter. @@ -3903,12 +4969,11 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); * is sometimes called _vertical synchronization_, _vertical retrace * synchronization_ or just _vsync_. * - * Contexts that support either of the `WGL_EXT_swap_control_tear` and - * `GLX_EXT_swap_control_tear` extensions also accept negative swap intervals, - * which allow the driver to swap even if a frame arrives a little bit late. - * You can check for the presence of these extensions using @ref - * glfwExtensionSupported. For more information about swap tearing, see the - * extension specifications. + * A context that support either of the `WGL_EXT_swap_control_tear` and + * `GLX_EXT_swap_control_tear` extensions also accepts _negative_ swap + * intervals, which allows the driver to swap immediately even if a frame + * arrives a little bit late. You can check for these extensions with @ref + * glfwExtensionSupported. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. @@ -3934,7 +4999,7 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); * @thread_safety This function may be called from any thread. * * @sa @ref buffer_swap - * @sa glfwSwapBuffers + * @sa @ref glfwSwapBuffers * * @since Added in version 1.0. * @@ -3972,7 +5037,7 @@ GLFWAPI void glfwSwapInterval(int interval); * @thread_safety This function may be called from any thread. * * @sa @ref context_glext - * @sa glfwGetProcAddress + * @sa @ref glfwGetProcAddress * * @since Added in version 1.0. * @@ -4014,7 +5079,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension); * @thread_safety This function may be called from any thread. * * @sa @ref context_glext - * @sa glfwExtensionSupported + * @sa @ref glfwExtensionSupported * * @since Added in version 1.0. * @@ -4022,19 +5087,21 @@ GLFWAPI int glfwExtensionSupported(const char* extension); */ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); -/*! @brief Returns whether the Vulkan loader has been found. +/*! @brief Returns whether the Vulkan loader and an ICD have been found. * - * This function returns whether the Vulkan loader has been found. This check - * is performed by @ref glfwInit. + * This function returns whether the Vulkan loader and any minimally functional + * ICD have been found. * - * The availability of a Vulkan loader does not by itself guarantee that window - * surface creation or even device creation is possible. Call @ref - * glfwGetRequiredInstanceExtensions to check whether the extensions necessary - * for Vulkan surface creation are available and @ref - * glfwGetPhysicalDevicePresentationSupport to check whether a queue family of - * a physical device supports image presentation. + * The availability of a Vulkan loader and even an ICD does not by itself + * guarantee that surface creation or even instance creation is possible. + * For example, on Fermi systems Nvidia will install an ICD that provides no + * actual Vulkan support. Call @ref glfwGetRequiredInstanceExtensions to check + * whether the extensions necessary for Vulkan surface creation are available + * and @ref glfwGetPhysicalDevicePresentationSupport to check whether a queue + * family of a physical device supports image presentation. * - * @return `GLFW_TRUE` if Vulkan is available, or `GLFW_FALSE` otherwise. + * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` + * otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * @@ -4058,7 +5125,7 @@ GLFWAPI int glfwVulkanSupported(void); * * If Vulkan is not available on the machine, this function returns `NULL` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is available. + * to check whether Vulkan is at least minimally available. * * If Vulkan is available but no set of extensions allowing window surface * creation was found, this function returns `NULL`. You may still use Vulkan @@ -4072,11 +5139,14 @@ GLFWAPI int glfwVulkanSupported(void); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_API_UNAVAILABLE. * - * @remarks Additional extensions may be required by future versions of GLFW. + * @remark Additional extensions may be required by future versions of GLFW. * You should check if any extensions you wish to enable are already in the * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. * + * @remark @macos This function currently only supports the + * `VK_MVK_macos_surface` extension from MoltenVK. + * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * library is terminated. @@ -4084,7 +5154,7 @@ GLFWAPI int glfwVulkanSupported(void); * @thread_safety This function may be called from any thread. * * @sa @ref vulkan_ext - * @sa glfwCreateWindowSurface + * @sa @ref glfwCreateWindowSurface * * @since Added in version 3.2. * @@ -4108,7 +5178,7 @@ GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count); * * If Vulkan is not available on the machine, this function returns `NULL` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is available. + * to check whether Vulkan is at least minimally available. * * This function is equivalent to calling `vkGetInstanceProcAddr` with * a platform-specific query of the Vulkan loader as a fallback. @@ -4144,7 +5214,7 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* p * not available on the machine, or if the specified instance was not created * with the required extensions, this function returns `GLFW_FALSE` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is available and @ref + * to check whether Vulkan is at least minimally available and @ref * glfwGetRequiredInstanceExtensions to check what instance extensions are * required. * @@ -4157,6 +5227,10 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* p * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * + * @remark @macos This function currently always returns `GLFW_TRUE`, as the + * `VK_MVK_macos_surface` extension does not provide + * a `vkGetPhysicalDevice*PresentationSupport` type function. + * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. * @@ -4172,10 +5246,10 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * * This function creates a Vulkan surface for the specified window. * - * If the Vulkan loader was not found at initialization, this function returns - * `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref GLFW_API_UNAVAILABLE - * error. Call @ref glfwVulkanSupported to check whether the Vulkan loader was - * found. + * If the Vulkan loader or at least one minimally functional ICD were not found, + * this function returns `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref + * GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported to check whether + * Vulkan is at least minimally available. * * If the required window surface creation instance extensions are not * available or if the specified instance was not created with these extensions @@ -4201,16 +5275,22 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * - * @remarks If an error occurs before the creation call is made, GLFW returns + * @remark If an error occurs before the creation call is made, GLFW returns * the Vulkan error code most appropriate for the error. Appropriate use of * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should * eliminate almost all occurrences of these errors. * + * @remark @macos This function currently only supports the + * `VK_MVK_macos_surface` extension from MoltenVK. + * + * @remark @macos This function creates and sets a `CAMetalLayer` instance for + * the window content view, which is required for MoltenVK to function. + * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. * * @sa @ref vulkan_surface - * @sa glfwGetRequiredInstanceExtensions + * @sa @ref glfwGetRequiredInstanceExtensions * * @since Added in version 3.2. * @@ -4237,6 +5317,13 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window #undef GLFW_CALLBACK_DEFINED #endif +/* Some OpenGL related headers need GLAPIENTRY, but it is unconditionally + * defined by some gl.h variants (OpenBSD) so define it after if needed. + */ +#ifndef GLAPIENTRY + #define GLAPIENTRY APIENTRY +#endif + /* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ diff --git a/raylib/external/glfw/include/GLFW/glfw3native.h b/raylib/external/glfw/include/GLFW/glfw3native.h index 30e1a57..4372cb7 100644 --- a/raylib/external/glfw/include/GLFW/glfw3native.h +++ b/raylib/external/glfw/include/GLFW/glfw3native.h @@ -1,9 +1,9 @@ /************************************************************************* - * GLFW 3.2 - www.glfw.org + * GLFW 3.3 - www.glfw.org * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Berglund + * Copyright (c) 2006-2016 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -45,12 +45,13 @@ extern "C" { * more information. */ /*! @defgroup native Native access + * @brief Functions related to accessing native handles. * * **By using the native access functions you assert that you know what you're * doing and how to fix problems caused by using them. If you don't, you * shouldn't be using them.** * - * Before the inclusion of @ref glfw3native.h, you may define exactly one + * Before the inclusion of @ref glfw3native.h, you may define zero or more * window system API macro and zero or more context creation API macros. * * The chosen backends must match those the library was compiled for. Failure @@ -68,6 +69,7 @@ extern "C" { * * `GLFW_EXPOSE_NATIVE_NSGL` * * `GLFW_EXPOSE_NATIVE_GLX` * * `GLFW_EXPOSE_NATIVE_EGL` + * * `GLFW_EXPOSE_NATIVE_OSMESA` * * These macros select which of the native access functions that are declared * and which platform-specific headers to include. It is then up your (by @@ -84,7 +86,10 @@ extern "C" { // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for // example to allow applications to correctly declare a GL_ARB_debug_output // callback) but windows.h assumes no one will define APIENTRY before it does - #undef APIENTRY + #if defined(GLFW_APIENTRY_DEFINED) + #undef APIENTRY + #undef GLFW_APIENTRY_DEFINED + #endif #include #elif defined(GLFW_EXPOSE_NATIVE_COCOA) #include @@ -114,6 +119,9 @@ extern "C" { #if defined(GLFW_EXPOSE_NATIVE_EGL) #include #endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) + #include +#endif /************************************************************************* @@ -284,6 +292,56 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); * @ingroup native */ GLFWAPI Window glfwGetX11Window(GLFWwindow* window); + +/*! @brief Sets the current primary selection to the specified string. + * + * @param[in] string A UTF-8 encoded string. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa glfwGetX11SelectionString + * @sa glfwSetClipboardString + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI void glfwSetX11SelectionString(const char* string); + +/*! @brief Returns the contents of the current primary selection as a string. + * + * If the selection is empty or if its contents cannot be converted, `NULL` + * is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated. + * + * @return The contents of the selection as a UTF-8 encoded string, or `NULL` + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref + * glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the + * library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa glfwSetX11SelectionString + * @sa glfwGetClipboardString + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetX11SelectionString(void); #endif #if defined(GLFW_EXPOSE_NATIVE_GLX) @@ -389,9 +447,9 @@ GLFWAPI MirConnection* glfwGetMirDisplay(void); */ GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); -/*! @brief Returns the `MirSurface*` of the specified window. +/*! @brief Returns the `MirWindow*` of the specified window. * - * @return The `MirSurface*` of the specified window, or `NULL` if an + * @return The `MirWindow*` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * * @thread_safety This function may be called from any thread. Access is not @@ -401,7 +459,7 @@ GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); * * @ingroup native */ -GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* window); +GLFWAPI MirWindow* glfwGetMirWindow(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_EGL) @@ -448,6 +506,64 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); #endif +#if defined(GLFW_EXPOSE_NATIVE_OSMESA) +/*! @brief Retrieves the color buffer associated with the specified window. + * + * @param[in] window The window whose color buffer to retrieve. + * @param[out] width Where to store the width of the color buffer, or `NULL`. + * @param[out] height Where to store the height of the color buffer, or `NULL`. + * @param[out] format Where to store the OSMesa pixel format of the color + * buffer, or `NULL`. + * @param[out] buffer Where to store the address of the color buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer); + +/*! @brief Retrieves the depth buffer associated with the specified window. + * + * @param[in] window The window whose depth buffer to retrieve. + * @param[out] width Where to store the width of the depth buffer, or `NULL`. + * @param[out] height Where to store the height of the depth buffer, or `NULL`. + * @param[out] bytesPerValue Where to store the number of bytes per depth + * buffer element, or `NULL`. + * @param[out] buffer Where to store the address of the depth buffer, or + * `NULL`. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer); + +/*! @brief Returns the `OSMesaContext` of the specified window. + * + * @return The `OSMesaContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.3. + * + * @ingroup native + */ +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window); +#endif + #ifdef __cplusplus } #endif diff --git a/raylib/external/glfw/src/cocoa_init.m b/raylib/external/glfw/src/cocoa_init.m index f10d638..01a746b 100644 --- a/raylib/external/glfw/src/cocoa_init.m +++ b/raylib/external/glfw/src/cocoa_init.m @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -28,8 +28,6 @@ #include // For MAXPATHLEN -#if defined(_GLFW_USE_CHDIR) - // Change to our application bundle's resources directory, if present // static void changeToResourcesDirectory(void) @@ -66,137 +64,135 @@ static void changeToResourcesDirectory(void) chdir(resourcesPath); } -#endif /* _GLFW_USE_CHDIR */ - // Create key code translation tables // static void createKeyTables(void) { int scancode; - memset(_glfw.ns.publicKeys, -1, sizeof(_glfw.ns.publicKeys)); - memset(_glfw.ns.nativeKeys, -1, sizeof(_glfw.ns.nativeKeys)); + memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes)); + memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes)); - _glfw.ns.publicKeys[0x1D] = GLFW_KEY_0; - _glfw.ns.publicKeys[0x12] = GLFW_KEY_1; - _glfw.ns.publicKeys[0x13] = GLFW_KEY_2; - _glfw.ns.publicKeys[0x14] = GLFW_KEY_3; - _glfw.ns.publicKeys[0x15] = GLFW_KEY_4; - _glfw.ns.publicKeys[0x17] = GLFW_KEY_5; - _glfw.ns.publicKeys[0x16] = GLFW_KEY_6; - _glfw.ns.publicKeys[0x1A] = GLFW_KEY_7; - _glfw.ns.publicKeys[0x1C] = GLFW_KEY_8; - _glfw.ns.publicKeys[0x19] = GLFW_KEY_9; - _glfw.ns.publicKeys[0x00] = GLFW_KEY_A; - _glfw.ns.publicKeys[0x0B] = GLFW_KEY_B; - _glfw.ns.publicKeys[0x08] = GLFW_KEY_C; - _glfw.ns.publicKeys[0x02] = GLFW_KEY_D; - _glfw.ns.publicKeys[0x0E] = GLFW_KEY_E; - _glfw.ns.publicKeys[0x03] = GLFW_KEY_F; - _glfw.ns.publicKeys[0x05] = GLFW_KEY_G; - _glfw.ns.publicKeys[0x04] = GLFW_KEY_H; - _glfw.ns.publicKeys[0x22] = GLFW_KEY_I; - _glfw.ns.publicKeys[0x26] = GLFW_KEY_J; - _glfw.ns.publicKeys[0x28] = GLFW_KEY_K; - _glfw.ns.publicKeys[0x25] = GLFW_KEY_L; - _glfw.ns.publicKeys[0x2E] = GLFW_KEY_M; - _glfw.ns.publicKeys[0x2D] = GLFW_KEY_N; - _glfw.ns.publicKeys[0x1F] = GLFW_KEY_O; - _glfw.ns.publicKeys[0x23] = GLFW_KEY_P; - _glfw.ns.publicKeys[0x0C] = GLFW_KEY_Q; - _glfw.ns.publicKeys[0x0F] = GLFW_KEY_R; - _glfw.ns.publicKeys[0x01] = GLFW_KEY_S; - _glfw.ns.publicKeys[0x11] = GLFW_KEY_T; - _glfw.ns.publicKeys[0x20] = GLFW_KEY_U; - _glfw.ns.publicKeys[0x09] = GLFW_KEY_V; - _glfw.ns.publicKeys[0x0D] = GLFW_KEY_W; - _glfw.ns.publicKeys[0x07] = GLFW_KEY_X; - _glfw.ns.publicKeys[0x10] = GLFW_KEY_Y; - _glfw.ns.publicKeys[0x06] = GLFW_KEY_Z; + _glfw.ns.keycodes[0x1D] = GLFW_KEY_0; + _glfw.ns.keycodes[0x12] = GLFW_KEY_1; + _glfw.ns.keycodes[0x13] = GLFW_KEY_2; + _glfw.ns.keycodes[0x14] = GLFW_KEY_3; + _glfw.ns.keycodes[0x15] = GLFW_KEY_4; + _glfw.ns.keycodes[0x17] = GLFW_KEY_5; + _glfw.ns.keycodes[0x16] = GLFW_KEY_6; + _glfw.ns.keycodes[0x1A] = GLFW_KEY_7; + _glfw.ns.keycodes[0x1C] = GLFW_KEY_8; + _glfw.ns.keycodes[0x19] = GLFW_KEY_9; + _glfw.ns.keycodes[0x00] = GLFW_KEY_A; + _glfw.ns.keycodes[0x0B] = GLFW_KEY_B; + _glfw.ns.keycodes[0x08] = GLFW_KEY_C; + _glfw.ns.keycodes[0x02] = GLFW_KEY_D; + _glfw.ns.keycodes[0x0E] = GLFW_KEY_E; + _glfw.ns.keycodes[0x03] = GLFW_KEY_F; + _glfw.ns.keycodes[0x05] = GLFW_KEY_G; + _glfw.ns.keycodes[0x04] = GLFW_KEY_H; + _glfw.ns.keycodes[0x22] = GLFW_KEY_I; + _glfw.ns.keycodes[0x26] = GLFW_KEY_J; + _glfw.ns.keycodes[0x28] = GLFW_KEY_K; + _glfw.ns.keycodes[0x25] = GLFW_KEY_L; + _glfw.ns.keycodes[0x2E] = GLFW_KEY_M; + _glfw.ns.keycodes[0x2D] = GLFW_KEY_N; + _glfw.ns.keycodes[0x1F] = GLFW_KEY_O; + _glfw.ns.keycodes[0x23] = GLFW_KEY_P; + _glfw.ns.keycodes[0x0C] = GLFW_KEY_Q; + _glfw.ns.keycodes[0x0F] = GLFW_KEY_R; + _glfw.ns.keycodes[0x01] = GLFW_KEY_S; + _glfw.ns.keycodes[0x11] = GLFW_KEY_T; + _glfw.ns.keycodes[0x20] = GLFW_KEY_U; + _glfw.ns.keycodes[0x09] = GLFW_KEY_V; + _glfw.ns.keycodes[0x0D] = GLFW_KEY_W; + _glfw.ns.keycodes[0x07] = GLFW_KEY_X; + _glfw.ns.keycodes[0x10] = GLFW_KEY_Y; + _glfw.ns.keycodes[0x06] = GLFW_KEY_Z; - _glfw.ns.publicKeys[0x27] = GLFW_KEY_APOSTROPHE; - _glfw.ns.publicKeys[0x2A] = GLFW_KEY_BACKSLASH; - _glfw.ns.publicKeys[0x2B] = GLFW_KEY_COMMA; - _glfw.ns.publicKeys[0x18] = GLFW_KEY_EQUAL; - _glfw.ns.publicKeys[0x32] = GLFW_KEY_GRAVE_ACCENT; - _glfw.ns.publicKeys[0x21] = GLFW_KEY_LEFT_BRACKET; - _glfw.ns.publicKeys[0x1B] = GLFW_KEY_MINUS; - _glfw.ns.publicKeys[0x2F] = GLFW_KEY_PERIOD; - _glfw.ns.publicKeys[0x1E] = GLFW_KEY_RIGHT_BRACKET; - _glfw.ns.publicKeys[0x29] = GLFW_KEY_SEMICOLON; - _glfw.ns.publicKeys[0x2C] = GLFW_KEY_SLASH; - _glfw.ns.publicKeys[0x0A] = GLFW_KEY_WORLD_1; + _glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE; + _glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH; + _glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA; + _glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL; + _glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT; + _glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET; + _glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS; + _glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD; + _glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET; + _glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON; + _glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH; + _glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1; - _glfw.ns.publicKeys[0x33] = GLFW_KEY_BACKSPACE; - _glfw.ns.publicKeys[0x39] = GLFW_KEY_CAPS_LOCK; - _glfw.ns.publicKeys[0x75] = GLFW_KEY_DELETE; - _glfw.ns.publicKeys[0x7D] = GLFW_KEY_DOWN; - _glfw.ns.publicKeys[0x77] = GLFW_KEY_END; - _glfw.ns.publicKeys[0x24] = GLFW_KEY_ENTER; - _glfw.ns.publicKeys[0x35] = GLFW_KEY_ESCAPE; - _glfw.ns.publicKeys[0x7A] = GLFW_KEY_F1; - _glfw.ns.publicKeys[0x78] = GLFW_KEY_F2; - _glfw.ns.publicKeys[0x63] = GLFW_KEY_F3; - _glfw.ns.publicKeys[0x76] = GLFW_KEY_F4; - _glfw.ns.publicKeys[0x60] = GLFW_KEY_F5; - _glfw.ns.publicKeys[0x61] = GLFW_KEY_F6; - _glfw.ns.publicKeys[0x62] = GLFW_KEY_F7; - _glfw.ns.publicKeys[0x64] = GLFW_KEY_F8; - _glfw.ns.publicKeys[0x65] = GLFW_KEY_F9; - _glfw.ns.publicKeys[0x6D] = GLFW_KEY_F10; - _glfw.ns.publicKeys[0x67] = GLFW_KEY_F11; - _glfw.ns.publicKeys[0x6F] = GLFW_KEY_F12; - _glfw.ns.publicKeys[0x69] = GLFW_KEY_F13; - _glfw.ns.publicKeys[0x6B] = GLFW_KEY_F14; - _glfw.ns.publicKeys[0x71] = GLFW_KEY_F15; - _glfw.ns.publicKeys[0x6A] = GLFW_KEY_F16; - _glfw.ns.publicKeys[0x40] = GLFW_KEY_F17; - _glfw.ns.publicKeys[0x4F] = GLFW_KEY_F18; - _glfw.ns.publicKeys[0x50] = GLFW_KEY_F19; - _glfw.ns.publicKeys[0x5A] = GLFW_KEY_F20; - _glfw.ns.publicKeys[0x73] = GLFW_KEY_HOME; - _glfw.ns.publicKeys[0x72] = GLFW_KEY_INSERT; - _glfw.ns.publicKeys[0x7B] = GLFW_KEY_LEFT; - _glfw.ns.publicKeys[0x3A] = GLFW_KEY_LEFT_ALT; - _glfw.ns.publicKeys[0x3B] = GLFW_KEY_LEFT_CONTROL; - _glfw.ns.publicKeys[0x38] = GLFW_KEY_LEFT_SHIFT; - _glfw.ns.publicKeys[0x37] = GLFW_KEY_LEFT_SUPER; - _glfw.ns.publicKeys[0x6E] = GLFW_KEY_MENU; - _glfw.ns.publicKeys[0x47] = GLFW_KEY_NUM_LOCK; - _glfw.ns.publicKeys[0x79] = GLFW_KEY_PAGE_DOWN; - _glfw.ns.publicKeys[0x74] = GLFW_KEY_PAGE_UP; - _glfw.ns.publicKeys[0x7C] = GLFW_KEY_RIGHT; - _glfw.ns.publicKeys[0x3D] = GLFW_KEY_RIGHT_ALT; - _glfw.ns.publicKeys[0x3E] = GLFW_KEY_RIGHT_CONTROL; - _glfw.ns.publicKeys[0x3C] = GLFW_KEY_RIGHT_SHIFT; - _glfw.ns.publicKeys[0x36] = GLFW_KEY_RIGHT_SUPER; - _glfw.ns.publicKeys[0x31] = GLFW_KEY_SPACE; - _glfw.ns.publicKeys[0x30] = GLFW_KEY_TAB; - _glfw.ns.publicKeys[0x7E] = GLFW_KEY_UP; + _glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE; + _glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK; + _glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE; + _glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN; + _glfw.ns.keycodes[0x77] = GLFW_KEY_END; + _glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER; + _glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE; + _glfw.ns.keycodes[0x7A] = GLFW_KEY_F1; + _glfw.ns.keycodes[0x78] = GLFW_KEY_F2; + _glfw.ns.keycodes[0x63] = GLFW_KEY_F3; + _glfw.ns.keycodes[0x76] = GLFW_KEY_F4; + _glfw.ns.keycodes[0x60] = GLFW_KEY_F5; + _glfw.ns.keycodes[0x61] = GLFW_KEY_F6; + _glfw.ns.keycodes[0x62] = GLFW_KEY_F7; + _glfw.ns.keycodes[0x64] = GLFW_KEY_F8; + _glfw.ns.keycodes[0x65] = GLFW_KEY_F9; + _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10; + _glfw.ns.keycodes[0x67] = GLFW_KEY_F11; + _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12; + _glfw.ns.keycodes[0x69] = GLFW_KEY_F13; + _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14; + _glfw.ns.keycodes[0x71] = GLFW_KEY_F15; + _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16; + _glfw.ns.keycodes[0x40] = GLFW_KEY_F17; + _glfw.ns.keycodes[0x4F] = GLFW_KEY_F18; + _glfw.ns.keycodes[0x50] = GLFW_KEY_F19; + _glfw.ns.keycodes[0x5A] = GLFW_KEY_F20; + _glfw.ns.keycodes[0x73] = GLFW_KEY_HOME; + _glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT; + _glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT; + _glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT; + _glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL; + _glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT; + _glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER; + _glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU; + _glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK; + _glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN; + _glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP; + _glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT; + _glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT; + _glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL; + _glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT; + _glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER; + _glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE; + _glfw.ns.keycodes[0x30] = GLFW_KEY_TAB; + _glfw.ns.keycodes[0x7E] = GLFW_KEY_UP; - _glfw.ns.publicKeys[0x52] = GLFW_KEY_KP_0; - _glfw.ns.publicKeys[0x53] = GLFW_KEY_KP_1; - _glfw.ns.publicKeys[0x54] = GLFW_KEY_KP_2; - _glfw.ns.publicKeys[0x55] = GLFW_KEY_KP_3; - _glfw.ns.publicKeys[0x56] = GLFW_KEY_KP_4; - _glfw.ns.publicKeys[0x57] = GLFW_KEY_KP_5; - _glfw.ns.publicKeys[0x58] = GLFW_KEY_KP_6; - _glfw.ns.publicKeys[0x59] = GLFW_KEY_KP_7; - _glfw.ns.publicKeys[0x5B] = GLFW_KEY_KP_8; - _glfw.ns.publicKeys[0x5C] = GLFW_KEY_KP_9; - _glfw.ns.publicKeys[0x45] = GLFW_KEY_KP_ADD; - _glfw.ns.publicKeys[0x41] = GLFW_KEY_KP_DECIMAL; - _glfw.ns.publicKeys[0x4B] = GLFW_KEY_KP_DIVIDE; - _glfw.ns.publicKeys[0x4C] = GLFW_KEY_KP_ENTER; - _glfw.ns.publicKeys[0x51] = GLFW_KEY_KP_EQUAL; - _glfw.ns.publicKeys[0x43] = GLFW_KEY_KP_MULTIPLY; - _glfw.ns.publicKeys[0x4E] = GLFW_KEY_KP_SUBTRACT; + _glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0; + _glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1; + _glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2; + _glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3; + _glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4; + _glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5; + _glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6; + _glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7; + _glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8; + _glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9; + _glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD; + _glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL; + _glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE; + _glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER; + _glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL; + _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY; + _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT; for (scancode = 0; scancode < 256; scancode++) { // Store the reverse translation for faster key name lookup - if (_glfw.ns.publicKeys[scancode] >= 0) - _glfw.ns.nativeKeys[_glfw.ns.publicKeys[scancode]] = scancode; + if (_glfw.ns.keycodes[scancode] >= 0) + _glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode; } } @@ -219,8 +215,9 @@ static GLFWbool updateUnicodeDataNS(void) return GLFW_FALSE; } - _glfw.ns.unicodeData = TISGetInputSourceProperty(_glfw.ns.inputSource, - kTISPropertyUnicodeKeyLayoutData); + _glfw.ns.unicodeData = + TISGetInputSourceProperty(_glfw.ns.inputSource, + kTISPropertyUnicodeKeyLayoutData); if (!_glfw.ns.unicodeData) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -236,7 +233,8 @@ static GLFWbool updateUnicodeDataNS(void) static GLFWbool initializeTIS(void) { // This works only because Cocoa has already loaded it properly - _glfw.ns.tis.bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); + _glfw.ns.tis.bundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); if (!_glfw.ns.tis.bundle) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -247,9 +245,6 @@ static GLFWbool initializeTIS(void) CFStringRef* kPropertyUnicodeKeyLayoutData = CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, CFSTR("kTISPropertyUnicodeKeyLayoutData")); - CFStringRef* kNotifySelectedKeyboardInputSourceChanged = - CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, - CFSTR("kTISNotifySelectedKeyboardInputSourceChanged")); _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource = CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); @@ -261,7 +256,6 @@ static GLFWbool initializeTIS(void) CFSTR("LMGetKbdType")); if (!kPropertyUnicodeKeyLayoutData || - !kNotifySelectedKeyboardInputSourceChanged || !TISCopyCurrentKeyboardLayoutInputSource || !TISGetInputSourceProperty || !LMGetKbdType) @@ -273,8 +267,6 @@ static GLFWbool initializeTIS(void) _glfw.ns.tis.kPropertyUnicodeKeyLayoutData = *kPropertyUnicodeKeyLayoutData; - _glfw.ns.tis.kNotifySelectedKeyboardInputSourceChanged = - *kNotifySelectedKeyboardInputSourceChanged; return updateUnicodeDataNS(); } @@ -300,17 +292,16 @@ int _glfwPlatformInit(void) { _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + if (_glfw.hints.init.ns.chdir) + changeToResourcesDirectory(); + _glfw.ns.listener = [[GLFWLayoutListener alloc] init]; - [[NSDistributedNotificationCenter defaultCenter] + [[NSNotificationCenter defaultCenter] addObserver:_glfw.ns.listener selector:@selector(selectedKeyboardInputSourceChanged:) - name:(__bridge NSString*)kTISNotifySelectedKeyboardInputSourceChanged + name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; -#if defined(_GLFW_USE_CHDIR) - changeToResourcesDirectory(); -#endif - createKeyTables(); _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); @@ -322,12 +313,10 @@ int _glfwPlatformInit(void) if (!initializeTIS()) return GLFW_FALSE; - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - _glfwInitTimerNS(); _glfwInitJoysticksNS(); + _glfwPollMonitorsNS(); return GLFW_TRUE; } @@ -355,24 +344,20 @@ void _glfwPlatformTerminate(void) if (_glfw.ns.listener) { - [[NSDistributedNotificationCenter defaultCenter] + [[NSNotificationCenter defaultCenter] removeObserver:_glfw.ns.listener - name:(__bridge NSString*)kTISNotifySelectedKeyboardInputSourceChanged + name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; - [[NSDistributedNotificationCenter defaultCenter] + [[NSNotificationCenter defaultCenter] removeObserver:_glfw.ns.listener]; [_glfw.ns.listener release]; _glfw.ns.listener = nil; } - [_glfw.ns.cursor release]; - _glfw.ns.cursor = nil; - free(_glfw.ns.clipboardString); _glfwTerminateNSGL(); _glfwTerminateJoysticksNS(); - _glfwTerminateThreadLocalStoragePOSIX(); [_glfw.ns.autoreleasePool release]; _glfw.ns.autoreleasePool = nil; @@ -381,15 +366,6 @@ void _glfwPlatformTerminate(void) const char* _glfwPlatformGetVersionString(void) { return _GLFW_VERSION_NUMBER " Cocoa NSGL" -#if defined(_GLFW_USE_CHDIR) - " chdir" -#endif -#if defined(_GLFW_USE_MENUBAR) - " menubar" -#endif -#if defined(_GLFW_USE_RETINA) - " retina" -#endif #if defined(_GLFW_BUILD_DLL) " dynamic" #endif diff --git a/raylib/external/glfw/src/cocoa_joystick.h b/raylib/external/glfw/src/cocoa_joystick.h index 3b80634..d18d032 100644 --- a/raylib/external/glfw/src/cocoa_joystick.h +++ b/raylib/external/glfw/src/cocoa_joystick.h @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 Cocoa - www.glfw.org +// GLFW 3.3 Cocoa - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -24,37 +24,27 @@ // //======================================================================== -#ifndef _glfw3_cocoa_joystick_h_ -#define _glfw3_cocoa_joystick_h_ - #include #include #include #include -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ - _GLFWjoystickNS ns_js[GLFW_JOYSTICK_LAST + 1] +#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE +#define _GLFW_PLATFORM_MAPPING_NAME "Mac OS X" // Cocoa-specific per-joystick data // typedef struct _GLFWjoystickNS { - GLFWbool present; - char name[256]; - - IOHIDDeviceRef deviceRef; - - CFMutableArrayRef axisElements; - CFMutableArrayRef buttonElements; - CFMutableArrayRef hatElements; - - float* axes; - unsigned char* buttons; + IOHIDDeviceRef device; + CFMutableArrayRef axes; + CFMutableArrayRef buttons; + CFMutableArrayRef hats; } _GLFWjoystickNS; void _glfwInitJoysticksNS(void); void _glfwTerminateJoysticksNS(void); -#endif // _glfw3_cocoa_joystick_h_ diff --git a/raylib/external/glfw/src/cocoa_joystick.m b/raylib/external/glfw/src/cocoa_joystick.m index 7423e3d..0831809 100644 --- a/raylib/external/glfw/src/cocoa_joystick.m +++ b/raylib/external/glfw/src/cocoa_joystick.m @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 Cocoa - www.glfw.org +// GLFW 3.3 Cocoa - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2016 Camilla Löwy // Copyright (c) 2012 Torsten Walluhn // // This software is provided 'as-is', without any express or implied @@ -42,42 +42,167 @@ // typedef struct _GLFWjoyelementNS { - IOHIDElementRef elementRef; - - long min; - long max; - - long minReport; - long maxReport; + IOHIDElementRef native; + uint32_t usage; + int index; + long minimum; + long maximum; } _GLFWjoyelementNS; -static void getElementsCFArrayHandler(const void* value, void* parameter); - -// Adds an element to the specified joystick +// Returns the value of the specified element of the specified joystick // -static void addJoystickElement(_GLFWjoystickNS* js, - IOHIDElementRef elementRef) +static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element) { - IOHIDElementType elementType; - long usagePage, usage; - CFMutableArrayRef elementsArray = NULL; + IOHIDValueRef valueRef; + long value = 0; - elementType = IOHIDElementGetType(elementRef); - usagePage = IOHIDElementGetUsagePage(elementRef); - usage = IOHIDElementGetUsage(elementRef); - - if ((elementType != kIOHIDElementTypeInput_Axis) && - (elementType != kIOHIDElementTypeInput_Button) && - (elementType != kIOHIDElementTypeInput_Misc)) + if (js->ns.device) { - return; + if (IOHIDDeviceGetValue(js->ns.device, + element->native, + &valueRef) == kIOReturnSuccess) + { + value = IOHIDValueGetIntegerValue(valueRef); + } } - switch (usagePage) + return value; +} + +// Comparison function for matching the SDL element order +// +static CFComparisonResult compareElements(const void* fp, + const void* sp, + void* user) +{ + const _GLFWjoyelementNS* fe = fp; + const _GLFWjoyelementNS* se = sp; + if (fe->usage < se->usage) + return kCFCompareLessThan; + if (fe->usage > se->usage) + return kCFCompareGreaterThan; + if (fe->index < se->index) + return kCFCompareLessThan; + if (fe->index > se->index) + return kCFCompareGreaterThan; + return kCFCompareEqualTo; +} + +// Removes the specified joystick +// +static void closeJoystick(_GLFWjoystick* js) +{ + int i; + + if (!js->present) + return; + + for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); + CFRelease(js->ns.axes); + + for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); + CFRelease(js->ns.buttons); + + for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) + free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); + CFRelease(js->ns.hats); + + _glfwFreeJoystick(js); + _glfwInputJoystick(js, GLFW_DISCONNECTED); +} + +// Callback for user-initiated joystick addition +// +static void matchCallback(void* context, + IOReturn result, + void* sender, + IOHIDDeviceRef device) +{ + int jid; + char name[256]; + char guid[33]; + CFIndex i; + CFTypeRef property; + uint32_t vendor = 0, product = 0, version = 0; + _GLFWjoystick* js; + CFMutableArrayRef axes, buttons, hats; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - case kHIDPage_GenericDesktop: + if (_glfw.joysticks[jid].ns.device == device) + return; + } + + axes = CFArrayCreateMutable(NULL, 0, NULL); + buttons = CFArrayCreateMutable(NULL, 0, NULL); + hats = CFArrayCreateMutable(NULL, 0, NULL); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (property) + { + CFStringGetCString(property, + name, + sizeof(name), + kCFStringEncodingUTF8); + } + else + strncpy(name, "Unknown", sizeof(name)); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)); + if (property) + CFNumberGetValue(property, kCFNumberSInt32Type, &vendor); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)); + if (property) + CFNumberGetValue(property, kCFNumberSInt32Type, &product); + + property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey)); + if (property) + CFNumberGetValue(property, kCFNumberSInt32Type, &version); + + // Generate a joystick GUID that matches the SDL 2.0.5+ one + if (vendor && product) + { + sprintf(guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000", + (uint8_t) vendor, (uint8_t) (vendor >> 8), + (uint8_t) product, (uint8_t) (product >> 8), + (uint8_t) version, (uint8_t) (version >> 8)); + } + else + { + sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", + name[0], name[1], name[2], name[3], + name[4], name[5], name[6], name[7], + name[8], name[9], name[10]); + } + + CFArrayRef elements = + IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); + + for (i = 0; i < CFArrayGetCount(elements); i++) + { + IOHIDElementRef native = (IOHIDElementRef) + CFArrayGetValueAtIndex(elements, i); + if (CFGetTypeID(native) != IOHIDElementGetTypeID()) + continue; + + const IOHIDElementType type = IOHIDElementGetType(native); + if ((type != kIOHIDElementTypeInput_Axis) && + (type != kIOHIDElementTypeInput_Button) && + (type != kIOHIDElementTypeInput_Misc)) + { + continue; + } + + CFMutableArrayRef target = NULL; + + const uint32_t usage = IOHIDElementGetUsage(native); + const uint32_t page = IOHIDElementGetUsagePage(native); + if (page == kHIDPage_GenericDesktop) { switch (usage) { @@ -90,241 +215,48 @@ static void addJoystickElement(_GLFWjoystickNS* js, case kHIDUsage_GD_Slider: case kHIDUsage_GD_Dial: case kHIDUsage_GD_Wheel: - elementsArray = js->axisElements; + target = axes; break; case kHIDUsage_GD_Hatswitch: - elementsArray = js->hatElements; + target = hats; break; } - - break; } + else if (page == kHIDPage_Button) + target = buttons; - case kHIDPage_Button: - elementsArray = js->buttonElements; - break; - default: - break; - } - - if (elementsArray) - { - _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); - - CFArrayAppendValue(elementsArray, element); - - element->elementRef = elementRef; - - element->minReport = IOHIDElementGetLogicalMin(elementRef); - element->maxReport = IOHIDElementGetLogicalMax(elementRef); - } -} - -// Adds an element to the specified joystick -// -static void getElementsCFArrayHandler(const void* value, void* parameter) -{ - if (CFGetTypeID(value) == IOHIDElementGetTypeID()) - { - addJoystickElement((_GLFWjoystickNS*) parameter, - (IOHIDElementRef) value); - } -} - -// Returns the value of the specified element of the specified joystick -// -static long getElementValue(_GLFWjoystickNS* js, _GLFWjoyelementNS* element) -{ - IOReturn result = kIOReturnSuccess; - IOHIDValueRef valueRef; - long value = 0; - - if (js && element && js->deviceRef) - { - result = IOHIDDeviceGetValue(js->deviceRef, - element->elementRef, - &valueRef); - - if (kIOReturnSuccess == result) + if (target) { - value = IOHIDValueGetIntegerValue(valueRef); - - // Record min and max for auto calibration - if (value < element->minReport) - element->minReport = value; - if (value > element->maxReport) - element->maxReport = value; + _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); + element->native = native; + element->usage = usage; + element->index = (int) CFArrayGetCount(target); + element->minimum = IOHIDElementGetLogicalMin(native); + element->maximum = IOHIDElementGetLogicalMax(native); + CFArrayAppendValue(target, element); } } - // Auto user scale - return value; -} + CFRelease(elements); -// Removes the specified joystick -// -static void removeJoystick(_GLFWjoystickNS* js) -{ - int i; + CFArraySortValues(axes, CFRangeMake(0, CFArrayGetCount(axes)), + compareElements, NULL); + CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), + compareElements, NULL); + CFArraySortValues(hats, CFRangeMake(0, CFArrayGetCount(hats)), + compareElements, NULL); - if (!js->present) - return; + js = _glfwAllocJoystick(name, guid, + (int) CFArrayGetCount(axes), + (int) CFArrayGetCount(buttons), + (int) CFArrayGetCount(hats)); - for (i = 0; i < CFArrayGetCount(js->axisElements); i++) - free((void*) CFArrayGetValueAtIndex(js->axisElements, i)); - CFArrayRemoveAllValues(js->axisElements); - CFRelease(js->axisElements); + js->ns.device = device; + js->ns.axes = axes; + js->ns.buttons = buttons; + js->ns.hats = hats; - for (i = 0; i < CFArrayGetCount(js->buttonElements); i++) - free((void*) CFArrayGetValueAtIndex(js->buttonElements, i)); - CFArrayRemoveAllValues(js->buttonElements); - CFRelease(js->buttonElements); - - for (i = 0; i < CFArrayGetCount(js->hatElements); i++) - free((void*) CFArrayGetValueAtIndex(js->hatElements, i)); - CFArrayRemoveAllValues(js->hatElements); - CFRelease(js->hatElements); - - free(js->axes); - free(js->buttons); - - memset(js, 0, sizeof(_GLFWjoystickNS)); - - _glfwInputJoystickChange(js - _glfw.ns_js, GLFW_DISCONNECTED); -} - -// Polls for joystick axis events and updates GLFW state -// -static GLFWbool pollJoystickAxisEvents(_GLFWjoystickNS* js) -{ - CFIndex i; - - if (!js->present) - return GLFW_FALSE; - - for (i = 0; i < CFArrayGetCount(js->axisElements); i++) - { - _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(js->axisElements, i); - - long value = getElementValue(js, axis); - long readScale = axis->maxReport - axis->minReport; - - if (readScale == 0) - js->axes[i] = value; - else - js->axes[i] = (2.f * (value - axis->minReport) / readScale) - 1.f; - } - - return GLFW_TRUE; -} - -// Polls for joystick button events and updates GLFW state -// -static GLFWbool pollJoystickButtonEvents(_GLFWjoystickNS* js) -{ - CFIndex i; - int buttonIndex = 0; - - if (!js->present) - return GLFW_FALSE; - - for (i = 0; i < CFArrayGetCount(js->buttonElements); i++) - { - _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(js->buttonElements, i); - - if (getElementValue(js, button)) - js->buttons[buttonIndex++] = GLFW_PRESS; - else - js->buttons[buttonIndex++] = GLFW_RELEASE; - } - - for (i = 0; i < CFArrayGetCount(js->hatElements); i++) - { - _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) - CFArrayGetValueAtIndex(js->hatElements, i); - - // Bit fields of button presses for each direction, including nil - const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; - - long j, value = getElementValue(js, hat); - if (value < 0 || value > 8) - value = 8; - - for (j = 0; j < 4; j++) - { - if (directions[value] & (1 << j)) - js->buttons[buttonIndex++] = GLFW_PRESS; - else - js->buttons[buttonIndex++] = GLFW_RELEASE; - } - } - - return GLFW_TRUE; -} - -// Callback for user-initiated joystick addition -// -static void matchCallback(void* context, - IOReturn result, - void* sender, - IOHIDDeviceRef deviceRef) -{ - _GLFWjoystickNS* js; - int joy; - - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - if (_glfw.ns_js[joy].present && _glfw.ns_js[joy].deviceRef == deviceRef) - return; - } - - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - if (!_glfw.ns_js[joy].present) - break; - } - - if (joy > GLFW_JOYSTICK_LAST) - return; - - js = _glfw.ns_js + joy; - js->present = GLFW_TRUE; - js->deviceRef = deviceRef; - - CFStringRef name = IOHIDDeviceGetProperty(deviceRef, - CFSTR(kIOHIDProductKey)); - if (name) - { - CFStringGetCString(name, - js->name, - sizeof(js->name), - kCFStringEncodingUTF8); - } - else - strncpy(js->name, "Unknown", sizeof(js->name)); - - js->axisElements = CFArrayCreateMutable(NULL, 0, NULL); - js->buttonElements = CFArrayCreateMutable(NULL, 0, NULL); - js->hatElements = CFArrayCreateMutable(NULL, 0, NULL); - - CFArrayRef arrayRef = IOHIDDeviceCopyMatchingElements(deviceRef, - NULL, - kIOHIDOptionsTypeNone); - CFRange range = { 0, CFArrayGetCount(arrayRef) }; - CFArrayApplyFunction(arrayRef, - range, - getElementsCFArrayHandler, - (void*) js); - - CFRelease(arrayRef); - - js->axes = calloc(CFArrayGetCount(js->axisElements), sizeof(float)); - js->buttons = calloc(CFArrayGetCount(js->buttonElements) + - CFArrayGetCount(js->hatElements) * 4, 1); - - _glfwInputJoystickChange(joy, GLFW_CONNECTED); + _glfwInputJoystick(js, GLFW_CONNECTED); } // Callback for user-initiated joystick removal @@ -332,60 +264,20 @@ static void matchCallback(void* context, static void removeCallback(void* context, IOReturn result, void* sender, - IOHIDDeviceRef deviceRef) + IOHIDDeviceRef device) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (_glfw.ns_js[joy].deviceRef == deviceRef) + if (_glfw.joysticks[jid].ns.device == device) { - removeJoystick(_glfw.ns_js + joy); + closeJoystick(_glfw.joysticks + jid); break; } } } -// Creates a dictionary to match against devices with the specified usage page -// and usage -// -static CFMutableDictionaryRef createMatchingDictionary(long usagePage, - long usage) -{ - CFMutableDictionaryRef result = - CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - if (result) - { - CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, - kCFNumberLongType, - &usagePage); - if (pageRef) - { - CFDictionarySetValue(result, - CFSTR(kIOHIDDeviceUsagePageKey), - pageRef); - CFRelease(pageRef); - - CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, - kCFNumberLongType, - &usage); - if (usageRef) - { - CFDictionarySetValue(result, - CFSTR(kIOHIDDeviceUsageKey), - usageRef); - CFRelease(usageRef); - } - } - } - - return result; -} - ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -395,56 +287,73 @@ static CFMutableDictionaryRef createMatchingDictionary(long usagePage, // void _glfwInitJoysticksNS(void) { - CFMutableArrayRef matchingCFArrayRef; + CFMutableArrayRef matching; + const long usages[] = + { + kHIDUsage_GD_Joystick, + kHIDUsage_GD_GamePad, + kHIDUsage_GD_MultiAxisController + }; _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, - 0, - &kCFTypeArrayCallBacks); - if (matchingCFArrayRef) + matching = CFArrayCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeArrayCallBacks); + if (!matching) { - CFDictionaryRef matchingCFDictRef = - createMatchingDictionary(kHIDPage_GenericDesktop, - kHIDUsage_GD_Joystick); - if (matchingCFDictRef) - { - CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); - CFRelease(matchingCFDictRef); - } - - matchingCFDictRef = createMatchingDictionary(kHIDPage_GenericDesktop, - kHIDUsage_GD_GamePad); - if (matchingCFDictRef) - { - CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); - CFRelease(matchingCFDictRef); - } - - matchingCFDictRef = - createMatchingDictionary(kHIDPage_GenericDesktop, - kHIDUsage_GD_MultiAxisController); - if (matchingCFDictRef) - { - CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); - CFRelease(matchingCFDictRef); - } - - IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, - matchingCFArrayRef); - CFRelease(matchingCFArrayRef); + _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array"); + return; } + for (int i = 0; i < sizeof(usages) / sizeof(long); i++) + { + const long page = kHIDPage_GenericDesktop; + + CFMutableDictionaryRef dict = + CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!dict) + continue; + + CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberLongType, + &page); + CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberLongType, + &usages[i]); + if (pageRef && usageRef) + { + CFDictionarySetValue(dict, + CFSTR(kIOHIDDeviceUsagePageKey), + pageRef); + CFDictionarySetValue(dict, + CFSTR(kIOHIDDeviceUsageKey), + usageRef); + CFArrayAppendValue(matching, dict); + } + + if (pageRef) + CFRelease(pageRef); + if (usageRef) + CFRelease(usageRef); + + CFRelease(dict); + } + + IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching); + CFRelease(matching); + IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager, &matchCallback, NULL); IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager, &removeCallback, NULL); - IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); - IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone); // Execute the run loop once in order to register any initially-attached @@ -456,13 +365,10 @@ void _glfwInitJoysticksNS(void) // void _glfwTerminateJoysticksNS(void) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - _GLFWjoystickNS* js = _glfw.ns_js + joy; - removeJoystick(js); - } + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + closeJoystick(_glfw.joysticks + jid); CFRelease(_glfw.ns.hidManager); _glfw.ns.hidManager = NULL; @@ -473,39 +379,84 @@ void _glfwTerminateJoysticksNS(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformJoystickPresent(int joy) +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { - _GLFWjoystickNS* js = _glfw.ns_js + joy; + if (mode & _GLFW_POLL_AXES) + { + CFIndex i; + + for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) + { + _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->ns.axes, i); + + const long raw = getElementValue(js, axis); + // Perform auto calibration + if (raw < axis->minimum) + axis->minimum = raw; + if (raw > axis->maximum) + axis->maximum = raw; + + const long delta = axis->maximum - axis->minimum; + if (delta == 0) + _glfwInputJoystickAxis(js, (int) i, 0.f); + else + { + const float value = (2.f * (raw - axis->minimum) / delta) - 1.f; + _glfwInputJoystickAxis(js, (int) i, value); + } + } + } + + if (mode & _GLFW_POLL_BUTTONS) + { + CFIndex i; + + for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) + { + _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->ns.buttons, i); + const char value = getElementValue(js, button) - button->minimum; + _glfwInputJoystickButton(js, (int) i, value); + } + + for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) + { + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + + _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->ns.hats, i); + long state = getElementValue(js, hat) - hat->minimum; + if (state < 0 || state > 8) + state = 8; + + _glfwInputJoystickHat(js, (int) i, states[state]); + } + } + return js->present; } -const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +void _glfwPlatformUpdateGamepadGUID(char* guid) { - _GLFWjoystickNS* js = _glfw.ns_js + joy; - if (!pollJoystickAxisEvents(js)) - return NULL; - - *count = (int) CFArrayGetCount(js->axisElements); - return js->axes; -} - -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) -{ - _GLFWjoystickNS* js = _glfw.ns_js + joy; - if (!pollJoystickButtonEvents(js)) - return NULL; - - *count = (int) CFArrayGetCount(js->buttonElements) + - (int) CFArrayGetCount(js->hatElements) * 4; - return js->buttons; -} - -const char* _glfwPlatformGetJoystickName(int joy) -{ - _GLFWjoystickNS* js = _glfw.ns_js + joy; - if (!js->present) - return NULL; - - return js->name; + if ((strncmp(guid + 4, "000000000000", 12) == 0) && + (strncmp(guid + 20, "000000000000", 12) == 0)) + { + char original[33]; + strcpy(original, guid); + sprintf(guid, "03000000%.4s0000%.4s000000000000", + original, original + 16); + } } diff --git a/raylib/external/glfw/src/cocoa_monitor.m b/raylib/external/glfw/src/cocoa_monitor.m index 9ac0a83..6108342 100644 --- a/raylib/external/glfw/src/cocoa_monitor.m +++ b/raylib/external/glfw/src/cocoa_monitor.m @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -36,39 +36,80 @@ #include -// Get the name of the specified display +// Get the name of the specified display, or NULL // static char* getDisplayName(CGDirectDisplayID displayID) { - char* name; - CFDictionaryRef info, names; - CFStringRef value; - CFIndex size; + io_iterator_t it; + io_service_t service; + CFDictionaryRef info; - // NOTE: This uses a deprecated function because Apple has - // (as of January 2015) not provided any alternative - info = IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), - kIODisplayOnlyPreferredName); - names = CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); - - if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), - (const void**) &value)) + if (IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceMatching("IODisplayConnect"), + &it) != 0) { // This may happen if a desktop Mac is running headless - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to retrieve display name"); - - CFRelease(info); - return strdup("Unknown"); + return NULL; } - size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), - kCFStringEncodingUTF8); - name = calloc(size + 1, 1); - CFStringGetCString(value, name, size, kCFStringEncodingUTF8); + while ((service = IOIteratorNext(it)) != 0) + { + info = IODisplayCreateInfoDictionary(service, + kIODisplayOnlyPreferredName); + + CFNumberRef vendorIDRef = + CFDictionaryGetValue(info, CFSTR(kDisplayVendorID)); + CFNumberRef productIDRef = + CFDictionaryGetValue(info, CFSTR(kDisplayProductID)); + if (!vendorIDRef || !productIDRef) + { + CFRelease(info); + continue; + } + + unsigned int vendorID, productID; + CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID); + CFNumberGetValue(productIDRef, kCFNumberIntType, &productID); + + if (CGDisplayVendorNumber(displayID) == vendorID && + CGDisplayModelNumber(displayID) == productID) + { + // Info dictionary is used and freed below + break; + } + + CFRelease(info); + } + + IOObjectRelease(it); + + if (!service) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find service port for display"); + return NULL; + } + + CFDictionaryRef names = + CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); + + CFStringRef nameRef; + + if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), + (const void**) &nameRef)) + { + // This may happen if a desktop Mac is running headless + CFRelease(info); + return NULL; + } + + const CFIndex size = + CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), + kCFStringEncodingUTF8); + char* name = calloc(size + 1, 1); + CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8); CFRelease(info); - return name; } @@ -77,15 +118,15 @@ static char* getDisplayName(CGDirectDisplayID displayID) static GLFWbool modeIsGood(CGDisplayModeRef mode) { uint32_t flags = CGDisplayModeGetIOFlags(mode); + if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag)) return GLFW_FALSE; - if (flags & kDisplayModeInterlacedFlag) return GLFW_FALSE; - if (flags & kDisplayModeStretchedFlag) return GLFW_FALSE; +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) && CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0)) @@ -95,6 +136,7 @@ static GLFWbool modeIsGood(CGDisplayModeRef mode) } CFRelease(format); +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ return GLFW_TRUE; } @@ -115,8 +157,8 @@ static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, result.refreshRate = (int) (time.timeScale / (double) time.timeValue); } +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); - if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0) { result.redBits = 5; @@ -124,13 +166,16 @@ static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, result.blueBits = 5; } else +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ { result.redBits = 8; result.greenBits = 8; result.blueBits = 8; } +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFRelease(format); +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ return result; } @@ -141,7 +186,13 @@ static CGDisplayFadeReservationToken beginFadeReservation(void) CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess) - CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); + { + CGDisplayFade(token, 0.3, + kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, + 0.0, 0.0, 0.0, + TRUE); + } return token; } @@ -152,7 +203,11 @@ static void endFadeReservation(CGDisplayFadeReservationToken token) { if (token != kCGDisplayFadeReservationInvalidToken) { - CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGDisplayFade(token, 0.5, + kCGDisplayBlendSolidColor, + kCGDisplayBlendNormal, + 0.0, 0.0, 0.0, + FALSE); CGReleaseDisplayFadeReservation(token); } } @@ -162,6 +217,74 @@ static void endFadeReservation(CGDisplayFadeReservationToken token) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsNS(void) +{ + uint32_t i, j, displayCount, disconnectedCount; + CGDirectDisplayID* displays; + _GLFWmonitor** disconnected = NULL; + + CGGetOnlineDisplayList(0, NULL, &displayCount); + displays = calloc(displayCount, sizeof(CGDirectDisplayID)); + CGGetOnlineDisplayList(displayCount, displays, &displayCount); + + for (i = 0; i < _glfw.monitorCount; i++) + _glfw.monitors[i]->ns.screen = nil; + + disconnectedCount = _glfw.monitorCount; + if (disconnectedCount) + { + disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + memcpy(disconnected, + _glfw.monitors, + _glfw.monitorCount * sizeof(_GLFWmonitor*)); + } + + for (i = 0; i < displayCount; i++) + { + _GLFWmonitor* monitor; + const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]); + + if (CGDisplayIsAsleep(displays[i])) + continue; + + for (j = 0; j < disconnectedCount; j++) + { + // HACK: Compare unit numbers instead of display IDs to work around + // display replacement on machines with automatic graphics + // switching + if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber) + { + disconnected[j] = NULL; + break; + } + } + + const CGSize size = CGDisplayScreenSize(displays[i]); + char* name = getDisplayName(displays[i]); + if (!name) + name = strdup("Unknown"); + + monitor = _glfwAllocMonitor(name, size.width, size.height); + monitor->ns.displayID = displays[i]; + monitor->ns.unitNumber = unitNumber; + + free(name); + + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); + } + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); + } + + free(disconnected); + free(displays); +} + // Change the current video mode // GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) @@ -241,53 +364,6 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) -{ - uint32_t i, found = 0, displayCount; - _GLFWmonitor** monitors; - CGDirectDisplayID* displays; - - *count = 0; - - CGGetOnlineDisplayList(0, NULL, &displayCount); - displays = calloc(displayCount, sizeof(CGDirectDisplayID)); - monitors = calloc(displayCount, sizeof(_GLFWmonitor*)); - - CGGetOnlineDisplayList(displayCount, displays, &displayCount); - - for (i = 0; i < displayCount; i++) - { - _GLFWmonitor* monitor; - - if (CGDisplayIsAsleep(displays[i])) - continue; - - const CGSize size = CGDisplayScreenSize(displays[i]); - char* name = getDisplayName(displays[i]); - - monitor = _glfwAllocMonitor(name, size.width, size.height); - monitor->ns.displayID = displays[i]; - monitor->ns.unitNumber = CGDisplayUnitNumber(displays[i]); - - free(name); - - found++; - monitors[found - 1] = monitor; - } - - free(displays); - - *count = found; - return monitors; -} - -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - // HACK: Compare unit numbers instead of display IDs to work around display - // replacement on machines with automatic graphics switching - return first->ns.unitNumber == second->ns.unitNumber; -} - void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { const CGRect bounds = CGDisplayBounds(monitor->ns.displayID); @@ -298,6 +374,48 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = (int) bounds.origin.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (!monitor->ns.screen) + { + NSUInteger i; + NSArray* screens = [NSScreen screens]; + + for (i = 0; i < [screens count]; i++) + { + NSScreen* screen = [screens objectAtIndex:i]; + NSNumber* displayID = + [[screen deviceDescription] objectForKey:@"NSScreenNumber"]; + + // HACK: Compare unit numbers instead of display IDs to work around + // display replacement on machines with automatic graphics + // switching + if (monitor->ns.unitNumber == + CGDisplayUnitNumber([displayID unsignedIntValue])) + { + monitor->ns.screen = screen; + break; + } + } + + if (i == [screens count]) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find a screen for monitor"); + return; + } + } + + const NSRect points = [monitor->ns.screen frame]; + const NSRect pixels = [monitor->ns.screen convertRectToBacking:points]; + + if (xscale) + *xscale = (float) (pixels.size.width / points.size.width); + if (yscale) + *yscale = (float) (pixels.size.height / points.size.height); +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { CFArrayRef modes; diff --git a/raylib/external/glfw/src/cocoa_platform.h b/raylib/external/glfw/src/cocoa_platform.h index 9221427..d5cc237 100644 --- a/raylib/external/glfw/src/cocoa_platform.h +++ b/raylib/external/glfw/src/cocoa_platform.h @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -24,9 +24,6 @@ // //======================================================================== -#ifndef _glfw3_cocoa_platform_h_ -#define _glfw3_cocoa_platform_h_ - #include #include @@ -39,26 +36,39 @@ typedef void* id; #endif -#include "posix_tls.h" +typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; + +typedef struct VkMacOSSurfaceCreateInfoMVK +{ + VkStructureType sType; + const void* pNext; + VkMacOSSurfaceCreateFlagsMVK flags; + const void* pView; +} VkMacOSSurfaceCreateInfoMVK; + +typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*); + +#include "posix_thread.h" #include "cocoa_joystick.h" #include "nsgl_context.h" +#include "egl_context.h" +#include "osmesa_context.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.view) +#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY + #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns -#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeNS ns_time +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns -#define _GLFW_EGL_CONTEXT_STATE -#define _GLFW_EGL_LIBRARY_CONTEXT_STATE - // HIToolbox.framework pointer typedefs #define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData -#define kTISNotifySelectedKeyboardInputSourceChanged _glfw.ns.tis.kNotifySelectedKeyboardInputSourceChanged typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void); #define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef); @@ -74,6 +84,13 @@ typedef struct _GLFWwindowNS id object; id delegate; id view; + id layer; + + GLFWbool maximized; + + // Cached window and framebuffer sizes used to filter out duplicate events + int width, height; + int fbWidth, fbHeight; // The total sum of the distances the cursor has been warped // since the last cursor motion event was processed @@ -89,16 +106,17 @@ typedef struct _GLFWlibraryNS CGEventSourceRef eventSource; id delegate; id autoreleasePool; - id cursor; + GLFWbool cursorHidden; TISInputSourceRef inputSource; IOHIDManagerRef hidManager; id unicodeData; id listener; char keyName[64]; - short int publicKeys[256]; - short int nativeKeys[GLFW_KEY_LAST + 1]; + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; char* clipboardString; + CGPoint cascadePoint; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active @@ -110,7 +128,6 @@ typedef struct _GLFWlibraryNS PFN_TISGetInputSourceProperty GetInputSourceProperty; PFN_LMGetKbdType GetKbdType; CFStringRef kPropertyUnicodeKeyLayoutData; - CFStringRef kNotifySelectedKeyboardInputSourceChanged; } tis; } _GLFWlibraryNS; @@ -122,6 +139,7 @@ typedef struct _GLFWmonitorNS CGDirectDisplayID displayID; CGDisplayModeRef previousMode; uint32_t unitNumber; + id screen; } _GLFWmonitorNS; @@ -135,16 +153,16 @@ typedef struct _GLFWcursorNS // Cocoa-specific global timer data // -typedef struct _GLFWtimeNS +typedef struct _GLFWtimerNS { uint64_t frequency; -} _GLFWtimeNS; +} _GLFWtimerNS; void _glfwInitTimerNS(void); +void _glfwPollMonitorsNS(void); GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); -#endif // _glfw3_cocoa_platform_h_ diff --git a/raylib/external/glfw/src/cocoa_time.c b/raylib/external/glfw/src/cocoa_time.c index dacfed0..3b27035 100644 --- a/raylib/external/glfw/src/cocoa_time.c +++ b/raylib/external/glfw/src/cocoa_time.c @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -40,7 +40,7 @@ void _glfwInitTimerNS(void) mach_timebase_info_data_t info; mach_timebase_info(&info); - _glfw.ns_time.frequency = (info.denom * 1e9) / info.numer; + _glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer; } @@ -55,6 +55,6 @@ uint64_t _glfwPlatformGetTimerValue(void) uint64_t _glfwPlatformGetTimerFrequency(void) { - return _glfw.ns_time.frequency; + return _glfw.timer.ns.frequency; } diff --git a/raylib/external/glfw/src/cocoa_window.m b/raylib/external/glfw/src/cocoa_window.m index b002e99..6824634 100644 --- a/raylib/external/glfw/src/cocoa_window.m +++ b/raylib/external/glfw/src/cocoa_window.m @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -32,29 +32,23 @@ // Needed for _NSGetProgname #include +// HACK: The 10.12 SDK adds new symbols and immediately deprecates the old ones +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + #define NSWindowStyleMaskBorderless NSBorderlessWindowMask + #define NSWindowStyleMaskClosable NSClosableWindowMask + #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask + #define NSWindowStyleMaskResizable NSResizableWindowMask + #define NSWindowStyleMaskTitled NSTitledWindowMask + #define NSEventModifierFlagCommand NSCommandKeyMask + #define NSEventModifierFlagControl NSControlKeyMask + #define NSEventModifierFlagOption NSAlternateKeyMask + #define NSEventModifierFlagShift NSShiftKeyMask + #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask + #define NSEventMaskAny NSAnyEventMask + #define NSEventTypeApplicationDefined NSApplicationDefined + #define NSEventTypeKeyUp NSKeyUp +#endif -// Returns the specified standard cursor -// -static NSCursor* getStandardCursor(int shape) -{ - switch (shape) - { - case GLFW_ARROW_CURSOR: - return [NSCursor arrowCursor]; - case GLFW_IBEAM_CURSOR: - return [NSCursor IBeamCursor]; - case GLFW_CROSSHAIR_CURSOR: - return [NSCursor crosshairCursor]; - case GLFW_HAND_CURSOR: - return [NSCursor pointingHandCursor]; - case GLFW_HRESIZE_CURSOR: - return [NSCursor resizeLeftRightCursor]; - case GLFW_VRESIZE_CURSOR: - return [NSCursor resizeUpDownCursor]; - } - - return nil; -} // Returns the style mask corresponding to the window settings // @@ -63,14 +57,15 @@ static NSUInteger getStyleMask(_GLFWwindow* window) NSUInteger styleMask = 0; if (window->monitor || !window->decorated) - styleMask |= NSBorderlessWindowMask; + styleMask |= NSWindowStyleMaskBorderless; else { - styleMask |= NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask; + styleMask |= NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable; if (window->resizable) - styleMask |= NSResizableWindowMask; + styleMask |= NSWindowStyleMaskResizable; } return styleMask; @@ -93,19 +88,43 @@ static GLFWbool cursorInClientArea(_GLFWwindow* window) return [window->ns.view mouse:pos inRect:[window->ns.view frame]]; } +// Hides the cursor if not already hidden +// +static void hideCursor(_GLFWwindow* window) +{ + if (!_glfw.ns.cursorHidden) + { + [NSCursor hide]; + _glfw.ns.cursorHidden = GLFW_TRUE; + } +} + +// Shows the cursor if not already shown +// +static void showCursor(_GLFWwindow* window) +{ + if (_glfw.ns.cursorHidden) + { + [NSCursor unhide]; + _glfw.ns.cursorHidden = GLFW_FALSE; + } +} + // Updates the cursor image according to its cursor mode // static void updateCursorImage(_GLFWwindow* window) { if (window->cursorMode == GLFW_CURSOR_NORMAL) { + showCursor(window); + if (window->cursor) [(NSCursor*) window->cursor->ns.object set]; else [[NSCursor arrowCursor] set]; } else - [(NSCursor*) _glfw.ns.cursor set]; + hideCursor(window); } // Transforms the specified y-coordinate between the CG display and NS screen @@ -129,7 +148,7 @@ static GLFWbool acquireMonitor(_GLFWwindow* window) [window->ns.object setFrame:frame display:YES]; - _glfwInputMonitorWindowChange(window->monitor, window); + _glfwInputMonitorWindow(window->monitor, window); return status; } @@ -140,36 +159,36 @@ static void releaseMonitor(_GLFWwindow* window) if (window->monitor->window != window) return; - _glfwInputMonitorWindowChange(window->monitor, NULL); + _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeNS(window->monitor); } -// Translates OS X key modifiers into GLFW ones +// Translates macOS key modifiers into GLFW ones // static int translateFlags(NSUInteger flags) { int mods = 0; - if (flags & NSShiftKeyMask) + if (flags & NSEventModifierFlagShift) mods |= GLFW_MOD_SHIFT; - if (flags & NSControlKeyMask) + if (flags & NSEventModifierFlagControl) mods |= GLFW_MOD_CONTROL; - if (flags & NSAlternateKeyMask) + if (flags & NSEventModifierFlagOption) mods |= GLFW_MOD_ALT; - if (flags & NSCommandKeyMask) + if (flags & NSEventModifierFlagCommand) mods |= GLFW_MOD_SUPER; return mods; } -// Translates a OS X keycode to a GLFW keycode +// Translates a macOS keycode to a GLFW keycode // static int translateKey(unsigned int key) { - if (key >= sizeof(_glfw.ns.publicKeys) / sizeof(_glfw.ns.publicKeys[0])) + if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0])) return GLFW_KEY_UNKNOWN; - return _glfw.ns.publicKeys[key]; + return _glfw.ns.keycodes[key]; } // Translate a GLFW keycode to a Cocoa modifier flag @@ -180,16 +199,16 @@ static NSUInteger translateKeyToModifierFlag(int key) { case GLFW_KEY_LEFT_SHIFT: case GLFW_KEY_RIGHT_SHIFT: - return NSShiftKeyMask; + return NSEventModifierFlagShift; case GLFW_KEY_LEFT_CONTROL: case GLFW_KEY_RIGHT_CONTROL: - return NSControlKeyMask; + return NSEventModifierFlagControl; case GLFW_KEY_LEFT_ALT: case GLFW_KEY_RIGHT_ALT: - return NSAlternateKeyMask; + return NSEventModifierFlagOption; case GLFW_KEY_LEFT_SUPER: case GLFW_KEY_RIGHT_SUPER: - return NSCommandKeyMask; + return NSEventModifierFlagCommand; } return 0; @@ -209,13 +228,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; _GLFWwindow* window; } -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; @end @implementation GLFWWindowDelegate -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow { self = [super init]; if (self != nil) @@ -238,11 +257,31 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; if (_glfw.ns.disabledCursorWindow == window) centerCursor(window); + const int maximized = [window->ns.object isZoomed]; + if (window->ns.maximized != maximized) + { + window->ns.maximized = maximized; + _glfwInputWindowMaximize(window, maximized); + } + const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; - _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); - _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height); + if (fbRect.size.width != window->ns.fbWidth || + fbRect.size.height != window->ns.fbHeight) + { + window->ns.fbWidth = fbRect.size.width; + window->ns.fbHeight = fbRect.size.height; + _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); + } + + if (contentRect.size.width != window->ns.width || + contentRect.size.height != window->ns.height) + { + window->ns.width = contentRect.size.width; + window->ns.height = contentRect.size.height; + _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height); + } } - (void)windowDidMove:(NSNotification *)notification @@ -315,7 +354,15 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)applicationDidChangeScreenParameters:(NSNotification *) notification { - _glfwInputMonitorChange(); + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + { + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + } + + _glfwPollMonitorsNS(); } - (void)applicationDidFinishLaunching:(NSNotification *)notification @@ -347,27 +394,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; NSMutableAttributedString* markedText; } -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; @end @implementation GLFWContentView -+ (void)initialize -{ - if (self == [GLFWContentView class]) - { - if (_glfw.ns.cursor == nil) - { - NSImage* data = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; - _glfw.ns.cursor = [[NSCursor alloc] initWithImage:data - hotSpot:NSZeroPoint]; - [data release]; - } - } -} - -- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow +- (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow { self = [super init]; if (self != nil) @@ -393,7 +426,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)isOpaque { - return YES; + return [window->ns.object isOpaque]; } - (BOOL)canBecomeKeyView @@ -406,6 +439,19 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; return YES; } +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (id)makeBackingLayer +{ + if (window->ns.layer) + return window->ns.layer; + + return [super makeBackingLayer]; +} + - (void)cursorUpdate:(NSEvent *)event { updateCursorImage(window); @@ -499,11 +545,17 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)mouseExited:(NSEvent *)event { + if (window->cursorMode == GLFW_CURSOR_HIDDEN) + showCursor(window); + _glfwInputCursorEnter(window, GLFW_FALSE); } - (void)mouseEntered:(NSEvent *)event { + if (window->cursorMode == GLFW_CURSOR_HIDDEN) + hideCursor(window); + _glfwInputCursorEnter(window, GLFW_TRUE); } @@ -512,7 +564,13 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; - _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); + if (fbRect.size.width != window->ns.fbWidth || + fbRect.size.height != window->ns.fbHeight) + { + window->ns.fbWidth = fbRect.size.width; + window->ns.fbHeight = fbRect.size.height; + _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); + } } - (void)drawRect:(NSRect)rect @@ -558,7 +616,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; { int action; const unsigned int modifierFlags = - [event modifierFlags] & NSDeviceIndependentModifierFlagsMask; + [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; const int key = translateKey([event keyCode]); const int mods = translateFlags(modifierFlags); const NSUInteger keyFlag = translateKeyToModifierFlag(key); @@ -628,17 +686,17 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; [sender draggingLocation].x, contentRect.size.height - [sender draggingLocation].y); - const int count = [files count]; + const NSUInteger count = [files count]; if (count) { NSEnumerator* e = [files objectEnumerator]; char** paths = calloc(count, sizeof(char*)); - int i; + NSUInteger i; for (i = 0; i < count; i++) paths[i] = strdup([[e nextObject] UTF8String]); - _glfwInputDrop(window, count, (const char**) paths); + _glfwInputDrop(window, (int) count, (const char**) paths); for (i = 0; i < count; i++) free(paths[i]); @@ -675,10 +733,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { + [markedText release]; if ([string isKindOfClass:[NSAttributedString class]]) - [markedText initWithAttributedString:string]; + markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string]; else - [markedText initWithString:string]; + markedText = [[NSMutableAttributedString alloc] initWithString:string]; } - (void)unmarkText @@ -753,7 +812,12 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (BOOL)canBecomeKeyWindow { - // Required for NSBorderlessWindowMask windows + // Required for NSWindowStyleMaskBorderless windows + return YES; +} + +- (BOOL)canBecomeMainWindow +{ return YES; } @@ -765,6 +829,10 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; //------------------------------------------------------------------------ @interface GLFWApplication : NSApplication +{ + NSArray* nibObjects; +} + @end @implementation GLFWApplication @@ -774,8 +842,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; // down the command key don't get sent to the key window. - (void)sendEvent:(NSEvent *)event { - if ([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask)) + if ([event type] == NSEventTypeKeyUp && + ([event modifierFlags] & NSEventModifierFlagCommand)) + { [[self keyWindow] sendEvent:event]; + } else [super sendEvent:event]; } @@ -786,53 +857,58 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)doNothing:(id)object { } + +- (void)loadMainMenu +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 100800 + [[NSBundle mainBundle] loadNibNamed:@"MainMenu" + owner:NSApp + topLevelObjects:&nibObjects]; +#else + [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp]; +#endif +} @end -#if defined(_GLFW_USE_MENUBAR) - -// Try to figure out what the calling application is called +// Set up the menu bar (manually) +// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that +// could go away at any moment, lots of stuff that really should be +// localize(d|able), etc. Add a nib to save us this horror. // -static NSString* findAppName(void) +static void createMenuBar(void) { size_t i; - NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary]; - - // Keys to search for as potential application names - NSString* GLFWNameKeys[] = + NSString* appName = nil; + NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary]; + NSString* nameKeys[] = { @"CFBundleDisplayName", @"CFBundleName", @"CFBundleExecutable", }; - for (i = 0; i < sizeof(GLFWNameKeys) / sizeof(GLFWNameKeys[0]); i++) + // Try to figure out what the calling application is called + + for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++) { - id name = [infoDictionary objectForKey:GLFWNameKeys[i]]; + id name = [bundleInfo objectForKey:nameKeys[i]]; if (name && [name isKindOfClass:[NSString class]] && ![name isEqualToString:@""]) { - return name; + appName = name; + break; } } - char** progname = _NSGetProgname(); - if (progname && *progname) - return [NSString stringWithUTF8String:*progname]; - - // Really shouldn't get here - return @"GLFW Application"; -} - -// Set up the menu bar (manually) -// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that -// could go away at any moment, lots of stuff that really should be -// localize(d|able), etc. Loading a nib would save us this horror, but that -// doesn't seem like a good thing to require of GLFW users. -// -static void createMenuBar(void) -{ - NSString* appName = findAppName(); + if (!appName) + { + char** progname = _NSGetProgname(); + if (progname && *progname) + appName = [NSString stringWithUTF8String:*progname]; + else + appName = @"GLFW Application"; + } NSMenu* bar = [[NSMenu alloc] init]; [NSApp setMainMenu:bar]; @@ -859,7 +935,7 @@ static void createMenuBar(void) [[appMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"] - setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask]; + setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand]; [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; @@ -891,7 +967,7 @@ static void createMenuBar(void) [[windowMenu addItemWithTitle:@"Enter Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] - setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask]; + setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; // Prior to Snow Leopard, we need to use this oddly-named semi-private API // to get the application menu working properly. @@ -899,8 +975,6 @@ static void createMenuBar(void) [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; } -#endif /* _GLFW_USE_MENUBAR */ - // Initialize the Cocoa Application Kit // static GLFWbool initializeAppKit(void) @@ -916,15 +990,20 @@ static GLFWbool initializeAppKit(void) toTarget:NSApp withObject:nil]; - // In case we are unbundled, make us a proper UI application - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + if (_glfw.hints.init.ns.menubar) + { + // In case we are unbundled, make us a proper UI application + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; -#if defined(_GLFW_USE_MENUBAR) - // Menu bar setup must go between sharedApplication above and - // finishLaunching below, in order to properly emulate the behavior - // of NSApplicationMain - createMenuBar(); -#endif + // Menu bar setup must go between sharedApplication above and + // finishLaunching below, in order to properly emulate the behavior + // of NSApplicationMain + + if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) + [NSApp loadMainMenu]; + else + createMenuBar(); + } // There can only be one application delegate, but we allocate it the // first time a window is created to keep all window code in this file @@ -939,13 +1018,21 @@ static GLFWbool initializeAppKit(void) [NSApp setDelegate:_glfw.ns.delegate]; [NSApp run]; + // Press and Hold prevents some keys from emitting repeated characters + NSDictionary* defaults = + [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], + @"ApplePressAndHoldEnabled", + nil]; + [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; + return GLFW_TRUE; } // Create the Cocoa window // static GLFWbool createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) { window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; if (window->ns.delegate == nil) @@ -987,9 +1074,17 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, else { [window->ns.object center]; + _glfw.ns.cascadePoint = + NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint: + NSPointFromCGPoint(_glfw.ns.cascadePoint)]); if (wndconfig->resizable) - [window->ns.object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + { + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenPrimary | + NSWindowCollectionBehaviorManaged; + [window->ns.object setCollectionBehavior:behavior]; + } if (wndconfig->floating) [window->ns.object setLevel:NSFloatingWindowLevel]; @@ -998,19 +1093,30 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, [window->ns.object zoom:nil]; } + if (wndconfig->ns.frame) + [window->ns.object setFrameAutosaveName:[NSString stringWithUTF8String:wndconfig->title]]; + window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window]; -#if defined(_GLFW_USE_RETINA) - [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; -#endif /*_GLFW_USE_RETINA*/ + if (wndconfig->ns.retina) + [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; + if (fbconfig->transparent) + { + [window->ns.object setOpaque:NO]; + [window->ns.object setBackgroundColor:[NSColor clearColor]]; + } + + [window->ns.object setContentView:window->ns.view]; [window->ns.object makeFirstResponder:window->ns.view]; [window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]]; [window->ns.object setDelegate:window->ns.delegate]; [window->ns.object setAcceptsMouseMovedEvents:YES]; - [window->ns.object setContentView:window->ns.view]; [window->ns.object setRestorable:NO]; + _glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height); + _glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight); + return GLFW_TRUE; } @@ -1027,7 +1133,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!initializeAppKit()) return GLFW_FALSE; - if (!createNativeWindow(window, wndconfig)) + if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) @@ -1039,10 +1145,19 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { - _glfwInputError(GLFW_API_UNAVAILABLE, "Cocoa: EGL not available"); - return GLFW_FALSE; + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; } } @@ -1053,7 +1168,8 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!acquireMonitor(window)) return GLFW_FALSE; - centerCursor(window); + if (wndconfig->centerCursor) + centerCursor(window); } return GLFW_TRUE; @@ -1088,7 +1204,11 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title) { - [window->ns.object setTitle:[NSString stringWithUTF8String:title]]; + NSString* string = [NSString stringWithUTF8String:title]; + [window->ns.object setTitle:string]; + // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it + // if the window lacks NSWindowStyleMaskTitled + [window->ns.object setMiniwindowTitle:string]; } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, @@ -1155,7 +1275,7 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) - [window->ns.object setContentAspectRatio:NSMakeSize(0, 0)]; + [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; else [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)]; } @@ -1190,6 +1310,18 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, *bottom = contentRect.origin.y - frameRect.origin.y; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + const NSRect points = [window->ns.view frame]; + const NSRect pixels = [window->ns.view convertRectToBacking:points]; + + if (xscale) + *xscale = (float) (pixels.size.width / points.size.width); + if (yscale) + *yscale = (float) (pixels.size.height / points.size.height); +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { [window->ns.object miniaturize:nil]; @@ -1219,6 +1351,11 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) [window->ns.object orderOut:nil]; } +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + [NSApp requestUserAttention:NSInformationalRequest]; +} + void _glfwPlatformFocusWindow(_GLFWwindow* window) { // Make us the active application @@ -1260,34 +1397,16 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) releaseMonitor(window); - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); + + // HACK: Allow the state cached in Cocoa to catch up to reality + // TODO: Solve this in a less terrible way + _glfwPlatformPollEvents(); const NSUInteger styleMask = getStyleMask(window); [window->ns.object setStyleMask:styleMask]; [window->ns.object makeFirstResponder:window->ns.view]; - NSRect contentRect; - - if (monitor) - { - GLFWvidmode mode; - - _glfwPlatformGetVideoMode(window->monitor, &mode); - _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); - - contentRect = NSMakeRect(xpos, transformY(ypos + mode.height), - mode.width, mode.height); - } - else - { - contentRect = NSMakeRect(xpos, transformY(ypos + height), - width, height); - } - - NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect - styleMask:styleMask]; - [window->ns.object setFrame:frameRect display:YES]; - if (monitor) { [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; @@ -1297,6 +1416,12 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, } else { + NSRect contentRect = NSMakeRect(xpos, transformY(ypos + height), + width, height); + NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect + styleMask:styleMask]; + [window->ns.object setFrame:frameRect display:YES]; + if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) { @@ -1324,6 +1449,9 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, [window->ns.object setLevel:NSNormalWindowLevel]; [window->ns.object setHasShadow:YES]; + // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window + // title property but the miniwindow title property is unaffected + [window->ns.object setTitle:[window->ns.object miniwindowTitle]]; } } @@ -1347,11 +1475,45 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return [window->ns.object isZoomed]; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return ![window->ns.object isOpaque] && ![window->ns.view isOpaque]; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + [window->ns.object setStyleMask:getStyleMask(window)]; +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + [window->ns.object setStyleMask:getStyleMask(window)]; + [window->ns.object makeFirstResponder:window->ns.view]; +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + if (enabled) + [window->ns.object setLevel:NSFloatingWindowLevel]; + else + [window->ns.object setLevel:NSNormalWindowLevel]; +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + return (float) [window->ns.object alphaValue]; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ + [window->ns.object setAlphaValue:opacity]; +} + void _glfwPlatformPollEvents(void) { for (;;) { - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1370,7 +1532,7 @@ void _glfwPlatformWaitEvents(void) // I wanted to pass NO to dequeue:, and rely on PollEvents to // dequeue and send. For reasons not at all clear to me, passing // NO to dequeue: causes this method never to return. - NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1382,7 +1544,7 @@ void _glfwPlatformWaitEvents(void) void _glfwPlatformWaitEventsTimeout(double timeout) { NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:date inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1395,7 +1557,7 @@ void _glfwPlatformWaitEventsTimeout(double timeout) void _glfwPlatformPostEmptyEvent(void) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined + NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 @@ -1469,14 +1631,8 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) updateCursorImage(window); } -const char* _glfwPlatformGetKeyName(int key, int scancode) +const char* _glfwPlatformGetScancodeName(int scancode) { - if (key != GLFW_KEY_UNKNOWN) - scancode = _glfw.ns.nativeKeys[key]; - - if (!_glfwIsPrintable(_glfw.ns.publicKeys[scancode])) - return NULL; - UInt32 deadKeyState = 0; UniChar characters[8]; UniCharCount characterCount = 0; @@ -1511,6 +1667,11 @@ const char* _glfwPlatformGetKeyName(int key, int scancode) return _glfw.ns.keyName; } +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.ns.scancodes[key]; +} + int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) @@ -1559,7 +1720,19 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) if (!initializeAppKit()) return GLFW_FALSE; - cursor->ns.object = getStandardCursor(shape); + if (shape == GLFW_ARROW_CURSOR) + cursor->ns.object = [NSCursor arrowCursor]; + else if (shape == GLFW_IBEAM_CURSOR) + cursor->ns.object = [NSCursor IBeamCursor]; + else if (shape == GLFW_CROSSHAIR_CURSOR) + cursor->ns.object = [NSCursor crosshairCursor]; + else if (shape == GLFW_HAND_CURSOR) + cursor->ns.object = [NSCursor pointingHandCursor]; + else if (shape == GLFW_HRESIZE_CURSOR) + cursor->ns.object = [NSCursor resizeLeftRightCursor]; + else if (shape == GLFW_VRESIZE_CURSOR) + cursor->ns.object = [NSCursor resizeUpDownCursor]; + if (!cursor->ns.object) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -1583,7 +1756,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) updateCursorImage(window); } -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +void _glfwPlatformSetClipboardString(const char* string) { NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil]; @@ -1593,7 +1766,7 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) forType:NSStringPboardType]; } -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +const char* _glfwPlatformGetClipboardString(void) { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; @@ -1618,17 +1791,20 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) return _glfw.ns.clipboardString; } -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { - *count = 0; - return NULL; + if (!_glfw.vk.KHR_surface || !_glfw.vk.MVK_macos_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_MVK_macos_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - return GLFW_FALSE; + return GLFW_TRUE; } VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, @@ -1636,7 +1812,57 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 + VkResult err; + VkMacOSSurfaceCreateInfoMVK sci; + PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; + + vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK) + vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK"); + if (!vkCreateMacOSSurfaceMVK) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + // HACK: Dynamically load Core Animation to avoid adding an extra + // dependency for the majority who don't use MoltenVK + NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"]; + if (!bundle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find QuartzCore.framework"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + // NOTE: Create the layer here as makeBackingLayer should not return nil + window->ns.layer = [[bundle classNamed:@"CAMetalLayer"] layer]; + if (!window->ns.layer) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create layer for view"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + [window->ns.view setWantsLayer:YES]; + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + sci.pView = window->ns.view; + + err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create Vulkan surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +#else return VK_ERROR_EXTENSION_NOT_PRESENT; +#endif } diff --git a/raylib/external/glfw/src/context.c b/raylib/external/glfw/src/context.c index 85bce7f..3842f0a 100644 --- a/raylib/external/glfw/src/context.c +++ b/raylib/external/glfw/src/context.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -41,10 +41,11 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) { if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && - ctxconfig->source != GLFW_EGL_CONTEXT_API) + ctxconfig->source != GLFW_EGL_CONTEXT_API && + ctxconfig->source != GLFW_OSMESA_CONTEXT_API) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context creation API %i", + "Invalid context creation API 0x%08X", ctxconfig->source); return GLFW_FALSE; } @@ -54,7 +55,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->client != GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid client API %i", + "Invalid client API 0x%08X", ctxconfig->client); return GLFW_FALSE; } @@ -84,7 +85,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid OpenGL profile %i", + "Invalid OpenGL profile 0x%08X", ctxconfig->profile); return GLFW_FALSE; } @@ -133,7 +134,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context robustness mode %i", + "Invalid context robustness mode 0x%08X", ctxconfig->robustness); return GLFW_FALSE; } @@ -145,7 +146,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) { _glfwInputError(GLFW_INVALID_ENUM, - "Invalid context release behavior %i", + "Invalid context release behavior 0x%08X", ctxconfig->release); return GLFW_FALSE; } @@ -207,6 +208,9 @@ const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, // not important to us here, so we count them as one missing++; } + + if (desired->transparent != current->transparent) + missing++; } // These polynomials make many small channel size differences matter @@ -330,7 +334,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) NULL }; - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.contextSlot); window->context.source = ctxconfig->source; window->context.client = GLFW_OPENGL_API; @@ -577,7 +581,7 @@ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFWwindow* previous = _glfwPlatformGetCurrentContext(); + _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot); _GLFW_REQUIRE_INIT(); @@ -600,7 +604,7 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) GLFWAPI GLFWwindow* glfwGetCurrentContext(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return (GLFWwindow*) _glfwPlatformGetCurrentContext(); + return _glfwPlatformGetTls(&_glfw.contextSlot); } GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) @@ -625,7 +629,7 @@ GLFWAPI void glfwSwapInterval(int interval) _GLFW_REQUIRE_INIT(); - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.contextSlot); if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); @@ -638,12 +642,11 @@ GLFWAPI void glfwSwapInterval(int interval) GLFWAPI int glfwExtensionSupported(const char* extension) { _GLFWwindow* window; - assert(extension != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.contextSlot); if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); @@ -708,7 +711,7 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.contextSlot); if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); diff --git a/raylib/external/glfw/src/egl_context.c b/raylib/external/glfw/src/egl_context.c index e3d6260..b2d11a4 100644 --- a/raylib/external/glfw/src/egl_context.c +++ b/raylib/external/glfw/src/egl_context.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 EGL - www.glfw.org +// GLFW 3.3 EGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -113,7 +113,7 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, _GLFWfbconfig* u = usableConfigs + usableCount; // Only consider RGB(A) EGLConfigs - if (!(getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) & EGL_RGB_BUFFER)) + if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER) continue; // Only consider window EGLConfigs @@ -121,9 +121,25 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, continue; #if defined(_GLFW_X11) + XVisualInfo vi = {0}; + // Only consider EGLConfigs with associated Visuals - if (!getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID)) + vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); + if (!vi.visualid) continue; + + if (desired->transparent) + { + int count; + XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display, + VisualIDMask, &vi, + &count); + if (vis) + { + u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); + XFree(vis); + } + } #endif // _GLFW_X11 if (ctxconfig->client == GLFW_OPENGL_ES_API) @@ -199,12 +215,12 @@ static void makeContextCurrentEGL(_GLFWwindow* window) } } - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.contextSlot, window); } static void swapBuffersEGL(_GLFWwindow* window) { - if (window != _glfwPlatformGetCurrentContext()) + if (window != _glfwPlatformGetTls(&_glfw.contextSlot)) { _glfwInputError(GLFW_PLATFORM_ERROR, "EGL: The context must be current on the calling thread when swapping buffers"); @@ -233,7 +249,7 @@ static int extensionSupportedEGL(const char* extension) static GLFWglproc getProcAddressEGL(const char* procname) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); if (window->context.egl.client) { @@ -286,11 +302,15 @@ GLFWbool _glfwInitEGL(void) int i; const char* sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_EGL_LIBRARY) + _GLFW_EGL_LIBRARY, +#elif defined(_GLFW_WIN32) "libEGL.dll", "EGL.dll", #elif defined(_GLFW_COCOA) "libEGL.dylib", +#elif defined(__CYGWIN__) + "libEGL-1.so", #else "libEGL.so.1", #endif @@ -315,37 +335,37 @@ GLFWbool _glfwInitEGL(void) _glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0); - _glfw.egl.GetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) + _glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib) _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib"); - _glfw.egl.GetConfigs = (PFNEGLGETCONFIGSPROC) + _glfw.egl.GetConfigs = (PFN_eglGetConfigs) _glfw_dlsym(_glfw.egl.handle, "eglGetConfigs"); - _glfw.egl.GetDisplay = (PFNEGLGETDISPLAYPROC) + _glfw.egl.GetDisplay = (PFN_eglGetDisplay) _glfw_dlsym(_glfw.egl.handle, "eglGetDisplay"); - _glfw.egl.GetError = (PFNEGLGETERRORPROC) + _glfw.egl.GetError = (PFN_eglGetError) _glfw_dlsym(_glfw.egl.handle, "eglGetError"); - _glfw.egl.Initialize = (PFNEGLINITIALIZEPROC) + _glfw.egl.Initialize = (PFN_eglInitialize) _glfw_dlsym(_glfw.egl.handle, "eglInitialize"); - _glfw.egl.Terminate = (PFNEGLTERMINATEPROC) + _glfw.egl.Terminate = (PFN_eglTerminate) _glfw_dlsym(_glfw.egl.handle, "eglTerminate"); - _glfw.egl.BindAPI = (PFNEGLBINDAPIPROC) + _glfw.egl.BindAPI = (PFN_eglBindAPI) _glfw_dlsym(_glfw.egl.handle, "eglBindAPI"); - _glfw.egl.CreateContext = (PFNEGLCREATECONTEXTPROC) + _glfw.egl.CreateContext = (PFN_eglCreateContext) _glfw_dlsym(_glfw.egl.handle, "eglCreateContext"); - _glfw.egl.DestroySurface = (PFNEGLDESTROYSURFACEPROC) + _glfw.egl.DestroySurface = (PFN_eglDestroySurface) _glfw_dlsym(_glfw.egl.handle, "eglDestroySurface"); - _glfw.egl.DestroyContext = (PFNEGLDESTROYCONTEXTPROC) + _glfw.egl.DestroyContext = (PFN_eglDestroyContext) _glfw_dlsym(_glfw.egl.handle, "eglDestroyContext"); - _glfw.egl.CreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) + _glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface) _glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface"); - _glfw.egl.MakeCurrent = (PFNEGLMAKECURRENTPROC) + _glfw.egl.MakeCurrent = (PFN_eglMakeCurrent) _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent"); - _glfw.egl.SwapBuffers = (PFNEGLSWAPBUFFERSPROC) + _glfw.egl.SwapBuffers = (PFN_eglSwapBuffers) _glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers"); - _glfw.egl.SwapInterval = (PFNEGLSWAPINTERVALPROC) + _glfw.egl.SwapInterval = (PFN_eglSwapInterval) _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval"); - _glfw.egl.QueryString = (PFNEGLQUERYSTRINGPROC) + _glfw.egl.QueryString = (PFN_eglQueryString) _glfw_dlsym(_glfw.egl.handle, "eglQueryString"); - _glfw.egl.GetProcAddress = (PFNEGLGETPROCADDRESSPROC) + _glfw.egl.GetProcAddress = (PFN_eglGetProcAddress) _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress"); if (!_glfw.egl.GetConfigAttrib || @@ -399,6 +419,10 @@ GLFWbool _glfwInitEGL(void) extensionSupportedEGL("EGL_KHR_create_context_no_error"); _glfw.egl.KHR_gl_colorspace = extensionSupportedEGL("EGL_KHR_gl_colorspace"); + _glfw.egl.KHR_get_all_proc_addresses = + extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); + _glfw.egl.KHR_context_flush_control = + extensionSupportedEGL("EGL_KHR_context_flush_control"); return GLFW_TRUE; } @@ -420,11 +444,11 @@ void _glfwTerminateEGL(void) } } -#define setEGLattrib(attribName, attribValue) \ +#define setAttrib(a, v) \ { \ - attribs[index++] = attribName; \ - attribs[index++] = attribValue; \ - assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ + assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ } // Create the OpenGL or OpenGL ES context @@ -436,6 +460,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, EGLint attribs[40]; EGLConfig config; EGLContext share = NULL; + int index = 0; if (!_glfw.egl.display) { @@ -476,7 +501,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (_glfw.egl.KHR_create_context) { - int index = 0, mask = 0, flags = 0; + int mask = 0, flags = 0; if (ctxconfig->client == GLFW_OPENGL_API) { @@ -487,12 +512,6 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; - - if (_glfw.egl.KHR_create_context_no_error) - { - if (ctxconfig->noerror) - flags |= EGL_CONTEXT_OPENGL_NO_ERROR_KHR; - } } if (ctxconfig->debug) @@ -502,44 +521,57 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_NO_RESET_NOTIFICATION_KHR); + setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_NO_RESET_NOTIFICATION_KHR); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, - EGL_LOSE_CONTEXT_ON_RESET_KHR); + setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_LOSE_CONTEXT_ON_RESET_KHR); } flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; } + if (ctxconfig->noerror) + { + if (_glfw.egl.KHR_create_context_no_error) + setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); + } + if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setEGLattrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); - setEGLattrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); + setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); + setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); } if (mask) - setEGLattrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); + setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); if (flags) - setEGLattrib(EGL_CONTEXT_FLAGS_KHR, flags); - - setEGLattrib(EGL_NONE, EGL_NONE); + setAttrib(EGL_CONTEXT_FLAGS_KHR, flags); } else { - int index = 0; - if (ctxconfig->client == GLFW_OPENGL_ES_API) - setEGLattrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); - - setEGLattrib(EGL_NONE, EGL_NONE); + setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); } - // Context release behaviors (GL_KHR_context_flush_control) are not yet - // supported on EGL but are not a hard constraint, so ignore and continue + if (_glfw.egl.KHR_context_flush_control) + { + if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) + { + setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); + } + else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) + { + setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); + } + } + + setAttrib(EGL_NONE, EGL_NONE); window->context.egl.handle = eglCreateContext(_glfw.egl.display, config, share, attribs); @@ -559,12 +591,10 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (fbconfig->sRGB) { if (_glfw.egl.KHR_gl_colorspace) - { - setEGLattrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); - } + setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); } - setEGLattrib(EGL_NONE, EGL_NONE); + setAttrib(EGL_NONE, EGL_NONE); } window->context.egl.surface = @@ -583,12 +613,15 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, window->context.egl.config = config; // Load the appropriate client library + if (!_glfw.egl.KHR_get_all_proc_addresses) { int i; const char** sonames; const char* es1sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_GLESV1_LIBRARY) + _GLFW_GLESV1_LIBRARY, +#elif defined(_GLFW_WIN32) "GLESv1_CM.dll", "libGLES_CM.dll", #elif defined(_GLFW_COCOA) @@ -601,11 +634,15 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, }; const char* es2sonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_GLESV2_LIBRARY) + _GLFW_GLESV2_LIBRARY, +#elif defined(_GLFW_WIN32) "GLESv2.dll", "libGLESv2.dll", #elif defined(_GLFW_COCOA) "libGLESv2.dylib", +#elif defined(__CYGWIN__) + "libGLESv2-2.so", #else "libGLESv2.so.2", #endif @@ -613,7 +650,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, }; const char* glsonames[] = { -#if defined(_GLFW_WIN32) +#if defined(_GLFW_OPENGL_LIBRARY) + _GLFW_OPENGL_LIBRARY, +#elif defined(_GLFW_WIN32) #elif defined(_GLFW_COCOA) #else "libGL.so.1", @@ -661,12 +700,13 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, return GLFW_TRUE; } -#undef setEGLattrib +#undef setAttrib // Returns the Visual and depth of the chosen EGLConfig // #if defined(_GLFW_X11) -GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth) { diff --git a/raylib/external/glfw/src/egl_context.h b/raylib/external/glfw/src/egl_context.h index 9bd8bb4..aa339ba 100644 --- a/raylib/external/glfw/src/egl_context.h +++ b/raylib/external/glfw/src/egl_context.h @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 EGL - www.glfw.org +// GLFW 3.3 EGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,15 +25,16 @@ // //======================================================================== -#ifndef _glfw3_egl_context_h_ -#define _glfw3_egl_context_h_ - #if defined(_GLFW_USE_EGLPLATFORM_H) #include #elif defined(_GLFW_WIN32) #define EGLAPIENTRY __stdcall typedef HDC EGLNativeDisplayType; typedef HWND EGLNativeWindowType; +#elif defined(_GLFW_COCOA) + #define EGLAPIENTRY +typedef void* EGLNativeDisplayType; +typedef id EGLNativeWindowType; #elif defined(_GLFW_X11) #define EGLAPIENTRY typedef Display* EGLNativeDisplayType; @@ -106,6 +107,9 @@ typedef MirEGLNativeWindowType EGLNativeWindowType; #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 #define EGL_GL_COLORSPACE_KHR 0x309d #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 typedef int EGLint; typedef unsigned int EGLBoolean; @@ -116,22 +120,22 @@ typedef void* EGLDisplay; typedef void* EGLSurface; // EGL function pointer typedefs -typedef EGLBoolean (EGLAPIENTRY * PFNEGLGETCONFIGATTRIBPROC)(EGLDisplay,EGLConfig,EGLint,EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLGETCONFIGSPROC)(EGLDisplay,EGLConfig*,EGLint,EGLint*); -typedef EGLDisplay (EGLAPIENTRY * PFNEGLGETDISPLAYPROC)(EGLNativeDisplayType); -typedef EGLint (EGLAPIENTRY * PFNEGLGETERRORPROC)(void); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLINITIALIZEPROC)(EGLDisplay,EGLint*,EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLTERMINATEPROC)(EGLDisplay); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLBINDAPIPROC)(EGLenum); -typedef EGLContext (EGLAPIENTRY * PFNEGLCREATECONTEXTPROC)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLDESTROYSURFACEPROC)(EGLDisplay,EGLSurface); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLDESTROYCONTEXTPROC)(EGLDisplay,EGLContext); -typedef EGLSurface (EGLAPIENTRY * PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLMAKECURRENTPROC)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLSWAPBUFFERSPROC)(EGLDisplay,EGLSurface); -typedef EGLBoolean (EGLAPIENTRY * PFNEGLSWAPINTERVALPROC)(EGLDisplay,EGLint); -typedef const char* (EGLAPIENTRY * PFNEGLQUERYSTRINGPROC)(EGLDisplay,EGLint); -typedef GLFWglproc (EGLAPIENTRY * PFNEGLGETPROCADDRESSPROC)(const char*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); +typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); +typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); +typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); +typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); +typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); +typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); #define eglGetConfigAttrib _glfw.egl.GetConfigAttrib #define eglGetConfigs _glfw.egl.GetConfigs #define eglGetDisplay _glfw.egl.GetDisplay @@ -176,25 +180,27 @@ typedef struct _GLFWlibraryEGL GLFWbool KHR_create_context; GLFWbool KHR_create_context_no_error; GLFWbool KHR_gl_colorspace; + GLFWbool KHR_get_all_proc_addresses; + GLFWbool KHR_context_flush_control; void* handle; - PFNEGLGETCONFIGATTRIBPROC GetConfigAttrib; - PFNEGLGETCONFIGSPROC GetConfigs; - PFNEGLGETDISPLAYPROC GetDisplay; - PFNEGLGETERRORPROC GetError; - PFNEGLINITIALIZEPROC Initialize; - PFNEGLTERMINATEPROC Terminate; - PFNEGLBINDAPIPROC BindAPI; - PFNEGLCREATECONTEXTPROC CreateContext; - PFNEGLDESTROYSURFACEPROC DestroySurface; - PFNEGLDESTROYCONTEXTPROC DestroyContext; - PFNEGLCREATEWINDOWSURFACEPROC CreateWindowSurface; - PFNEGLMAKECURRENTPROC MakeCurrent; - PFNEGLSWAPBUFFERSPROC SwapBuffers; - PFNEGLSWAPINTERVALPROC SwapInterval; - PFNEGLQUERYSTRINGPROC QueryString; - PFNEGLGETPROCADDRESSPROC GetProcAddress; + PFN_eglGetConfigAttrib GetConfigAttrib; + PFN_eglGetConfigs GetConfigs; + PFN_eglGetDisplay GetDisplay; + PFN_eglGetError GetError; + PFN_eglInitialize Initialize; + PFN_eglTerminate Terminate; + PFN_eglBindAPI BindAPI; + PFN_eglCreateContext CreateContext; + PFN_eglDestroySurface DestroySurface; + PFN_eglDestroyContext DestroyContext; + PFN_eglCreateWindowSurface CreateWindowSurface; + PFN_eglMakeCurrent MakeCurrent; + PFN_eglSwapBuffers SwapBuffers; + PFN_eglSwapInterval SwapInterval; + PFN_eglQueryString QueryString; + PFN_eglGetProcAddress GetProcAddress; } _GLFWlibraryEGL; @@ -205,9 +211,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #if defined(_GLFW_X11) -GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); #endif /*_GLFW_X11*/ -#endif // _glfw3_egl_context_h_ diff --git a/raylib/external/glfw/src/glx_context.c b/raylib/external/glfw/src/glx_context.c index 251b7fc..40da6c2 100644 --- a/raylib/external/glfw/src/glx_context.c +++ b/raylib/external/glfw/src/glx_context.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 GLX - www.glfw.org +// GLFW 3.3 GLX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -47,7 +47,8 @@ static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib) // Return the GLXFBConfig most closely matching the specified hints // -static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result) +static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, + GLXFBConfig* result) { GLXFBConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; @@ -59,12 +60,12 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res // HACK: This is a (hopefully temporary) workaround for Chromium // (VirtualBox GL) not setting the window bit on any GLXFBConfigs vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); - if (strcmp(vendor, "Chromium") == 0) + if (vendor && strcmp(vendor, "Chromium") == 0) trustWindowBit = GLFW_FALSE; nativeConfigs = glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount); - if (!nativeCount) + if (!nativeConfigs || !nativeCount) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned"); return GLFW_FALSE; @@ -89,6 +90,16 @@ static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* res continue; } + if (desired->transparent) + { + XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n); + if (vi) + { + u->transparent = _glfwIsVisualTransparentX11(vi->visual); + XFree(vi); + } + } + u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE); u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE); @@ -165,7 +176,7 @@ static void makeContextCurrentGLX(_GLFWwindow* window) } } - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.contextSlot, window); } static void swapBuffersGLX(_GLFWwindow* window) @@ -175,7 +186,7 @@ static void swapBuffersGLX(_GLFWwindow* window) static void swapIntervalGLX(int interval) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); if (_glfw.glx.EXT_swap_control) { @@ -244,7 +255,9 @@ GLFWbool _glfwInitGLX(void) int i; const char* sonames[] = { -#if defined(__CYGWIN__) +#if defined(_GLFW_GLX_LIBRARY) + _GLFW_GLX_LIBRARY, +#elif defined(__CYGWIN__) "libGL-1.so", #else "libGL.so.1", @@ -397,6 +410,9 @@ GLFWbool _glfwInitGLX(void) if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile")) _glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE; + if (extensionSupportedGLX("GLX_ARB_create_context_no_error")) + _glfw.glx.ARB_create_context_no_error = GLFW_TRUE; + if (extensionSupportedGLX("GLX_ARB_context_flush_control")) _glfw.glx.ARB_context_flush_control = GLFW_TRUE; @@ -417,11 +433,11 @@ void _glfwTerminateGLX(void) } } -#define setGLXattrib(attribName, attribValue) \ +#define setAttrib(a, v) \ { \ - attribs[index++] = attribName; \ - attribs[index++] = attribValue; \ - assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ + assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ } // Create the OpenGL or OpenGL ES context @@ -498,8 +514,6 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, if (ctxconfig->debug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB; - if (ctxconfig->noerror) - flags |= GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR; if (ctxconfig->robustness) { @@ -507,13 +521,13 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_NO_RESET_NOTIFICATION_ARB); + setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - GLX_LOSE_CONTEXT_ON_RESET_ARB); + setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_LOSE_CONTEXT_ON_RESET_ARB); } flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; @@ -526,33 +540,39 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, - GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } + if (ctxconfig->noerror) + { + if (_glfw.glx.ARB_create_context_no_error) + setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); + } + // NOTE: Only request an explicitly versioned context when necessary, as // explicitly requesting version 1.0 does not always return the // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setGLXattrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setGLXattrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (mask) - setGLXattrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); + setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); if (flags) - setGLXattrib(GLX_CONTEXT_FLAGS_ARB, flags); + setAttrib(GLX_CONTEXT_FLAGS_ARB, flags); - setGLXattrib(None, None); + setAttrib(None, None); window->context.glx.handle = _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, @@ -609,11 +629,12 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, return GLFW_TRUE; } -#undef setGLXattrib +#undef setAttrib // Returns the Visual and depth of the chosen GLXFBConfig // -GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth) { @@ -636,7 +657,7 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, } *visual = result->visual; - *depth = result->depth; + *depth = result->depth; XFree(result); return GLFW_TRUE; diff --git a/raylib/external/glfw/src/glx_context.h b/raylib/external/glfw/src/glx_context.h index 3abed0e..f767cb1 100644 --- a/raylib/external/glfw/src/glx_context.h +++ b/raylib/external/glfw/src/glx_context.h @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 GLX - www.glfw.org +// GLFW 3.3 GLX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,9 +25,6 @@ // //======================================================================== -#ifndef _glfw3_glx_context_h_ -#define _glfw3_glx_context_h_ - #define GLX_VENDOR 1 #define GLX_RGBA_BIT 0x00000001 #define GLX_WINDOW_BIT 0x00000001 @@ -67,6 +64,7 @@ #define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 typedef XID GLXWindow; typedef XID GLXDrawable; @@ -85,14 +83,15 @@ typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); -typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); -typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); -typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); + // libGL.so function pointer typedefs #define glXGetFBConfigs _glfw.glx.GetFBConfigs #define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib @@ -164,19 +163,19 @@ typedef struct _GLFWlibraryGLX GLFWbool ARB_create_context_profile; GLFWbool ARB_create_context_robustness; GLFWbool EXT_create_context_es2_profile; + GLFWbool ARB_create_context_no_error; GLFWbool ARB_context_flush_control; } _GLFWlibraryGLX; - GLFWbool _glfwInitGLX(void); void _glfwTerminateGLX(void); GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextGLX(_GLFWwindow* window); -GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, +GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); -#endif // _glfw3_glx_context_h_ diff --git a/raylib/external/glfw/src/init.c b/raylib/external/glfw/src/init.c index 9d4a2b2..ae82831 100644 --- a/raylib/external/glfw/src/init.c +++ b/raylib/external/glfw/src/init.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -26,33 +26,45 @@ //======================================================================== #include "internal.h" +#include "mappings.h" #include #include #include #include +#include -// The three global variables below comprise all global data in GLFW. +// The global variables below comprise all global data in GLFW. // Any other global variable is a bug. // Global state shared between compilation units of GLFW -// These are documented in internal.h // -GLFWbool _glfwInitialized = GLFW_FALSE; -_GLFWlibrary _glfw; +_GLFWlibrary _glfw = { GLFW_FALSE }; -// This is outside of _glfw so it can be initialized and usable before -// glfwInit is called, which lets that function report errors +// These are outside of _glfw so they can be used before initialization and +// after termination // -static GLFWerrorfun _glfwErrorCallback = NULL; - +static _GLFWerror _glfwMainThreadError; +static GLFWerrorfun _glfwErrorCallback; +static _GLFWinitconfig _glfwInitHints = +{ + GLFW_TRUE, // hat buttons + { + GLFW_TRUE, // macOS menu bar + GLFW_TRUE // macOS bundle chdir + }, + { + "", // X11 WM_CLASS name + "" // X11 WM_CLASS class + } +}; // Returns a generic string representation of the specified error // -static const char* getErrorString(int error) +static const char* getErrorString(int code) { - switch (error) + switch (code) { case GLFW_NOT_INITIALIZED: return "The GLFW library is not initialized"; @@ -69,7 +81,7 @@ static const char* getErrorString(int error) case GLFW_VERSION_UNAVAILABLE: return "The requested API version is unavailable"; case GLFW_PLATFORM_ERROR: - return "A platform-specific error occurred"; + return "An undocumented platform-specific error occurred"; case GLFW_FORMAT_UNAVAILABLE: return "The requested format is unavailable"; case GLFW_NO_WINDOW_CONTEXT: @@ -79,75 +91,12 @@ static const char* getErrorString(int error) } } - -////////////////////////////////////////////////////////////////////////// -////// GLFW event API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwInputError(int error, const char* format, ...) -{ - if (_glfwErrorCallback) - { - char buffer[8192]; - const char* description; - - if (format) - { - int count; - va_list vl; - - va_start(vl, format); - count = vsnprintf(buffer, sizeof(buffer), format, vl); - va_end(vl); - - if (count < 0) - buffer[sizeof(buffer) - 1] = '\0'; - - description = buffer; - } - else - description = getErrorString(error); - - _glfwErrorCallback(error, description); - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW public API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI int glfwInit(void) -{ - if (_glfwInitialized) - return GLFW_TRUE; - - memset(&_glfw, 0, sizeof(_glfw)); - - if (!_glfwPlatformInit()) - { - _glfwPlatformTerminate(); - return GLFW_FALSE; - } - - _glfw.monitors = _glfwPlatformGetMonitors(&_glfw.monitorCount); - _glfwInitialized = GLFW_TRUE; - - _glfw.timerOffset = _glfwPlatformGetTimerValue(); - - // Not all window hints have zero as their default value - glfwDefaultWindowHints(); - - return GLFW_TRUE; -} - -GLFWAPI void glfwTerminate(void) +// Terminate the library +// +static void terminate(void) { int i; - if (!_glfwInitialized) - return; - memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks)); while (_glfw.windowListHead) @@ -161,28 +110,185 @@ GLFWAPI void glfwTerminate(void) _GLFWmonitor* monitor = _glfw.monitors[i]; if (monitor->originalRamp.size) _glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp); + _glfwFreeMonitor(monitor); } - _glfwTerminateVulkan(); - - _glfwFreeMonitors(_glfw.monitors, _glfw.monitorCount); + free(_glfw.monitors); _glfw.monitors = NULL; _glfw.monitorCount = 0; + free(_glfw.mappings); + _glfw.mappings = NULL; + _glfw.mappingCount = 0; + + _glfwTerminateVulkan(); _glfwPlatformTerminate(); + _glfw.initialized = GLFW_FALSE; + + while (_glfw.errorListHead) + { + _GLFWerror* error = _glfw.errorListHead; + _glfw.errorListHead = error->next; + free(error); + } + + _glfwPlatformDestroyTls(&_glfw.contextSlot); + _glfwPlatformDestroyTls(&_glfw.errorSlot); + _glfwPlatformDestroyMutex(&_glfw.errorLock); + memset(&_glfw, 0, sizeof(_glfw)); - _glfwInitialized = GLFW_FALSE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwInputError(int code, const char* format, ...) +{ + _GLFWerror* error; + char description[_GLFW_MESSAGE_SIZE]; + + if (format) + { + va_list vl; + + va_start(vl, format); + vsnprintf(description, sizeof(description), format, vl); + va_end(vl); + + description[sizeof(description) - 1] = '\0'; + } + else + strcpy(description, getErrorString(code)); + + if (_glfw.initialized) + { + error = _glfwPlatformGetTls(&_glfw.errorSlot); + if (!error) + { + error = calloc(1, sizeof(_GLFWerror)); + _glfwPlatformSetTls(&_glfw.errorSlot, error); + _glfwPlatformLockMutex(&_glfw.errorLock); + error->next = _glfw.errorListHead; + _glfw.errorListHead = error; + _glfwPlatformUnlockMutex(&_glfw.errorLock); + } + } + else + error = &_glfwMainThreadError; + + error->code = code; + strcpy(error->description, description); + + if (_glfwErrorCallback) + _glfwErrorCallback(code, description); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwInit(void) +{ + if (_glfw.initialized) + return GLFW_TRUE; + + memset(&_glfw, 0, sizeof(_glfw)); + _glfw.hints.init = _glfwInitHints; + + if (!_glfwPlatformInit()) + { + terminate(); + return GLFW_FALSE; + } + + if (!_glfwPlatformCreateMutex(&_glfw.errorLock) || + !_glfwPlatformCreateTls(&_glfw.errorSlot) || + !_glfwPlatformCreateTls(&_glfw.contextSlot)) + { + terminate(); + return GLFW_FALSE; + } + + _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError); + + _glfw.initialized = GLFW_TRUE; + _glfw.timer.offset = _glfwPlatformGetTimerValue(); + + glfwDefaultWindowHints(); + + { + int i; + + for (i = 0; _glfwDefaultMappings[i]; i++) + { + if (!glfwUpdateGamepadMappings(_glfwDefaultMappings[i])) + { + terminate(); + return GLFW_FALSE; + } + } + } + + return GLFW_TRUE; +} + +GLFWAPI void glfwTerminate(void) +{ + if (!_glfw.initialized) + return; + + terminate(); +} + +GLFWAPI void glfwInitHint(int hint, int value) +{ + switch (hint) + { + case GLFW_JOYSTICK_HAT_BUTTONS: + _glfwInitHints.hatButtons = value; + return; + case GLFW_COCOA_CHDIR_RESOURCES: + _glfwInitHints.ns.chdir = value; + return; + case GLFW_COCOA_MENUBAR: + _glfwInitHints.ns.menubar = value; + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid integer type init hint 0x%08X", hint); +} + +GLFWAPI void glfwInitHintString(int hint, const char* value) +{ + assert(value != NULL); + + switch (hint) + { + case GLFW_X11_WM_CLASS_NAME: + strncpy(_glfwInitHints.x11.className, value, + sizeof(_glfwInitHints.x11.className) - 1); + return; + case GLFW_X11_WM_CLASS_CLASS: + strncpy(_glfwInitHints.x11.classClass, value, + sizeof(_glfwInitHints.x11.classClass) - 1); + return; + } + + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid string type init hint 0x%08X", hint); } GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) { if (major != NULL) *major = GLFW_VERSION_MAJOR; - if (minor != NULL) *minor = GLFW_VERSION_MINOR; - if (rev != NULL) *rev = GLFW_VERSION_REVISION; } @@ -192,6 +298,30 @@ GLFWAPI const char* glfwGetVersionString(void) return _glfwPlatformGetVersionString(); } +GLFWAPI int glfwGetError(const char** description) +{ + _GLFWerror* error; + int code = GLFW_NO_ERROR; + + if (description) + *description = NULL; + + if (_glfw.initialized) + error = _glfwPlatformGetTls(&_glfw.errorSlot); + else + error = &_glfwMainThreadError; + + if (error) + { + code = error->code; + error->code = GLFW_NO_ERROR; + if (description && code) + *description = error->description; + } + + return code; +} + GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) { _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); diff --git a/raylib/external/glfw/src/input.c b/raylib/external/glfw/src/input.c index 614c6ef..b20e9e6 100644 --- a/raylib/external/glfw/src/input.c +++ b/raylib/external/glfw/src/input.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -29,11 +29,144 @@ #include #include +#include #include +#include +#include // Internal key state used for sticky keys #define _GLFW_STICK 3 +// Internal constants for gamepad mapping source types +#define _GLFW_JOYSTICK_AXIS 1 +#define _GLFW_JOYSTICK_BUTTON 2 +#define _GLFW_JOYSTICK_HATBIT 3 + +// Finds a mapping based on joystick GUID +// +static _GLFWmapping* findMapping(const char* guid) +{ + int i; + + for (i = 0; i < _glfw.mappingCount; i++) + { + if (strcmp(_glfw.mappings[i].guid, guid) == 0) + return _glfw.mappings + i; + } + + return NULL; +} + +// Parses an SDL_GameControllerDB line and adds it to the mapping list +// +static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) +{ + const char* c = string; + size_t i, length; + struct + { + const char* name; + _GLFWmapelement* element; + } fields[] = + { + { "platform", NULL }, + { "a", mapping->buttons + GLFW_GAMEPAD_BUTTON_A }, + { "b", mapping->buttons + GLFW_GAMEPAD_BUTTON_B }, + { "x", mapping->buttons + GLFW_GAMEPAD_BUTTON_X }, + { "y", mapping->buttons + GLFW_GAMEPAD_BUTTON_Y }, + { "back", mapping->buttons + GLFW_GAMEPAD_BUTTON_BACK }, + { "start", mapping->buttons + GLFW_GAMEPAD_BUTTON_START }, + { "guide", mapping->buttons + GLFW_GAMEPAD_BUTTON_GUIDE }, + { "leftshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER }, + { "rightshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER }, + { "leftstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_THUMB }, + { "rightstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB }, + { "dpup", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_UP }, + { "dpright", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT }, + { "dpdown", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_DOWN }, + { "dpleft", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_LEFT }, + { "lefttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER }, + { "righttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER }, + { "leftx", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_X }, + { "lefty", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_Y }, + { "rightx", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_X }, + { "righty", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_Y } + }; + + length = strcspn(c, ","); + if (length != 32 || c[length] != ',') + { + _glfwInputError(GLFW_INVALID_VALUE, NULL); + return GLFW_FALSE; + } + + memcpy(mapping->guid, c, length); + c += length + 1; + + length = strcspn(c, ","); + if (length >= sizeof(mapping->name) || c[length] != ',') + { + _glfwInputError(GLFW_INVALID_VALUE, NULL); + return GLFW_FALSE; + } + + memcpy(mapping->name, c, length); + c += length + 1; + + while (*c) + { + for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) + { + length = strlen(fields[i].name); + if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':') + continue; + + c += length + 1; + + if (fields[i].element) + { + if (*c == 'a') + fields[i].element->type = _GLFW_JOYSTICK_AXIS; + else if (*c == 'b') + fields[i].element->type = _GLFW_JOYSTICK_BUTTON; + else if (*c == 'h') + fields[i].element->type = _GLFW_JOYSTICK_HATBIT; + else + break; + + if (fields[i].element->type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned long hat = strtoul(c + 1, (char**) &c, 10); + const unsigned long bit = strtoul(c + 1, (char**) &c, 10); + fields[i].element->value = (uint8_t) ((hat << 4) | bit); + } + else + fields[i].element->value = (uint8_t) strtoul(c + 1, (char**) &c, 10); + } + else + { + length = strlen(_GLFW_PLATFORM_MAPPING_NAME); + if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0) + return GLFW_FALSE; + } + + break; + } + + c += strcspn(c, ","); + c += strspn(c, ","); + } + + for (i = 0; i < 32; i++) + { + if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F') + mapping->guid[i] += 'a' - 'A'; + } + + _glfwPlatformUpdateGamepadGUID(mapping->guid); + return GLFW_TRUE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// @@ -90,7 +223,6 @@ void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) return; - // Register mouse button action if (action == GLFW_RELEASE && window->stickyMouseButtons) window->mouseButtons[button] = _GLFW_STICK; else @@ -124,10 +256,34 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) window->callbacks.drop((GLFWwindow*) window, count, paths); } -void _glfwInputJoystickChange(int joy, int event) +void _glfwInputJoystick(_GLFWjoystick* js, int event) { + const int jid = (int) (js - _glfw.joysticks); + if (_glfw.callbacks.joystick) - _glfw.callbacks.joystick(joy, event); + _glfw.callbacks.joystick(jid, event); +} + +void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value) +{ + js->axes[axis] = value; +} + +void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value) +{ + js->buttons[button] = value; +} + +void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) +{ + const int base = js->buttonCount + hat * 4; + + js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE; + + js->hats[hat] = value; } @@ -135,11 +291,47 @@ void _glfwInputJoystickChange(int joy, int event) ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -GLFWbool _glfwIsPrintable(int key) +_GLFWjoystick* _glfwAllocJoystick(const char* name, + const char* guid, + int axisCount, + int buttonCount, + int hatCount) { - return (key >= GLFW_KEY_APOSTROPHE && key <= GLFW_KEY_WORLD_2) || - (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) || - key == GLFW_KEY_KP_EQUAL; + int jid; + _GLFWjoystick* js; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.joysticks[jid].present) + break; + } + + if (jid > GLFW_JOYSTICK_LAST) + return NULL; + + js = _glfw.joysticks + jid; + js->present = GLFW_TRUE; + js->name = strdup(name); + js->axes = calloc(axisCount, sizeof(float)); + js->buttons = calloc(buttonCount + hatCount * 4, 1); + js->hats = calloc(hatCount, 1); + js->axisCount = axisCount; + js->buttonCount = buttonCount; + js->hatCount = hatCount; + js->mapping = findMapping(guid); + + strcpy(js->guid, guid); + + return js; +} + +void _glfwFreeJoystick(_GLFWjoystick* js) +{ + free(js->name); + free(js->axes); + free(js->buttons); + free(js->hats); + memset(js, 0, sizeof(_GLFWjoystick)); } @@ -162,10 +354,10 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) return window->stickyKeys; case GLFW_STICKY_MOUSE_BUTTONS: return window->stickyMouseButtons; - default: - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); - return 0; } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); + return 0; } GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) @@ -175,85 +367,104 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) _GLFW_REQUIRE_INIT(); - switch (mode) + if (mode == GLFW_CURSOR) { - case GLFW_CURSOR: + if (value != GLFW_CURSOR_NORMAL && + value != GLFW_CURSOR_HIDDEN && + value != GLFW_CURSOR_DISABLED) { - if (value != GLFW_CURSOR_NORMAL && - value != GLFW_CURSOR_HIDDEN && - value != GLFW_CURSOR_DISABLED) - { - _glfwInputError(GLFW_INVALID_ENUM, - "Invalid cursor mode %i", - value); - return; - } - - if (window->cursorMode == value) - return; - - window->cursorMode = value; - - _glfwPlatformGetCursorPos(window, - &window->virtualCursorPosX, - &window->virtualCursorPosY); - - if (_glfwPlatformWindowFocused(window)) - _glfwPlatformSetCursorMode(window, value); - + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid cursor mode 0x%08X", + value); return; } - case GLFW_STICKY_KEYS: - { - if (window->stickyKeys == value) - return; - - if (!value) - { - int i; - - // Release all sticky keys - for (i = 0; i <= GLFW_KEY_LAST; i++) - { - if (window->keys[i] == _GLFW_STICK) - window->keys[i] = GLFW_RELEASE; - } - } - - window->stickyKeys = value ? GLFW_TRUE : GLFW_FALSE; + if (window->cursorMode == value) return; - } - case GLFW_STICKY_MOUSE_BUTTONS: - { - if (window->stickyMouseButtons == value) - return; + window->cursorMode = value; - if (!value) - { - int i; + _glfwPlatformGetCursorPos(window, + &window->virtualCursorPosX, + &window->virtualCursorPosY); - // Release all sticky mouse buttons - for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) - { - if (window->mouseButtons[i] == _GLFW_STICK) - window->mouseButtons[i] = GLFW_RELEASE; - } - } - - window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE; - return; - } + if (_glfwPlatformWindowFocused(window)) + _glfwPlatformSetCursorMode(window, value); } + else if (mode == GLFW_STICKY_KEYS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyKeys == value) + return; - _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); + if (!value) + { + int i; + + // Release all sticky keys + for (i = 0; i <= GLFW_KEY_LAST; i++) + { + if (window->keys[i] == _GLFW_STICK) + window->keys[i] = GLFW_RELEASE; + } + } + + window->stickyKeys = value ? GLFW_TRUE : GLFW_FALSE; + } + else if (mode == GLFW_STICKY_MOUSE_BUTTONS) + { + value = value ? GLFW_TRUE : GLFW_FALSE; + if (window->stickyMouseButtons == value) + return; + + if (!value) + { + int i; + + // Release all sticky mouse buttons + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == _GLFW_STICK) + window->mouseButtons[i] = GLFW_RELEASE; + } + } + + window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE; + } + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); } GLFWAPI const char* glfwGetKeyName(int key, int scancode) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfwPlatformGetKeyName(key, scancode); + + if (key != GLFW_KEY_UNKNOWN) + { + if (key != GLFW_KEY_KP_EQUAL && + (key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) && + (key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2)) + { + return NULL; + } + + scancode = _glfwPlatformGetKeyScancode(key); + } + + return _glfwPlatformGetScancodeName(scancode); +} + +GLFWAPI int glfwGetKeyScancode(int key) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(-1); + + if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); + return GLFW_RELEASE; + } + + return _glfwPlatformGetKeyScancode(key); } GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) @@ -391,7 +602,7 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) shape != GLFW_HRESIZE_CURSOR && shape != GLFW_VRESIZE_CURSOR) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor %i", shape); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape); return NULL; } @@ -540,62 +751,167 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) return cbfun; } -GLFWAPI int glfwJoystickPresent(int joy) +GLFWAPI int glfwJoystickPresent(int jid) { - _GLFW_REQUIRE_INIT_OR_RETURN(0); + _GLFWjoystick* js; - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); - return 0; + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return GLFW_FALSE; } - return _glfwPlatformJoystickPresent(joy); + js = _glfw.joysticks + jid; + if (!js->present) + return GLFW_FALSE; + + return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); } -GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count) +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) { + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); assert(count != NULL); + *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } - return _glfwPlatformGetJoystickAxes(joy, count); + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES)) + return NULL; + + *count = js->axisCount; + return js->axes; } -GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count) +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) { + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); assert(count != NULL); + *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } - return _glfwPlatformGetJoystickButtons(joy, count); + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) + return NULL; + + if (_glfw.hints.init.hatButtons) + *count = js->buttonCount + js->hatCount * 4; + else + *count = js->buttonCount; + + return js->buttons; } -GLFWAPI const char* glfwGetJoystickName(int joy) +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) { + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(count != NULL); + + *count = 0; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (joy < 0 || joy > GLFW_JOYSTICK_LAST) + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { - _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", joy); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } - return _glfwPlatformGetJoystickName(joy); + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) + return NULL; + + *count = js->hatCount; + return js->hats; +} + +GLFWAPI const char* glfwGetJoystickName(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return NULL; + + return js->name; +} + +GLFWAPI const char* glfwGetJoystickGUID(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return NULL; + + return js->guid; } GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) @@ -605,29 +921,200 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) return cbfun; } +GLFWAPI int glfwUpdateGamepadMappings(const char* string) +{ + int jid; + const char* c = string; + + assert(string != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + while (*c) + { + if (isxdigit(*c)) + { + char line[1024]; + + const size_t length = strcspn(c, "\r\n"); + if (length < sizeof(line)) + { + _GLFWmapping mapping = {{0}}; + + memcpy(line, c, length); + line[length] = '\0'; + + if (parseMapping(&mapping, line)) + { + _GLFWmapping* previous = findMapping(mapping.guid); + if (previous) + *previous = mapping; + else + { + _glfw.mappingCount++; + _glfw.mappings = + realloc(_glfw.mappings, + sizeof(_GLFWmapping) * _glfw.mappingCount); + _glfw.mappings[_glfw.mappingCount - 1] = mapping; + } + } + } + + c += length; + } + else + { + c += strcspn(c, "\r\n"); + c += strspn(c, "\r\n"); + } + } + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + js->mapping = findMapping(js->guid); + } + + return GLFW_TRUE; +} + +GLFWAPI int glfwJoystickIsGamepad(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return GLFW_FALSE; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return GLFW_FALSE; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return GLFW_FALSE; + + return js->mapping != NULL; +} + +GLFWAPI const char* glfwGetGamepadName(int jid) +{ + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return NULL; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return NULL; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) + return NULL; + + if (!js->mapping) + return NULL; + + return js->mapping->name; +} + +GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) +{ + int i; + _GLFWjoystick* js; + + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); + assert(state != NULL); + + memset(state, 0, sizeof(GLFWgamepadstate)); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); + return GLFW_FALSE; + } + + js = _glfw.joysticks + jid; + if (!js->present) + return GLFW_FALSE; + + if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL)) + return GLFW_FALSE; + + if (!js->mapping) + return GLFW_FALSE; + + for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) + { + if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_AXIS) + { + if (fabs(js->axes[js->mapping->buttons[i].value]) > 0.5) + state->buttons[i] = GLFW_PRESS; + } + else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned int hat = js->mapping->buttons[i].value >> 4; + const unsigned int bit = js->mapping->buttons[i].value & 0xf; + if (js->hats[hat] & bit) + state->buttons[i] = GLFW_PRESS; + } + else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON) + state->buttons[i] = js->buttons[js->mapping->buttons[i].value]; + } + + for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) + { + if (js->mapping->axes[i].type == _GLFW_JOYSTICK_AXIS) + state->axes[i] = js->axes[js->mapping->axes[i].value]; + else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_HATBIT) + { + const unsigned int hat = js->mapping->buttons[i].value >> 4; + const unsigned int bit = js->mapping->buttons[i].value & 0xf; + if (js->hats[hat] & bit) + state->axes[i] = 1.f; + } + else if (js->mapping->buttons[i].type == _GLFW_JOYSTICK_BUTTON) + state->axes[i] = (float) js->buttons[js->mapping->axes[i].value]; + } + + return GLFW_TRUE; +} + GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) { - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); assert(string != NULL); _GLFW_REQUIRE_INIT(); - _glfwPlatformSetClipboardString(window, string); + _glfwPlatformSetClipboardString(string); } GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) { - _GLFWwindow* window = (_GLFWwindow*) handle; - assert(window != NULL); - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfwPlatformGetClipboardString(window); + return _glfwPlatformGetClipboardString(); } GLFWAPI double glfwGetTime(void) { _GLFW_REQUIRE_INIT_OR_RETURN(0.0); - return (double) (_glfwPlatformGetTimerValue() - _glfw.timerOffset) / + return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) / _glfwPlatformGetTimerFrequency(); } @@ -641,7 +1128,7 @@ GLFWAPI void glfwSetTime(double time) return; } - _glfw.timerOffset = _glfwPlatformGetTimerValue() - + _glfw.timer.offset = _glfwPlatformGetTimerValue() - (uint64_t) (time * _glfwPlatformGetTimerFrequency()); } diff --git a/raylib/external/glfw/src/internal.h b/raylib/external/glfw/src/internal.h index 8e84efd..0a2cbc4 100644 --- a/raylib/external/glfw/src/internal.h +++ b/raylib/external/glfw/src/internal.h @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,9 +25,7 @@ // //======================================================================== -#ifndef _glfw3_internal_h_ -#define _glfw3_internal_h_ - +#pragma once #if defined(_GLFW_USE_CONFIG_H) #include "glfw_config.h" @@ -37,6 +35,8 @@ defined(GLFW_INCLUDE_ES1) || \ defined(GLFW_INCLUDE_ES2) || \ defined(GLFW_INCLUDE_ES3) || \ + defined(GLFW_INCLUDE_ES31) || \ + defined(GLFW_INCLUDE_ES32) || \ defined(GLFW_INCLUDE_NONE) || \ defined(GLFW_INCLUDE_GLEXT) || \ defined(GLFW_INCLUDE_GLU) || \ @@ -48,8 +48,20 @@ #define GLFW_INCLUDE_NONE #include "../include/GLFW/glfw3.h" +#define _GLFW_INSERT_FIRST 0 +#define _GLFW_INSERT_LAST 1 + +#define _GLFW_POLL_PRESENCE 0 +#define _GLFW_POLL_AXES 1 +#define _GLFW_POLL_BUTTONS 2 +#define _GLFW_POLL_ALL (_GLFW_POLL_AXES | _GLFW_POLL_BUTTONS) + +#define _GLFW_MESSAGE_SIZE 1024 + typedef int GLFWbool; +typedef struct _GLFWerror _GLFWerror; +typedef struct _GLFWinitconfig _GLFWinitconfig; typedef struct _GLFWwndconfig _GLFWwndconfig; typedef struct _GLFWctxconfig _GLFWctxconfig; typedef struct _GLFWfbconfig _GLFWfbconfig; @@ -58,6 +70,11 @@ typedef struct _GLFWwindow _GLFWwindow; typedef struct _GLFWlibrary _GLFWlibrary; typedef struct _GLFWmonitor _GLFWmonitor; typedef struct _GLFWcursor _GLFWcursor; +typedef struct _GLFWmapelement _GLFWmapelement; +typedef struct _GLFWmapping _GLFWmapping; +typedef struct _GLFWjoystick _GLFWjoystick; +typedef struct _GLFWtls _GLFWtls; +typedef struct _GLFWmutex _GLFWmutex; typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); @@ -69,6 +86,7 @@ typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); #define GL_VERSION 0x1f02 #define GL_NONE 0 #define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_UNSIGNED_BYTE 0x1401 #define GL_EXTENSIONS 0x1f03 #define GL_NUM_EXTENSIONS 0x821d #define GL_CONTEXT_FLAGS 0x821e @@ -110,6 +128,7 @@ typedef enum VkStructureType VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000053000, VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType; @@ -171,6 +190,8 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); #include "wl_platform.h" #elif defined(_GLFW_MIR) #include "mir_platform.h" +#elif defined(_GLFW_OSMESA) + #include "null_platform.h" #else #error "No supported window creation API selected" #endif @@ -216,13 +237,13 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); // Checks for whether the library has been initialized #define _GLFW_REQUIRE_INIT() \ - if (!_glfwInitialized) \ + if (!_glfw.initialized) \ { \ _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ return; \ } #define _GLFW_REQUIRE_INIT_OR_RETURN(x) \ - if (!_glfwInitialized) \ + if (!_glfw.initialized) \ { \ _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ return x; \ @@ -242,6 +263,30 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); // Platform-independent structures //======================================================================== +struct _GLFWerror +{ + _GLFWerror* next; + int code; + char description[_GLFW_MESSAGE_SIZE]; +}; + +/*! @brief Initialization configuration. + * + * Parameters relating to the initialization of the library. + */ +struct _GLFWinitconfig +{ + GLFWbool hatButtons; + struct { + GLFWbool menubar; + GLFWbool chdir; + } ns; + struct { + char className[256]; + char classClass[256]; + } x11; +}; + /*! @brief Window configuration. * * Parameters relating to the creation of the window but not directly related @@ -260,6 +305,11 @@ struct _GLFWwndconfig GLFWbool autoIconify; GLFWbool floating; GLFWbool maximized; + GLFWbool centerCursor; + struct { + GLFWbool retina; + GLFWbool frame; + } ns; }; /*! @brief Context configuration. @@ -281,6 +331,9 @@ struct _GLFWctxconfig int robustness; int release; _GLFWwindow* share; + struct { + GLFWbool offline; + } nsgl; }; /*! @brief Framebuffer configuration. @@ -308,6 +361,7 @@ struct _GLFWfbconfig int samples; GLFWbool sRGB; GLFWbool doublebuffer; + GLFWbool transparent; uintptr_t handle; }; @@ -338,6 +392,8 @@ struct _GLFWcontext _GLFW_PLATFORM_CONTEXT_STATE; // This is defined in egl_context.h _GLFW_EGL_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_CONTEXT_STATE; }; /*! @brief Window and context structure. @@ -351,7 +407,7 @@ struct _GLFWwindow GLFWbool decorated; GLFWbool autoIconify; GLFWbool floating; - GLFWbool closed; + GLFWbool shouldClose; void* userPointer; GLFWvidmode videoMode; _GLFWmonitor* monitor; @@ -378,6 +434,7 @@ struct _GLFWwindow GLFWwindowrefreshfun refresh; GLFWwindowfocusfun focus; GLFWwindowiconifyfun iconify; + GLFWwindowmaximizefun maximize; GLFWframebuffersizefun fbsize; GLFWmousebuttonfun mouseButton; GLFWcursorposfun cursorPos; @@ -426,41 +483,115 @@ struct _GLFWcursor _GLFW_PLATFORM_CURSOR_STATE; }; +/*! @brief Gamepad mapping element structure + */ +struct _GLFWmapelement +{ + uint8_t type; + uint8_t value; +}; + +/*! @brief Gamepad mapping structure + */ +struct _GLFWmapping +{ + char name[128]; + char guid[33]; + _GLFWmapelement buttons[15]; + _GLFWmapelement axes[6]; +}; + +/*! @brief Joystick structure + */ +struct _GLFWjoystick +{ + GLFWbool present; + float* axes; + int axisCount; + unsigned char* buttons; + int buttonCount; + unsigned char* hats; + int hatCount; + char* name; + char guid[33]; + _GLFWmapping* mapping; + + // This is defined in the joystick API's joystick.h + _GLFW_PLATFORM_JOYSTICK_STATE; +}; + +/*! @brief Thread local storage structure. + */ +struct _GLFWtls +{ + // This is defined in the platform's thread.h + _GLFW_PLATFORM_TLS_STATE; +}; + +/*! @brief Mutex structure. + */ +struct _GLFWmutex +{ + // This is defined in the platform's thread.h + _GLFW_PLATFORM_MUTEX_STATE; +}; + /*! @brief Library global data. */ struct _GLFWlibrary { + GLFWbool initialized; + struct { + _GLFWinitconfig init; _GLFWfbconfig framebuffer; _GLFWwndconfig window; _GLFWctxconfig context; int refreshRate; } hints; + _GLFWerror* errorListHead; _GLFWcursor* cursorListHead; - _GLFWwindow* windowListHead; _GLFWmonitor** monitors; int monitorCount; - uint64_t timerOffset; + _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; + _GLFWmapping* mappings; + int mappingCount; + + _GLFWtls errorSlot; + _GLFWtls contextSlot; + _GLFWmutex errorLock; + + struct { + uint64_t offset; + // This is defined in the platform's time.h + _GLFW_PLATFORM_LIBRARY_TIMER_STATE; + } timer; struct { GLFWbool available; void* handle; - char** extensions; - uint32_t extensionCount; + char* extensions[2]; #if !defined(_GLFW_VULKAN_STATIC) PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; PFN_vkGetInstanceProcAddr GetInstanceProcAddr; #endif GLFWbool KHR_surface; +#if defined(_GLFW_WIN32) GLFWbool KHR_win32_surface; +#elif defined(_GLFW_COCOA) + GLFWbool MVK_macos_surface; +#elif defined(_GLFW_X11) GLFWbool KHR_xlib_surface; GLFWbool KHR_xcb_surface; +#elif defined(_GLFW_WAYLAND) GLFWbool KHR_wayland_surface; +#elif defined(_GLFW_MIR) GLFWbool KHR_mir_surface; +#endif } vk; struct { @@ -472,14 +603,12 @@ struct _GLFWlibrary _GLFW_PLATFORM_LIBRARY_WINDOW_STATE; // This is defined in the context API's context.h _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; - // This is defined in the platform's time.h - _GLFW_PLATFORM_LIBRARY_TIME_STATE; // This is defined in the platform's joystick.h _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; - // This is defined in the platform's tls.h - _GLFW_PLATFORM_LIBRARY_TLS_STATE; // This is defined in egl_context.h _GLFW_EGL_LIBRARY_CONTEXT_STATE; + // This is defined in osmesa_context.h + _GLFW_OSMESA_LIBRARY_CONTEXT_STATE; }; @@ -487,13 +616,7 @@ struct _GLFWlibrary // Global state shared between compilation units of GLFW //======================================================================== -/*! @brief Flag indicating whether GLFW has been successfully initialized. - */ -extern GLFWbool _glfwInitialized; - -/*! @brief All global data protected by @ref _glfwInitialized. - * This should only be touched after a call to @ref glfwInit that has not been - * followed by a call to @ref glfwTerminate. +/*! @brief All global data shared between compilation units. */ extern _GLFWlibrary _glfw; @@ -502,312 +625,101 @@ extern _GLFWlibrary _glfw; // Platform API functions //======================================================================== -/*! @brief Initializes the platform-specific part of the library. - * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an error occurred. - * @ingroup platform - */ +/*! @addtogroup platform @{ */ + int _glfwPlatformInit(void); - -/*! @brief Terminates the platform-specific part of the library. - * @ingroup platform - */ void _glfwPlatformTerminate(void); - -/*! @copydoc glfwGetVersionString - * @ingroup platform - * - * @note The returned string must be available for the duration of the program. - * - * @note The returned string must not change for the duration of the program. - */ const char* _glfwPlatformGetVersionString(void); -/*! @copydoc glfwGetCursorPos - * @ingroup platform - */ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); - -/*! @copydoc glfwSetCursorPos - * @ingroup platform - */ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); - -/*! @brief Sets the specified cursor mode of the specified window. - * @param[in] window The window whose cursor mode to set. - * @ingroup platform - */ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); -/*! @copydoc glfwGetKeyName - * @ingroup platform - */ -const char* _glfwPlatformGetKeyName(int key, int scancode); +const char* _glfwPlatformGetScancodeName(int scancode); +int _glfwPlatformGetKeyScancode(int key); -/*! @copydoc glfwGetMonitors - * @ingroup platform - */ -_GLFWmonitor** _glfwPlatformGetMonitors(int* count); - -/*! @brief Checks whether two monitor objects represent the same monitor. - * - * @param[in] first The first monitor. - * @param[in] second The second monitor. - * @return @c GLFW_TRUE if the monitor objects represent the same monitor, or - * @c GLFW_FALSE otherwise. - * @ingroup platform - */ -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second); - -/*! @copydoc glfwGetMonitorPos - * @ingroup platform - */ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); - -/*! @copydoc glfwGetVideoModes - * @ingroup platform - */ +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale); GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); - -/*! @ingroup platform - */ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); - -/*! @copydoc glfwGetGammaRamp - * @ingroup platform - */ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); - -/*! @copydoc glfwSetGammaRamp - * @ingroup platform - */ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); -/*! @copydoc glfwSetClipboardString - * @ingroup platform - */ -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string); +void _glfwPlatformSetClipboardString(const char* string); +const char* _glfwPlatformGetClipboardString(void); -/*! @copydoc glfwGetClipboardString - * @ingroup platform - * - * @note The returned string must be valid until the next call to @ref - * _glfwPlatformGetClipboardString or @ref _glfwPlatformSetClipboardString. - */ -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window); +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode); +void _glfwPlatformUpdateGamepadGUID(char* guid); -/*! @copydoc glfwJoystickPresent - * @ingroup platform - */ -int _glfwPlatformJoystickPresent(int joy); - -/*! @copydoc glfwGetJoystickAxes - * @ingroup platform - */ -const float* _glfwPlatformGetJoystickAxes(int joy, int* count); - -/*! @copydoc glfwGetJoystickButtons - * @ingroup platform - */ -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count); - -/*! @copydoc glfwGetJoystickName - * @ingroup platform - */ -const char* _glfwPlatformGetJoystickName(int joy); - -/*! @copydoc glfwGetTimerValue - * @ingroup platform - */ uint64_t _glfwPlatformGetTimerValue(void); - -/*! @copydoc glfwGetTimerFrequency - * @ingroup platform - */ uint64_t _glfwPlatformGetTimerFrequency(void); -/*! @ingroup platform - */ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); - -/*! @ingroup platform - */ void _glfwPlatformDestroyWindow(_GLFWwindow* window); - -/*! @copydoc glfwSetWindowTitle - * @ingroup platform - */ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); - -/*! @copydoc glfwSetWindowIcon - * @ingroup platform - */ void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images); - -/*! @copydoc glfwGetWindowPos - * @ingroup platform - */ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos); - -/*! @copydoc glfwSetWindowPos - * @ingroup platform - */ void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos); - -/*! @copydoc glfwGetWindowSize - * @ingroup platform - */ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height); - -/*! @copydoc glfwSetWindowSize - * @ingroup platform - */ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height); - -/*! @copydoc glfwSetWindowSizeLimits - * @ingroup platform - */ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); - -/*! @copydoc glfwSetWindowAspectRatio - * @ingroup platform - */ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); - -/*! @copydoc glfwGetFramebufferSize - * @ingroup platform - */ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); - -/*! @copydoc glfwGetWindowFrameSize - * @ingroup platform - */ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); - -/*! @copydoc glfwIconifyWindow - * @ingroup platform - */ +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale); void _glfwPlatformIconifyWindow(_GLFWwindow* window); - -/*! @copydoc glfwRestoreWindow - * @ingroup platform - */ void _glfwPlatformRestoreWindow(_GLFWwindow* window); - -/*! @copydoc glfwMaximizeWindow - * @ingroup platform - */ void _glfwPlatformMaximizeWindow(_GLFWwindow* window); - -/*! @copydoc glfwShowWindow - * @ingroup platform - */ void _glfwPlatformShowWindow(_GLFWwindow* window); - -/*! @copydoc glfwHideWindow - * @ingroup platform - */ void _glfwPlatformHideWindow(_GLFWwindow* window); - -/*! @copydoc glfwFocusWindow - * @ingroup platform - */ +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window); void _glfwPlatformFocusWindow(_GLFWwindow* window); - -/*! @copydoc glfwSetWindowMonitor - * @ingroup platform - */ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); - -/*! @brief Returns whether the window is focused. - * @ingroup platform - */ int _glfwPlatformWindowFocused(_GLFWwindow* window); - -/*! @brief Returns whether the window is iconified. - * @ingroup platform - */ int _glfwPlatformWindowIconified(_GLFWwindow* window); - -/*! @brief Returns whether the window is visible. - * @ingroup platform - */ int _glfwPlatformWindowVisible(_GLFWwindow* window); - -/*! @brief Returns whether the window is maximized. - * @ingroup platform - */ int _glfwPlatformWindowMaximized(_GLFWwindow* window); +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); -/*! @copydoc glfwPollEvents - * @ingroup platform - */ void _glfwPlatformPollEvents(void); - -/*! @copydoc glfwWaitEvents - * @ingroup platform - */ void _glfwPlatformWaitEvents(void); - -/*! @copydoc glfwWaitEventsTimeout - * @ingroup platform - */ void _glfwPlatformWaitEventsTimeout(double timeout); - -/*! @copydoc glfwPostEmptyEvent - * @ingroup platform - */ void _glfwPlatformPostEmptyEvent(void); -/*! @ingroup platform - */ -void _glfwPlatformSetCurrentContext(_GLFWwindow* context); - -/*! @copydoc glfwGetCurrentContext - * @ingroup platform - */ -_GLFWwindow* _glfwPlatformGetCurrentContext(void); - -/*! @copydoc glfwCreateCursor - * @ingroup platform - */ -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); - -/*! @copydoc glfwCreateStandardCursor - * @ingroup platform - */ -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); - -/*! @copydoc glfwDestroyCursor - * @ingroup platform - */ -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); - -/*! @copydoc glfwSetCursor - * @ingroup platform - */ -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); - -/*! @ingroup platform - */ -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count); - -/*! @ingroup platform - */ +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); - -/*! @ingroup platform - */ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls); +void _glfwPlatformDestroyTls(_GLFWtls* tls); +void* _glfwPlatformGetTls(_GLFWtls* tls); +void _glfwPlatformSetTls(_GLFWtls* tls, void* value); + +GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex); +void _glfwPlatformDestroyMutex(_GLFWmutex* mutex); +void _glfwPlatformLockMutex(_GLFWmutex* mutex); +void _glfwPlatformUnlockMutex(_GLFWmutex* mutex); + +/*! @} */ + //======================================================================== // Event API functions //======================================================================== -/*! @brief Notifies shared code of a window focus event. +/*! @brief Notifies shared code that a window has lost or received input focus. * @param[in] window The window that received the event. * @param[in] focused `GLFW_TRUE` if the window received focus, or `GLFW_FALSE` * if it lost focus. @@ -815,7 +727,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* wind */ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); -/*! @brief Notifies shared code of a window movement event. +/*! @brief Notifies shared code that a window has moved. * @param[in] window The window that received the event. * @param[in] xpos The new x-coordinate of the client area of the window. * @param[in] ypos The new y-coordinate of the client area of the window. @@ -823,7 +735,7 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); */ void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); -/*! @brief Notifies shared code of a window resize event. +/*! @brief Notifies shared code that a window has been resized. * @param[in] window The window that received the event. * @param[in] width The new width of the client area of the window. * @param[in] height The new height of the client area of the window. @@ -831,7 +743,7 @@ void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); */ void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); -/*! @brief Notifies shared code of a framebuffer resize event. +/*! @brief Notifies shared code that a window framebuffer has been resized. * @param[in] window The window that received the event. * @param[in] width The new width, in pixels, of the framebuffer. * @param[in] height The new height, in pixels, of the framebuffer. @@ -839,7 +751,7 @@ void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); */ void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); -/*! @brief Notifies shared code of a window iconification event. +/*! @brief Notifies shared code that a window has been iconified or restored. * @param[in] window The window that received the event. * @param[in] iconified `GLFW_TRUE` if the window was iconified, or * `GLFW_FALSE` if it was restored. @@ -847,18 +759,31 @@ void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); */ void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified); -/*! @brief Notifies shared code of a window damage event. +/*! @brief Notifies shared code that a window has been maximized or restored. + * @param[in] window The window that received the event. + * @param[in] maximized `GLFW_TRUE` if the window was maximized, or + * `GLFW_FALSE` if it was restored. + * @ingroup event + */ +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized); + +/*! @brief Notifies shared code that a window's contents needs updating. * @param[in] window The window that received the event. */ void _glfwInputWindowDamage(_GLFWwindow* window); -/*! @brief Notifies shared code of a window close request event +/*! @brief Notifies shared code that the user wishes to close a window. * @param[in] window The window that received the event. * @ingroup event */ void _glfwInputWindowCloseRequest(_GLFWwindow* window); -void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor); +/*! @brief Notifies shared code that a window has changed its desired monitor. + * @param[in] window The window that received the event. + * @param[in] monitor The new desired monitor, or `NULL`. + * @ingroup event + */ +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor); /*! @brief Notifies shared code of a physical key event. * @param[in] window The window that received the event. @@ -892,6 +817,7 @@ void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); * @param[in] window The window that received the event. * @param[in] button The button that was pressed or released. * @param[in] action @ref GLFW_PRESS or @ref GLFW_RELEASE. + * @param[in] mods The modifiers pressed when the event was generated. * @ingroup event */ void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); @@ -914,27 +840,35 @@ void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); */ void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); -/*! @ingroup event +/*! @brief Notifies shared code of a monitor connection or disconnection. + * @param[in] monitor The monitor that was connected or disconnected. + * @param[in] action One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * @param[in] placement `_GLFW_INSERT_FIRST` or `_GLFW_INSERT_LAST`. + * @ingroup event */ -void _glfwInputMonitorChange(void); +void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); -/*! @ingroup event +/*! @brief Notifies shared code that a full screen window has acquired or + * released a monitor. + * @param[in] monitor The monitor that was acquired or released. + * @param[in] window The window that acquired the monitor, or `NULL`. + * @ingroup event */ -void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window); +void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); /*! @brief Notifies shared code of an error. - * @param[in] error The error code most suitable for the error. + * @param[in] code The error code most suitable for the error. * @param[in] format The `printf` style format string of the error * description. * @ingroup event */ #if defined(__GNUC__) -void _glfwInputError(int error, const char* format, ...) __attribute__((format(printf, 2, 3))); +void _glfwInputError(int code, const char* format, ...) __attribute__((format(printf, 2, 3))); #else -void _glfwInputError(int error, const char* format, ...); +void _glfwInputError(int code, const char* format, ...); #endif -/*! @brief Notifies dropped object over window. +/*! @brief Notifies shared code of files or directories dropped on a window. * @param[in] window The window that received the event. * @param[in] count The number of dropped objects. * @param[in] names The names of the dropped objects. @@ -942,19 +876,41 @@ void _glfwInputError(int error, const char* format, ...); */ void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); -/*! @brief Notifies shared code of a joystick connection/disconnection event. - * @param[in] joy The joystick that was connected or disconnected. +/*! @brief Notifies shared code of a joystick connection or disconnection. + * @param[in] js The joystick that was connected or disconnected. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. * @ingroup event */ -void _glfwInputJoystickChange(int joy, int event); +void _glfwInputJoystick(_GLFWjoystick* js, int event); + +/*! @brief Notifies shared code of the new value of a joystick axis. + * @param[in] js The joystick whose axis to update. + * @param[in] axis The index of the axis to update. + * @param[in] value The new value of the axis. + */ +void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); + +/*! @brief Notifies shared code of the new value of a joystick button. + * @param[in] js The joystick whose button to update. + * @param[in] button The index of the button to update. + * @param[in] value The new value of the button. + */ +void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); + +/*! @brief Notifies shared code of the new value of a joystick hat. + * @param[in] js The joystick whose hat to update. + * @param[in] button The index of the hat to update. + * @param[in] value The new value of the hat. + */ +void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); //======================================================================== // Utility functions //======================================================================== -/*! @ingroup utility +/*! @brief Chooses the video mode most closely matching the desired one. + * @ingroup utility */ const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, const GLFWvidmode* desired); @@ -1009,11 +965,13 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig); */ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig); -/*! @ingroup utility +/*! @brief Allocates red, green and blue value arrays of the specified size. + * @ingroup utility */ void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); -/*! @ingroup utility +/*! @brief Frees the red, green and blue value arrays and clears the struct. + * @ingroup utility */ void _glfwFreeGammaArrays(GLFWgammaramp* ramp); @@ -1032,17 +990,23 @@ _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM); */ void _glfwFreeMonitor(_GLFWmonitor* monitor); -/*! @ingroup utility +/*! @brief Returns an available joystick object with arrays and name allocated. + * @ingroup utility */ -void _glfwFreeMonitors(_GLFWmonitor** monitors, int count); +_GLFWjoystick* _glfwAllocJoystick(const char* name, + const char* guid, + int axisCount, + int buttonCount, + int hatCount); + +/*! @brief Frees arrays and name and flags the joystick object as unused. + * @ingroup utility + */ +void _glfwFreeJoystick(_GLFWjoystick* js); /*! @ingroup utility */ -GLFWbool _glfwIsPrintable(int key); - -/*! @ingroup utility - */ -GLFWbool _glfwInitVulkan(void); +GLFWbool _glfwInitVulkan(int mode); /*! @ingroup utility */ @@ -1052,4 +1016,3 @@ void _glfwTerminateVulkan(void); */ const char* _glfwGetVulkanResultString(VkResult result); -#endif // _glfw3_internal_h_ diff --git a/raylib/external/glfw/src/linux_joystick.c b/raylib/external/glfw/src/linux_joystick.c index 561b1eb..d73961f 100644 --- a/raylib/external/glfw/src/linux_joystick.c +++ b/raylib/external/glfw/src/linux_joystick.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 Linux - www.glfw.org +// GLFW 3.3 Linux - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -27,9 +27,6 @@ #include "internal.h" -#if defined(__linux__) -#include - #include #include #include @@ -40,129 +37,220 @@ #include #include #include -#endif // __linux__ +// Apply an EV_KEY event to the specified joystick +// +static void handleKeyEvent(_GLFWjoystick* js, int code, int value) +{ + _glfwInputJoystickButton(js, + js->linjs.keyMap[code - BTN_MISC], + value ? GLFW_PRESS : GLFW_RELEASE); +} + +// Apply an EV_ABS event to the specified joystick +// +static void handleAbsEvent(_GLFWjoystick* js, int code, int value) +{ + const int index = js->linjs.absMap[code]; + + if (code >= ABS_HAT0X && code <= ABS_HAT3Y) + { + static const char stateMap[3][3] = + { + { GLFW_HAT_CENTERED, GLFW_HAT_UP, GLFW_HAT_DOWN }, + { GLFW_HAT_LEFT, GLFW_HAT_LEFT_UP, GLFW_HAT_LEFT_DOWN }, + { GLFW_HAT_RIGHT, GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT_DOWN }, + }; + + const int hat = (code - ABS_HAT0X) / 2; + const int axis = (code - ABS_HAT0X) % 2; + int* state = js->linjs.hats[hat]; + + // NOTE: Looking at several input drivers, it seems all hat events use + // -1 for left / up, 0 for centered and 1 for right / down + if (value == 0) + state[axis] = 0; + else if (value < 0) + state[axis] = 1; + else if (value > 0) + state[axis] = 2; + + _glfwInputJoystickHat(js, index, stateMap[state[0]][state[1]]); + } + else + { + const struct input_absinfo* info = &js->linjs.absInfo[code]; + float normalized = value; + + const int range = info->maximum - info->minimum; + if (range) + { + // Normalize to 0.0 -> 1.0 + normalized = (normalized - info->minimum) / range; + // Normalize to -1.0 -> 1.0 + normalized = normalized * 2.0f - 1.0f; + } + + _glfwInputJoystickAxis(js, index, normalized); + } +} + +// Poll state of absolute axes +// +static void pollAbsState(_GLFWjoystick* js) +{ + int code; + + for (code = 0; code < ABS_CNT; code++) + { + if (js->linjs.absMap[code] < 0) + continue; + + struct input_absinfo* info = &js->linjs.absInfo[code]; + + if (ioctl(js->linjs.fd, EVIOCGABS(code), info) < 0) + continue; + + handleAbsEvent(js, code, info->value); + } +} + +#define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8))) // Attempt to open the specified joystick device // -#if defined(__linux__) static GLFWbool openJoystickDevice(const char* path) { - char axisCount, buttonCount; + int jid, code; char name[256] = ""; - int joy, fd, version; - _GLFWjoystickLinux* js; + char guid[33] = ""; + char evBits[(EV_CNT + 7) / 8] = {0}; + char keyBits[(KEY_CNT + 7) / 8] = {0}; + char absBits[(ABS_CNT + 7) / 8] = {0}; + int axisCount = 0, buttonCount = 0, hatCount = 0; + struct input_id id; + _GLFWjoystickLinux linjs = {0}; + _GLFWjoystick* js = NULL; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (!_glfw.linux_js.js[joy].present) + if (!_glfw.joysticks[jid].present) continue; - - if (strcmp(_glfw.linux_js.js[joy].path, path) == 0) + if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) return GLFW_FALSE; } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - if (!_glfw.linux_js.js[joy].present) - break; - } - - if (joy > GLFW_JOYSTICK_LAST) + linjs.fd = open(path, O_RDONLY | O_NONBLOCK); + if (linjs.fd == -1) return GLFW_FALSE; - fd = open(path, O_RDONLY | O_NONBLOCK); - if (fd == -1) - return GLFW_FALSE; - - // Verify that the joystick driver version is at least 1.0 - ioctl(fd, JSIOCGVERSION, &version); - if (version < 0x010000) + if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 || + ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 || + ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 || + ioctl(linjs.fd, EVIOCGID, &id) < 0) { - // It's an old 0.x interface (we don't support it) - close(fd); + _glfwInputError(GLFW_PLATFORM_ERROR, + "Linux: Failed to query input device: %s", + strerror(errno)); + close(linjs.fd); return GLFW_FALSE; } - if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0) + // Ensure this device supports the events expected of a joystick + if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits)) + { + close(linjs.fd); + return GLFW_FALSE; + } + + if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0) strncpy(name, "Unknown", sizeof(name)); - js = _glfw.linux_js.js + joy; - js->present = GLFW_TRUE; - js->name = strdup(name); - js->path = strdup(path); - js->fd = fd; + // Generate a joystick GUID that matches the SDL 2.0.5+ one + if (id.vendor && id.product && id.version) + { + sprintf(guid, "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000", + id.bustype & 0xff, id.bustype >> 8, + id.vendor & 0xff, id.vendor >> 8, + id.product & 0xff, id.product >> 8, + id.version & 0xff, id.version >> 8); + } + else + { + sprintf(guid, "%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", + id.bustype & 0xff, id.bustype >> 8, + name[0], name[1], name[2], name[3], + name[4], name[5], name[6], name[7], + name[8], name[9], name[10]); + } - ioctl(fd, JSIOCGAXES, &axisCount); - js->axisCount = (int) axisCount; - js->axes = calloc(axisCount, sizeof(float)); + for (code = BTN_MISC; code < KEY_CNT; code++) + { + if (!isBitSet(code, keyBits)) + continue; - ioctl(fd, JSIOCGBUTTONS, &buttonCount); - js->buttonCount = (int) buttonCount; - js->buttons = calloc(buttonCount, 1); + linjs.keyMap[code - BTN_MISC] = buttonCount; + buttonCount++; + } - _glfwInputJoystickChange(joy, GLFW_CONNECTED); + for (code = 0; code < ABS_CNT; code++) + { + linjs.absMap[code] = -1; + if (!isBitSet(code, absBits)) + continue; + + if (code >= ABS_HAT0X && code <= ABS_HAT3Y) + { + linjs.absMap[code] = hatCount; + hatCount++; + // Skip the Y axis + code++; + } + else + { + if (ioctl(linjs.fd, EVIOCGABS(code), &linjs.absInfo[code]) < 0) + continue; + + linjs.absMap[code] = axisCount; + axisCount++; + } + } + + js = _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount); + if (!js) + { + close(linjs.fd); + return GLFW_FALSE; + } + + strncpy(linjs.path, path, sizeof(linjs.path)); + memcpy(&js->linjs, &linjs, sizeof(linjs)); + + pollAbsState(js); + + _glfwInputJoystick(js, GLFW_CONNECTED); return GLFW_TRUE; } -#endif // __linux__ -// Polls for and processes events the specified joystick +#undef isBitSet + +// Frees all resources associated with the specified joystick // -static GLFWbool pollJoystickEvents(_GLFWjoystickLinux* js) +static void closeJoystick(_GLFWjoystick* js) { -#if defined(__linux__) - _glfwPollJoystickEvents(); - - if (!js->present) - return GLFW_FALSE; - - // Read all queued events (non-blocking) - for (;;) - { - struct js_event e; - - errno = 0; - if (read(js->fd, &e, sizeof(e)) < 0) - { - // Reset the joystick slot if the device was disconnected - if (errno == ENODEV) - { - free(js->axes); - free(js->buttons); - free(js->name); - free(js->path); - - memset(js, 0, sizeof(_GLFWjoystickLinux)); - - _glfwInputJoystickChange(js - _glfw.linux_js.js, - GLFW_DISCONNECTED); - } - - break; - } - - // Clear the initial-state bit - e.type &= ~JS_EVENT_INIT; - - if (e.type == JS_EVENT_AXIS) - js->axes[e.number] = (float) e.value / 32767.0f; - else if (e.type == JS_EVENT_BUTTON) - js->buttons[e.number] = e.value ? GLFW_PRESS : GLFW_RELEASE; - } -#endif // __linux__ - return js->present; + close(js->linjs.fd); + _glfwFreeJoystick(js); + _glfwInputJoystick(js, GLFW_DISCONNECTED); } // Lexically compare joysticks by name; used by qsort // -#if defined(__linux__) static int compareJoysticks(const void* fp, const void* sp) { - const _GLFWjoystickLinux* fj = fp; - const _GLFWjoystickLinux* sj = sp; - return strcmp(fj->path, sj->path); + const _GLFWjoystick* fj = fp; + const _GLFWjoystick* sj = sp; + return strcmp(fj->linjs.path, sj->linjs.path); } -#endif // __linux__ ////////////////////////////////////////////////////////////////////////// @@ -173,36 +261,24 @@ static int compareJoysticks(const void* fp, const void* sp) // GLFWbool _glfwInitJoysticksLinux(void) { -#if defined(__linux__) DIR* dir; int count = 0; const char* dirname = "/dev/input"; - _glfw.linux_js.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); - if (_glfw.linux_js.inotify == -1) + _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (_glfw.linjs.inotify > 0) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Linux: Failed to initialize inotify: %s", - strerror(errno)); - return GLFW_FALSE; + // HACK: Register for IN_ATTRIB to get notified when udev is done + // This works well in practice but the true way is libudev + + _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify, + dirname, + IN_CREATE | IN_ATTRIB | IN_DELETE); } - // HACK: Register for IN_ATTRIB as well to get notified when udev is done - // This works well in practice but the true way is libudev + // Continue without device connection notifications if inotify fails - _glfw.linux_js.watch = inotify_add_watch(_glfw.linux_js.inotify, - dirname, - IN_CREATE | IN_ATTRIB); - if (_glfw.linux_js.watch == -1) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Linux: Failed to watch for joystick connections in %s: %s", - dirname, - strerror(errno)); - // Continue without device connection notifications - } - - if (regcomp(&_glfw.linux_js.regex, "^js[0-9]\\+$", 0) != 0) + if (regcomp(&_glfw.linjs.regex, "^event[0-9]\\+$", 0) != 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex"); return GLFW_FALSE; @@ -215,31 +291,25 @@ GLFWbool _glfwInitJoysticksLinux(void) while ((entry = readdir(dir))) { - char path[20]; regmatch_t match; - if (regexec(&_glfw.linux_js.regex, entry->d_name, 1, &match, 0) != 0) + if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0) continue; + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); + if (openJoystickDevice(path)) count++; } closedir(dir); } - else - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Linux: Failed to open joystick device directory %s: %s", - dirname, - strerror(errno)); - // Continue with no joysticks detected - } - qsort(_glfw.linux_js.js, count, sizeof(_GLFWjoystickLinux), compareJoysticks); -#endif // __linux__ + // Continue with no joysticks if enumeration fails + qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks); return GLFW_TRUE; } @@ -247,56 +317,65 @@ GLFWbool _glfwInitJoysticksLinux(void) // void _glfwTerminateJoysticksLinux(void) { -#if defined(__linux__) - int i; + int jid; - for (i = 0; i <= GLFW_JOYSTICK_LAST; i++) + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (_glfw.linux_js.js[i].present) - { - close(_glfw.linux_js.js[i].fd); - free(_glfw.linux_js.js[i].axes); - free(_glfw.linux_js.js[i].buttons); - free(_glfw.linux_js.js[i].name); - free(_glfw.linux_js.js[i].path); - } + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + closeJoystick(js); } - regfree(&_glfw.linux_js.regex); + regfree(&_glfw.linjs.regex); - if (_glfw.linux_js.inotify > 0) + if (_glfw.linjs.inotify > 0) { - if (_glfw.linux_js.watch > 0) - inotify_rm_watch(_glfw.linux_js.inotify, _glfw.linux_js.watch); + if (_glfw.linjs.watch > 0) + inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch); - close(_glfw.linux_js.inotify); + close(_glfw.linjs.inotify); } -#endif // __linux__ } -void _glfwPollJoystickEvents(void) +void _glfwDetectJoystickConnectionLinux(void) { -#if defined(__linux__) ssize_t offset = 0; char buffer[16384]; - const ssize_t size = read(_glfw.linux_js.inotify, buffer, sizeof(buffer)); + if (_glfw.linjs.inotify <= 0) + return; + + const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer)); while (size > offset) { regmatch_t match; const struct inotify_event* e = (struct inotify_event*) (buffer + offset); - if (regexec(&_glfw.linux_js.regex, e->name, 1, &match, 0) == 0) - { - char path[20]; - snprintf(path, sizeof(path), "/dev/input/%s", e->name); - openJoystickDevice(path); - } - offset += sizeof(struct inotify_event) + e->len; + + if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0) + continue; + + char path[PATH_MAX]; + snprintf(path, sizeof(path), "/dev/input/%s", e->name); + + if (e->mask & (IN_CREATE | IN_ATTRIB)) + openJoystickDevice(path); + else if (e->mask & IN_DELETE) + { + int jid; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) + { + closeJoystick(_glfw.joysticks + jid); + break; + } + } + } } -#endif } @@ -304,38 +383,47 @@ void _glfwPollJoystickEvents(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformJoystickPresent(int joy) +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; - return pollJoystickEvents(js); + // Read all queued events (non-blocking) + for (;;) + { + struct input_event e; + + errno = 0; + if (read(js->linjs.fd, &e, sizeof(e)) < 0) + { + // Reset the joystick slot if the device was disconnected + if (errno == ENODEV) + closeJoystick(js); + + break; + } + + if (e.type == EV_SYN) + { + if (e.code == SYN_DROPPED) + _glfw.linjs.dropped = GLFW_TRUE; + else if (e.code == SYN_REPORT) + { + _glfw.linjs.dropped = GLFW_FALSE; + pollAbsState(js); + } + } + + if (_glfw.linjs.dropped) + continue; + + if (e.type == EV_KEY) + handleKeyEvent(js, e.code, e.value); + else if (e.type == EV_ABS) + handleAbsEvent(js, e.code, e.value); + } + + return js->present; } -const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +void _glfwPlatformUpdateGamepadGUID(char* guid) { - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; - if (!pollJoystickEvents(js)) - return NULL; - - *count = js->axisCount; - return js->axes; -} - -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) -{ - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; - if (!pollJoystickEvents(js)) - return NULL; - - *count = js->buttonCount; - return js->buttons; -} - -const char* _glfwPlatformGetJoystickName(int joy) -{ - _GLFWjoystickLinux* js = _glfw.linux_js.js + joy; - if (!pollJoystickEvents(js)) - return NULL; - - return js->name; } diff --git a/raylib/external/glfw/src/linux_joystick.h b/raylib/external/glfw/src/linux_joystick.h index e9d1f2b..2eabfa1 100644 --- a/raylib/external/glfw/src/linux_joystick.h +++ b/raylib/external/glfw/src/linux_joystick.h @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.2 Linux - www.glfw.org +// GLFW 3.3 Linux - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // @@ -24,45 +24,39 @@ // //======================================================================== -#ifndef _glfw3_linux_joystick_h_ -#define _glfw3_linux_joystick_h_ - +#include +#include #include -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWjoylistLinux linux_js +#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs +#define _GLFW_PLATFORM_MAPPING_NAME "Linux" // Linux-specific joystick data // typedef struct _GLFWjoystickLinux { - GLFWbool present; - int fd; - float* axes; - int axisCount; - unsigned char* buttons; - int buttonCount; - char* name; - char* path; + int fd; + char path[PATH_MAX]; + int keyMap[KEY_CNT - BTN_MISC]; + int absMap[ABS_CNT]; + struct input_absinfo absInfo[ABS_CNT]; + int hats[4][2]; } _GLFWjoystickLinux; // Linux-specific joystick API data // -typedef struct _GLFWjoylistLinux +typedef struct _GLFWlibraryLinux { - _GLFWjoystickLinux js[GLFW_JOYSTICK_LAST + 1]; - -#if defined(__linux__) - int inotify; - int watch; - regex_t regex; -#endif /*__linux__*/ -} _GLFWjoylistLinux; + int inotify; + int watch; + regex_t regex; + GLFWbool dropped; +} _GLFWlibraryLinux; GLFWbool _glfwInitJoysticksLinux(void); void _glfwTerminateJoysticksLinux(void); +void _glfwDetectJoystickConnectionLinux(void); -void _glfwPollJoystickEvents(void); - -#endif // _glfw3_linux_joystick_h_ diff --git a/raylib/external/glfw/src/mappings.h b/raylib/external/glfw/src/mappings.h new file mode 100644 index 0000000..dd74760 --- /dev/null +++ b/raylib/external/glfw/src/mappings.h @@ -0,0 +1,310 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As mappings.h.in, this file is used by CMake to produce the mappings.h +// header file. If you are adding a GLFW specific gamepad mapping, this is +// where to put it. +//======================================================================== +// As mappings.h, this provides all pre-defined gamepad mappings, including +// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad +// mappings not specific to GLFW should be submitted to SDL_GameControllerDB. +// This file can be re-generated from mappings.h.in and the upstream +// gamecontrollerdb.txt with the GenerateMappings.cmake script. +//======================================================================== + +// All gamepad mappings not labeled GLFW are copied from the +// SDL_GameControllerDB project under the following license: +// +// Simple DirectMedia Layer +// Copyright (C) 1997-2013 Sam Lantinga +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +const char* _glfwDefaultMappings[] = +{ +"02200090000000000000504944564944,8Bitdo NES30 PRO USB,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"20380900000000000000504944564944,8Bitdo NES30 PRO Wireless,platform:Windows,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,leftstick:b13,rightstick:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"10280900000000000000504944564944,8Bitdo SFC30 GamePad,a:b1,b:b0,y:b3,x:b4,start:b11,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,platform:Windows,", +"8f0e1200000000000000504944564944,Acme,platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,", +"341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"c0111352000000000000504944564944,Battalife Joystick,platform:Windows,x:b4,a:b6,b:b7,y:b5,back:b2,start:b3,leftshoulder:b0,rightshoulder:b1,leftx:a0,lefty:a1,", +"d81d0b00000000000000504944564944,BUFFALO BSGP1601 Series ,platform:Windows,x:b4,a:b5,b:b3,y:b2,back:b12,start:b13,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b6,rightshoulder:b9,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"5e048e02000000000000504944564944,Controller (XBOX 360 For Windows),platform:Windows,x:b2,a:b0,b:b1,y:b3,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,righttrigger:a2,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"4f0423b3000000000000504944564944,Dual Trigger 3-in-1,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"341a0108000000000000504944564944,EXEQ RF USB Gamepad 8206,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,leftstick:b8,rightstick:b7,back:b8,start:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,", +"0d0f8500000000000000504944564944,Fighting Commander 2016 PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"0d0f5f00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"0d0f5e00000000000000504944564944,Fighting Commander 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:a3,platform:Windows,", +"0d0f8400000000000000504944564944,Fighting Commander 5,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"0d0f8700000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"0d0f8800000000000000504944564944,Fighting Stick mini 4,a:b1,b:b2,x:b0,y:b3,back:b9,guide:b12,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"0d0f2700000000000000504944564944,FIGHTING STICK V3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"79000600000000000000504944564944,G-Shark GS-GP702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"28040140000000000000504944564944,GamePad Pro USB,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,", +"ffff0000000000000000504944564944,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", +"6d0416c2000000000000504944564944,Generic DirectInput Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"45130010000000000000504944564944,Generic USB Joystick,a:b0,b:b1,x:b2,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"0d0f4900000000000000504944564944,Hatsune Miku Sho Controller,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"d8140862000000000000504944564944,HitBox Edition Cthulhu+,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,", +"0d0f4000000000000000504944564944,Hori Fighting Stick Mini 3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b4,righttrigger:b6,platform:Windows,", +"0d0f6e00000000000000504944564944,HORIPAD 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"0d0f4d00000000000000504944564944,HORIPAD3 A,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"25090017000000000000504944564944,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b5,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b6,platform:Windows,", +"d81d0f00000000000000504944564944,iBUFFALO BSGP1204 Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"d81d1000000000000000504944564944,iBUFFALO BSGP1204P Series,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"83056020000000000000504944564944,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,y:b2,x:b3,start:b7,back:b6,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Windows,", +"6f0e2401000000000000504944564944,INJUSTICE FightStick for PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Windows", +"49190204000000000000504944564944,Ipega PG-9023,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows,", +"6d0419c2000000000000504944564944,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", +"6d0418c2000000000000504944564944,Logitech RumblePad 2 USB,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"38075032000000000000504944564944,Mad Catz FightPad PRO PS3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"38075082000000000000504944564944,Mad Catz FightPad PRO PS4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b13,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,", +"38078433000000000000504944564944,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"38078483000000000000504944564944,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:b6,platform:Windows,", +"38078134000000000000504944564944,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b7,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b4,platform:Windows,", +"38078184000000000000504944564944,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a4,righttrigger:b7,platform:Windows,", +"38078034000000000000504944564944,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"38078084000000000000504944564944,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,", +"38078532000000000000504944564944,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"38073888000000000000504944564944,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"38071888000000000000504944564944,MadCatz SFIV FightStick PS3,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b7,righttrigger:b6,platform:Windows,", +"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"25090128000000000000504944564944,Mayflash Arcade Stick,a:b1,b:b2,x:b5,y:b6,back:b8,start:b9,leftshoulder:b0,rightshoulder:b3,leftx:a0,lefty:a1,rightx:h0.4,righty:h0.0,lefttrigger:b4,righttrigger:b7,platform:Windows,", +"79004318000000000000504944564944,Mayflash GameCube Controller Adapter,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b0,start:b9,guide:b0,leftshoulder:b4,rightshoulder:b7,leftstick:b0,rightstick:b0,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,", +"8f0e1030000000000000504944564944,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,x:b3,y:b4,start:b9,leftshoulder:b6,rightshoulder:b2,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b5,righttrigger:b7,platform:Windows,", +"2509e803000000000000504944564944,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"79000018000000000000504944564944,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"8f0e0d31000000000000504944564944,Multilaser JS071 USB,platform:Windows,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,", +"100801e5000000000000504944564944,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Windows,", +"bd1215d0000000000000504944564944,Nintendo Retrolink USB Super SNES Classic Controller,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,start:b9,back:b8,leftx:a0,lefty:a1,platform:Windows,", +"4b12014d000000000000504944564944,NYKO AIRFLO,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b10,start:b9,leftstick:a0,rightstick:a2,leftshoulder:a3,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:h0.6,lefty:h0.12,rightx:h0.9,righty:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"36280100000000000000504944564944,OUYA Controller,platform:Windows,a:b0,b:b3,y:b2,x:b1,start:b14,guide:b15,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b8,dpleft:b10,dpdown:b9,dpright:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b12,righttrigger:b13,", +"120cf60e000000000000504944564944,P4 Wired Gamepad,a:b1,b:b2,x:b0,y:b3,back:b12,guide:b8,start:b9,leftshoulder:b5,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.0,lefttrigger:b7,righttrigger:b6,platform:Windows,", +"8f0e0300000000000000504944564944,Piranha xtreme,platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,", +"d6206dca000000000000504944564944,PowerA Pro Ex,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.0,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"10080100000000000000504944564944,PS1 USB,platform:Windows,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,", +"10080300000000000000504944564944,PS2 USB,platform:Windows,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a4,righty:a2,lefttrigger:b4,righttrigger:b5,", +"88880803000000000000504944564944,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", +"4c056802000000000000504944564944,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,", +"25090500000000000000504944564944,PS3 DualShock,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,", +"10008200000000000000504944564944,PS360+ v1.66,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:h0.4,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"4c05c405000000000000504944564944,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", +"300f0011000000000000504944564944,QanBa Arcade JoyStick 1008,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,start:b10,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,", +"300f1611000000000000504944564944,QanBa Arcade JoyStick 4018,a:b1,b:b2,x:b0,y:b3,back:b10,guide:b9,start:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"222c0020000000000000504944564944,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:a3,righttrigger:a4,platform:Windows,", +"300f1210000000000000504944564944,QanBa Joystick Plus,a:b0,b:b1,x:b2,y:b3,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,platform:Windows,", +"341a0104000000000000504944564944,QanBa Joystick Q4RAF,a:b5,b:b6,x:b1,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b0,rightshoulder:b3,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b7,platform:Windows,", +"222c0223000000000000504944564944,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"222c0023000000000000504944564944,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,x:b0,y:b3,back:b13,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,", +"0d0f1100000000000000504944564944,REAL ARCADE PRO.3,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"0d0f8b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"0d0f8a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,", +"0d0f6b00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"0d0f6a00000000000000504944564944,Real Arcade Pro.4,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,", +"0d0f7000000000000000504944564944,REAL ARCADE PRO.4 VLX,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"0d0f2200000000000000504944564944,REAL ARCADE Pro.V3,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"00f00300000000000000504944564944,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,", +"00f0f100000000000000504944564944,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Windows,", +"6f0e1e01000000000000504944564944,Rock Candy Gamepad for PS3,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,", +"300f1201000000000000504944564944,Saitek Dual Analog Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,", +"a3060cff000000000000504944564944,Saitek P2500,a:b2,b:b3,y:b1,x:b0,start:b4,guide:b10,back:b5,leftstick:b8,rightstick:b9,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Windows,", +"300f1001000000000000504944564944,Saitek P480 Rumble Pad,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Windows,", +"9b280500000000000000504944564944,Saturn_Adapter_2.0,a:b1,b:b2,x:b0,y:b3,start:b9,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b4,righttrigger:b5,platform:Windows,", +"79001100000000000000504944564944,Sega Saturn Gamepad,a:b1,b:b2,x:b4,y:b5,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a4,lefttrigger:b3,righttrigger:b0,platform:Windows,", +"4c05cc09000000000000504944564944,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,", +"4c05a00b000000000000504944564944,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Windows,", +"ff113133000000000000504944564944,SVEN X-PAD,platform:Windows,a:b2,b:b3,y:b1,x:b0,start:b5,back:b4,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b8,righttrigger:b9,", +"4f0415b3000000000000504944564944,Thrustmaster Dual Analog 3.2,platform:Windows,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"4f0400b3000000000000504944564944,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Windows,", +"66660488000000000000504944564944,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:b12,dpdown:b14,dpleft:b15,dpright:b13,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,platform:Windows,", +"38076652000000000000504944564944,UnKnown,platform:Windows,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"63252305000000000000504944564944,USB Vibration Joystick (BM),platform:Windows,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"79001b18000000000000504944564944,Venom Arcade Joystick,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,lefttrigger:b6,righttrigger:b7,platform:Windows,", +"10280000000000000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,", +"79000000000000000600000000000000,G-Shark GP-702,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,", +"AD1B00000000000001F9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", +"0d0f0000000000004d00000000000000,HORI Gem Pad 3,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,", +"0d0f0000000000006600000000000000,HORIPAD FPS PLUS 4,platform:Mac OS X,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:a4,", +"83050000000000006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,", +"6d0400000000000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"6d0400000000000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"6d040000000000001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"6d0400000000000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", +"2509000000000000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,x:b3,y:b2,back:b8,guide:b10,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:b11,dpdown:b13,dpleft:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,", +"79000000000000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,x:b0,y:b12,back:b32,start:b36,leftstick:b40,rightstick:b44,leftshoulder:b16,rightshoulder:b20,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a4,rightx:a8,righty:a12,lefttrigger:b24,righttrigger:b28,platform:Mac OS X,", +"d814000000000000cecf000000000000,MC Cthulhu,platform:Mac OS X,leftx:,lefty:,rightx:,righty:,lefttrigger:b6,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,righttrigger:b7,", +"8f0e0000000000000300000000000000,Piranha xtreme,platform:Mac OS X,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,", +"4c050000000000006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", +"4c05000000000000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", +"891600000000000000fd000000000000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b8,guide:b10,back:b9,leftstick:b6,rightstick:b7,leftshoulder:b4,rightshoulder:b5,dpup:b11,dpleft:b13,dpdown:b12,dpright:b14,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Mac OS X,", +"79000000000000001100000000000000,Retrolink Classic Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a3,lefty:a4,platform:Mac OS X,", +"81170000000000007e05000000000000,Sega Saturn,x:b0,a:b2,b:b4,y:b6,start:b13,dpleft:b15,dpdown:b16,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,lefttrigger:b10,rightshoulder:b9,righttrigger:a4,righttrigger:b11,leftx:a0,lefty:a2,platform:Mac OS X,", +"b4040000000000000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,x:b3,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,", +"351200000000000021ab000000000000,SFC30 Joystick,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Mac OS X,", +"4c05000000000000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,", +"4c05000000000000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X,", +"4f0400000000000015b3000000000000,Thrustmaster Dual Analog 3.2,platform:Mac OS X,x:b1,a:b0,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"4f0400000000000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Mac OS X,", +"bd1200000000000015d0000000000000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Mac OS X,", +"10080000000000000100000000000000,Twin USB Joystick,a:b4,b:b2,x:b6,y:b0,back:b16,start:b18,leftstick:b20,rightstick:b22,leftshoulder:b12,rightshoulder:b14,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a2,rightx:a6,righty:a4,lefttrigger:b8,righttrigger:b10,platform:Mac OS X,", +"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,y:b9,x:b10,start:b6,guide:b8,back:b7,dpup:b2,dpleft:b0,dpdown:b3,dpright:b1,leftx:a0,lefty:a1,lefttrigger:b12,righttrigger:,leftshoulder:b11,platform:Mac OS X,", +"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,x:b18,y:b17,back:b7,guide:b8,start:b6,leftstick:b23,rightstick:b24,leftshoulder:b19,rightshoulder:b20,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b21,righttrigger:b22,platform:Mac OS X,", +"5e040000000000008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", +"5e04000000000000dd02000000000000,Xbox One Wired Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"5e04000000000000ea02000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b9,guide:b10,start:b8,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b6,rightstick:b7,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"5e04000000000000e002000000000000,Xbox Wireless Controller,platform:Mac OS X,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b10,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"05000000102800000900000000010000,8Bitdo SFC30 GamePad,platform:Linux,x:b4,a:b1,b:b0,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,", +"05000000a00500003232000001000000,8Bitdo Zero GamePad,platform:Linux,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,", +"030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,", +"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"030000006f0e00003001000001010000,EA Sports PS3 Controller,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,", +"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,y:b3,x:b0,start:b9,guide:,back:,leftstick:,rightstick:,leftshoulder:,dpleft:b15,dpdown:b14,dpright:b13,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,rightshoulder:b7,dpup:b12,platform:Linux,", +"03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,y:b3,x:b1,start:b7,leftshoulder:,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,rightstick:,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux,", +"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", +"030000006f0e00001f01000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"030000006f0e00001304000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:a0,rightstick:a3,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"03000000280400000140000000010000,Gravis GamePad Pro USB ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1,", +"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,", +"030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,", +"03000000ff1100004133000010010000,GreenAsia Inc.USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,", +"06000000adde0000efbe000002010000,Hidromancer Game Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,leftx:a0,lefty:a1,", +"03000000c9110000f055000011010000,HJC Game GAMEPAD,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b2,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:h0.2,righttrigger:b7,b:b1,platform:Linux,", +"030000000d0f00000d00000000010000,hori,platform:Linux,a:b0,b:b6,y:b2,x:b1,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,start:b9,guide:b10,back:b8,leftshoulder:b3,rightshoulder:b7,leftx:b4,lefty:b5,", +"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7", +"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,", +"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,", +"03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,", +"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),platform:Linux,a:b3,b:b4,y:b1,x:b0,start:b7,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,", +"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b10,guide:b12,start:b11,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,", +"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", +"030000006f0e00000103000000020000,Logic3 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux,", +"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", +"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000006d04000016c2000010010000,Logitech Logitech Dual Action,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"030000006d04000018c2000010010000,Logitech Logitech RumblePad 2 USB,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,y:b4,x:b3,start:b8,guide:b5,back:b2,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b9,righttrigger:b10,platform:Linux,", +"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:a2,righttrigger:a5,", +"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,", +"03000000780000000600000010010000,Microntek USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftx:a0,lefty:a1,", +"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,", +"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"030000005e040000d102000001010000,Microsoft X-Box One pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"030000005e040000dd02000003020000,Microsoft X-Box One pad v2,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux,", +"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"05000000d6200000ad0d000001000000,Moga Pro,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,", +"030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Linux,", +"050000007e0500003003000001000000,Nintendo Wii U Pro Controller,platform:Linux,a:b0,b:b1,x:b3,y:b2,back:b8,start:b9,guide:b10,leftshoulder:b4,rightshoulder:b5,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,", +"05000000010000000100000003000000,Nintendo Wiimote,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,", +"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,", +"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,", +"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,", +"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux,", +"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", +"060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,", +"050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,", +"05000000504c415953544154494f4e00,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux,", +"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,platform:Linux,x:b1,a:b0,b:b4,y:b5,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,", +"030000008916000001fd000024010000,Razer Onza Classic Edition,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:b11,dpdown:b14,dpright:b12,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,", +"03000000790000001100000010010000,RetroLink Saturn Classic Controller,platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,", +"0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,", +"0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux,", +"030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,", +"030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,platform:Linux,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Linux,", +"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,", +"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,y:b1,x:b0,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b6,righttrigger:b7,platform:Linux,", +"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,platform:Linux,a:b12,b:b10,x:b13,y:b11,back:b4,start:b5,leftstick:b14,rightstick:b15,leftshoulder:b9,rightshoulder:b8,dpup:b0,dpdown:b2,dpleft:b3,dpright:b1,leftx:a1,lefty:a0,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,", +"030000004c050000c405000011810000,Sony DualShock 4,a:b0,b:b1,y:b2,x:b3,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,", +"030000004c050000c405000011010000,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Linux,", +"050000004c050000cc09000000810000,Sony DualShock 4 (CUH-ZCT2U) (Bluetooth),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"030000004c050000cc09000011810000,Sony DualShock 4 (CUH-ZCT2U) (USB),platform:Linux,a:b0,b:b1,y:b2,x:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,guide:b10,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"050000004c050000c405000000010000,Sony DualShock 4 BT,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", +"030000004c050000cc09000011010000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,", +"050000004c050000cc09000000010000,Sony DualShock 4 V2 BT,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,", +"030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux,", +"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,platform:Linux,a:b2,b:b1,y:b0,x:b3,start:b8,back:b9,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,", +"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,", +"03000000666600000488000000010000,Super Joy Box 5 Pro,platform:Linux,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,dpup:b12,dpleft:b15,dpdown:b14,dpright:b13,", +"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,y:b3,x:b1,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,", +"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,platform:Linux,a:b0,b:b2,x:b1,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,", +"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5,", +"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux,", +"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,", +"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,", +"03000000bd12000015d0000010010000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux,", +"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,platform:Linux,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,", +"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,y:b0,x:b3,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux,", +"03000000100800000300000010010000,USB Gamepad,platform:Linux,a:b2,b:b1,x:b3,y:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,", +"03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"05000000ac0500003232000001000000,VR-BOX,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,", +"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,", +"030000005e040000a102000000010000,X360 Wireless Controller,platform:Linux,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,", +"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b7,back:b6,guide:b8,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,lefttrigger:a5,righttrigger:a4,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a2,righty:a3,", +"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", +"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,platform:Linux,y:b0,x:b1,b:b3,a:b4,leftshoulder:b2,rightshoulder:b5,back:b6,start:b7,guide:b9,dpleft:b13,dpdown:b12,dpright:b14,dpup:b11,leftx:a0,lefty:a1,", + +"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", +NULL +}; + diff --git a/raylib/external/glfw/src/mir_init.c b/raylib/external/glfw/src/mir_init.c index 3076f5f..5f9ed37 100644 --- a/raylib/external/glfw/src/mir_init.c +++ b/raylib/external/glfw/src/mir_init.c @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 Mir - www.glfw.org +// GLFW 3.3 Mir - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2014-2015 Brandon Schaefer +// Copyright (c) 2014-2017 Brandon Schaefer // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -35,124 +35,134 @@ // static void createKeyTables(void) { - memset(_glfw.mir.publicKeys, -1, sizeof(_glfw.mir.publicKeys)); + int scancode; - _glfw.mir.publicKeys[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; - _glfw.mir.publicKeys[KEY_1] = GLFW_KEY_1; - _glfw.mir.publicKeys[KEY_2] = GLFW_KEY_2; - _glfw.mir.publicKeys[KEY_3] = GLFW_KEY_3; - _glfw.mir.publicKeys[KEY_4] = GLFW_KEY_4; - _glfw.mir.publicKeys[KEY_5] = GLFW_KEY_5; - _glfw.mir.publicKeys[KEY_6] = GLFW_KEY_6; - _glfw.mir.publicKeys[KEY_7] = GLFW_KEY_7; - _glfw.mir.publicKeys[KEY_8] = GLFW_KEY_8; - _glfw.mir.publicKeys[KEY_9] = GLFW_KEY_9; - _glfw.mir.publicKeys[KEY_0] = GLFW_KEY_0; - _glfw.mir.publicKeys[KEY_MINUS] = GLFW_KEY_MINUS; - _glfw.mir.publicKeys[KEY_EQUAL] = GLFW_KEY_EQUAL; - _glfw.mir.publicKeys[KEY_Q] = GLFW_KEY_Q; - _glfw.mir.publicKeys[KEY_W] = GLFW_KEY_W; - _glfw.mir.publicKeys[KEY_E] = GLFW_KEY_E; - _glfw.mir.publicKeys[KEY_R] = GLFW_KEY_R; - _glfw.mir.publicKeys[KEY_T] = GLFW_KEY_T; - _glfw.mir.publicKeys[KEY_Y] = GLFW_KEY_Y; - _glfw.mir.publicKeys[KEY_U] = GLFW_KEY_U; - _glfw.mir.publicKeys[KEY_I] = GLFW_KEY_I; - _glfw.mir.publicKeys[KEY_O] = GLFW_KEY_O; - _glfw.mir.publicKeys[KEY_P] = GLFW_KEY_P; - _glfw.mir.publicKeys[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; - _glfw.mir.publicKeys[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; - _glfw.mir.publicKeys[KEY_A] = GLFW_KEY_A; - _glfw.mir.publicKeys[KEY_S] = GLFW_KEY_S; - _glfw.mir.publicKeys[KEY_D] = GLFW_KEY_D; - _glfw.mir.publicKeys[KEY_F] = GLFW_KEY_F; - _glfw.mir.publicKeys[KEY_G] = GLFW_KEY_G; - _glfw.mir.publicKeys[KEY_H] = GLFW_KEY_H; - _glfw.mir.publicKeys[KEY_J] = GLFW_KEY_J; - _glfw.mir.publicKeys[KEY_K] = GLFW_KEY_K; - _glfw.mir.publicKeys[KEY_L] = GLFW_KEY_L; - _glfw.mir.publicKeys[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; - _glfw.mir.publicKeys[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; - _glfw.mir.publicKeys[KEY_Z] = GLFW_KEY_Z; - _glfw.mir.publicKeys[KEY_X] = GLFW_KEY_X; - _glfw.mir.publicKeys[KEY_C] = GLFW_KEY_C; - _glfw.mir.publicKeys[KEY_V] = GLFW_KEY_V; - _glfw.mir.publicKeys[KEY_B] = GLFW_KEY_B; - _glfw.mir.publicKeys[KEY_N] = GLFW_KEY_N; - _glfw.mir.publicKeys[KEY_M] = GLFW_KEY_M; - _glfw.mir.publicKeys[KEY_COMMA] = GLFW_KEY_COMMA; - _glfw.mir.publicKeys[KEY_DOT] = GLFW_KEY_PERIOD; - _glfw.mir.publicKeys[KEY_SLASH] = GLFW_KEY_SLASH; - _glfw.mir.publicKeys[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; - _glfw.mir.publicKeys[KEY_ESC] = GLFW_KEY_ESCAPE; - _glfw.mir.publicKeys[KEY_TAB] = GLFW_KEY_TAB; - _glfw.mir.publicKeys[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; - _glfw.mir.publicKeys[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; - _glfw.mir.publicKeys[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; - _glfw.mir.publicKeys[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; - _glfw.mir.publicKeys[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; - _glfw.mir.publicKeys[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; - _glfw.mir.publicKeys[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; - _glfw.mir.publicKeys[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; - _glfw.mir.publicKeys[KEY_MENU] = GLFW_KEY_MENU; - _glfw.mir.publicKeys[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; - _glfw.mir.publicKeys[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; - _glfw.mir.publicKeys[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; - _glfw.mir.publicKeys[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; - _glfw.mir.publicKeys[KEY_PAUSE] = GLFW_KEY_PAUSE; - _glfw.mir.publicKeys[KEY_DELETE] = GLFW_KEY_DELETE; - _glfw.mir.publicKeys[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; - _glfw.mir.publicKeys[KEY_ENTER] = GLFW_KEY_ENTER; - _glfw.mir.publicKeys[KEY_HOME] = GLFW_KEY_HOME; - _glfw.mir.publicKeys[KEY_END] = GLFW_KEY_END; - _glfw.mir.publicKeys[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; - _glfw.mir.publicKeys[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; - _glfw.mir.publicKeys[KEY_INSERT] = GLFW_KEY_INSERT; - _glfw.mir.publicKeys[KEY_LEFT] = GLFW_KEY_LEFT; - _glfw.mir.publicKeys[KEY_RIGHT] = GLFW_KEY_RIGHT; - _glfw.mir.publicKeys[KEY_DOWN] = GLFW_KEY_DOWN; - _glfw.mir.publicKeys[KEY_UP] = GLFW_KEY_UP; - _glfw.mir.publicKeys[KEY_F1] = GLFW_KEY_F1; - _glfw.mir.publicKeys[KEY_F2] = GLFW_KEY_F2; - _glfw.mir.publicKeys[KEY_F3] = GLFW_KEY_F3; - _glfw.mir.publicKeys[KEY_F4] = GLFW_KEY_F4; - _glfw.mir.publicKeys[KEY_F5] = GLFW_KEY_F5; - _glfw.mir.publicKeys[KEY_F6] = GLFW_KEY_F6; - _glfw.mir.publicKeys[KEY_F7] = GLFW_KEY_F7; - _glfw.mir.publicKeys[KEY_F8] = GLFW_KEY_F8; - _glfw.mir.publicKeys[KEY_F9] = GLFW_KEY_F9; - _glfw.mir.publicKeys[KEY_F10] = GLFW_KEY_F10; - _glfw.mir.publicKeys[KEY_F11] = GLFW_KEY_F11; - _glfw.mir.publicKeys[KEY_F12] = GLFW_KEY_F12; - _glfw.mir.publicKeys[KEY_F13] = GLFW_KEY_F13; - _glfw.mir.publicKeys[KEY_F14] = GLFW_KEY_F14; - _glfw.mir.publicKeys[KEY_F15] = GLFW_KEY_F15; - _glfw.mir.publicKeys[KEY_F16] = GLFW_KEY_F16; - _glfw.mir.publicKeys[KEY_F17] = GLFW_KEY_F17; - _glfw.mir.publicKeys[KEY_F18] = GLFW_KEY_F18; - _glfw.mir.publicKeys[KEY_F19] = GLFW_KEY_F19; - _glfw.mir.publicKeys[KEY_F20] = GLFW_KEY_F20; - _glfw.mir.publicKeys[KEY_F21] = GLFW_KEY_F21; - _glfw.mir.publicKeys[KEY_F22] = GLFW_KEY_F22; - _glfw.mir.publicKeys[KEY_F23] = GLFW_KEY_F23; - _glfw.mir.publicKeys[KEY_F24] = GLFW_KEY_F24; - _glfw.mir.publicKeys[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; - _glfw.mir.publicKeys[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; - _glfw.mir.publicKeys[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; - _glfw.mir.publicKeys[KEY_KPPLUS] = GLFW_KEY_KP_ADD; - _glfw.mir.publicKeys[KEY_KP0] = GLFW_KEY_KP_0; - _glfw.mir.publicKeys[KEY_KP1] = GLFW_KEY_KP_1; - _glfw.mir.publicKeys[KEY_KP2] = GLFW_KEY_KP_2; - _glfw.mir.publicKeys[KEY_KP3] = GLFW_KEY_KP_3; - _glfw.mir.publicKeys[KEY_KP4] = GLFW_KEY_KP_4; - _glfw.mir.publicKeys[KEY_KP5] = GLFW_KEY_KP_5; - _glfw.mir.publicKeys[KEY_KP6] = GLFW_KEY_KP_6; - _glfw.mir.publicKeys[KEY_KP7] = GLFW_KEY_KP_7; - _glfw.mir.publicKeys[KEY_KP8] = GLFW_KEY_KP_8; - _glfw.mir.publicKeys[KEY_KP9] = GLFW_KEY_KP_9; - _glfw.mir.publicKeys[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; - _glfw.mir.publicKeys[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; - _glfw.mir.publicKeys[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + memset(_glfw.mir.keycodes, -1, sizeof(_glfw.mir.keycodes)); + memset(_glfw.mir.scancodes, -1, sizeof(_glfw.mir.scancodes)); + + _glfw.mir.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; + _glfw.mir.keycodes[KEY_1] = GLFW_KEY_1; + _glfw.mir.keycodes[KEY_2] = GLFW_KEY_2; + _glfw.mir.keycodes[KEY_3] = GLFW_KEY_3; + _glfw.mir.keycodes[KEY_4] = GLFW_KEY_4; + _glfw.mir.keycodes[KEY_5] = GLFW_KEY_5; + _glfw.mir.keycodes[KEY_6] = GLFW_KEY_6; + _glfw.mir.keycodes[KEY_7] = GLFW_KEY_7; + _glfw.mir.keycodes[KEY_8] = GLFW_KEY_8; + _glfw.mir.keycodes[KEY_9] = GLFW_KEY_9; + _glfw.mir.keycodes[KEY_0] = GLFW_KEY_0; + _glfw.mir.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; + _glfw.mir.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; + _glfw.mir.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; + _glfw.mir.keycodes[KEY_Q] = GLFW_KEY_Q; + _glfw.mir.keycodes[KEY_W] = GLFW_KEY_W; + _glfw.mir.keycodes[KEY_E] = GLFW_KEY_E; + _glfw.mir.keycodes[KEY_R] = GLFW_KEY_R; + _glfw.mir.keycodes[KEY_T] = GLFW_KEY_T; + _glfw.mir.keycodes[KEY_Y] = GLFW_KEY_Y; + _glfw.mir.keycodes[KEY_U] = GLFW_KEY_U; + _glfw.mir.keycodes[KEY_I] = GLFW_KEY_I; + _glfw.mir.keycodes[KEY_O] = GLFW_KEY_O; + _glfw.mir.keycodes[KEY_P] = GLFW_KEY_P; + _glfw.mir.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; + _glfw.mir.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; + _glfw.mir.keycodes[KEY_A] = GLFW_KEY_A; + _glfw.mir.keycodes[KEY_S] = GLFW_KEY_S; + _glfw.mir.keycodes[KEY_D] = GLFW_KEY_D; + _glfw.mir.keycodes[KEY_F] = GLFW_KEY_F; + _glfw.mir.keycodes[KEY_G] = GLFW_KEY_G; + _glfw.mir.keycodes[KEY_H] = GLFW_KEY_H; + _glfw.mir.keycodes[KEY_J] = GLFW_KEY_J; + _glfw.mir.keycodes[KEY_K] = GLFW_KEY_K; + _glfw.mir.keycodes[KEY_L] = GLFW_KEY_L; + _glfw.mir.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; + _glfw.mir.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; + _glfw.mir.keycodes[KEY_Z] = GLFW_KEY_Z; + _glfw.mir.keycodes[KEY_X] = GLFW_KEY_X; + _glfw.mir.keycodes[KEY_C] = GLFW_KEY_C; + _glfw.mir.keycodes[KEY_V] = GLFW_KEY_V; + _glfw.mir.keycodes[KEY_B] = GLFW_KEY_B; + _glfw.mir.keycodes[KEY_N] = GLFW_KEY_N; + _glfw.mir.keycodes[KEY_M] = GLFW_KEY_M; + _glfw.mir.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; + _glfw.mir.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; + _glfw.mir.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; + _glfw.mir.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; + _glfw.mir.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; + _glfw.mir.keycodes[KEY_TAB] = GLFW_KEY_TAB; + _glfw.mir.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; + _glfw.mir.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; + _glfw.mir.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; + _glfw.mir.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; + _glfw.mir.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; + _glfw.mir.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; + _glfw.mir.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; + _glfw.mir.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; + _glfw.mir.keycodes[KEY_MENU] = GLFW_KEY_MENU; + _glfw.mir.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; + _glfw.mir.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; + _glfw.mir.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; + _glfw.mir.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; + _glfw.mir.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; + _glfw.mir.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; + _glfw.mir.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; + _glfw.mir.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; + _glfw.mir.keycodes[KEY_HOME] = GLFW_KEY_HOME; + _glfw.mir.keycodes[KEY_END] = GLFW_KEY_END; + _glfw.mir.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; + _glfw.mir.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; + _glfw.mir.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; + _glfw.mir.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; + _glfw.mir.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; + _glfw.mir.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; + _glfw.mir.keycodes[KEY_UP] = GLFW_KEY_UP; + _glfw.mir.keycodes[KEY_F1] = GLFW_KEY_F1; + _glfw.mir.keycodes[KEY_F2] = GLFW_KEY_F2; + _glfw.mir.keycodes[KEY_F3] = GLFW_KEY_F3; + _glfw.mir.keycodes[KEY_F4] = GLFW_KEY_F4; + _glfw.mir.keycodes[KEY_F5] = GLFW_KEY_F5; + _glfw.mir.keycodes[KEY_F6] = GLFW_KEY_F6; + _glfw.mir.keycodes[KEY_F7] = GLFW_KEY_F7; + _glfw.mir.keycodes[KEY_F8] = GLFW_KEY_F8; + _glfw.mir.keycodes[KEY_F9] = GLFW_KEY_F9; + _glfw.mir.keycodes[KEY_F10] = GLFW_KEY_F10; + _glfw.mir.keycodes[KEY_F11] = GLFW_KEY_F11; + _glfw.mir.keycodes[KEY_F12] = GLFW_KEY_F12; + _glfw.mir.keycodes[KEY_F13] = GLFW_KEY_F13; + _glfw.mir.keycodes[KEY_F14] = GLFW_KEY_F14; + _glfw.mir.keycodes[KEY_F15] = GLFW_KEY_F15; + _glfw.mir.keycodes[KEY_F16] = GLFW_KEY_F16; + _glfw.mir.keycodes[KEY_F17] = GLFW_KEY_F17; + _glfw.mir.keycodes[KEY_F18] = GLFW_KEY_F18; + _glfw.mir.keycodes[KEY_F19] = GLFW_KEY_F19; + _glfw.mir.keycodes[KEY_F20] = GLFW_KEY_F20; + _glfw.mir.keycodes[KEY_F21] = GLFW_KEY_F21; + _glfw.mir.keycodes[KEY_F22] = GLFW_KEY_F22; + _glfw.mir.keycodes[KEY_F23] = GLFW_KEY_F23; + _glfw.mir.keycodes[KEY_F24] = GLFW_KEY_F24; + _glfw.mir.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; + _glfw.mir.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; + _glfw.mir.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; + _glfw.mir.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; + _glfw.mir.keycodes[KEY_KP0] = GLFW_KEY_KP_0; + _glfw.mir.keycodes[KEY_KP1] = GLFW_KEY_KP_1; + _glfw.mir.keycodes[KEY_KP2] = GLFW_KEY_KP_2; + _glfw.mir.keycodes[KEY_KP3] = GLFW_KEY_KP_3; + _glfw.mir.keycodes[KEY_KP4] = GLFW_KEY_KP_4; + _glfw.mir.keycodes[KEY_KP5] = GLFW_KEY_KP_5; + _glfw.mir.keycodes[KEY_KP6] = GLFW_KEY_KP_6; + _glfw.mir.keycodes[KEY_KP7] = GLFW_KEY_KP_7; + _glfw.mir.keycodes[KEY_KP8] = GLFW_KEY_KP_8; + _glfw.mir.keycodes[KEY_KP9] = GLFW_KEY_KP_9; + _glfw.mir.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; + _glfw.mir.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; + _glfw.mir.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + + for (scancode = 0; scancode < 256; scancode++) + { + if (_glfw.mir.keycodes[scancode] > 0) + _glfw.mir.scancodes[_glfw.mir.keycodes[scancode]] = scancode; + } } @@ -180,21 +190,15 @@ int _glfwPlatformInit(void) createKeyTables(); - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; _glfwInitTimerPOSIX(); - // Need the default conf for when we set a NULL cursor - _glfw.mir.default_conf = mir_cursor_configuration_from_name(mir_arrow_cursor_name); + _glfw.mir.eventQueue = calloc(1, sizeof(EventQueue)); + _glfwInitEventQueueMir(_glfw.mir.eventQueue); - _glfw.mir.event_queue = calloc(1, sizeof(EventQueue)); - _glfwInitEventQueueMir(_glfw.mir.event_queue); - - error = pthread_mutex_init(&_glfw.mir.event_mutex, NULL); + error = pthread_mutex_init(&_glfw.mir.eventMutex, NULL); if (error) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -203,6 +207,7 @@ int _glfwPlatformInit(void) return GLFW_FALSE; } + _glfwPollMonitorsMir(); return GLFW_TRUE; } @@ -210,11 +215,10 @@ void _glfwPlatformTerminate(void) { _glfwTerminateEGL(); _glfwTerminateJoysticksLinux(); - _glfwTerminateThreadLocalStoragePOSIX(); - _glfwDeleteEventQueueMir(_glfw.mir.event_queue); + _glfwDeleteEventQueueMir(_glfw.mir.eventQueue); - pthread_mutex_destroy(&_glfw.mir.event_mutex); + pthread_mutex_destroy(&_glfw.mir.eventMutex); mir_connection_release(_glfw.mir.connection); } @@ -227,9 +231,7 @@ const char* _glfwPlatformGetVersionString(void) #else " gettimeofday" #endif -#if defined(__linux__) - " /dev/js" -#endif + " evdev" #if defined(_GLFW_BUILD_DLL) " shared" #endif diff --git a/raylib/external/glfw/src/mir_monitor.c b/raylib/external/glfw/src/mir_monitor.c index 90aa6c9..b300cac 100644 --- a/raylib/external/glfw/src/mir_monitor.c +++ b/raylib/external/glfw/src/mir_monitor.c @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 Mir - www.glfw.org +// GLFW 3.3 Mir - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2014-2015 Brandon Schaefer +// Copyright (c) 2014-2017 Brandon Schaefer // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -30,53 +30,55 @@ ////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// +////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsMir(void) { - int i, found = 0; - _GLFWmonitor** monitors = NULL; - MirDisplayConfiguration* displayConfig = - mir_connection_create_display_config(_glfw.mir.connection); + int i; + MirDisplayConfig* displayConfig = + mir_connection_create_display_configuration(_glfw.mir.connection); - *count = 0; + int numOutputs = mir_display_config_get_num_outputs(displayConfig); - for (i = 0; i < displayConfig->num_outputs; i++) + for (i = 0; i < numOutputs; i++) { - const MirDisplayOutput* out = displayConfig->outputs + i; + const MirOutput* output = mir_display_config_get_output(displayConfig, i); + MirOutputConnectionState state = mir_output_get_connection_state(output); + bool enabled = mir_output_is_enabled(output); - if (out->used && - out->connected && - out->num_modes && - out->current_mode < out->num_modes) + if (enabled && state == mir_output_connection_state_connected) { - found++; - monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); - monitors[i] = _glfwAllocMonitor("Unknown", - out->physical_width_mm, - out->physical_height_mm); + int widthMM = mir_output_get_physical_width_mm(output); + int heightMM = mir_output_get_physical_height_mm(output); + int x = mir_output_get_position_x(output); + int y = mir_output_get_position_y(output); + int id = mir_output_get_id(output); + size_t currentMode = mir_output_get_current_mode_index(output); + const char* name = mir_output_type_name(mir_output_get_type(output)); - monitors[i]->mir.x = out->position_x; - monitors[i]->mir.y = out->position_y; - monitors[i]->mir.output_id = out->output_id; - monitors[i]->mir.cur_mode = out->current_mode; + _GLFWmonitor* monitor = _glfwAllocMonitor(name, + widthMM, + heightMM); + monitor->mir.x = x; + monitor->mir.y = y; + monitor->mir.outputId = id; + monitor->mir.curMode = currentMode; + monitor->modes = _glfwPlatformGetVideoModes(monitor, &monitor->modeCount); - monitors[i]->modes = _glfwPlatformGetVideoModes(monitors[i], - &monitors[i]->modeCount); + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } } - mir_display_config_destroy(displayConfig); - - *count = found; - return monitors; + mir_display_config_release(displayConfig); } -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - return first->mir.output_id == second->mir.output_id; -} + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { @@ -86,7 +88,16 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = monitor->mir.y; } -void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf) +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + +static void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf) { switch (pf) { @@ -123,37 +134,59 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { int i; GLFWvidmode* modes = NULL; - MirDisplayConfiguration* displayConfig = - mir_connection_create_display_config(_glfw.mir.connection); + MirDisplayConfig* displayConfig = + mir_connection_create_display_configuration(_glfw.mir.connection); - for (i = 0; i < displayConfig->num_outputs; i++) + int numOutputs = mir_display_config_get_num_outputs(displayConfig); + + for (i = 0; i < numOutputs; i++) { - const MirDisplayOutput* out = displayConfig->outputs + i; - if (out->output_id != monitor->mir.output_id) + const MirOutput* output = mir_display_config_get_output(displayConfig, i); + int id = mir_output_get_id(output); + + if (id != monitor->mir.outputId) continue; - modes = calloc(out->num_modes, sizeof(GLFWvidmode)); + MirOutputConnectionState state = mir_output_get_connection_state(output); + bool enabled = mir_output_is_enabled(output); - for (*found = 0; *found < out->num_modes; (*found)++) + // We must have been disconnected + if (!enabled || state != mir_output_connection_state_connected) { - modes[*found].width = out->modes[*found].horizontal_resolution; - modes[*found].height = out->modes[*found].vertical_resolution; - modes[*found].refreshRate = out->modes[*found].refresh_rate; + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Monitor no longer connected"); + return NULL; + } - FillInRGBBitsFromPixelFormat(&modes[*found], out->output_formats[*found]); + int numModes = mir_output_get_num_modes(output); + modes = calloc(numModes, sizeof(GLFWvidmode)); + + for (*found = 0; *found < numModes; (*found)++) + { + const MirOutputMode* mode = mir_output_get_mode(output, *found); + int width = mir_output_mode_get_width(mode); + int height = mir_output_mode_get_height(mode); + double refreshRate = mir_output_mode_get_refresh_rate(mode); + MirPixelFormat currentFormat = mir_output_get_current_pixel_format(output); + + modes[*found].width = width; + modes[*found].height = height; + modes[*found].refreshRate = refreshRate; + + FillInRGBBitsFromPixelFormat(&modes[*found], currentFormat); } break; } - mir_display_config_destroy(displayConfig); + mir_display_config_release(displayConfig); return modes; } void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { - *mode = monitor->modes[monitor->mir.cur_mode]; + *mode = monitor->modes[monitor->mir.curMode]; } void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) @@ -177,6 +210,5 @@ GLFWAPI int glfwGetMirMonitor(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(0); - return monitor->mir.output_id; + return monitor->mir.outputId; } - diff --git a/raylib/external/glfw/src/mir_platform.h b/raylib/external/glfw/src/mir_platform.h index 8f1cf4e..da00a32 100644 --- a/raylib/external/glfw/src/mir_platform.h +++ b/raylib/external/glfw/src/mir_platform.h @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 Mir - www.glfw.org +// GLFW 3.3 Mir - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2014-2015 Brandon Schaefer +// Copyright (c) 2014-2017 Brandon Schaefer // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -24,40 +24,38 @@ // //======================================================================== -#ifndef _glfw3_mir_platform_h_ -#define _glfw3_mir_platform_h_ - #include #include #include #include -typedef VkFlags VkMirSurfaceCreateFlagsKHR; +typedef VkFlags VkMirWindowCreateFlagsKHR; -typedef struct VkMirSurfaceCreateInfoKHR +typedef struct VkMirWindowCreateInfoKHR { VkStructureType sType; const void* pNext; - VkMirSurfaceCreateFlagsKHR flags; + VkMirWindowCreateFlagsKHR flags; MirConnection* connection; - MirSurface* mirSurface; -} VkMirSurfaceCreateInfoKHR; + MirWindow* mirWindow; +} VkMirWindowCreateInfoKHR; -typedef VkResult (APIENTRY *PFN_vkCreateMirSurfaceKHR)(VkInstance,const VkMirSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkResult (APIENTRY *PFN_vkCreateMirWindowKHR)(VkInstance,const VkMirWindowCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(VkPhysicalDevice,uint32_t,MirConnection*); -#include "posix_tls.h" +#include "posix_thread.h" #include "posix_time.h" #include "linux_joystick.h" #include "xkb_unicode.h" #include "egl_context.h" +#include "osmesa_context.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) -#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->mir.window) +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->mir.nativeWindow) #define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.mir.display) #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowMir mir @@ -80,10 +78,11 @@ typedef struct EventQueue // typedef struct _GLFWwindowMir { - MirSurface* surface; + MirWindow* window; int width; int height; - MirEGLNativeWindowType window; + MirEGLNativeWindowType nativeWindow; + _GLFWcursor* currentCursor; } _GLFWwindowMir; @@ -91,8 +90,8 @@ typedef struct _GLFWwindowMir // typedef struct _GLFWmonitorMir { - int cur_mode; - int output_id; + int curMode; + int outputId; int x; int y; @@ -104,13 +103,16 @@ typedef struct _GLFWlibraryMir { MirConnection* connection; MirEGLNativeDisplayType display; - MirCursorConfiguration* default_conf; - EventQueue* event_queue; + EventQueue* eventQueue; - short int publicKeys[256]; + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; - pthread_mutex_t event_mutex; - pthread_cond_t event_cond; + pthread_mutex_t eventMutex; + pthread_cond_t eventCond; + + // The window whose disabled cursor mode is active + _GLFWwindow* disabledCursorWindow; } _GLFWlibraryMir; @@ -120,11 +122,12 @@ typedef struct _GLFWlibraryMir typedef struct _GLFWcursorMir { MirCursorConfiguration* conf; - MirBufferStream* custom_cursor; + MirBufferStream* customCursor; + char const* cursorName; // only needed for system cursors } _GLFWcursorMir; +extern void _glfwPollMonitorsMir(void); extern void _glfwInitEventQueueMir(EventQueue* queue); extern void _glfwDeleteEventQueueMir(EventQueue* queue); -#endif // _glfw3_mir_platform_h_ diff --git a/raylib/external/glfw/src/mir_window.c b/raylib/external/glfw/src/mir_window.c index 411f906..fd8f291 100644 --- a/raylib/external/glfw/src/mir_window.c +++ b/raylib/external/glfw/src/mir_window.c @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 Mir - www.glfw.org +// GLFW 3.3 Mir - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2014-2015 Brandon Schaefer +// Copyright (c) 2014-2017 Brandon Schaefer // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -54,44 +54,41 @@ static GLFWbool emptyEventQueue(EventQueue* queue) // for single threaded event handling. static EventNode* newEventNode(const MirEvent* event, _GLFWwindow* context) { - EventNode* new_node = calloc(1, sizeof(EventNode)); - new_node->event = mir_event_ref(event); - new_node->window = context; + EventNode* newNode = calloc(1, sizeof(EventNode)); + newNode->event = mir_event_ref(event); + newNode->window = context; - return new_node; + return newNode; } static void enqueueEvent(const MirEvent* event, _GLFWwindow* context) { - pthread_mutex_lock(&_glfw.mir.event_mutex); + pthread_mutex_lock(&_glfw.mir.eventMutex); - EventNode* new_node = newEventNode(event, context); - TAILQ_INSERT_TAIL(&_glfw.mir.event_queue->head, new_node, entries); + EventNode* newNode = newEventNode(event, context); + TAILQ_INSERT_TAIL(&_glfw.mir.eventQueue->head, newNode, entries); - pthread_cond_signal(&_glfw.mir.event_cond); + pthread_cond_signal(&_glfw.mir.eventCond); - pthread_mutex_unlock(&_glfw.mir.event_mutex); + pthread_mutex_unlock(&_glfw.mir.eventMutex); } static EventNode* dequeueEvent(EventQueue* queue) { EventNode* node = NULL; - pthread_mutex_lock(&_glfw.mir.event_mutex); + pthread_mutex_lock(&_glfw.mir.eventMutex); node = queue->head.tqh_first; if (node) TAILQ_REMOVE(&queue->head, node, entries); - pthread_mutex_unlock(&_glfw.mir.event_mutex); + pthread_mutex_unlock(&_glfw.mir.eventMutex); return node; } -/* FIXME Soon to be changed upstream mir! So we can use an egl config to figure out - the best pixel format! -*/ static MirPixelFormat findValidPixelFormat(void) { unsigned int i, validFormats, mirPixelFormats = 32; @@ -132,8 +129,8 @@ static int mirModToGLFWMod(uint32_t mods) static int toGLFWKeyCode(uint32_t key) { - if (key < sizeof(_glfw.mir.publicKeys) / sizeof(_glfw.mir.publicKeys[0])) - return _glfw.mir.publicKeys[key]; + if (key < sizeof(_glfw.mir.keycodes) / sizeof(_glfw.mir.keycodes[0])) + return _glfw.mir.keycodes[key]; return GLFW_KEY_UNKNOWN; } @@ -201,16 +198,31 @@ static void handlePointerButton(_GLFWwindow* window, static void handlePointerMotion(_GLFWwindow* window, const MirPointerEvent* pointer_event) { - int current_x = window->virtualCursorPosX; - int current_y = window->virtualCursorPosY; - int x = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_x); - int y = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_y); - int dx = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_hscroll); - int dy = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_vscroll); + const int hscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_hscroll); + const int vscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_vscroll); - _glfwInputCursorPos(window, x, y); - if (dx != 0 || dy != 0) - _glfwInputScroll(window, dx, dy); + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (_glfw.mir.disabledCursorWindow != window) + return; + + const int dx = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_relative_x); + const int dy = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_relative_y); + const int current_x = window->virtualCursorPosX; + const int current_y = window->virtualCursorPosY; + + _glfwInputCursorPos(window, dx + current_x, dy + current_y); + } + else + { + const int x = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_x); + const int y = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_y); + + _glfwInputCursorPos(window, x, y); + } + + if (hscroll != 0 || vscroll != 0) + _glfwInputScroll(window, hscroll, vscroll); } static void handlePointerEvent(const MirPointerEvent* pointer_event, @@ -234,7 +246,6 @@ static void handlePointerEvent(const MirPointerEvent* pointer_event, break; default: break; - } } @@ -269,14 +280,14 @@ static void handleEvent(const MirEvent* event, _GLFWwindow* window) } } -static void addNewEvent(MirSurface* surface, const MirEvent* event, void* context) +static void addNewEvent(MirWindow* window, const MirEvent* event, void* context) { enqueueEvent(event, context); } -static GLFWbool createSurface(_GLFWwindow* window) +static GLFWbool createWindow(_GLFWwindow* window) { - MirSurfaceSpec* spec; + MirWindowSpec* spec; MirBufferUsage buffer_usage = mir_buffer_usage_hardware; MirPixelFormat pixel_format = findValidPixelFormat(); @@ -286,32 +297,42 @@ static GLFWbool createSurface(_GLFWwindow* window) "Mir: Unable to find a correct pixel format"); return GLFW_FALSE; } - - spec = mir_connection_create_spec_for_normal_surface(_glfw.mir.connection, - window->mir.width, - window->mir.height, - pixel_format); - mir_surface_spec_set_buffer_usage(spec, buffer_usage); - mir_surface_spec_set_name(spec, "MirSurface"); + spec = mir_create_normal_window_spec(_glfw.mir.connection, + window->mir.width, + window->mir.height); - window->mir.surface = mir_surface_create_sync(spec); - mir_surface_spec_release(spec); + mir_window_spec_set_pixel_format(spec, pixel_format); + mir_window_spec_set_buffer_usage(spec, buffer_usage); - if (!mir_surface_is_valid(window->mir.surface)) + window->mir.window = mir_create_window_sync(spec); + mir_window_spec_release(spec); + + if (!mir_window_is_valid(window->mir.window)) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unable to create surface: %s", - mir_surface_get_error_message(window->mir.surface)); + "Mir: Unable to create window: %s", + mir_window_get_error_message(window->mir.window)); return GLFW_FALSE; } - mir_surface_set_event_handler(window->mir.surface, addNewEvent, window); + mir_window_set_event_handler(window->mir.window, addNewEvent, window); return GLFW_TRUE; } +static void setWindowConfinement(_GLFWwindow* window, MirPointerConfinementState state) +{ + MirWindowSpec* spec; + + spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_pointer_confinement(spec, state); + + mir_window_apply_spec(window->mir.window, spec); + mir_window_spec_release(spec); +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// @@ -356,12 +377,12 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, GLFWvidmode mode; _glfwPlatformGetVideoMode(window->monitor, &mode); - mir_surface_set_state(window->mir.surface, mir_surface_state_fullscreen); + mir_window_set_state(window->mir.window, mir_window_state_fullscreen); if (wndconfig->width > mode.width || wndconfig->height > mode.height) { _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Requested surface size too large: %ix%i", + "Mir: Requested window size too large: %ix%i", wndconfig->width, wndconfig->height); return GLFW_FALSE; @@ -370,19 +391,31 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, window->mir.width = wndconfig->width; window->mir.height = wndconfig->height; + window->mir.currentCursor = NULL; - if (!createSurface(window)) + if (!createWindow(window)) return GLFW_FALSE; - window->mir.window = mir_buffer_stream_get_egl_native_window( - mir_surface_get_buffer_stream(window->mir.surface)); + window->mir.nativeWindow = mir_buffer_stream_get_egl_native_window( + mir_window_get_buffer_stream(window->mir.window)); if (ctxconfig->client != GLFW_NO_API) { - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; + if (ctxconfig->source == GLFW_EGL_CONTEXT_API || + ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } return GLFW_TRUE; @@ -390,10 +423,13 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, void _glfwPlatformDestroyWindow(_GLFWwindow* window) { - if (mir_surface_is_valid(window->mir.surface)) + if (_glfw.mir.disabledCursorWindow == window) + _glfw.mir.disabledCursorWindow = NULL; + + if (mir_window_is_valid(window->mir.window)) { - mir_surface_release_sync(window->mir.surface); - window->mir.surface = NULL; + mir_window_release_sync(window->mir.window); + window->mir.window= NULL; } if (window->context.destroy) @@ -402,14 +438,12 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { - MirSurfaceSpec* spec; - const char* e_title = title ? title : ""; + MirWindowSpec* spec; - spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); - mir_surface_spec_set_name(spec, e_title); - - mir_surface_apply_spec(window->mir.surface, spec); - mir_surface_spec_release(spec); + spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_name(spec, title); + mir_window_apply_spec(window->mir.window, spec); + mir_window_spec_release(spec); } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, @@ -421,22 +455,30 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window, void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { - MirSurfaceSpec* spec; + MirWindowSpec* spec; - spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); - mir_surface_spec_set_width (spec, width); - mir_surface_spec_set_height(spec, height); + spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_width (spec, width); + mir_window_spec_set_height(spec, height); - mir_surface_apply_spec(window->mir.surface, spec); - mir_surface_spec_release(spec); + mir_window_apply_spec(window->mir.window, spec); + mir_window_spec_release(spec); } void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + MirWindowSpec* spec; + + spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_max_width (spec, maxwidth); + mir_window_spec_set_max_height(spec, maxheight); + mir_window_spec_set_min_width (spec, minwidth); + mir_window_spec_set_min_height(spec, minheight); + + mir_window_apply_spec(window->mir.window, spec); + mir_window_spec_release(spec); } void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) @@ -473,42 +515,74 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) *height = window->mir.height; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { - mir_surface_set_state(window->mir.surface, mir_surface_state_minimized); + MirWindowSpec* spec; + + spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_state(spec, mir_window_state_minimized); + + mir_window_apply_spec(window->mir.window, spec); + mir_window_spec_release(spec); } void _glfwPlatformRestoreWindow(_GLFWwindow* window) { - mir_surface_set_state(window->mir.surface, mir_surface_state_restored); + MirWindowSpec* spec; + + spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_state(spec, mir_window_state_restored); + + mir_window_apply_spec(window->mir.window, spec); + mir_window_spec_release(spec); } void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + MirWindowSpec* spec; + + spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_state(spec, mir_window_state_maximized); + + mir_window_apply_spec(window->mir.window, spec); + mir_window_spec_release(spec); } void _glfwPlatformHideWindow(_GLFWwindow* window) { - MirSurfaceSpec* spec; + MirWindowSpec* spec; - spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); - mir_surface_spec_set_state(spec, mir_surface_state_hidden); + spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_state(spec, mir_window_state_hidden); - mir_surface_apply_spec(window->mir.surface, spec); - mir_surface_spec_release(spec); + mir_window_apply_spec(window->mir.window, spec); + mir_window_spec_release(spec); } void _glfwPlatformShowWindow(_GLFWwindow* window) { - MirSurfaceSpec* spec; + MirWindowSpec* spec; - spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); - mir_surface_spec_set_state(spec, mir_surface_state_restored); + spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_state(spec, mir_window_state_restored); - mir_surface_apply_spec(window->mir.surface, spec); - mir_surface_spec_release(spec); + mir_window_apply_spec(window->mir.window, spec); + mir_window_spec_release(spec); +} + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); } void _glfwPlatformFocusWindow(_GLFWwindow* window) @@ -529,9 +603,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, int _glfwPlatformWindowFocused(_GLFWwindow* window) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - return GLFW_FALSE; + return mir_window_get_focus_state(window->mir.window) == mir_window_focus_state_focused; } int _glfwPlatformWindowIconified(_GLFWwindow* window) @@ -543,53 +615,85 @@ int _glfwPlatformWindowIconified(_GLFWwindow* window) int _glfwPlatformWindowVisible(_GLFWwindow* window) { - return mir_surface_get_visibility(window->mir.surface) == mir_surface_visibility_exposed; + return mir_window_get_visibility(window->mir.window) == mir_window_visibility_exposed; } int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return mir_window_get_state(window->mir.window) == mir_window_state_maximized; +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { _glfwInputError(GLFW_PLATFORM_ERROR, "Mir: Unsupported function %s", __PRETTY_FUNCTION__); return GLFW_FALSE; } +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ +} + void _glfwPlatformPollEvents(void) { EventNode* node = NULL; - while ((node = dequeueEvent(_glfw.mir.event_queue))) + while ((node = dequeueEvent(_glfw.mir.eventQueue))) { handleEvent(node->event, node->window); - deleteNode(_glfw.mir.event_queue, node); + deleteNode(_glfw.mir.eventQueue, node); } } void _glfwPlatformWaitEvents(void) { - pthread_mutex_lock(&_glfw.mir.event_mutex); + pthread_mutex_lock(&_glfw.mir.eventMutex); - if (emptyEventQueue(_glfw.mir.event_queue)) - pthread_cond_wait(&_glfw.mir.event_cond, &_glfw.mir.event_mutex); + while (emptyEventQueue(_glfw.mir.eventQueue)) + pthread_cond_wait(&_glfw.mir.eventCond, &_glfw.mir.eventMutex); - pthread_mutex_unlock(&_glfw.mir.event_mutex); + pthread_mutex_unlock(&_glfw.mir.eventMutex); _glfwPlatformPollEvents(); } void _glfwPlatformWaitEventsTimeout(double timeout) { - pthread_mutex_lock(&_glfw.mir.event_mutex); + pthread_mutex_lock(&_glfw.mir.eventMutex); - if (emptyEventQueue(_glfw.mir.event_queue)) + if (emptyEventQueue(_glfw.mir.eventQueue)) { struct timespec time; clock_gettime(CLOCK_REALTIME, &time); time.tv_sec += (long) timeout; time.tv_nsec += (long) ((timeout - (long) timeout) * 1e9); - pthread_cond_timedwait(&_glfw.mir.event_cond, &_glfw.mir.event_mutex, &time); + pthread_cond_timedwait(&_glfw.mir.eventCond, &_glfw.mir.eventMutex, &time); } - pthread_mutex_unlock(&_glfw.mir.event_mutex); + pthread_mutex_unlock(&_glfw.mir.eventMutex); _glfwPlatformPollEvents(); } @@ -606,60 +710,45 @@ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* heigh *height = window->mir.height; } -// FIXME implement int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { MirBufferStream* stream; - MirPixelFormat pixel_format = findValidPixelFormat(); int i_w = image->width; int i_h = image->height; - if (pixel_format == mir_pixel_format_invalid) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unable to find a correct pixel format"); - return GLFW_FALSE; - } - stream = mir_connection_create_buffer_stream_sync(_glfw.mir.connection, i_w, i_h, - pixel_format, + mir_pixel_format_argb_8888, mir_buffer_usage_software); cursor->mir.conf = mir_cursor_configuration_from_buffer_stream(stream, xhot, yhot); - char* dest; - unsigned char *pixels; - int i, r_stride, bytes_per_pixel, bytes_per_row; - MirGraphicsRegion region; mir_buffer_stream_get_graphics_region(stream, ®ion); - // FIXME Figure this out based on the current_pf - bytes_per_pixel = 4; - bytes_per_row = bytes_per_pixel * i_w; + unsigned char* pixels = image->pixels; + char* dest = region.vaddr; + int i; - dest = region.vaddr; - pixels = image->pixels; - - r_stride = region.stride; - - for (i = 0; i < i_h; i++) + for (i = 0; i < i_w * i_h; i++, pixels += 4) { - memcpy(dest, pixels, bytes_per_row); - dest += r_stride; - pixels += r_stride; + unsigned int alpha = pixels[3]; + *dest++ = (char)(pixels[2] * alpha / 255); + *dest++ = (char)(pixels[1] * alpha / 255); + *dest++ = (char)(pixels[0] * alpha / 255); + *dest++ = (char)alpha; } - cursor->mir.custom_cursor = stream; + mir_buffer_stream_swap_buffers_sync(stream); + cursor->mir.customCursor = stream; return GLFW_TRUE; } -const char* getSystemCursorName(int shape) +static const char* getSystemCursorName(int shape) { switch (shape) { @@ -682,40 +771,47 @@ const char* getSystemCursorName(int shape) int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - const char* cursor_name = getSystemCursorName(shape); + cursor->mir.conf = NULL; + cursor->mir.customCursor = NULL; + cursor->mir.cursorName = getSystemCursorName(shape); - if (cursor_name) - { - cursor->mir.conf = mir_cursor_configuration_from_name(cursor_name); - cursor->mir.custom_cursor = NULL; - - return GLFW_TRUE; - } - - return GLFW_FALSE; + return cursor->mir.cursorName != NULL; } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { if (cursor->mir.conf) mir_cursor_configuration_destroy(cursor->mir.conf); - if (cursor->mir.custom_cursor) - mir_buffer_stream_release_sync(cursor->mir.custom_cursor); + if (cursor->mir.customCursor) + mir_buffer_stream_release_sync(cursor->mir.customCursor); +} + +static void setCursorNameForWindow(MirWindow* window, char const* name) +{ + MirWindowSpec* spec = mir_create_window_spec(_glfw.mir.connection); + mir_window_spec_set_cursor_name(spec, name); + mir_window_apply_spec(window, spec); + mir_window_spec_release(spec); } void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { - if (cursor && cursor->mir.conf) + if (cursor) { - mir_wait_for(mir_surface_configure_cursor(window->mir.surface, cursor->mir.conf)); - if (cursor->mir.custom_cursor) + window->mir.currentCursor = cursor; + + if (cursor->mir.cursorName) { - mir_buffer_stream_swap_buffers_sync(cursor->mir.custom_cursor); + setCursorNameForWindow(window->mir.window, cursor->mir.cursorName); + } + else if (cursor->mir.conf) + { + mir_window_configure_cursor(window->mir.window, cursor->mir.conf); } } else { - mir_wait_for(mir_surface_configure_cursor(window->mir.surface, _glfw.mir.default_conf)); + setCursorNameForWindow(window->mir.window, mir_default_cursor_name); } } @@ -733,24 +829,51 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + if (mode == GLFW_CURSOR_DISABLED) + { + _glfw.mir.disabledCursorWindow = window; + setWindowConfinement(window, mir_pointer_confined_to_window); + setCursorNameForWindow(window->mir.window, mir_disabled_cursor_name); + } + else + { + // If we were disabled before lets undo that! + if (_glfw.mir.disabledCursorWindow == window) + { + _glfw.mir.disabledCursorWindow = NULL; + setWindowConfinement(window, mir_pointer_unconfined); + } + + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + _glfwPlatformSetCursor(window, window->mir.currentCursor); + } + else if (window->cursorMode == GLFW_CURSOR_HIDDEN) + { + setCursorNameForWindow(window->mir.window, mir_disabled_cursor_name); + } + } } -const char* _glfwPlatformGetKeyName(int key, int scancode) +const char* _glfwPlatformGetScancodeName(int scancode) { _glfwInputError(GLFW_PLATFORM_ERROR, "Mir: Unsupported function %s", __PRETTY_FUNCTION__); return NULL; } -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.mir.scancodes[key]; +} + +void _glfwPlatformSetClipboardString(const char* string) { _glfwInputError(GLFW_PLATFORM_ERROR, "Mir: Unsupported function %s", __PRETTY_FUNCTION__); } -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +const char* _glfwPlatformGetClipboardString(void) { _glfwInputError(GLFW_PLATFORM_ERROR, "Mir: Unsupported function %s", __PRETTY_FUNCTION__); @@ -758,28 +881,21 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) return NULL; } -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { - char** extensions; + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_mir_surface) + return; - *count = 0; - - if (!_glfw.vk.KHR_mir_surface) - return NULL; - - extensions = calloc(2, sizeof(char*)); - extensions[0] = strdup("VK_KHR_surface"); - extensions[1] = strdup("VK_KHR_mir_surface"); - - *count = 2; - return extensions; + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_mir_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - PFN_vkGetPhysicalDeviceMirPresentationSupportKHR vkGetPhysicalDeviceMirPresentationSupportKHR = + PFN_vkGetPhysicalDeviceMirPresentationSupportKHR + vkGetPhysicalDeviceMirPresentationSupportKHR = (PFN_vkGetPhysicalDeviceMirPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR"); if (!vkGetPhysicalDeviceMirPresentationSupportKHR) @@ -800,12 +916,12 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, VkSurfaceKHR* surface) { VkResult err; - VkMirSurfaceCreateInfoKHR sci; - PFN_vkCreateMirSurfaceKHR vkCreateMirSurfaceKHR; + VkMirWindowCreateInfoKHR sci; + PFN_vkCreateMirWindowKHR vkCreateMirWindowKHR; - vkCreateMirSurfaceKHR = (PFN_vkCreateMirSurfaceKHR) - vkGetInstanceProcAddr(instance, "vkCreateMirSurfaceKHR"); - if (!vkCreateMirSurfaceKHR) + vkCreateMirWindowKHR = (PFN_vkCreateMirWindowKHR) + vkGetInstanceProcAddr(instance, "vkCreateMirWindowKHR"); + if (!vkCreateMirWindowKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "Mir: Vulkan instance missing VK_KHR_mir_surface extension"); @@ -815,9 +931,9 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR; sci.connection = _glfw.mir.connection; - sci.mirSurface = window->mir.surface; + sci.mirWindow = window->mir.window; - err = vkCreateMirSurfaceKHR(instance, &sci, allocator, surface); + err = vkCreateMirWindowKHR(instance, &sci, allocator, surface); if (err) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -839,10 +955,10 @@ GLFWAPI MirConnection* glfwGetMirDisplay(void) return _glfw.mir.connection; } -GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* handle) +GLFWAPI MirWindow* glfwGetMirWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return window->mir.surface; + return window->mir.window; } diff --git a/raylib/external/glfw/src/monitor.c b/raylib/external/glfw/src/monitor.c index b6c312d..6f64cca 100644 --- a/raylib/external/glfw/src/monitor.c +++ b/raylib/external/glfw/src/monitor.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -86,83 +86,62 @@ static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) ////// GLFW event API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwInputMonitorChange(void) +void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) { - int i, j, monitorCount = _glfw.monitorCount; - _GLFWmonitor** monitors = _glfw.monitors; - - _glfw.monitors = _glfwPlatformGetMonitors(&_glfw.monitorCount); - - // Re-use still connected monitor objects - - for (i = 0; i < _glfw.monitorCount; i++) + if (action == GLFW_CONNECTED) { - for (j = 0; j < monitorCount; j++) + _glfw.monitorCount++; + _glfw.monitors = + realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount); + + if (placement == _GLFW_INSERT_FIRST) { - if (_glfwPlatformIsSameMonitor(_glfw.monitors[i], monitors[j])) - { - _glfwFreeMonitor(_glfw.monitors[i]); - _glfw.monitors[i] = monitors[j]; - break; - } + memmove(_glfw.monitors + 1, + _glfw.monitors, + (_glfw.monitorCount - 1) * sizeof(_GLFWmonitor*)); + _glfw.monitors[0] = monitor; } + else + _glfw.monitors[_glfw.monitorCount - 1] = monitor; } - - // Find and report disconnected monitors (not in the new list) - - for (i = 0; i < monitorCount; i++) + else if (action == GLFW_DISCONNECTED) { + int i; _GLFWwindow* window; - for (j = 0; j < _glfw.monitorCount; j++) - { - if (monitors[i] == _glfw.monitors[j]) - break; - } - - if (j < _glfw.monitorCount) - continue; - for (window = _glfw.windowListHead; window; window = window->next) { - if (window->monitor == monitors[i]) + if (window->monitor == monitor) { - int width, height; + int width, height, xoff, yoff; _glfwPlatformGetWindowSize(window, &width, &height); _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0); + _glfwPlatformGetWindowFrameSize(window, &xoff, &yoff, NULL, NULL); + _glfwPlatformSetWindowPos(window, xoff, yoff); } } - if (_glfw.callbacks.monitor) - _glfw.callbacks.monitor((GLFWmonitor*) monitors[i], GLFW_DISCONNECTED); - } - - // Find and report newly connected monitors (not in the old list) - // Re-used monitor objects are then removed from the old list to avoid - // having them destroyed at the end of this function - - for (i = 0; i < _glfw.monitorCount; i++) - { - for (j = 0; j < monitorCount; j++) + for (i = 0; i < _glfw.monitorCount; i++) { - if (_glfw.monitors[i] == monitors[j]) + if (_glfw.monitors[i] == monitor) { - monitors[j] = NULL; + _glfw.monitorCount--; + memmove(_glfw.monitors + i, + _glfw.monitors + i + 1, + (_glfw.monitorCount - i) * sizeof(_GLFWmonitor*)); break; } } - - if (j < monitorCount) - continue; - - if (_glfw.callbacks.monitor) - _glfw.callbacks.monitor((GLFWmonitor*) _glfw.monitors[i], GLFW_CONNECTED); } - _glfwFreeMonitors(monitors, monitorCount); + if (_glfw.callbacks.monitor) + _glfw.callbacks.monitor((GLFWmonitor*) monitor, action); + + if (action == GLFW_DISCONNECTED) + _glfwFreeMonitor(monitor); } -void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window) +void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) { monitor->window = window; } @@ -175,10 +154,12 @@ void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window) _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) { _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); - monitor->name = strdup(name); monitor->widthMM = widthMM; monitor->heightMM = heightMM; + if (name) + monitor->name = strdup(name); + return monitor; } @@ -212,16 +193,6 @@ void _glfwFreeGammaArrays(GLFWgammaramp* ramp) memset(ramp, 0, sizeof(GLFWgammaramp)); } -void _glfwFreeMonitors(_GLFWmonitor** monitors, int count) -{ - int i; - - for (i = 0; i < count; i++) - _glfwFreeMonitor(monitors[i]); - - free(monitors); -} - const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, const GLFWvidmode* desired) { @@ -304,6 +275,7 @@ void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) GLFWAPI GLFWmonitor** glfwGetMonitors(int* count) { assert(count != NULL); + *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -355,6 +327,21 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* *heightMM = monitor->heightMM; } +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, + float* xscale, float* yscale) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetMonitorContentScale(monitor, xscale, yscale); +} + GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; @@ -404,6 +391,10 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) int i; unsigned short values[256]; GLFWgammaramp ramp; + assert(handle != NULL); + assert(gamma == gamma); + assert(gamma >= 0.f); + assert(gamma <= FLT_MAX); _GLFW_REQUIRE_INIT(); @@ -455,6 +446,7 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); assert(ramp != NULL); + assert(ramp->size > 0); assert(ramp->red != NULL); assert(ramp->green != NULL); assert(ramp->blue != NULL); diff --git a/raylib/external/glfw/src/nsgl_context.h b/raylib/external/glfw/src/nsgl_context.h index 1304f2e..18042de 100644 --- a/raylib/external/glfw/src/nsgl_context.h +++ b/raylib/external/glfw/src/nsgl_context.h @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -24,9 +24,6 @@ // //======================================================================== -#ifndef _glfw3_nsgl_context_h_ -#define _glfw3_nsgl_context_h_ - #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl @@ -57,4 +54,3 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextNSGL(_GLFWwindow* window); -#endif // _glfw3_nsgl_context_h_ diff --git a/raylib/external/glfw/src/nsgl_context.m b/raylib/external/glfw/src/nsgl_context.m index 22ebdba..a7cbf00 100644 --- a/raylib/external/glfw/src/nsgl_context.m +++ b/raylib/external/glfw/src/nsgl_context.m @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 OS X - www.glfw.org +// GLFW 3.3 macOS - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2009-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -34,7 +34,7 @@ static void makeContextCurrentNSGL(_GLFWwindow* window) else [NSOpenGLContext clearCurrentContext]; - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.contextSlot, window); } static void swapBuffersNSGL(_GLFWwindow* window) @@ -45,7 +45,7 @@ static void swapBuffersNSGL(_GLFWwindow* window) static void swapIntervalNSGL(int interval) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); GLint sync = interval; [window->context.nsgl.object setValues:&sync @@ -119,70 +119,83 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - unsigned int attributeCount = 0; - if (ctxconfig->client == GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_API_UNAVAILABLE, - "NSGL: OpenGL ES is not available on OS X"); - return GLFW_FALSE; - } - - if (ctxconfig->major == 3 && ctxconfig->minor < 2) - { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of OS X does not support OpenGL 3.0 or 3.1"); + "NSGL: OpenGL ES is not available on macOS"); return GLFW_FALSE; } if (ctxconfig->major > 2) { - if (!ctxconfig->forward) + if (ctxconfig->major == 3 && ctxconfig->minor < 2) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of OS X only supports forward-compatible contexts for OpenGL 3.2 and above"); + "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above"); return GLFW_FALSE; } - if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE) + if (!ctxconfig->forward || ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "NSGL: The targeted version of OS X only supports core profile contexts for OpenGL 3.2 and above"); + "NSGL: The targeted version of macOS only supports forward-compatible core profile contexts for OpenGL 3.2 and above"); return GLFW_FALSE; } } - // Context robustness modes (GL_KHR_robustness) are not yet supported on - // OS X but are not a hard constraint, so ignore and continue + // Context robustness modes (GL_KHR_robustness) are not yet supported by + // macOS but are not a hard constraint, so ignore and continue // Context release behaviors (GL_KHR_context_flush_control) are not yet - // supported on OS X but are not a hard constraint, so ignore and continue + // supported by macOS but are not a hard constraint, so ignore and continue -#define ADD_ATTR(x) { attributes[attributeCount++] = x; } -#define ADD_ATTR2(x, y) { ADD_ATTR(x); ADD_ATTR(y); } + // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not + // a hard constraint, so ignore and continue - // Arbitrary array size here - NSOpenGLPixelFormatAttribute attributes[40]; + // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but + // are not a hard constraint, so ignore and continue - ADD_ATTR(NSOpenGLPFAAccelerated); - ADD_ATTR(NSOpenGLPFAClosestPolicy); +#define addAttrib(a) \ +{ \ + assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ +} +#define setAttrib(a, v) { addAttrib(a); addAttrib(v); } + + NSOpenGLPixelFormatAttribute attribs[40]; + int index = 0; + + addAttrib(NSOpenGLPFAAccelerated); + addAttrib(NSOpenGLPFAClosestPolicy); + + if (ctxconfig->nsgl.offline) + { + addAttrib(NSOpenGLPFAAllowOfflineRenderers); + // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in + // Info.plist for unbundled applications + // HACK: This assumes that NSOpenGLPixelFormat will remain + // a straightforward wrapper of its CGL counterpart +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 100800 + addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching); +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 if (ctxconfig->major >= 4) { - ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); + setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); } else #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ if (ctxconfig->major >= 3) { - ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); + setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); } if (ctxconfig->major <= 2) { if (fbconfig->auxBuffers != GLFW_DONT_CARE) - ADD_ATTR2(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); + setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); if (fbconfig->accumRedBits != GLFW_DONT_CARE && fbconfig->accumGreenBits != GLFW_DONT_CARE && @@ -194,7 +207,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, fbconfig->accumBlueBits + fbconfig->accumAlphaBits; - ADD_ATTR2(NSOpenGLPFAAccumSize, accumBits); + setAttrib(NSOpenGLPFAAccumSize, accumBits); } } @@ -206,53 +219,61 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, fbconfig->greenBits + fbconfig->blueBits; - // OS X needs non-zero color size, so set reasonable values + // macOS needs non-zero color size, so set reasonable values if (colorBits == 0) colorBits = 24; else if (colorBits < 15) colorBits = 15; - ADD_ATTR2(NSOpenGLPFAColorSize, colorBits); + setAttrib(NSOpenGLPFAColorSize, colorBits); } if (fbconfig->alphaBits != GLFW_DONT_CARE) - ADD_ATTR2(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); + setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); if (fbconfig->depthBits != GLFW_DONT_CARE) - ADD_ATTR2(NSOpenGLPFADepthSize, fbconfig->depthBits); + setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits); if (fbconfig->stencilBits != GLFW_DONT_CARE) - ADD_ATTR2(NSOpenGLPFAStencilSize, fbconfig->stencilBits); + setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits); if (fbconfig->stereo) - ADD_ATTR(NSOpenGLPFAStereo); + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "NSGL: Stereo rendering is deprecated"); + return GLFW_FALSE; +#else + addAttrib(NSOpenGLPFAStereo); +#endif + } if (fbconfig->doublebuffer) - ADD_ATTR(NSOpenGLPFADoubleBuffer); + addAttrib(NSOpenGLPFADoubleBuffer); if (fbconfig->samples != GLFW_DONT_CARE) { if (fbconfig->samples == 0) { - ADD_ATTR2(NSOpenGLPFASampleBuffers, 0); + setAttrib(NSOpenGLPFASampleBuffers, 0); } else { - ADD_ATTR2(NSOpenGLPFASampleBuffers, 1); - ADD_ATTR2(NSOpenGLPFASamples, fbconfig->samples); + setAttrib(NSOpenGLPFASampleBuffers, 1); + setAttrib(NSOpenGLPFASamples, fbconfig->samples); } } // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB // framebuffer, so there's no need (and no way) to request it - ADD_ATTR(0); + addAttrib(0); -#undef ADD_ATTR -#undef ADD_ATTR2 +#undef addAttrib +#undef setAttrib window->context.nsgl.pixelFormat = - [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; if (window->context.nsgl.pixelFormat == nil) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, @@ -275,6 +296,12 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, return GLFW_FALSE; } + if (fbconfig->transparent) + { + GLint opaque = 0; + [window->context.nsgl.object setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + } + [window->context.nsgl.object setView:window->ns.view]; window->context.makeCurrent = makeContextCurrentNSGL; diff --git a/raylib/external/glfw/src/posix_tls.c b/raylib/external/glfw/src/null_init.c similarity index 57% rename from raylib/external/glfw/src/posix_tls.c rename to raylib/external/glfw/src/null_init.c index c9517c6..3414738 100644 --- a/raylib/external/glfw/src/posix_tls.c +++ b/raylib/external/glfw/src/null_init.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 POSIX - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -28,41 +28,23 @@ #include "internal.h" -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwInitThreadLocalStoragePOSIX(void) -{ - if (pthread_key_create(&_glfw.posix_tls.context, NULL) != 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "POSIX: Failed to create context TLS"); - return GLFW_FALSE; - } - - _glfw.posix_tls.allocated = GLFW_TRUE; - return GLFW_TRUE; -} - -void _glfwTerminateThreadLocalStoragePOSIX(void) -{ - if (_glfw.posix_tls.allocated) - pthread_key_delete(_glfw.posix_tls.context); -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformSetCurrentContext(_GLFWwindow* context) +int _glfwPlatformInit(void) { - pthread_setspecific(_glfw.posix_tls.context, context); + _glfwInitTimerPOSIX(); + return GLFW_TRUE; } -_GLFWwindow* _glfwPlatformGetCurrentContext(void) +void _glfwPlatformTerminate(void) { - return pthread_getspecific(_glfw.posix_tls.context); + _glfwTerminateOSMesa(); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " null OSMesa"; } diff --git a/raylib/external/glfw/src/null_joystick.c b/raylib/external/glfw/src/null_joystick.c new file mode 100644 index 0000000..afd65e1 --- /dev/null +++ b/raylib/external/glfw/src/null_joystick.c @@ -0,0 +1,42 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) +{ + return GLFW_FALSE; +} + +void _glfwPlatformUpdateGamepadGUID(char* guid) +{ +} + diff --git a/raylib/external/glfw/src/null_joystick.h b/raylib/external/glfw/src/null_joystick.h new file mode 100644 index 0000000..3075815 --- /dev/null +++ b/raylib/external/glfw/src/null_joystick.h @@ -0,0 +1,31 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define _GLFW_PLATFORM_JOYSTICK_STATE int nulljs +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int nulljs + +#define _GLFW_PLATFORM_MAPPING_NAME "" + diff --git a/raylib/external/glfw/src/win32_tls.c b/raylib/external/glfw/src/null_monitor.c similarity index 56% rename from raylib/external/glfw/src/win32_tls.c rename to raylib/external/glfw/src/null_monitor.c index ab79fcc..007dd1a 100644 --- a/raylib/external/glfw/src/win32_tls.c +++ b/raylib/external/glfw/src/null_monitor.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 Win32 - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -28,42 +28,37 @@ #include "internal.h" -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwInitThreadLocalStorageWin32(void) -{ - _glfw.win32_tls.context = TlsAlloc(); - if (_glfw.win32_tls.context == TLS_OUT_OF_INDEXES) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to allocate TLS index"); - return GLFW_FALSE; - } - - _glfw.win32_tls.allocated = GLFW_TRUE; - return GLFW_TRUE; -} - -void _glfwTerminateThreadLocalStorageWin32(void) -{ - if (_glfw.win32_tls.allocated) - TlsFree(_glfw.win32_tls.context); -} - - ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformSetCurrentContext(_GLFWwindow* context) +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { - TlsSetValue(_glfw.win32_tls.context, context); } -_GLFWwindow* _glfwPlatformGetCurrentContext(void) +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) +{ + return NULL; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ +} + +void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { - return TlsGetValue(_glfw.win32_tls.context); } diff --git a/raylib/external/glfw/src/null_platform.h b/raylib/external/glfw/src/null_platform.h new file mode 100644 index 0000000..2d67c50 --- /dev/null +++ b/raylib/external/glfw/src/null_platform.h @@ -0,0 +1,62 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null + +#define _GLFW_PLATFORM_CONTEXT_STATE +#define _GLFW_PLATFORM_MONITOR_STATE +#define _GLFW_PLATFORM_CURSOR_STATE +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE +#define _GLFW_EGL_CONTEXT_STATE +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE + +#include "osmesa_context.h" +#include "posix_time.h" +#include "posix_thread.h" +#include "null_joystick.h" + +#if defined(_GLFW_WIN32) + #define _glfw_dlopen(name) LoadLibraryA(name) + #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) + #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) +#else + #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) + #define _glfw_dlclose(handle) dlclose(handle) + #define _glfw_dlsym(handle, name) dlsym(handle, name) +#endif + +// Null-specific per-window data +// +typedef struct _GLFWwindowNull +{ + int width; + int height; +} _GLFWwindowNull; + diff --git a/raylib/external/glfw/src/null_window.c b/raylib/external/glfw/src/null_window.c new file mode 100644 index 0000000..6eb4ac6 --- /dev/null +++ b/raylib/external/glfw/src/null_window.c @@ -0,0 +1,316 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + + +static int createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig) +{ + window->null.width = wndconfig->width; + window->null.height = wndconfig->height; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!createNativeWindow(window, wndconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API || + ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Null: EGL not available"); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (window->context.destroy) + window->context.destroy(window); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, + const GLFWimage* images) +{ +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->null.width; + if (height) + *height = window->null.height; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + window->null.width = width; + window->null.height = height; +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d) +{ +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->null.width; + if (height) + *height = window->null.height; +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ +} + +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ +} + + +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ +} + +void _glfwPlatformUnhideWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return GLFW_FALSE; +} + +void _glfwPlatformPollEvents(void) +{ +} + +void _glfwPlatformWaitEvents(void) +{ +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ +} + +void _glfwPlatformPostEmptyEvent(void) +{ +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ +} + +void _glfwPlatformSetClipboardString(const char* string) +{ +} + +const char* _glfwPlatformGetClipboardString(void) +{ + return NULL; +} + +const char* _glfwPlatformGetScancodeName(int scancode) +{ + return ""; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return -1; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + return GLFW_FALSE; +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + // This seems like the most appropriate error to return here + return VK_ERROR_INITIALIZATION_FAILED; +} + diff --git a/raylib/external/glfw/src/osmesa_context.c b/raylib/external/glfw/src/osmesa_context.c new file mode 100644 index 0000000..a7de33f --- /dev/null +++ b/raylib/external/glfw/src/osmesa_context.c @@ -0,0 +1,370 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include +#include +#include + +#include "internal.h" + + +static void makeContextCurrentOSMesa(_GLFWwindow* window) +{ + if (window) + { + int width, height; + _glfwPlatformGetFramebufferSize(window, &width, &height); + + // Check to see if we need to allocate a new buffer + if ((window->context.osmesa.buffer == NULL) || + (width != window->context.osmesa.width) || + (height != window->context.osmesa.height)) + { + free(window->context.osmesa.buffer); + + // Allocate the new buffer (width * height * 8-bit RGBA) + window->context.osmesa.buffer = calloc(4, width * height); + window->context.osmesa.width = width; + window->context.osmesa.height = height; + } + + if (!OSMesaMakeCurrent(window->context.osmesa.handle, + window->context.osmesa.buffer, + GL_UNSIGNED_BYTE, + width, height)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to make context current"); + return; + } + } + + _glfwPlatformSetTls(&_glfw.contextSlot, window); +} + +static GLFWglproc getProcAddressOSMesa(const char* procname) +{ + return (GLFWglproc) OSMesaGetProcAddress(procname); +} + +static void destroyContextOSMesa(_GLFWwindow* window) +{ + if (window->context.osmesa.handle) + { + OSMesaDestroyContext(window->context.osmesa.handle); + window->context.osmesa.handle = NULL; + } + + if (window->context.osmesa.buffer) + { + free(window->context.osmesa.buffer); + window->context.osmesa.width = 0; + window->context.osmesa.height = 0; + } +} + +static void swapBuffersOSMesa(_GLFWwindow* window) +{ + // No double buffering on OSMesa +} + +static void swapIntervalOSMesa(int interval) +{ + // No swap interval on OSMesa +} + +static int extensionSupportedOSMesa(const char* extension) +{ + // OSMesa does not have extensions + return GLFW_FALSE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitOSMesa(void) +{ + int i; + const char* sonames[] = + { +#if defined(_GLFW_OSMESA_LIBRARY) + _GLFW_OSMESA_LIBRARY, +#elif defined(_WIN32) + "libOSMesa.dll", + "OSMesa.dll", +#elif defined(__APPLE__) + "libOSMesa.8.dylib", +#elif defined(__CYGWIN__) + "libOSMesa-8.so", +#else + "libOSMesa.so.8", + "libOSMesa.so.6", +#endif + NULL + }; + + if (_glfw.osmesa.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.osmesa.handle = _glfw_dlopen(sonames[i]); + if (_glfw.osmesa.handle) + break; + } + + if (!_glfw.osmesa.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found"); + return GLFW_FALSE; + } + + _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt"); + _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); + _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext"); + _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent"); + _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); + _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer"); + _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress) + _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); + + if (!_glfw.osmesa.CreateContextExt || + !_glfw.osmesa.DestroyContext || + !_glfw.osmesa.MakeCurrent || + !_glfw.osmesa.GetColorBuffer || + !_glfw.osmesa.GetDepthBuffer || + !_glfw.osmesa.GetProcAddress) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to load required entry points"); + + _glfwTerminateOSMesa(); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwTerminateOSMesa(void) +{ + if (_glfw.osmesa.handle) + { + _glfw_dlclose(_glfw.osmesa.handle); + _glfw.osmesa.handle = NULL; + } +} + +#define setAttrib(a, v) \ +{ \ + assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + OSMesaContext share = NULL; + const int accumBits = fbconfig->accumRedBits + + fbconfig->accumGreenBits + + fbconfig->accumBlueBits + + fbconfig->accumAlphaBits; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "OSMesa: OpenGL ES is not available on OSMesa"); + return GLFW_FALSE; + } + + if (ctxconfig->share) + share = ctxconfig->share->context.osmesa.handle; + + if (OSMesaCreateContextAttribs) + { + int index = 0, attribs[40]; + + setAttrib(OSMESA_FORMAT, OSMESA_RGBA); + setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); + setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); + setAttrib(OSMESA_ACCUM_BITS, accumBits); + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); + } + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + { + setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); + } + + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); + setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); + } + + if (ctxconfig->forward) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Foward-compatible contexts not supported"); + return GLFW_FALSE; + } + + setAttrib(0, 0); + + window->context.osmesa.handle = + OSMesaCreateContextAttribs(attribs, share); + } + else + { + if (ctxconfig->profile) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: OpenGL profiles unavailable"); + return GLFW_FALSE; + } + + window->context.osmesa.handle = + OSMesaCreateContextExt(OSMESA_RGBA, + fbconfig->depthBits, + fbconfig->stencilBits, + accumBits, + share); + } + + if (window->context.osmesa.handle == NULL) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "OSMesa: Failed to create context"); + return GLFW_FALSE; + } + + window->context.makeCurrent = makeContextCurrentOSMesa; + window->context.swapBuffers = swapBuffersOSMesa; + window->context.swapInterval = swapIntervalOSMesa; + window->context.extensionSupported = extensionSupportedOSMesa; + window->context.getProcAddress = getProcAddressOSMesa; + window->context.destroy = destroyContextOSMesa; + + return GLFW_TRUE; +} + +#undef setAttrib + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width, + int* height, int* format, void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaFormat; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetColorBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaFormat, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve color buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (format) + *format = mesaFormat; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle, + int* width, int* height, + int* bytesPerValue, + void** buffer) +{ + void* mesaBuffer; + GLint mesaWidth, mesaHeight, mesaBytes; + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!OSMesaGetDepthBuffer(window->context.osmesa.handle, + &mesaWidth, &mesaHeight, + &mesaBytes, &mesaBuffer)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OSMesa: Failed to retrieve depth buffer"); + return GLFW_FALSE; + } + + if (width) + *width = mesaWidth; + if (height) + *height = mesaHeight; + if (bytesPerValue) + *bytesPerValue = mesaBytes; + if (buffer) + *buffer = mesaBuffer; + + return GLFW_TRUE; +} + +GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.osmesa.handle; +} + diff --git a/raylib/external/glfw/src/osmesa_context.h b/raylib/external/glfw/src/osmesa_context.h new file mode 100644 index 0000000..07bb469 --- /dev/null +++ b/raylib/external/glfw/src/osmesa_context.h @@ -0,0 +1,94 @@ +//======================================================================== +// GLFW 3.3 OSMesa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2016 Google Inc. +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define OSMESA_RGBA 0x1908 +#define OSMESA_FORMAT 0x22 +#define OSMESA_DEPTH_BITS 0x30 +#define OSMESA_STENCIL_BITS 0x31 +#define OSMESA_ACCUM_BITS 0x32 +#define OSMESA_PROFILE 0x33 +#define OSMESA_CORE_PROFILE 0x34 +#define OSMESA_COMPAT_PROFILE 0x35 +#define OSMESA_CONTEXT_MAJOR_VERSION 0x36 +#define OSMESA_CONTEXT_MINOR_VERSION 0x37 + +typedef void* OSMesaContext; +typedef void (*OSMESAproc)(void); + +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); +typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); +typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); +typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); +typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); +typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); +#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt +#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs +#define OSMesaDestroyContext _glfw.osmesa.DestroyContext +#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent +#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer +#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer +#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress + +#define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa +#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa + + +// OSMesa-specific per-context data +// +typedef struct _GLFWcontextOSMesa +{ + OSMesaContext handle; + int width; + int height; + void* buffer; + +} _GLFWcontextOSMesa; + +// OSMesa-specific global data +// +typedef struct _GLFWlibraryOSMesa +{ + void* handle; + + PFN_OSMesaCreateContextExt CreateContextExt; + PFN_OSMesaCreateContextAttribs CreateContextAttribs; + PFN_OSMesaDestroyContext DestroyContext; + PFN_OSMesaMakeCurrent MakeCurrent; + PFN_OSMesaGetColorBuffer GetColorBuffer; + PFN_OSMesaGetDepthBuffer GetDepthBuffer; + PFN_OSMesaGetProcAddress GetProcAddress; + +} _GLFWlibraryOSMesa; + + +GLFWbool _glfwInitOSMesa(void); +void _glfwTerminateOSMesa(void); +GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + diff --git a/raylib/external/glfw/src/posix_thread.c b/raylib/external/glfw/src/posix_thread.c new file mode 100644 index 0000000..ce0bc39 --- /dev/null +++ b/raylib/external/glfw/src/posix_thread.c @@ -0,0 +1,103 @@ +//======================================================================== +// GLFW 3.3 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) +{ + assert(tls->posix.allocated == GLFW_FALSE); + + if (pthread_key_create(&tls->posix.key, NULL) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "POSIX: Failed to create context TLS"); + return GLFW_FALSE; + } + + tls->posix.allocated = GLFW_TRUE; + return GLFW_TRUE; +} + +void _glfwPlatformDestroyTls(_GLFWtls* tls) +{ + if (tls->posix.allocated) + pthread_key_delete(tls->posix.key); + memset(tls, 0, sizeof(_GLFWtls)); +} + +void* _glfwPlatformGetTls(_GLFWtls* tls) +{ + assert(tls->posix.allocated == GLFW_TRUE); + return pthread_getspecific(tls->posix.key); +} + +void _glfwPlatformSetTls(_GLFWtls* tls, void* value) +{ + assert(tls->posix.allocated == GLFW_TRUE); + pthread_setspecific(tls->posix.key, value); +} + +GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex) +{ + assert(mutex->posix.allocated == GLFW_FALSE); + + if (pthread_mutex_init(&mutex->posix.handle, NULL) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "POSIX: Failed to create mutex"); + return GLFW_FALSE; + } + + return mutex->posix.allocated = GLFW_TRUE; +} + +void _glfwPlatformDestroyMutex(_GLFWmutex* mutex) +{ + if (mutex->posix.allocated) + pthread_mutex_destroy(&mutex->posix.handle); + memset(mutex, 0, sizeof(_GLFWmutex)); +} + +void _glfwPlatformLockMutex(_GLFWmutex* mutex) +{ + assert(mutex->posix.allocated == GLFW_TRUE); + pthread_mutex_lock(&mutex->posix.handle); +} + +void _glfwPlatformUnlockMutex(_GLFWmutex* mutex) +{ + assert(mutex->posix.allocated == GLFW_TRUE); + pthread_mutex_unlock(&mutex->posix.handle); +} + diff --git a/raylib/external/glfw/src/posix_tls.h b/raylib/external/glfw/src/posix_thread.h similarity index 74% rename from raylib/external/glfw/src/posix_tls.h rename to raylib/external/glfw/src/posix_thread.h index 0d408ae..bdddf41 100644 --- a/raylib/external/glfw/src/posix_tls.h +++ b/raylib/external/glfw/src/posix_thread.h @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 POSIX - www.glfw.org +// GLFW 3.3 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,25 +25,27 @@ // //======================================================================== -#ifndef _glfw3_posix_tls_h_ -#define _glfw3_posix_tls_h_ - #include -#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsPOSIX posix_tls +#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix +#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix -// POSIX-specific global TLS data +// POSIX-specific thread local storage data // typedef struct _GLFWtlsPOSIX { GLFWbool allocated; - pthread_key_t context; + pthread_key_t key; } _GLFWtlsPOSIX; +// POSIX-specific mutex data +// +typedef struct _GLFWmutexPOSIX +{ + GLFWbool allocated; + pthread_mutex_t handle; -GLFWbool _glfwInitThreadLocalStoragePOSIX(void); -void _glfwTerminateThreadLocalStoragePOSIX(void); +} _GLFWmutexPOSIX; -#endif // _glfw3_posix_tls_h_ diff --git a/raylib/external/glfw/src/posix_time.c b/raylib/external/glfw/src/posix_time.c index 8d0d4cd..00b2831 100644 --- a/raylib/external/glfw/src/posix_time.c +++ b/raylib/external/glfw/src/posix_time.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 POSIX - www.glfw.org +// GLFW 3.3 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -44,14 +44,14 @@ void _glfwInitTimerPOSIX(void) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - _glfw.posix_time.monotonic = GLFW_TRUE; - _glfw.posix_time.frequency = 1000000000; + _glfw.timer.posix.monotonic = GLFW_TRUE; + _glfw.timer.posix.frequency = 1000000000; } else #endif { - _glfw.posix_time.monotonic = GLFW_FALSE; - _glfw.posix_time.frequency = 1000000; + _glfw.timer.posix.monotonic = GLFW_FALSE; + _glfw.timer.posix.frequency = 1000000; } } @@ -63,7 +63,7 @@ void _glfwInitTimerPOSIX(void) uint64_t _glfwPlatformGetTimerValue(void) { #if defined(CLOCK_MONOTONIC) - if (_glfw.posix_time.monotonic) + if (_glfw.timer.posix.monotonic) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -80,6 +80,6 @@ uint64_t _glfwPlatformGetTimerValue(void) uint64_t _glfwPlatformGetTimerFrequency(void) { - return _glfw.posix_time.frequency; + return _glfw.timer.posix.frequency; } diff --git a/raylib/external/glfw/src/posix_time.h b/raylib/external/glfw/src/posix_time.h index 4730ca7..f1a69eb 100644 --- a/raylib/external/glfw/src/posix_time.h +++ b/raylib/external/glfw/src/posix_time.h @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 POSIX - www.glfw.org +// GLFW 3.3 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,24 +25,20 @@ // //======================================================================== -#ifndef _glfw3_posix_time_h_ -#define _glfw3_posix_time_h_ - -#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimePOSIX posix_time +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix #include // POSIX-specific global timer data // -typedef struct _GLFWtimePOSIX +typedef struct _GLFWtimerPOSIX { GLFWbool monotonic; uint64_t frequency; -} _GLFWtimePOSIX; +} _GLFWtimerPOSIX; void _glfwInitTimerPOSIX(void); -#endif // _glfw3_posix_time_h_ diff --git a/raylib/external/glfw/src/vulkan.c b/raylib/external/glfw/src/vulkan.c index 20011de..1fd15fa 100644 --- a/raylib/external/glfw/src/vulkan.c +++ b/raylib/external/glfw/src/vulkan.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -31,30 +31,40 @@ #include #include +#define _GLFW_FIND_LOADER 1 +#define _GLFW_REQUIRE_LOADER 2 + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// -GLFWbool _glfwInitVulkan(void) +GLFWbool _glfwInitVulkan(int mode) { VkResult err; VkExtensionProperties* ep; uint32_t i, count; -#if !defined(_GLFW_VULKAN_STATIC) -#if defined(_GLFW_WIN32) - const char* name = "vulkan-1.dll"; -#else - const char* name = "libvulkan.so.1"; -#endif - if (_glfw.vk.available) return GLFW_TRUE; - _glfw.vk.handle = _glfw_dlopen(name); +#if !defined(_GLFW_VULKAN_STATIC) +#if defined(_GLFW_VULKAN_LIBRARY) + _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY); +#elif defined(_GLFW_WIN32) + _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); +#elif defined(_GLFW_COCOA) + _glfw.vk.handle = _glfw_dlopen("libMoltenVK.dylib"); +#else + _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); +#endif if (!_glfw.vk.handle) + { + if (mode == _GLFW_REQUIRE_LOADER) + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); + return GLFW_FALSE; + } _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); @@ -82,9 +92,13 @@ GLFWbool _glfwInitVulkan(void) err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); if (err) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Vulkan: Failed to query instance extension count: %s", - _glfwGetVulkanResultString(err)); + // NOTE: This happens on systems with a loader but without any Vulkan ICD + if (mode == _GLFW_REQUIRE_LOADER) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Failed to query instance extension count: %s", + _glfwGetVulkanResultString(err)); + } _glfwTerminateVulkan(); return GLFW_FALSE; @@ -95,7 +109,7 @@ GLFWbool _glfwInitVulkan(void) err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); if (err) { - _glfwInputError(GLFW_PLATFORM_ERROR, + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Failed to query instance extensions: %s", _glfwGetVulkanResultString(err)); @@ -108,41 +122,41 @@ GLFWbool _glfwInitVulkan(void) { if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) _glfw.vk.KHR_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) +#if defined(_GLFW_WIN32) + else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) _glfw.vk.KHR_win32_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) +#elif defined(_GLFW_COCOA) + else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) + _glfw.vk.MVK_macos_surface = GLFW_TRUE; +#elif defined(_GLFW_X11) + else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) _glfw.vk.KHR_xlib_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) + else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) _glfw.vk.KHR_xcb_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) +#elif defined(_GLFW_WAYLAND) + else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) _glfw.vk.KHR_wayland_surface = GLFW_TRUE; - if (strcmp(ep[i].extensionName, "VK_KHR_mir_surface") == 0) +#elif defined(_GLFW_MIR) + else if (strcmp(ep[i].extensionName, "VK_KHR_mir_surface") == 0) _glfw.vk.KHR_mir_surface = GLFW_TRUE; +#endif } free(ep); _glfw.vk.available = GLFW_TRUE; - if (_glfw.vk.KHR_surface) - { - _glfw.vk.extensions = - _glfwPlatformGetRequiredInstanceExtensions(&_glfw.vk.extensionCount); - } + _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions); return GLFW_TRUE; } void _glfwTerminateVulkan(void) { - uint32_t i; - - for (i = 0; i < _glfw.vk.extensionCount; i++) - free(_glfw.vk.extensions[i]); - free(_glfw.vk.extensions); - +#if !defined(_GLFW_VULKAN_STATIC) if (_glfw.vk.handle) _glfw_dlclose(_glfw.vk.handle); +#endif } const char* _glfwGetVulkanResultString(VkResult result) @@ -208,22 +222,24 @@ const char* _glfwGetVulkanResultString(VkResult result) GLFWAPI int glfwVulkanSupported(void) { _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - return _glfwInitVulkan(); + return _glfwInitVulkan(_GLFW_FIND_LOADER); } GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) { + assert(count != NULL); + *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; - } - *count = _glfw.vk.extensionCount; + if (!_glfw.vk.extensions[0]) + return NULL; + + *count = 2; return (const char**) _glfw.vk.extensions; } @@ -231,18 +247,24 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname) { GLFWvkproc proc; + assert(procname != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; - } proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); +#if defined(_GLFW_VULKAN_STATIC) + if (!proc) + { + if (strcmp(procname, "vkGetInstanceProcAddr") == 0) + return (GLFWvkproc) vkGetInstanceProcAddr; + } +#else if (!proc) proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); +#endif return proc; } @@ -251,15 +273,15 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { + assert(instance != VK_NULL_HANDLE); + assert(device != VK_NULL_HANDLE); + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return GLFW_FALSE; - } - if (!_glfw.vk.extensions) + if (!_glfw.vk.extensions[0]) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Window surface creation extensions not found"); @@ -277,6 +299,7 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, VkSurfaceKHR* surface) { _GLFWwindow* window = (_GLFWwindow*) handle; + assert(instance != VK_NULL_HANDLE); assert(window != NULL); assert(surface != NULL); @@ -284,13 +307,10 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); - if (!_glfwInitVulkan()) - { - _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return VK_ERROR_INITIALIZATION_FAILED; - } - if (!_glfw.vk.extensions) + if (!_glfw.vk.extensions[0]) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Window surface creation extensions not found"); diff --git a/raylib/external/glfw/src/wgl_context.c b/raylib/external/glfw/src/wgl_context.c index 696c4cb..d864a47 100644 --- a/raylib/external/glfw/src/wgl_context.c +++ b/raylib/external/glfw/src/wgl_context.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 WGL - www.glfw.org +// GLFW 3.3 WGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -44,9 +44,8 @@ static int getPixelFormatAttrib(_GLFWwindow* window, int pixelFormat, int attrib pixelFormat, 0, 1, &attrib, &value)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve pixel format attribute %i", - attrib); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve pixel format attribute"); return 0; } @@ -55,7 +54,9 @@ static int getPixelFormatAttrib(_GLFWwindow* window, int pixelFormat, int attrib // Return a list of available and usable framebuffer configs // -static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) +static int choosePixelFormat(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) { _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; @@ -128,19 +129,33 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) if (_glfw.wgl.ARB_multisample) u->samples = getPixelFormatAttrib(window, n, WGL_SAMPLES_ARB); - if (_glfw.wgl.ARB_framebuffer_sRGB || - _glfw.wgl.EXT_framebuffer_sRGB) + if (ctxconfig->client == GLFW_OPENGL_API) { - if (getPixelFormatAttrib(window, n, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) - u->sRGB = GLFW_TRUE; + if (_glfw.wgl.ARB_framebuffer_sRGB || + _glfw.wgl.EXT_framebuffer_sRGB) + { + if (getPixelFormatAttrib(window, n, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) + u->sRGB = GLFW_TRUE; + } + } + else + { + if (_glfw.wgl.EXT_colorspace) + { + if (getPixelFormatAttrib(window, n, WGL_COLORSPACE_EXT) == + WGL_COLORSPACE_SRGB_EXT) + { + u->sRGB = GLFW_TRUE; + } + } } } else { - PIXELFORMATDESCRIPTOR pfd; - // Get pixel format attributes through legacy PFDs + PIXELFORMATDESCRIPTOR pfd; + if (!DescribePixelFormat(window->context.wgl.dc, n, sizeof(PIXELFORMATDESCRIPTOR), @@ -198,7 +213,7 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) return 0; } - closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + closest = _glfwChooseFBConfig(fbconfig, usableConfigs, usableCount); if (!closest) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, @@ -214,54 +229,39 @@ static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) return pixelFormat; } -// Returns whether desktop compositing is enabled -// -static GLFWbool isCompositionEnabled(void) -{ - BOOL enabled; - - if (!_glfw_DwmIsCompositionEnabled) - return FALSE; - - if (_glfw_DwmIsCompositionEnabled(&enabled) != S_OK) - return FALSE; - - return enabled; -} - static void makeContextCurrentWGL(_GLFWwindow* window) { if (window) { if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle)) - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.contextSlot, window); else { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to make context current"); - _glfwPlatformSetCurrentContext(NULL); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to make context current"); + _glfwPlatformSetTls(&_glfw.contextSlot, NULL); } } else { if (!wglMakeCurrent(NULL, NULL)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to clear current context"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to clear current context"); } - _glfwPlatformSetCurrentContext(NULL); + _glfwPlatformSetTls(&_glfw.contextSlot, NULL); } } static void swapBuffersWGL(_GLFWwindow* window) { // HACK: Use DwmFlush when desktop composition is enabled - if (isCompositionEnabled() && !window->monitor) + if (_glfwIsCompositionEnabledWin32() && !window->monitor) { int count = abs(window->context.wgl.interval); while (count--) - _glfw_DwmFlush(); + DwmFlush(); } SwapBuffers(window->context.wgl.dc); @@ -269,13 +269,13 @@ static void swapBuffersWGL(_GLFWwindow* window) static void swapIntervalWGL(int interval) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); window->context.wgl.interval = interval; // HACK: Disable WGL swap interval when desktop composition is enabled to // avoid interfering with DWM vsync - if (isCompositionEnabled() && !window->monitor) + if (_glfwIsCompositionEnabledWin32() && !window->monitor) interval = 0; if (_glfw.wgl.EXT_swap_control) @@ -353,25 +353,24 @@ static void loadWGLExtensions(void) if (!SetPixelFormat(dc, ChoosePixelFormat(dc, &pfd), &pfd)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to set pixel format for dummy context"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to set pixel format for dummy context"); return; } rc = wglCreateContext(dc); if (!rc) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to create dummy context"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to create dummy context"); return; } if (!wglMakeCurrent(dc, rc)) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to make dummy context current"); wglDeleteContext(rc); - - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to make dummy context current"); return; } @@ -404,8 +403,12 @@ static void loadWGLExtensions(void) extensionSupportedWGL("WGL_EXT_create_context_es2_profile"); _glfw.wgl.ARB_create_context_robustness = extensionSupportedWGL("WGL_ARB_create_context_robustness"); + _glfw.wgl.ARB_create_context_no_error = + extensionSupportedWGL("WGL_ARB_create_context_no_error"); _glfw.wgl.EXT_swap_control = extensionSupportedWGL("WGL_EXT_swap_control"); + _glfw.wgl.EXT_colorspace = + extensionSupportedWGL("WGL_EXT_colorspace"); _glfw.wgl.ARB_pixel_format = extensionSupportedWGL("WGL_ARB_pixel_format"); _glfw.wgl.ARB_context_flush_control = @@ -430,21 +433,22 @@ GLFWbool _glfwInitWGL(void) _glfw.wgl.instance = LoadLibraryA("opengl32.dll"); if (!_glfw.wgl.instance) { - _glfwInputError(GLFW_PLATFORM_ERROR, "WGL: Failed to load opengl32.dll"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to load opengl32.dll"); return GLFW_FALSE; } - _glfw.wgl.CreateContext = (WGLCREATECONTEXT_T) + _glfw.wgl.CreateContext = (PFN_wglCreateContext) GetProcAddress(_glfw.wgl.instance, "wglCreateContext"); - _glfw.wgl.DeleteContext = (WGLDELETECONTEXT_T) + _glfw.wgl.DeleteContext = (PFN_wglDeleteContext) GetProcAddress(_glfw.wgl.instance, "wglDeleteContext"); - _glfw.wgl.GetProcAddress = (WGLGETPROCADDRESS_T) + _glfw.wgl.GetProcAddress = (PFN_wglGetProcAddress) GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress"); - _glfw.wgl.GetCurrentDC = (WGLGETCURRENTDC_T) + _glfw.wgl.GetCurrentDC = (PFN_wglGetCurrentDC) GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC"); - _glfw.wgl.MakeCurrent = (WGLMAKECURRENT_T) + _glfw.wgl.MakeCurrent = (PFN_wglMakeCurrent) GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent"); - _glfw.wgl.ShareLists = (WGLSHARELISTS_T) + _glfw.wgl.ShareLists = (PFN_wglShareLists) GetProcAddress(_glfw.wgl.instance, "wglShareLists"); return GLFW_TRUE; @@ -458,11 +462,11 @@ void _glfwTerminateWGL(void) FreeLibrary(_glfw.wgl.instance); } -#define setWGLattrib(attribName, attribValue) \ +#define setAttrib(a, v) \ { \ - attribs[index++] = attribName; \ - attribs[index++] = attribValue; \ - assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ + assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ } // Create the OpenGL or OpenGL ES context @@ -490,22 +494,22 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, return GLFW_FALSE; } - pixelFormat = choosePixelFormat(window, fbconfig); + pixelFormat = choosePixelFormat(window, ctxconfig, fbconfig); if (!pixelFormat) return GLFW_FALSE; if (!DescribePixelFormat(window->context.wgl.dc, pixelFormat, sizeof(pfd), &pfd)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to retrieve PFD for selected pixel format"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve PFD for selected pixel format"); return GLFW_FALSE; } if (!SetPixelFormat(window->context.wgl.dc, pixelFormat, &pfd)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to set selected pixel format"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to set selected pixel format"); return GLFW_FALSE; } @@ -562,8 +566,6 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, if (ctxconfig->debug) flags |= WGL_CONTEXT_DEBUG_BIT_ARB; - if (ctxconfig->noerror) - flags |= GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR; if (ctxconfig->robustness) { @@ -571,13 +573,13 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { - setWGLattrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_NO_RESET_NOTIFICATION_ARB); + setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { - setWGLattrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - WGL_LOSE_CONTEXT_ON_RESET_ARB); + setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_LOSE_CONTEXT_ON_RESET_ARB); } flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; @@ -590,33 +592,39 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { - setWGLattrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { - setWGLattrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, - WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } + if (ctxconfig->noerror) + { + if (_glfw.wgl.ARB_create_context_no_error) + setAttrib(WGL_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); + } + // NOTE: Only request an explicitly versioned context when necessary, as // explicitly requesting version 1.0 does not always return the // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { - setWGLattrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); - setWGLattrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + setAttrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + setAttrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (flags) - setWGLattrib(WGL_CONTEXT_FLAGS_ARB, flags); + setAttrib(WGL_CONTEXT_FLAGS_ARB, flags); if (mask) - setWGLattrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); + setAttrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); - setWGLattrib(0, 0); + setAttrib(0, 0); window->context.wgl.handle = _glfw.wgl.CreateContextAttribsARB(window->context.wgl.dc, @@ -647,6 +655,11 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: Driver does not support the requested OpenGL profile"); } + else if (error == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) + { + _glfwInputError(GLFW_INVALID_VALUE, + "WGL: The share context is not compatible with the requested context"); + } else { if (ctxconfig->client == GLFW_OPENGL_API) @@ -669,8 +682,8 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, window->context.wgl.handle = wglCreateContext(window->context.wgl.dc); if (!window->context.wgl.handle) { - _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "WGL: Failed to create OpenGL context"); + _glfwInputErrorWin32(GLFW_VERSION_UNAVAILABLE, + "WGL: Failed to create OpenGL context"); return GLFW_FALSE; } @@ -678,8 +691,8 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, { if (!wglShareLists(share, window->context.wgl.handle)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "WGL: Failed to enable sharing with specified OpenGL context"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "WGL: Failed to enable sharing with specified OpenGL context"); return GLFW_FALSE; } } @@ -695,7 +708,7 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, return GLFW_TRUE; } -#undef setWGLattrib +#undef setAttrib ////////////////////////////////////////////////////////////////////////// diff --git a/raylib/external/glfw/src/wgl_context.h b/raylib/external/glfw/src/wgl_context.h index b837d43..9fae911 100644 --- a/raylib/external/glfw/src/wgl_context.h +++ b/raylib/external/glfw/src/wgl_context.h @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 WGL - www.glfw.org +// GLFW 3.3 WGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,10 +25,6 @@ // //======================================================================== -#ifndef _glfw3_wgl_context_h_ -#define _glfw3_wgl_context_h_ - - #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_DRAW_TO_WINDOW_ARB 0x2001 @@ -72,9 +68,13 @@ #define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 +#define WGL_COLORSPACE_EXT 0x309d +#define WGL_COLORSPACE_SRGB_EXT 0x3089 #define ERROR_INVALID_VERSION_ARB 0x2095 #define ERROR_INVALID_PROFILE_ARB 0x2096 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); @@ -82,12 +82,12 @@ typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); -typedef HGLRC (WINAPI * WGLCREATECONTEXT_T)(HDC); -typedef BOOL (WINAPI * WGLDELETECONTEXT_T)(HGLRC); -typedef PROC (WINAPI * WGLGETPROCADDRESS_T)(LPCSTR); -typedef HDC (WINAPI * WGLGETCURRENTDC_T)(void); -typedef BOOL (WINAPI * WGLMAKECURRENT_T)(HDC,HGLRC); -typedef BOOL (WINAPI * WGLSHARELISTS_T)(HGLRC,HGLRC); +typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); +typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); +typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); +typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); +typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); +typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC); // opengl32.dll function pointer typedefs #define wglCreateContext _glfw.wgl.CreateContext @@ -120,12 +120,12 @@ typedef struct _GLFWcontextWGL typedef struct _GLFWlibraryWGL { HINSTANCE instance; - WGLCREATECONTEXT_T CreateContext; - WGLDELETECONTEXT_T DeleteContext; - WGLGETPROCADDRESS_T GetProcAddress; - WGLGETCURRENTDC_T GetCurrentDC; - WGLMAKECURRENT_T MakeCurrent; - WGLSHARELISTS_T ShareLists; + PFN_wglCreateContext CreateContext; + PFN_wglDeleteContext DeleteContext; + PFN_wglGetProcAddress GetProcAddress; + PFN_wglGetCurrentDC GetCurrentDC; + PFN_wglMakeCurrent MakeCurrent; + PFN_wglShareLists ShareLists; GLFWbool extensionsLoaded; @@ -135,6 +135,7 @@ typedef struct _GLFWlibraryWGL PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; GLFWbool EXT_swap_control; + GLFWbool EXT_colorspace; GLFWbool ARB_multisample; GLFWbool ARB_framebuffer_sRGB; GLFWbool EXT_framebuffer_sRGB; @@ -143,6 +144,7 @@ typedef struct _GLFWlibraryWGL GLFWbool ARB_create_context_profile; GLFWbool EXT_create_context_es2_profile; GLFWbool ARB_create_context_robustness; + GLFWbool ARB_create_context_no_error; GLFWbool ARB_context_flush_control; } _GLFWlibraryWGL; @@ -154,4 +156,3 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); -#endif // _glfw3_wgl_context_h_ diff --git a/raylib/external/glfw/src/win32_init.c b/raylib/external/glfw/src/win32_init.c index b2a0a67..ee7ccfd 100644 --- a/raylib/external/glfw/src/win32_init.c +++ b/raylib/external/glfw/src/win32_init.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 Win32 - www.glfw.org +// GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -30,20 +30,22 @@ #include #include -#include -DEFINE_GUID(GUID_DEVINTERFACE_HID,0x4d1e55b2,0xf16f,0x11cf,0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30); +static const GUID _glfw_GUID_DEVINTERFACE_HID = + {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}}; + +#define GUID_DEVINTERFACE_HID _glfw_GUID_DEVINTERFACE_HID #if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) -// Applications exporting this symbol with this value will be automatically -// directed to the high-performance GPU on Nvidia Optimus systems with -// up-to-date drivers +// Executables (but not DLLs) exporting this symbol with this value will be +// automatically directed to the high-performance GPU on Nvidia Optimus systems +// with up-to-date drivers // __declspec(dllexport) DWORD NvOptimusEnablement = 1; -// Applications exporting this symbol with this value will be automatically -// directed to the high-performance GPU on AMD PowerXpress systems with -// up-to-date drivers +// Executables (but not DLLs) exporting this symbol with this value will be +// automatically directed to the high-performance GPU on AMD PowerXpress systems +// with up-to-date drivers // __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; @@ -60,6 +62,17 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) #endif // _GLFW_BUILD_DLL +// HACK: Define versionhelpers.h functions manually as MinGW lacks the header +BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + return VerifyVersionInfoW(&osvi, mask, cond); +} + // Load necessary libraries (DLLs) // static GLFWbool loadLibraries(void) @@ -67,29 +80,31 @@ static GLFWbool loadLibraries(void) _glfw.win32.winmm.instance = LoadLibraryA("winmm.dll"); if (!_glfw.win32.winmm.instance) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to load winmm.dll"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to load winmm.dll"); return GLFW_FALSE; } - _glfw.win32.winmm.timeGetTime = (TIMEGETTIME_T) + _glfw.win32.winmm.GetTime = (PFN_timeGetTime) GetProcAddress(_glfw.win32.winmm.instance, "timeGetTime"); _glfw.win32.user32.instance = LoadLibraryA("user32.dll"); if (!_glfw.win32.user32.instance) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to load user32.dll"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to load user32.dll"); return GLFW_FALSE; } - _glfw.win32.user32.SetProcessDPIAware = (SETPROCESSDPIAWARE_T) + _glfw.win32.user32.SetProcessDPIAware_ = (PFN_SetProcessDPIAware) GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); - _glfw.win32.user32.ChangeWindowMessageFilterEx = (CHANGEWINDOWMESSAGEFILTEREX_T) + _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx) GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); if (_glfw.win32.dinput8.instance) { - _glfw.win32.dinput8.DirectInput8Create = (DIRECTINPUT8CREATE_T) + _glfw.win32.dinput8.Create = (PFN_DirectInput8Create) GetProcAddress(_glfw.win32.dinput8.instance, "DirectInput8Create"); } @@ -110,9 +125,9 @@ static GLFWbool loadLibraries(void) _glfw.win32.xinput.instance = LoadLibraryA(names[i]); if (_glfw.win32.xinput.instance) { - _glfw.win32.xinput.XInputGetCapabilities = (XINPUTGETCAPABILITIES_T) + _glfw.win32.xinput.GetCapabilities = (PFN_XInputGetCapabilities) GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities"); - _glfw.win32.xinput.XInputGetState = (XINPUTGETSTATE_T) + _glfw.win32.xinput.GetState = (PFN_XInputGetState) GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState"); break; @@ -123,17 +138,21 @@ static GLFWbool loadLibraries(void) _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll"); if (_glfw.win32.dwmapi.instance) { - _glfw.win32.dwmapi.DwmIsCompositionEnabled = (DWMISCOMPOSITIONENABLED_T) + _glfw.win32.dwmapi.IsCompositionEnabled = (PFN_DwmIsCompositionEnabled) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); - _glfw.win32.dwmapi.DwmFlush = (DWMFLUSH_T) + _glfw.win32.dwmapi.Flush = (PFN_DwmFlush) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); + _glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) + GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); } _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); if (_glfw.win32.shcore.instance) { - _glfw.win32.shcore.SetProcessDpiAwareness = (SETPROCESSDPIAWARENESS_T) + _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); + _glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor) + GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor"); } return GLFW_TRUE; @@ -168,135 +187,135 @@ static void createKeyTables(void) { int scancode; - memset(_glfw.win32.publicKeys, -1, sizeof(_glfw.win32.publicKeys)); - memset(_glfw.win32.nativeKeys, -1, sizeof(_glfw.win32.nativeKeys)); + memset(_glfw.win32.keycodes, -1, sizeof(_glfw.win32.keycodes)); + memset(_glfw.win32.scancodes, -1, sizeof(_glfw.win32.scancodes)); - _glfw.win32.publicKeys[0x00B] = GLFW_KEY_0; - _glfw.win32.publicKeys[0x002] = GLFW_KEY_1; - _glfw.win32.publicKeys[0x003] = GLFW_KEY_2; - _glfw.win32.publicKeys[0x004] = GLFW_KEY_3; - _glfw.win32.publicKeys[0x005] = GLFW_KEY_4; - _glfw.win32.publicKeys[0x006] = GLFW_KEY_5; - _glfw.win32.publicKeys[0x007] = GLFW_KEY_6; - _glfw.win32.publicKeys[0x008] = GLFW_KEY_7; - _glfw.win32.publicKeys[0x009] = GLFW_KEY_8; - _glfw.win32.publicKeys[0x00A] = GLFW_KEY_9; - _glfw.win32.publicKeys[0x01E] = GLFW_KEY_A; - _glfw.win32.publicKeys[0x030] = GLFW_KEY_B; - _glfw.win32.publicKeys[0x02E] = GLFW_KEY_C; - _glfw.win32.publicKeys[0x020] = GLFW_KEY_D; - _glfw.win32.publicKeys[0x012] = GLFW_KEY_E; - _glfw.win32.publicKeys[0x021] = GLFW_KEY_F; - _glfw.win32.publicKeys[0x022] = GLFW_KEY_G; - _glfw.win32.publicKeys[0x023] = GLFW_KEY_H; - _glfw.win32.publicKeys[0x017] = GLFW_KEY_I; - _glfw.win32.publicKeys[0x024] = GLFW_KEY_J; - _glfw.win32.publicKeys[0x025] = GLFW_KEY_K; - _glfw.win32.publicKeys[0x026] = GLFW_KEY_L; - _glfw.win32.publicKeys[0x032] = GLFW_KEY_M; - _glfw.win32.publicKeys[0x031] = GLFW_KEY_N; - _glfw.win32.publicKeys[0x018] = GLFW_KEY_O; - _glfw.win32.publicKeys[0x019] = GLFW_KEY_P; - _glfw.win32.publicKeys[0x010] = GLFW_KEY_Q; - _glfw.win32.publicKeys[0x013] = GLFW_KEY_R; - _glfw.win32.publicKeys[0x01F] = GLFW_KEY_S; - _glfw.win32.publicKeys[0x014] = GLFW_KEY_T; - _glfw.win32.publicKeys[0x016] = GLFW_KEY_U; - _glfw.win32.publicKeys[0x02F] = GLFW_KEY_V; - _glfw.win32.publicKeys[0x011] = GLFW_KEY_W; - _glfw.win32.publicKeys[0x02D] = GLFW_KEY_X; - _glfw.win32.publicKeys[0x015] = GLFW_KEY_Y; - _glfw.win32.publicKeys[0x02C] = GLFW_KEY_Z; + _glfw.win32.keycodes[0x00B] = GLFW_KEY_0; + _glfw.win32.keycodes[0x002] = GLFW_KEY_1; + _glfw.win32.keycodes[0x003] = GLFW_KEY_2; + _glfw.win32.keycodes[0x004] = GLFW_KEY_3; + _glfw.win32.keycodes[0x005] = GLFW_KEY_4; + _glfw.win32.keycodes[0x006] = GLFW_KEY_5; + _glfw.win32.keycodes[0x007] = GLFW_KEY_6; + _glfw.win32.keycodes[0x008] = GLFW_KEY_7; + _glfw.win32.keycodes[0x009] = GLFW_KEY_8; + _glfw.win32.keycodes[0x00A] = GLFW_KEY_9; + _glfw.win32.keycodes[0x01E] = GLFW_KEY_A; + _glfw.win32.keycodes[0x030] = GLFW_KEY_B; + _glfw.win32.keycodes[0x02E] = GLFW_KEY_C; + _glfw.win32.keycodes[0x020] = GLFW_KEY_D; + _glfw.win32.keycodes[0x012] = GLFW_KEY_E; + _glfw.win32.keycodes[0x021] = GLFW_KEY_F; + _glfw.win32.keycodes[0x022] = GLFW_KEY_G; + _glfw.win32.keycodes[0x023] = GLFW_KEY_H; + _glfw.win32.keycodes[0x017] = GLFW_KEY_I; + _glfw.win32.keycodes[0x024] = GLFW_KEY_J; + _glfw.win32.keycodes[0x025] = GLFW_KEY_K; + _glfw.win32.keycodes[0x026] = GLFW_KEY_L; + _glfw.win32.keycodes[0x032] = GLFW_KEY_M; + _glfw.win32.keycodes[0x031] = GLFW_KEY_N; + _glfw.win32.keycodes[0x018] = GLFW_KEY_O; + _glfw.win32.keycodes[0x019] = GLFW_KEY_P; + _glfw.win32.keycodes[0x010] = GLFW_KEY_Q; + _glfw.win32.keycodes[0x013] = GLFW_KEY_R; + _glfw.win32.keycodes[0x01F] = GLFW_KEY_S; + _glfw.win32.keycodes[0x014] = GLFW_KEY_T; + _glfw.win32.keycodes[0x016] = GLFW_KEY_U; + _glfw.win32.keycodes[0x02F] = GLFW_KEY_V; + _glfw.win32.keycodes[0x011] = GLFW_KEY_W; + _glfw.win32.keycodes[0x02D] = GLFW_KEY_X; + _glfw.win32.keycodes[0x015] = GLFW_KEY_Y; + _glfw.win32.keycodes[0x02C] = GLFW_KEY_Z; - _glfw.win32.publicKeys[0x028] = GLFW_KEY_APOSTROPHE; - _glfw.win32.publicKeys[0x02B] = GLFW_KEY_BACKSLASH; - _glfw.win32.publicKeys[0x033] = GLFW_KEY_COMMA; - _glfw.win32.publicKeys[0x00D] = GLFW_KEY_EQUAL; - _glfw.win32.publicKeys[0x029] = GLFW_KEY_GRAVE_ACCENT; - _glfw.win32.publicKeys[0x01A] = GLFW_KEY_LEFT_BRACKET; - _glfw.win32.publicKeys[0x00C] = GLFW_KEY_MINUS; - _glfw.win32.publicKeys[0x034] = GLFW_KEY_PERIOD; - _glfw.win32.publicKeys[0x01B] = GLFW_KEY_RIGHT_BRACKET; - _glfw.win32.publicKeys[0x027] = GLFW_KEY_SEMICOLON; - _glfw.win32.publicKeys[0x035] = GLFW_KEY_SLASH; - _glfw.win32.publicKeys[0x056] = GLFW_KEY_WORLD_2; + _glfw.win32.keycodes[0x028] = GLFW_KEY_APOSTROPHE; + _glfw.win32.keycodes[0x02B] = GLFW_KEY_BACKSLASH; + _glfw.win32.keycodes[0x033] = GLFW_KEY_COMMA; + _glfw.win32.keycodes[0x00D] = GLFW_KEY_EQUAL; + _glfw.win32.keycodes[0x029] = GLFW_KEY_GRAVE_ACCENT; + _glfw.win32.keycodes[0x01A] = GLFW_KEY_LEFT_BRACKET; + _glfw.win32.keycodes[0x00C] = GLFW_KEY_MINUS; + _glfw.win32.keycodes[0x034] = GLFW_KEY_PERIOD; + _glfw.win32.keycodes[0x01B] = GLFW_KEY_RIGHT_BRACKET; + _glfw.win32.keycodes[0x027] = GLFW_KEY_SEMICOLON; + _glfw.win32.keycodes[0x035] = GLFW_KEY_SLASH; + _glfw.win32.keycodes[0x056] = GLFW_KEY_WORLD_2; - _glfw.win32.publicKeys[0x00E] = GLFW_KEY_BACKSPACE; - _glfw.win32.publicKeys[0x153] = GLFW_KEY_DELETE; - _glfw.win32.publicKeys[0x14F] = GLFW_KEY_END; - _glfw.win32.publicKeys[0x01C] = GLFW_KEY_ENTER; - _glfw.win32.publicKeys[0x001] = GLFW_KEY_ESCAPE; - _glfw.win32.publicKeys[0x147] = GLFW_KEY_HOME; - _glfw.win32.publicKeys[0x152] = GLFW_KEY_INSERT; - _glfw.win32.publicKeys[0x15D] = GLFW_KEY_MENU; - _glfw.win32.publicKeys[0x151] = GLFW_KEY_PAGE_DOWN; - _glfw.win32.publicKeys[0x149] = GLFW_KEY_PAGE_UP; - _glfw.win32.publicKeys[0x045] = GLFW_KEY_PAUSE; - _glfw.win32.publicKeys[0x146] = GLFW_KEY_PAUSE; - _glfw.win32.publicKeys[0x039] = GLFW_KEY_SPACE; - _glfw.win32.publicKeys[0x00F] = GLFW_KEY_TAB; - _glfw.win32.publicKeys[0x03A] = GLFW_KEY_CAPS_LOCK; - _glfw.win32.publicKeys[0x145] = GLFW_KEY_NUM_LOCK; - _glfw.win32.publicKeys[0x046] = GLFW_KEY_SCROLL_LOCK; - _glfw.win32.publicKeys[0x03B] = GLFW_KEY_F1; - _glfw.win32.publicKeys[0x03C] = GLFW_KEY_F2; - _glfw.win32.publicKeys[0x03D] = GLFW_KEY_F3; - _glfw.win32.publicKeys[0x03E] = GLFW_KEY_F4; - _glfw.win32.publicKeys[0x03F] = GLFW_KEY_F5; - _glfw.win32.publicKeys[0x040] = GLFW_KEY_F6; - _glfw.win32.publicKeys[0x041] = GLFW_KEY_F7; - _glfw.win32.publicKeys[0x042] = GLFW_KEY_F8; - _glfw.win32.publicKeys[0x043] = GLFW_KEY_F9; - _glfw.win32.publicKeys[0x044] = GLFW_KEY_F10; - _glfw.win32.publicKeys[0x057] = GLFW_KEY_F11; - _glfw.win32.publicKeys[0x058] = GLFW_KEY_F12; - _glfw.win32.publicKeys[0x064] = GLFW_KEY_F13; - _glfw.win32.publicKeys[0x065] = GLFW_KEY_F14; - _glfw.win32.publicKeys[0x066] = GLFW_KEY_F15; - _glfw.win32.publicKeys[0x067] = GLFW_KEY_F16; - _glfw.win32.publicKeys[0x068] = GLFW_KEY_F17; - _glfw.win32.publicKeys[0x069] = GLFW_KEY_F18; - _glfw.win32.publicKeys[0x06A] = GLFW_KEY_F19; - _glfw.win32.publicKeys[0x06B] = GLFW_KEY_F20; - _glfw.win32.publicKeys[0x06C] = GLFW_KEY_F21; - _glfw.win32.publicKeys[0x06D] = GLFW_KEY_F22; - _glfw.win32.publicKeys[0x06E] = GLFW_KEY_F23; - _glfw.win32.publicKeys[0x076] = GLFW_KEY_F24; - _glfw.win32.publicKeys[0x038] = GLFW_KEY_LEFT_ALT; - _glfw.win32.publicKeys[0x01D] = GLFW_KEY_LEFT_CONTROL; - _glfw.win32.publicKeys[0x02A] = GLFW_KEY_LEFT_SHIFT; - _glfw.win32.publicKeys[0x15B] = GLFW_KEY_LEFT_SUPER; - _glfw.win32.publicKeys[0x137] = GLFW_KEY_PRINT_SCREEN; - _glfw.win32.publicKeys[0x138] = GLFW_KEY_RIGHT_ALT; - _glfw.win32.publicKeys[0x11D] = GLFW_KEY_RIGHT_CONTROL; - _glfw.win32.publicKeys[0x036] = GLFW_KEY_RIGHT_SHIFT; - _glfw.win32.publicKeys[0x15C] = GLFW_KEY_RIGHT_SUPER; - _glfw.win32.publicKeys[0x150] = GLFW_KEY_DOWN; - _glfw.win32.publicKeys[0x14B] = GLFW_KEY_LEFT; - _glfw.win32.publicKeys[0x14D] = GLFW_KEY_RIGHT; - _glfw.win32.publicKeys[0x148] = GLFW_KEY_UP; + _glfw.win32.keycodes[0x00E] = GLFW_KEY_BACKSPACE; + _glfw.win32.keycodes[0x153] = GLFW_KEY_DELETE; + _glfw.win32.keycodes[0x14F] = GLFW_KEY_END; + _glfw.win32.keycodes[0x01C] = GLFW_KEY_ENTER; + _glfw.win32.keycodes[0x001] = GLFW_KEY_ESCAPE; + _glfw.win32.keycodes[0x147] = GLFW_KEY_HOME; + _glfw.win32.keycodes[0x152] = GLFW_KEY_INSERT; + _glfw.win32.keycodes[0x15D] = GLFW_KEY_MENU; + _glfw.win32.keycodes[0x151] = GLFW_KEY_PAGE_DOWN; + _glfw.win32.keycodes[0x149] = GLFW_KEY_PAGE_UP; + _glfw.win32.keycodes[0x045] = GLFW_KEY_PAUSE; + _glfw.win32.keycodes[0x146] = GLFW_KEY_PAUSE; + _glfw.win32.keycodes[0x039] = GLFW_KEY_SPACE; + _glfw.win32.keycodes[0x00F] = GLFW_KEY_TAB; + _glfw.win32.keycodes[0x03A] = GLFW_KEY_CAPS_LOCK; + _glfw.win32.keycodes[0x145] = GLFW_KEY_NUM_LOCK; + _glfw.win32.keycodes[0x046] = GLFW_KEY_SCROLL_LOCK; + _glfw.win32.keycodes[0x03B] = GLFW_KEY_F1; + _glfw.win32.keycodes[0x03C] = GLFW_KEY_F2; + _glfw.win32.keycodes[0x03D] = GLFW_KEY_F3; + _glfw.win32.keycodes[0x03E] = GLFW_KEY_F4; + _glfw.win32.keycodes[0x03F] = GLFW_KEY_F5; + _glfw.win32.keycodes[0x040] = GLFW_KEY_F6; + _glfw.win32.keycodes[0x041] = GLFW_KEY_F7; + _glfw.win32.keycodes[0x042] = GLFW_KEY_F8; + _glfw.win32.keycodes[0x043] = GLFW_KEY_F9; + _glfw.win32.keycodes[0x044] = GLFW_KEY_F10; + _glfw.win32.keycodes[0x057] = GLFW_KEY_F11; + _glfw.win32.keycodes[0x058] = GLFW_KEY_F12; + _glfw.win32.keycodes[0x064] = GLFW_KEY_F13; + _glfw.win32.keycodes[0x065] = GLFW_KEY_F14; + _glfw.win32.keycodes[0x066] = GLFW_KEY_F15; + _glfw.win32.keycodes[0x067] = GLFW_KEY_F16; + _glfw.win32.keycodes[0x068] = GLFW_KEY_F17; + _glfw.win32.keycodes[0x069] = GLFW_KEY_F18; + _glfw.win32.keycodes[0x06A] = GLFW_KEY_F19; + _glfw.win32.keycodes[0x06B] = GLFW_KEY_F20; + _glfw.win32.keycodes[0x06C] = GLFW_KEY_F21; + _glfw.win32.keycodes[0x06D] = GLFW_KEY_F22; + _glfw.win32.keycodes[0x06E] = GLFW_KEY_F23; + _glfw.win32.keycodes[0x076] = GLFW_KEY_F24; + _glfw.win32.keycodes[0x038] = GLFW_KEY_LEFT_ALT; + _glfw.win32.keycodes[0x01D] = GLFW_KEY_LEFT_CONTROL; + _glfw.win32.keycodes[0x02A] = GLFW_KEY_LEFT_SHIFT; + _glfw.win32.keycodes[0x15B] = GLFW_KEY_LEFT_SUPER; + _glfw.win32.keycodes[0x137] = GLFW_KEY_PRINT_SCREEN; + _glfw.win32.keycodes[0x138] = GLFW_KEY_RIGHT_ALT; + _glfw.win32.keycodes[0x11D] = GLFW_KEY_RIGHT_CONTROL; + _glfw.win32.keycodes[0x036] = GLFW_KEY_RIGHT_SHIFT; + _glfw.win32.keycodes[0x15C] = GLFW_KEY_RIGHT_SUPER; + _glfw.win32.keycodes[0x150] = GLFW_KEY_DOWN; + _glfw.win32.keycodes[0x14B] = GLFW_KEY_LEFT; + _glfw.win32.keycodes[0x14D] = GLFW_KEY_RIGHT; + _glfw.win32.keycodes[0x148] = GLFW_KEY_UP; - _glfw.win32.publicKeys[0x052] = GLFW_KEY_KP_0; - _glfw.win32.publicKeys[0x04F] = GLFW_KEY_KP_1; - _glfw.win32.publicKeys[0x050] = GLFW_KEY_KP_2; - _glfw.win32.publicKeys[0x051] = GLFW_KEY_KP_3; - _glfw.win32.publicKeys[0x04B] = GLFW_KEY_KP_4; - _glfw.win32.publicKeys[0x04C] = GLFW_KEY_KP_5; - _glfw.win32.publicKeys[0x04D] = GLFW_KEY_KP_6; - _glfw.win32.publicKeys[0x047] = GLFW_KEY_KP_7; - _glfw.win32.publicKeys[0x048] = GLFW_KEY_KP_8; - _glfw.win32.publicKeys[0x049] = GLFW_KEY_KP_9; - _glfw.win32.publicKeys[0x04E] = GLFW_KEY_KP_ADD; - _glfw.win32.publicKeys[0x053] = GLFW_KEY_KP_DECIMAL; - _glfw.win32.publicKeys[0x135] = GLFW_KEY_KP_DIVIDE; - _glfw.win32.publicKeys[0x11C] = GLFW_KEY_KP_ENTER; - _glfw.win32.publicKeys[0x037] = GLFW_KEY_KP_MULTIPLY; - _glfw.win32.publicKeys[0x04A] = GLFW_KEY_KP_SUBTRACT; + _glfw.win32.keycodes[0x052] = GLFW_KEY_KP_0; + _glfw.win32.keycodes[0x04F] = GLFW_KEY_KP_1; + _glfw.win32.keycodes[0x050] = GLFW_KEY_KP_2; + _glfw.win32.keycodes[0x051] = GLFW_KEY_KP_3; + _glfw.win32.keycodes[0x04B] = GLFW_KEY_KP_4; + _glfw.win32.keycodes[0x04C] = GLFW_KEY_KP_5; + _glfw.win32.keycodes[0x04D] = GLFW_KEY_KP_6; + _glfw.win32.keycodes[0x047] = GLFW_KEY_KP_7; + _glfw.win32.keycodes[0x048] = GLFW_KEY_KP_8; + _glfw.win32.keycodes[0x049] = GLFW_KEY_KP_9; + _glfw.win32.keycodes[0x04E] = GLFW_KEY_KP_ADD; + _glfw.win32.keycodes[0x053] = GLFW_KEY_KP_DECIMAL; + _glfw.win32.keycodes[0x135] = GLFW_KEY_KP_DIVIDE; + _glfw.win32.keycodes[0x11C] = GLFW_KEY_KP_ENTER; + _glfw.win32.keycodes[0x037] = GLFW_KEY_KP_MULTIPLY; + _glfw.win32.keycodes[0x04A] = GLFW_KEY_KP_SUBTRACT; for (scancode = 0; scancode < 512; scancode++) { - if (_glfw.win32.publicKeys[scancode] > 0) - _glfw.win32.nativeKeys[_glfw.win32.publicKeys[scancode]] = scancode; + if (_glfw.win32.keycodes[scancode] > 0) + _glfw.win32.scancodes[_glfw.win32.keycodes[scancode]] = scancode; } } @@ -304,18 +323,19 @@ static void createKeyTables(void) // static HWND createHelperWindow(void) { + MSG msg; HWND window = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, _GLFW_WNDCLASSNAME, - L"GLFW helper window", + L"GLFW message window", WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 1, 1, - HWND_MESSAGE, NULL, + NULL, NULL, GetModuleHandleW(NULL), NULL); if (!window) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create helper window"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create helper window"); return NULL; } @@ -336,6 +356,12 @@ static HWND createHelperWindow(void) DEVICE_NOTIFY_WINDOW_HANDLE); } + while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + return window; } @@ -349,16 +375,22 @@ static HWND createHelperWindow(void) WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) { WCHAR* target; - int length; + int count; - length = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); - if (!length) - return NULL; - - target = calloc(length, sizeof(WCHAR)); - - if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, length)) + count = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); + if (!count) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string from UTF-8"); + return NULL; + } + + target = calloc(count, sizeof(WCHAR)); + + if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, count)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string from UTF-8"); free(target); return NULL; } @@ -371,16 +403,22 @@ WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) { char* target; - int length; + int size; - length = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); - if (!length) - return NULL; - - target = calloc(length, 1); - - if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, length, NULL, NULL)) + size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); + if (!size) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string to UTF-8"); + return NULL; + } + + target = calloc(size, 1); + + if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string to UTF-8"); free(target); return NULL; } @@ -388,6 +426,81 @@ char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) return target; } +// Reports the specified error, appending information about the last Win32 error +// +void _glfwInputErrorWin32(int error, const char* description) +{ + WCHAR buffer[_GLFW_MESSAGE_SIZE] = L""; + char message[_GLFW_MESSAGE_SIZE] = ""; + + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, + GetLastError() & 0xffff, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buffer, + sizeof(buffer), + NULL); + WideCharToMultiByte(CP_UTF8, 0, buffer, -1, message, sizeof(message), NULL, NULL); + + _glfwInputError(error, "%s: %s", description, message); +} + +// Updates key names according to the current keyboard layout +// +void _glfwUpdateKeyNamesWin32(void) +{ + int key; + BYTE state[256] = {0}; + + memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames)); + + for (key = GLFW_KEY_SPACE; key <= GLFW_KEY_LAST; key++) + { + UINT vk; + int scancode, length; + WCHAR chars[16]; + + scancode = _glfw.win32.scancodes[key]; + if (scancode == -1) + continue; + + if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) + { + const UINT vks[] = { + VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, + VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, + VK_NUMPAD8, VK_NUMPAD9, VK_DECIMAL, VK_DIVIDE, + VK_MULTIPLY, VK_SUBTRACT, VK_ADD + }; + + vk = vks[key - GLFW_KEY_KP_0]; + } + else + vk = MapVirtualKey(scancode, MAPVK_VSC_TO_VK); + + length = ToUnicode(vk, scancode, state, + chars, sizeof(chars) / sizeof(WCHAR), + 0); + + if (length == -1) + { + length = ToUnicode(vk, scancode, state, + chars, sizeof(chars) / sizeof(WCHAR), + 0); + } + + if (length < 1) + continue; + + WideCharToMultiByte(CP_UTF8, 0, chars, 1, + _glfw.win32.keynames[key], + sizeof(_glfw.win32.keynames[key]), + NULL, NULL); + } +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -395,9 +508,6 @@ char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) int _glfwPlatformInit(void) { - if (!_glfwInitThreadLocalStorageWin32()) - return GLFW_FALSE; - // To make SetForegroundWindow work as we want, we need to fiddle // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early // as possible in the hope of still being the foreground process) @@ -410,11 +520,12 @@ int _glfwPlatformInit(void) return GLFW_FALSE; createKeyTables(); + _glfwUpdateKeyNamesWin32(); - if (_glfw_SetProcessDpiAwareness) - _glfw_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - else if (_glfw_SetProcessDPIAware) - _glfw_SetProcessDPIAware(); + if (IsWindows8Point1OrGreater()) + SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + else if (IsWindowsVistaOrGreater()) + SetProcessDPIAware(); if (!_glfwRegisterWindowClassWin32()) return GLFW_FALSE; @@ -423,11 +534,10 @@ int _glfwPlatformInit(void) if (!_glfw.win32.helperWindowHandle) return GLFW_FALSE; - _glfwPlatformPollEvents(); - _glfwInitTimerWin32(); _glfwInitJoysticksWin32(); + _glfwPollMonitorsWin32(); return GLFW_TRUE; } @@ -444,12 +554,12 @@ void _glfwPlatformTerminate(void) SPIF_SENDCHANGE); free(_glfw.win32.clipboardString); + free(_glfw.win32.rawInput); _glfwTerminateWGL(); _glfwTerminateEGL(); _glfwTerminateJoysticksWin32(); - _glfwTerminateThreadLocalStorageWin32(); freeLibraries(); } diff --git a/raylib/external/glfw/src/win32_joystick.c b/raylib/external/glfw/src/win32_joystick.c index 49f3b87..d9d341f 100644 --- a/raylib/external/glfw/src/win32_joystick.c +++ b/raylib/external/glfw/src/win32_joystick.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.1 Win32 - www.glfw.org +// GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -27,13 +27,9 @@ #include "internal.h" +#include #include -#include - -#define _GLFW_PRESENCE_ONLY 1 -#define _GLFW_UPDATE_STATE 2 - #define _GLFW_TYPE_AXIS 0 #define _GLFW_TYPE_SLIDER 1 #define _GLFW_TYPE_BUTTON 2 @@ -52,18 +48,36 @@ typedef struct _GLFWobjenumWin32 int povCount; } _GLFWobjenumWin32; -// Define only the necessary GUIDs (it's bad enough that we're exporting these) +// Define local copies of the necessary GUIDs // -DEFINE_GUID(IID_IDirectInput8W,0xbf798031,0x483a,0x4da2,0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00); -DEFINE_GUID(GUID_XAxis,0xa36d02e0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_YAxis,0xa36d02e1,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_ZAxis,0xa36d02e2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_RxAxis,0xa36d02f4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_RyAxis,0xa36d02f5,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_RzAxis,0xa36d02e3,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_Slider,0xa36d02e4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_Button,0xa36d02f0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); -DEFINE_GUID(GUID_POV,0xa36d02f2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +static const GUID _glfw_IID_IDirectInput8W = + {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}}; +static const GUID _glfw_GUID_XAxis = + {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_YAxis = + {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_ZAxis = + {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RxAxis = + {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RyAxis = + {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_RzAxis = + {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_Slider = + {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; +static const GUID _glfw_GUID_POV = + {0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; + +#define IID_IDirectInput8W _glfw_IID_IDirectInput8W +#define GUID_XAxis _glfw_GUID_XAxis +#define GUID_YAxis _glfw_GUID_YAxis +#define GUID_ZAxis _glfw_GUID_ZAxis +#define GUID_RxAxis _glfw_GUID_RxAxis +#define GUID_RyAxis _glfw_GUID_RyAxis +#define GUID_RzAxis _glfw_GUID_RzAxis +#define GUID_Slider _glfw_GUID_Slider +#define GUID_POV _glfw_GUID_POV // Object data array for our clone of c_dfDIJoystick // Generated with https://github.com/elmindreda/c_dfDIJoystick2 @@ -149,9 +163,9 @@ static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic) case XINPUT_DEVSUBTYPE_GAMEPAD: { if (xic->Flags & XINPUT_CAPS_WIRELESS) - return "Wireless Xbox 360 Controller"; + return "Wireless Xbox Controller"; else - return "Xbox 360 Controller"; + return "Xbox Controller"; } } @@ -238,25 +252,20 @@ static GLFWbool supportsXInput(const GUID* guid) // Frees all resources associated with the specified joystick // -static void closeJoystick(_GLFWjoystickWin32* js) +static void closeJoystick(_GLFWjoystick* js) { - if (js->device) + if (js->win32.device) { - IDirectInputDevice8_Unacquire(js->device); - IDirectInputDevice8_Release(js->device); + IDirectInputDevice8_Unacquire(js->win32.device); + IDirectInputDevice8_Release(js->win32.device); } - free(js->name); - free(js->axes); - free(js->buttons); - free(js->objects); - memset(js, 0, sizeof(_GLFWjoystickWin32)); - - _glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED); + _glfwFreeJoystick(js); + _glfwInputJoystick(js, GLFW_DISCONNECTED); } // DirectInput device object enumeration callback -// Insights gleaned from SDL2 +// Insights gleaned from SDL // static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, void* user) @@ -332,28 +341,25 @@ static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, // static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) { - int joy = 0; + int jid = 0; DIDEVCAPS dc; DIPROPDWORD dipd; IDirectInputDevice8* device; _GLFWobjenumWin32 data; - _GLFWjoystickWin32* js; + _GLFWjoystick* js; + char guid[33]; + char name[256]; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { - if (memcmp(&_glfw.win32_js[joy].guid, &di->guidInstance, sizeof(GUID)) == 0) - return DIENUM_CONTINUE; + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + { + if (memcmp(&js->win32.guid, &di->guidInstance, sizeof(GUID)) == 0) + return DIENUM_CONTINUE; + } } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - { - if (!_glfw.win32_js[joy].present) - break; - } - - if (joy > GLFW_JOYSTICK_LAST) - return DIENUM_STOP; - if (supportsXInput(&di->guidProduct)) return DIENUM_CONTINUE; @@ -362,14 +368,14 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) &device, NULL))) { - _glfwInputError(GLFW_PLATFORM_ERROR, "DI: Failed to create device"); + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create device"); return DIENUM_CONTINUE; } if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to set device data format"); + "Win32: Failed to set device data format"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; @@ -381,7 +387,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to query device capabilities"); + "Win32: Failed to query device capabilities"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; @@ -398,7 +404,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) &dipd.diph))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to set device axis mode"); + "Win32: Failed to set device axis mode"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; @@ -415,7 +421,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to enumerate device objects"); + "Win32: Failed to enumerate device objects"); IDirectInputDevice8_Release(device); free(data.objects); @@ -426,224 +432,54 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) sizeof(_GLFWjoyobjectWin32), compareJoystickObjects); - js = _glfw.win32_js + joy; - js->device = device; - js->guid = di->guidInstance; - js->axisCount = data.axisCount + data.sliderCount; - js->axes = calloc(js->axisCount, sizeof(float)); - js->buttonCount += data.buttonCount + data.povCount * 4; - js->buttons = calloc(js->buttonCount, 1); - js->objects = data.objects; - js->objectCount = data.objectCount; - js->name = _glfwCreateUTF8FromWideStringWin32(di->tszInstanceName); - js->present = GLFW_TRUE; - - _glfwInputJoystickChange(joy, GLFW_CONNECTED); - return DIENUM_CONTINUE; -} - -// Attempt to open the specified joystick device -// TODO: Pack state arrays for non-gamepad devices -// -static GLFWbool openXinputDevice(DWORD index) -{ - int joy; - XINPUT_CAPABILITIES xic; - _GLFWjoystickWin32* js; - - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + if (!WideCharToMultiByte(CP_UTF8, 0, + di->tszInstanceName, -1, + name, sizeof(name), + NULL, NULL)) { - if (_glfw.win32_js[joy].present && - _glfw.win32_js[joy].device == NULL && - _glfw.win32_js[joy].index == index) - { - return GLFW_FALSE; - } + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert joystick name to UTF-8"); + + IDirectInputDevice8_Release(device); + free(data.objects); + return DIENUM_STOP; } - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) + // Generate a joystick GUID that matches the SDL 2.0.5+ one + if (memcmp(&di->guidProduct.Data4[2], "PIDVID", 6) == 0) { - if (!_glfw.win32_js[joy].present) - break; - } - - if (joy > GLFW_JOYSTICK_LAST) - return GLFW_FALSE; - - if (_glfw_XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) - return GLFW_FALSE; - - js = _glfw.win32_js + joy; - js->axisCount = 6; - js->axes = calloc(js->axisCount, sizeof(float)); - js->buttonCount = 14; - js->buttons = calloc(js->buttonCount, 1); - js->present = GLFW_TRUE; - js->name = strdup(getDeviceDescription(&xic)); - js->index = index; - - _glfwInputJoystickChange(joy, GLFW_CONNECTED); - - return GLFW_TRUE; -} - -// Polls for and processes events the specified joystick -// -static GLFWbool pollJoystickState(_GLFWjoystickWin32* js, int mode) -{ - if (!js->present) - return GLFW_FALSE; - - if (js->device) - { - int i, j, ai = 0, bi = 0; - HRESULT result; - DIJOYSTATE state; - - IDirectInputDevice8_Poll(js->device); - result = IDirectInputDevice8_GetDeviceState(js->device, - sizeof(state), - &state); - if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) - { - IDirectInputDevice8_Acquire(js->device); - IDirectInputDevice8_Poll(js->device); - result = IDirectInputDevice8_GetDeviceState(js->device, - sizeof(state), - &state); - } - - if (FAILED(result)) - { - closeJoystick(js); - return GLFW_FALSE; - } - - if (mode == _GLFW_PRESENCE_ONLY) - return GLFW_TRUE; - - for (i = 0; i < js->objectCount; i++) - { - const void* data = (char*) &state + js->objects[i].offset; - - switch (js->objects[i].type) - { - case _GLFW_TYPE_AXIS: - case _GLFW_TYPE_SLIDER: - { - js->axes[ai++] = (*((LONG*) data) + 0.5f) / 32767.5f; - break; - } - - case _GLFW_TYPE_BUTTON: - { - if (*((BYTE*) data) & 0x80) - js->buttons[bi++] = GLFW_PRESS; - else - js->buttons[bi++] = GLFW_RELEASE; - - break; - } - - case _GLFW_TYPE_POV: - { - const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; - // Screams of horror are appropriate at this point - int value = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); - if (value < 0 || value > 8) - value = 8; - - for (j = 0; j < 4; j++) - { - if (directions[value] & (1 << j)) - js->buttons[bi++] = GLFW_PRESS; - else - js->buttons[bi++] = GLFW_RELEASE; - } - - break; - } - } - } - - return GLFW_TRUE; + sprintf(guid, "03000000%02x%02x0000%02x%02x000000000000", + (uint8_t) di->guidProduct.Data1, + (uint8_t) (di->guidProduct.Data1 >> 8), + (uint8_t) (di->guidProduct.Data1 >> 16), + (uint8_t) (di->guidProduct.Data1 >> 24)); } else { - int i; - DWORD result; - XINPUT_STATE xis; - const WORD buttons[14] = - { - XINPUT_GAMEPAD_A, - XINPUT_GAMEPAD_B, - XINPUT_GAMEPAD_X, - XINPUT_GAMEPAD_Y, - XINPUT_GAMEPAD_LEFT_SHOULDER, - XINPUT_GAMEPAD_RIGHT_SHOULDER, - XINPUT_GAMEPAD_BACK, - XINPUT_GAMEPAD_START, - XINPUT_GAMEPAD_LEFT_THUMB, - XINPUT_GAMEPAD_RIGHT_THUMB, - XINPUT_GAMEPAD_DPAD_UP, - XINPUT_GAMEPAD_DPAD_RIGHT, - XINPUT_GAMEPAD_DPAD_DOWN, - XINPUT_GAMEPAD_DPAD_LEFT - }; - - result = _glfw_XInputGetState(js->index, &xis); - if (result != ERROR_SUCCESS) - { - if (result == ERROR_DEVICE_NOT_CONNECTED) - closeJoystick(js); - - return GLFW_FALSE; - } - - if (mode == _GLFW_PRESENCE_ONLY) - return GLFW_TRUE; - - if (sqrt((double) (xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX + - xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY)) > - (double) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) - { - js->axes[0] = (xis.Gamepad.sThumbLX + 0.5f) / 32767.f; - js->axes[1] = (xis.Gamepad.sThumbLY + 0.5f) / 32767.f; - } - else - { - js->axes[0] = 0.f; - js->axes[1] = 0.f; - } - - if (sqrt((double) (xis.Gamepad.sThumbRX * xis.Gamepad.sThumbRX + - xis.Gamepad.sThumbRY * xis.Gamepad.sThumbRY)) > - (double) XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) - { - js->axes[2] = (xis.Gamepad.sThumbRX + 0.5f) / 32767.f; - js->axes[3] = (xis.Gamepad.sThumbRY + 0.5f) / 32767.f; - } - else - { - js->axes[2] = 0.f; - js->axes[3] = 0.f; - } - - if (xis.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) - js->axes[4] = xis.Gamepad.bLeftTrigger / 127.5f - 1.f; - else - js->axes[4] = -1.f; - - if (xis.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) - js->axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f; - else - js->axes[5] = -1.f; - - for (i = 0; i < 14; i++) - js->buttons[i] = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; - - return GLFW_TRUE; + sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", + name[0], name[1], name[2], name[3], + name[4], name[5], name[6], name[7], + name[8], name[9], name[10]); } + + js = _glfwAllocJoystick(name, guid, + data.axisCount + data.sliderCount, + data.buttonCount, + data.povCount); + if (!js) + { + IDirectInputDevice8_Release(device); + free(data.objects); + return DIENUM_STOP; + } + + js->win32.device = device; + js->win32.guid = di->guidInstance; + js->win32.objects = data.objects; + js->win32.objectCount = data.objectCount; + + _glfwInputJoystick(js, GLFW_CONNECTED); + return DIENUM_CONTINUE; } @@ -657,14 +493,14 @@ void _glfwInitJoysticksWin32(void) { if (_glfw.win32.dinput8.instance) { - if (FAILED(_glfw_DirectInput8Create(GetModuleHandle(NULL), - DIRECTINPUT_VERSION, - &IID_IDirectInput8W, - (void**) &_glfw.win32.dinput8.api, - NULL))) + if (FAILED(DirectInput8Create(GetModuleHandle(NULL), + DIRECTINPUT_VERSION, + &IID_IDirectInput8W, + (void**) &_glfw.win32.dinput8.api, + NULL))) { _glfwInputError(GLFW_PLATFORM_ERROR, - "DI: Failed to create interface"); + "Win32: Failed to create interface"); } } @@ -675,10 +511,10 @@ void _glfwInitJoysticksWin32(void) // void _glfwTerminateJoysticksWin32(void) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - closeJoystick(_glfw.win32_js + joy); + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + closeJoystick(_glfw.joysticks + jid); if (_glfw.win32.dinput8.api) IDirectInput8_Release(_glfw.win32.dinput8.api); @@ -690,10 +526,43 @@ void _glfwDetectJoystickConnectionWin32(void) { if (_glfw.win32.xinput.instance) { - DWORD i; + DWORD index; - for (i = 0; i < XUSER_MAX_COUNT; i++) - openXinputDevice(i); + for (index = 0; index < XUSER_MAX_COUNT; index++) + { + int jid; + char guid[33]; + XINPUT_CAPABILITIES xic; + _GLFWjoystick* js; + + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (_glfw.joysticks[jid].present && + _glfw.joysticks[jid].win32.device == NULL && + _glfw.joysticks[jid].win32.index == index) + { + break; + } + } + + if (jid <= GLFW_JOYSTICK_LAST) + continue; + + if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) + continue; + + // Generate a joystick GUID that matches the SDL 2.0.5+ one + sprintf(guid, "78696e707574%02x000000000000000000", + xic.SubType & 0xff); + + js = _glfwAllocJoystick(getDeviceDescription(&xic), guid, 6, 10, 1); + if (!js) + continue; + + js->win32.index = index; + + _glfwInputJoystick(js, GLFW_CONNECTED); + } } if (_glfw.win32.dinput8.api) @@ -715,10 +584,14 @@ void _glfwDetectJoystickConnectionWin32(void) // void _glfwDetectJoystickDisconnectionWin32(void) { - int joy; + int jid; - for (joy = GLFW_JOYSTICK_1; joy <= GLFW_JOYSTICK_LAST; joy++) - pollJoystickState(_glfw.win32_js + joy, _GLFW_PRESENCE_ONLY); + for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) + { + _GLFWjoystick* js = _glfw.joysticks + jid; + if (js->present) + _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); + } } @@ -726,38 +599,153 @@ void _glfwDetectJoystickDisconnectionWin32(void) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformJoystickPresent(int joy) +int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { - _GLFWjoystickWin32* js = _glfw.win32_js + joy; - return pollJoystickState(js, _GLFW_PRESENCE_ONLY); + if (js->win32.device) + { + int i, ai = 0, bi = 0, pi = 0; + HRESULT result; + DIJOYSTATE state; + + IDirectInputDevice8_Poll(js->win32.device); + result = IDirectInputDevice8_GetDeviceState(js->win32.device, + sizeof(state), + &state); + if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) + { + IDirectInputDevice8_Acquire(js->win32.device); + IDirectInputDevice8_Poll(js->win32.device); + result = IDirectInputDevice8_GetDeviceState(js->win32.device, + sizeof(state), + &state); + } + + if (FAILED(result)) + { + closeJoystick(js); + return GLFW_FALSE; + } + + if (mode == _GLFW_POLL_PRESENCE) + return GLFW_TRUE; + + for (i = 0; i < js->win32.objectCount; i++) + { + const void* data = (char*) &state + js->win32.objects[i].offset; + + switch (js->win32.objects[i].type) + { + case _GLFW_TYPE_AXIS: + case _GLFW_TYPE_SLIDER: + { + const float value = (*((LONG*) data) + 0.5f) / 32767.5f; + _glfwInputJoystickAxis(js, ai, value); + ai++; + break; + } + + case _GLFW_TYPE_BUTTON: + { + const char value = (*((BYTE*) data) & 0x80) != 0; + _glfwInputJoystickButton(js, bi, value); + bi++; + break; + } + + case _GLFW_TYPE_POV: + { + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + + // Screams of horror are appropriate at this point + int state = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); + if (state < 0 || state > 8) + state = 8; + + _glfwInputJoystickHat(js, pi, states[state]); + pi++; + break; + } + } + } + } + else + { + int i, dpad = 0; + DWORD result; + XINPUT_STATE xis; + const WORD buttons[10] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB + }; + + result = XInputGetState(js->win32.index, &xis); + if (result != ERROR_SUCCESS) + { + if (result == ERROR_DEVICE_NOT_CONNECTED) + closeJoystick(js); + + return GLFW_FALSE; + } + + if (mode == _GLFW_POLL_PRESENCE) + return GLFW_TRUE; + + _glfwInputJoystickAxis(js, 0, (xis.Gamepad.sThumbLX + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 1, -(xis.Gamepad.sThumbLY + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 2, (xis.Gamepad.sThumbRX + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 3, -(xis.Gamepad.sThumbRY + 0.5f) / 32767.5f); + _glfwInputJoystickAxis(js, 4, xis.Gamepad.bLeftTrigger / 127.5f - 1.f); + _glfwInputJoystickAxis(js, 5, xis.Gamepad.bRightTrigger / 127.5f - 1.f); + + for (i = 0; i < 10; i++) + { + const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; + _glfwInputJoystickButton(js, i, value); + } + + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) + dpad |= GLFW_HAT_UP; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) + dpad |= GLFW_HAT_RIGHT; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + dpad |= GLFW_HAT_DOWN; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) + dpad |= GLFW_HAT_LEFT; + + _glfwInputJoystickHat(js, 0, dpad); + } + + return GLFW_TRUE; } -const float* _glfwPlatformGetJoystickAxes(int joy, int* count) +void _glfwPlatformUpdateGamepadGUID(char* guid) { - _GLFWjoystickWin32* js = _glfw.win32_js + joy; - if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) - return NULL; - - *count = js->axisCount; - return js->axes; -} - -const unsigned char* _glfwPlatformGetJoystickButtons(int joy, int* count) -{ - _GLFWjoystickWin32* js = _glfw.win32_js + joy; - if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) - return NULL; - - *count = js->buttonCount; - return js->buttons; -} - -const char* _glfwPlatformGetJoystickName(int joy) -{ - _GLFWjoystickWin32* js = _glfw.win32_js + joy; - if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY)) - return NULL; - - return js->name; + if (strcmp(guid + 20, "504944564944") == 0) + { + char original[33]; + strcpy(original, guid); + sprintf(guid, "03000000%.4s0000%.4s000000000000", + original, original + 4); + } } diff --git a/raylib/external/glfw/src/win32_joystick.h b/raylib/external/glfw/src/win32_joystick.h index 6a75b41..9156f6c 100644 --- a/raylib/external/glfw/src/win32_joystick.h +++ b/raylib/external/glfw/src/win32_joystick.h @@ -1,7 +1,7 @@ //======================================================================== -// GLFW 3.2 Win32 - www.glfw.org +// GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -24,11 +24,10 @@ // //======================================================================== -#ifndef _glfw3_win32_joystick_h_ -#define _glfw3_win32_joystick_h_ +#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32 +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE int dummy -#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ - _GLFWjoystickWin32 win32_js[GLFW_JOYSTICK_LAST + 1] +#define _GLFW_PLATFORM_MAPPING_NAME "Windows" // Joystick element (axis, button or slider) // @@ -42,14 +41,8 @@ typedef struct _GLFWjoyobjectWin32 // typedef struct _GLFWjoystickWin32 { - GLFWbool present; - float* axes; - int axisCount; - unsigned char* buttons; - int buttonCount; _GLFWjoyobjectWin32* objects; int objectCount; - char* name; IDirectInputDevice8W* device; DWORD index; GUID guid; @@ -61,4 +54,3 @@ void _glfwTerminateJoysticksWin32(void); void _glfwDetectJoystickConnectionWin32(void); void _glfwDetectJoystickDisconnectionWin32(void); -#endif // _glfw3_win32_joystick_h_ diff --git a/raylib/external/glfw/src/win32_monitor.c b/raylib/external/glfw/src/win32_monitor.c index e55c9a7..74dd825 100644 --- a/raylib/external/glfw/src/win32_monitor.c +++ b/raylib/external/glfw/src/win32_monitor.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 Win32 - www.glfw.org +// GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -33,33 +33,66 @@ #include +// Callback for EnumDisplayMonitors in createMonitor +// +static BOOL CALLBACK monitorCallback(HMONITOR handle, + HDC dc, + RECT* rect, + LPARAM data) +{ + MONITORINFOEXW mi; + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + + if (GetMonitorInfoW(handle, (MONITORINFO*) &mi)) + { + _GLFWmonitor* monitor = (_GLFWmonitor*) data; + if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0) + monitor->win32.handle = handle; + } + + return TRUE; +} + // Create monitor from an adapter and (optionally) a display // static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* display) { _GLFWmonitor* monitor; + int widthMM, heightMM; char* name; HDC dc; + DEVMODEW dm; + RECT rect; if (display) name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString); else name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString); if (!name) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert string to UTF-8"); return NULL; - } + + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm); dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); - monitor = _glfwAllocMonitor(name, - GetDeviceCaps(dc, HORZSIZE), - GetDeviceCaps(dc, VERTSIZE)); + if (IsWindows8Point1OrGreater()) + { + widthMM = GetDeviceCaps(dc, HORZSIZE); + heightMM = GetDeviceCaps(dc, VERTSIZE); + } + else + { + widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX)); + heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY)); + } DeleteDC(dc); + + monitor = _glfwAllocMonitor(name, widthMM, heightMM); free(name); if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) @@ -82,6 +115,12 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, NULL, NULL); } + rect.left = dm.dmPosition.x; + rect.top = dm.dmPosition.y; + rect.right = dm.dmPosition.x + dm.dmPelsWidth; + rect.bottom = dm.dmPosition.y + dm.dmPelsHeight; + + EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor); return monitor; } @@ -90,6 +129,116 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsWin32(void) +{ + int i, disconnectedCount; + _GLFWmonitor** disconnected = NULL; + DWORD adapterIndex, displayIndex; + DISPLAY_DEVICEW adapter, display; + _GLFWmonitor* monitor; + + disconnectedCount = _glfw.monitorCount; + if (disconnectedCount) + { + disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + memcpy(disconnected, + _glfw.monitors, + _glfw.monitorCount * sizeof(_GLFWmonitor*)); + } + + for (adapterIndex = 0; ; adapterIndex++) + { + int type = _GLFW_INSERT_LAST; + + ZeroMemory(&adapter, sizeof(adapter)); + adapter.cb = sizeof(adapter); + + if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) + break; + + if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + type = _GLFW_INSERT_FIRST; + + for (displayIndex = 0; ; displayIndex++) + { + ZeroMemory(&display, sizeof(display)); + display.cb = sizeof(display); + + if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) + break; + + if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i] && + wcscmp(disconnected[i]->win32.displayName, + display.DeviceName) == 0) + { + disconnected[i] = NULL; + break; + } + } + + if (i < disconnectedCount) + continue; + + monitor = createMonitor(&adapter, &display); + if (!monitor) + { + free(disconnected); + return; + } + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); + + type = _GLFW_INSERT_LAST; + } + + // HACK: If an active adapter does not have any display devices + // (as sometimes happens), add it directly as a monitor + if (displayIndex == 0) + { + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i] && + wcscmp(disconnected[i]->win32.adapterName, + adapter.DeviceName) == 0) + { + disconnected[i] = NULL; + break; + } + } + + if (i < disconnectedCount) + continue; + + monitor = createMonitor(&adapter, NULL); + if (!monitor) + { + free(disconnected); + return; + } + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); + } + } + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); + } + + free(disconnected); +} + // Change the current video mode // GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) @@ -97,6 +246,7 @@ GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desire GLFWvidmode current; const GLFWvidmode* best; DEVMODEW dm; + LONG result; best = _glfwChooseVideoMode(monitor, desired); _glfwPlatformGetVideoMode(monitor, ¤t); @@ -104,7 +254,7 @@ GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desire return GLFW_TRUE; ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(DEVMODEW); + dm.dmSize = sizeof(dm); dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; dm.dmPelsWidth = best->width; @@ -115,13 +265,34 @@ GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desire if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24) dm.dmBitsPerPel = 32; - if (ChangeDisplaySettingsExW(monitor->win32.adapterName, - &dm, - NULL, - CDS_FULLSCREEN, - NULL) != DISP_CHANGE_SUCCESSFUL) + result = ChangeDisplaySettingsExW(monitor->win32.adapterName, + &dm, + NULL, + CDS_FULLSCREEN, + NULL); + if (result != DISP_CHANGE_SUCCESSFUL) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to set video mode"); + const char* description = "Unknown error"; + + if (result == DISP_CHANGE_BADDUALVIEW) + description = "The system uses DualView"; + else if (result == DISP_CHANGE_BADFLAGS) + description = "Invalid flags"; + else if (result == DISP_CHANGE_BADMODE) + description = "Graphics mode not supported"; + else if (result == DISP_CHANGE_BADPARAM) + description = "Invalid parameter"; + else if (result == DISP_CHANGE_FAILED) + description = "Graphics mode failed"; + else if (result == DISP_CHANGE_NOTUPDATED) + description = "Failed to write to registry"; + else if (result == DISP_CHANGE_RESTART) + description = "Computer restart required"; + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to set video mode: %s", + description); + return GLFW_FALSE; } @@ -141,111 +312,52 @@ void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) } } +void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) +{ + UINT xdpi, ydpi; + + if (IsWindows8Point1OrGreater()) + GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + else + { + const HDC dc = GetDC(NULL); + xdpi = GetDeviceCaps(dc, LOGPIXELSX); + ydpi = GetDeviceCaps(dc, LOGPIXELSY); + ReleaseDC(NULL, dc); + } + + if (xscale) + *xscale = xdpi / 96.f; + if (yscale) + *yscale = ydpi / 96.f; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) -{ - int found = 0; - DWORD adapterIndex, displayIndex, primaryIndex = 0; - DISPLAY_DEVICEW adapter, display; - GLFWbool hasDisplays = GLFW_FALSE; - _GLFWmonitor** monitors = NULL; - - *count = 0; - - // HACK: Check if any active adapters have connected displays - // If not, this is a headless system or a VMware guest - - for (adapterIndex = 0; ; adapterIndex++) - { - ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); - adapter.cb = sizeof(DISPLAY_DEVICEW); - - if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) - break; - - if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - - ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); - display.cb = sizeof(DISPLAY_DEVICEW); - - if (EnumDisplayDevicesW(adapter.DeviceName, 0, &display, 0)) - { - hasDisplays = GLFW_TRUE; - break; - } - } - - for (adapterIndex = 0; ; adapterIndex++) - { - ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); - adapter.cb = sizeof(DISPLAY_DEVICEW); - - if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) - break; - - if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - - if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) - primaryIndex = found; - - if (hasDisplays) - { - for (displayIndex = 0; ; displayIndex++) - { - ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); - display.cb = sizeof(DISPLAY_DEVICEW); - - if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) - break; - - found++; - monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); - monitors[found - 1] = createMonitor(&adapter, &display); - } - } - else - { - found++; - monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); - monitors[found - 1] = createMonitor(&adapter, NULL); - } - } - - _GLFW_SWAP_POINTERS(monitors[0], monitors[primaryIndex]); - - *count = found; - return monitors; -} - -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - if (wcslen(first->win32.displayName)) - return wcscmp(first->win32.displayName, second->win32.displayName) == 0; - else - return wcscmp(first->win32.adapterName, second->win32.adapterName) == 0; -} - void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { - DEVMODEW settings; - ZeroMemory(&settings, sizeof(DEVMODEW)); - settings.dmSize = sizeof(DEVMODEW); + DEVMODEW dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); EnumDisplaySettingsExW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, - &settings, + &dm, EDS_ROTATEDMODE); if (xpos) - *xpos = settings.dmPosition.x; + *xpos = dm.dmPosition.x; if (ypos) - *ypos = settings.dmPosition.y; + *ypos = dm.dmPosition.y; +} + +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale); } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) @@ -261,8 +373,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) GLFWvidmode mode; DEVMODEW dm; - ZeroMemory(&dm, sizeof(DEVMODEW)); - dm.dmSize = sizeof(DEVMODEW); + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm)) break; @@ -328,9 +440,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { DEVMODEW dm; - - ZeroMemory(&dm, sizeof(DEVMODEW)); - dm.dmSize = sizeof(DEVMODEW); + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm); diff --git a/raylib/external/glfw/src/win32_platform.h b/raylib/external/glfw/src/win32_platform.h index c0dcff1..608d375 100644 --- a/raylib/external/glfw/src/win32_platform.h +++ b/raylib/external/glfw/src/win32_platform.h @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 Win32 - www.glfw.org +// GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,9 +25,6 @@ // //======================================================================== -#ifndef _glfw3_win32_platform_h_ -#define _glfw3_win32_platform_h_ - // We don't need all the fancy stuff #ifndef NOMINMAX #define NOMINMAX @@ -66,7 +63,6 @@ #include #include -#include #include #include #include @@ -104,6 +100,9 @@ #ifndef DISPLAY_DEVICE_ACTIVE #define DISPLAY_DEVICE_ACTIVE 0x00000001 #endif +#ifndef _WIN32_WINNT_WINBLUE + #define _WIN32_WINNT_WINBLUE 0x0602 +#endif #if WINVER < 0x0601 typedef struct tagCHANGEFILTERSTRUCT @@ -117,6 +116,18 @@ typedef struct tagCHANGEFILTERSTRUCT #endif #endif /*Windows 7*/ +#if WINVER < 0x0600 +#define DWM_BB_ENABLE 0x00000001 +#define DWM_BB_BLURREGION 0x00000002 +typedef struct +{ + DWORD dwFlags; + BOOL fEnable; + HRGN hRgnBlur; + BOOL fTransitionOnMaximized; +} DWM_BLURBEHIND; +#endif /*Windows Vista*/ + #ifndef DPI_ENUMS_DECLARED typedef enum PROCESS_DPI_AWARENESS { @@ -124,8 +135,30 @@ typedef enum PROCESS_DPI_AWARENESS PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; +typedef enum MONITOR_DPI_TYPE +{ + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; #endif /*DPI_ENUMS_DECLARED*/ +// HACK: Define versionhelpers.h functions manually as MinGW lacks the header +BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp); +#define IsWindowsVistaOrGreater() \ + IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), \ + LOBYTE(_WIN32_WINNT_VISTA), 0) +#define IsWindows7OrGreater() \ + IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), \ + LOBYTE(_WIN32_WINNT_WIN7), 0) +#define IsWindows8OrGreater() \ + IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), \ + LOBYTE(_WIN32_WINNT_WIN8), 0) +#define IsWindows8Point1OrGreater() \ + IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), \ + LOBYTE(_WIN32_WINNT_WINBLUE), 0) + // HACK: Define macros that some xinput.h variants don't #ifndef XINPUT_CAPS_WIRELESS #define XINPUT_CAPS_WIRELESS 0x0002 @@ -161,34 +194,38 @@ typedef enum PROCESS_DPI_AWARENESS #endif // winmm.dll function pointer typedefs -typedef DWORD (WINAPI * TIMEGETTIME_T)(void); -#define _glfw_timeGetTime _glfw.win32.winmm.timeGetTime +typedef DWORD (WINAPI * PFN_timeGetTime)(void); +#define timeGetTime _glfw.win32.winmm.GetTime // xinput.dll function pointer typedefs -typedef DWORD (WINAPI * XINPUTGETCAPABILITIES_T)(DWORD,DWORD,XINPUT_CAPABILITIES*); -typedef DWORD (WINAPI * XINPUTGETSTATE_T)(DWORD,XINPUT_STATE*); -#define _glfw_XInputGetCapabilities _glfw.win32.xinput.XInputGetCapabilities -#define _glfw_XInputGetState _glfw.win32.xinput.XInputGetState +typedef DWORD (WINAPI * PFN_XInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); +typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); +#define XInputGetCapabilities _glfw.win32.xinput.GetCapabilities +#define XInputGetState _glfw.win32.xinput.GetState // dinput8.dll function pointer typedefs -typedef HRESULT (WINAPI * DIRECTINPUT8CREATE_T)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); -#define _glfw_DirectInput8Create _glfw.win32.dinput8.DirectInput8Create +typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); +#define DirectInput8Create _glfw.win32.dinput8.Create // user32.dll function pointer typedefs -typedef BOOL (WINAPI * SETPROCESSDPIAWARE_T)(void); -typedef BOOL (WINAPI * CHANGEWINDOWMESSAGEFILTEREX_T)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT); -#define _glfw_SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware -#define _glfw_ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx +typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void); +typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT); +#define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ +#define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ // dwmapi.dll function pointer typedefs -typedef HRESULT (WINAPI * DWMISCOMPOSITIONENABLED_T)(BOOL*); -typedef HRESULT (WINAPI * DWMFLUSH_T)(VOID); -#define _glfw_DwmIsCompositionEnabled _glfw.win32.dwmapi.DwmIsCompositionEnabled -#define _glfw_DwmFlush _glfw.win32.dwmapi.DwmFlush +typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); +typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); +typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); +#define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled +#define DwmFlush _glfw.win32.dwmapi.Flush +#define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow // shcore.dll function pointer typedefs -typedef HRESULT (WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); -#define _glfw_SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness +typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); +typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); +#define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_ +#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_ typedef VkFlags VkWin32SurfaceCreateFlagsKHR; @@ -207,6 +244,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #include "win32_joystick.h" #include "wgl_context.h" #include "egl_context.h" +#include "osmesa_context.h" #define _GLFW_WNDCLASSNAME L"GLFW30" @@ -219,10 +257,11 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 -#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeWin32 win32_time -#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsWin32 win32_tls +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerWin32 win32 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32 +#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsWin32 win32 +#define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexWin32 win32 // Win32-specific per-window data @@ -234,7 +273,11 @@ typedef struct _GLFWwindowWin32 HICON smallIcon; GLFWbool cursorTracked; + GLFWbool frameAction; GLFWbool iconified; + GLFWbool maximized; + // Whether to enable framebuffer transparency on DWM + GLFWbool transparent; // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; @@ -247,47 +290,52 @@ typedef struct _GLFWlibraryWin32 { HWND helperWindowHandle; DWORD foregroundLockTimeout; + int acquiredMonitorCount; char* clipboardString; - char keyName[64]; - short int publicKeys[512]; - short int nativeKeys[GLFW_KEY_LAST + 1]; + short int keycodes[512]; + short int scancodes[GLFW_KEY_LAST + 1]; + char keynames[GLFW_KEY_LAST + 1][5]; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active _GLFWwindow* disabledCursorWindow; + RAWINPUT* rawInput; + int rawInputSize; struct { - HINSTANCE instance; - TIMEGETTIME_T timeGetTime; + HINSTANCE instance; + PFN_timeGetTime GetTime; } winmm; struct { - HINSTANCE instance; - DIRECTINPUT8CREATE_T DirectInput8Create; - IDirectInput8W* api; + HINSTANCE instance; + PFN_DirectInput8Create Create; + IDirectInput8W* api; } dinput8; struct { - HINSTANCE instance; - XINPUTGETCAPABILITIES_T XInputGetCapabilities; - XINPUTGETSTATE_T XInputGetState; + HINSTANCE instance; + PFN_XInputGetCapabilities GetCapabilities; + PFN_XInputGetState GetState; } xinput; struct { - HINSTANCE instance; - SETPROCESSDPIAWARE_T SetProcessDPIAware; - CHANGEWINDOWMESSAGEFILTEREX_T ChangeWindowMessageFilterEx; + HINSTANCE instance; + PFN_SetProcessDPIAware SetProcessDPIAware_; + PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_; } user32; struct { - HINSTANCE instance; - DWMISCOMPOSITIONENABLED_T DwmIsCompositionEnabled; - DWMFLUSH_T DwmFlush; + HINSTANCE instance; + PFN_DwmIsCompositionEnabled IsCompositionEnabled; + PFN_DwmFlush Flush; + PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; } dwmapi; struct { - HINSTANCE instance; - SETPROCESSDPIAWARENESS_T SetProcessDpiAwareness; + HINSTANCE instance; + PFN_SetProcessDpiAwareness SetProcessDpiAwareness_; + PFN_GetDpiForMonitor GetDpiForMonitor_; } shcore; } _GLFWlibraryWin32; @@ -296,11 +344,12 @@ typedef struct _GLFWlibraryWin32 // typedef struct _GLFWmonitorWin32 { + HMONITOR handle; // This size matches the static size of DISPLAY_DEVICE.DeviceName WCHAR adapterName[32]; WCHAR displayName[32]; - char publicAdapterName[64]; - char publicDisplayName[64]; + char publicAdapterName[32]; + char publicDisplayName[32]; GLFWbool modesPruned; GLFWbool modeChanged; @@ -310,41 +359,51 @@ typedef struct _GLFWmonitorWin32 // typedef struct _GLFWcursorWin32 { - HCURSOR handle; + HCURSOR handle; } _GLFWcursorWin32; // Win32-specific global timer data // -typedef struct _GLFWtimeWin32 +typedef struct _GLFWtimerWin32 { GLFWbool hasPC; uint64_t frequency; -} _GLFWtimeWin32; +} _GLFWtimerWin32; -// Win32-specific global TLS data +// Win32-specific thread local storage data // typedef struct _GLFWtlsWin32 { - GLFWbool allocated; - DWORD context; + GLFWbool allocated; + DWORD index; } _GLFWtlsWin32; +// Win32-specific mutex data +// +typedef struct _GLFWmutexWin32 +{ + GLFWbool allocated; + CRITICAL_SECTION section; + +} _GLFWmutexWin32; + GLFWbool _glfwRegisterWindowClassWin32(void); void _glfwUnregisterWindowClassWin32(void); - -GLFWbool _glfwInitThreadLocalStorageWin32(void); -void _glfwTerminateThreadLocalStorageWin32(void); +GLFWbool _glfwIsCompositionEnabledWin32(void); WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); +void _glfwInputErrorWin32(int error, const char* description); +void _glfwUpdateKeyNamesWin32(void); void _glfwInitTimerWin32(void); +void _glfwPollMonitorsWin32(void); GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); +void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale); -#endif // _glfw3_win32_platform_h_ diff --git a/raylib/external/glfw/src/win32_thread.c b/raylib/external/glfw/src/win32_thread.c new file mode 100644 index 0000000..98231c1 --- /dev/null +++ b/raylib/external/glfw/src/win32_thread.c @@ -0,0 +1,97 @@ +//======================================================================== +// GLFW 3.3 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) +{ + assert(tls->win32.allocated == GLFW_FALSE); + + tls->win32.index = TlsAlloc(); + if (tls->win32.index == TLS_OUT_OF_INDEXES) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to allocate TLS index"); + return GLFW_FALSE; + } + + tls->win32.allocated = GLFW_TRUE; + return GLFW_TRUE; +} + +void _glfwPlatformDestroyTls(_GLFWtls* tls) +{ + if (tls->win32.allocated) + TlsFree(tls->win32.index); + memset(tls, 0, sizeof(_GLFWtls)); +} + +void* _glfwPlatformGetTls(_GLFWtls* tls) +{ + assert(tls->win32.allocated == GLFW_TRUE); + return TlsGetValue(tls->win32.index); +} + +void _glfwPlatformSetTls(_GLFWtls* tls, void* value) +{ + assert(tls->win32.allocated == GLFW_TRUE); + TlsSetValue(tls->win32.index, value); +} + +GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex) +{ + assert(mutex->win32.allocated == GLFW_FALSE); + InitializeCriticalSection(&mutex->win32.section); + return mutex->win32.allocated = GLFW_TRUE; +} + +void _glfwPlatformDestroyMutex(_GLFWmutex* mutex) +{ + if (mutex->win32.allocated) + DeleteCriticalSection(&mutex->win32.section); + memset(mutex, 0, sizeof(_GLFWmutex)); +} + +void _glfwPlatformLockMutex(_GLFWmutex* mutex) +{ + assert(mutex->win32.allocated == GLFW_TRUE); + EnterCriticalSection(&mutex->win32.section); +} + +void _glfwPlatformUnlockMutex(_GLFWmutex* mutex) +{ + assert(mutex->win32.allocated == GLFW_TRUE); + LeaveCriticalSection(&mutex->win32.section); +} + diff --git a/raylib/external/glfw/src/win32_time.c b/raylib/external/glfw/src/win32_time.c index d972f56..f333cd4 100644 --- a/raylib/external/glfw/src/win32_time.c +++ b/raylib/external/glfw/src/win32_time.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 Win32 - www.glfw.org +// GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -40,13 +40,13 @@ void _glfwInitTimerWin32(void) if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) { - _glfw.win32_time.hasPC = GLFW_TRUE; - _glfw.win32_time.frequency = frequency; + _glfw.timer.win32.hasPC = GLFW_TRUE; + _glfw.timer.win32.frequency = frequency; } else { - _glfw.win32_time.hasPC = GLFW_FALSE; - _glfw.win32_time.frequency = 1000; + _glfw.timer.win32.hasPC = GLFW_FALSE; + _glfw.timer.win32.frequency = 1000; } } @@ -57,18 +57,18 @@ void _glfwInitTimerWin32(void) uint64_t _glfwPlatformGetTimerValue(void) { - if (_glfw.win32_time.hasPC) + if (_glfw.timer.win32.hasPC) { uint64_t value; QueryPerformanceCounter((LARGE_INTEGER*) &value); return value; } else - return (uint64_t) _glfw_timeGetTime(); + return (uint64_t) timeGetTime(); } uint64_t _glfwPlatformGetTimerFrequency(void) { - return _glfw.win32_time.frequency; + return _glfw.timer.win32.frequency; } diff --git a/raylib/external/glfw/src/win32_window.c b/raylib/external/glfw/src/win32_window.c index 8e30eb6..1aa3213 100644 --- a/raylib/external/glfw/src/win32_window.c +++ b/raylib/external/glfw/src/win32_window.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 Win32 - www.glfw.org +// GLFW 3.3 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -46,9 +46,11 @@ static DWORD getWindowStyle(const _GLFWwindow* window) style |= WS_POPUP; else { + style |= WS_SYSMENU | WS_MINIMIZEBOX; + if (window->decorated) { - style |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + style |= WS_CAPTION; if (window->resizable) style |= WS_MAXIMIZEBOX | WS_THICKFRAME; @@ -109,7 +111,7 @@ static HICON createIcon(const GLFWimage* image, unsigned char* source = image->pixels; ZeroMemory(&bi, sizeof(bi)); - bi.bV5Size = sizeof(BITMAPV5HEADER); + bi.bV5Size = sizeof(bi); bi.bV5Width = image->width; bi.bV5Height = -image->height; bi.bV5Planes = 1; @@ -131,16 +133,16 @@ static HICON createIcon(const GLFWimage* image, if (!color) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create RGBA bitmap"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create RGBA bitmap"); return NULL; } mask = CreateBitmap(image->width, image->height, 1, 1, NULL); if (!mask) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create mask bitmap"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create mask bitmap"); DeleteObject(color); return NULL; } @@ -170,9 +172,15 @@ static HICON createIcon(const GLFWimage* image, if (!handle) { if (icon) - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create icon"); + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create icon"); + } else - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create cursor"); + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create cursor"); + } } return handle; @@ -278,6 +286,75 @@ static void updateClipRect(_GLFWwindow* window) ClipCursor(NULL); } +// Update native window styles to match attributes +// +static void updateWindowStyles(const _GLFWwindow* window) +{ + RECT rect; + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP); + style |= getWindowStyle(window); + + GetClientRect(window->win32.handle, &rect); + AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window)); + ClientToScreen(window->win32.handle, (POINT*) &rect.left); + ClientToScreen(window->win32.handle, (POINT*) &rect.right); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + SetWindowPos(window->win32.handle, HWND_TOP, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); +} + +// Update window framebuffer transparency +// +static void updateFramebufferTransparency(const _GLFWwindow* window) +{ + if (!IsWindowsVistaOrGreater()) + return; + + if (_glfwIsCompositionEnabledWin32()) + { + HRGN region = CreateRectRgn(0, 0, -1, -1); + DWM_BLURBEHIND bb = {0}; + bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + bb.hRgnBlur = region; + bb.fEnable = TRUE; + + if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb))) + { + // Decorated windows don't repaint the transparent background + // leaving a trail behind animations + // HACK: Making the window layered with a transparency color key + // seems to fix this. Normally, when specifying + // a transparency color key to be used when composing the + // layered window, all pixels painted by the window in this + // color will be transparent. That doesn't seem to be the + // case anymore, at least when used with blur behind window + // plus negative region. + LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + exStyle |= WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); + + // Using a color key not equal to black to fix the trailing + // issue. When set to black, something is making the hit test + // not resize with the window frame. + SetLayeredWindowAttributes(window->win32.handle, + RGB(0, 193, 48), 255, LWA_COLORKEY); + } + + DeleteObject(region); + } + else + { + LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + exStyle &= ~WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle); + RedrawWindow(window->win32.handle, NULL, NULL, + RDW_ERASE | RDW_INVALIDATE | RDW_FRAME); + } +} + // Translates a GLFW standard cursor to a resource ID // static LPWSTR translateCursorShape(int shape) @@ -341,20 +418,19 @@ static int getAsyncKeyMods(void) // static int translateKey(WPARAM wParam, LPARAM lParam) { + // The Ctrl keys require special handling if (wParam == VK_CONTROL) { - // The CTRL keys require special handling - MSG next; DWORD time; - // Is this an extended key (i.e. right key)? + // Right side keys have the extended key bit set if (lParam & 0x01000000) return GLFW_KEY_RIGHT_CONTROL; - // Here is a trick: "Alt Gr" sends LCTRL, then RALT. We only - // want the RALT message, so we try to see if the next message - // is a RALT message. In that case, this is a false LCTRL! + // HACK: Alt Gr sends Left Ctrl and then Right Alt in close sequence + // We only want the Right Alt message, so if the next message is + // Right Alt we ignore this (synthetic) Left Ctrl message time = GetMessageTime(); if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) @@ -368,8 +444,7 @@ static int translateKey(WPARAM wParam, LPARAM lParam) (next.lParam & 0x01000000) && next.time == time) { - // Next message is a RALT down message, which - // means that this is not a proper LCTRL message + // Next message is Right Alt down so discard this return _GLFW_KEY_INVALID; } } @@ -385,7 +460,7 @@ static int translateKey(WPARAM wParam, LPARAM lParam) return _GLFW_KEY_INVALID; } - return _glfw.win32.publicKeys[HIWORD(lParam) & 0x1FF]; + return _glfw.win32.keycodes[HIWORD(lParam) & 0x1FF]; } // Make the specified window and its video mode active on its monitor @@ -396,6 +471,11 @@ static GLFWbool acquireMonitor(_GLFWwindow* window) GLFWbool status; int xpos, ypos; + if (!_glfw.win32.acquiredMonitorCount) + SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); + if (!window->monitor->window) + _glfw.win32.acquiredMonitorCount++; + status = _glfwSetVideoModeWin32(window->monitor, &window->videoMode); _glfwPlatformGetVideoMode(window->monitor, &mode); @@ -405,7 +485,7 @@ static GLFWbool acquireMonitor(_GLFWwindow* window) xpos, ypos, mode.width, mode.height, SWP_NOACTIVATE | SWP_NOCOPYBITS); - _glfwInputMonitorWindowChange(window->monitor, window); + _glfwInputMonitorWindow(window->monitor, window); return status; } @@ -416,7 +496,11 @@ static void releaseMonitor(_GLFWwindow* window) if (window->monitor->window != window) return; - _glfwInputMonitorWindowChange(window->monitor, NULL); + _glfw.win32.acquiredMonitorCount--; + if (!_glfw.win32.acquiredMonitorCount) + SetThreadExecutionState(ES_CONTINUOUS); + + _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeWin32(window->monitor); } @@ -432,30 +516,23 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, switch (uMsg) { + case WM_DISPLAYCHANGE: + _glfwPollMonitorsWin32(); + break; + case WM_DEVICECHANGE: { - if (wParam == DBT_DEVNODES_CHANGED) - { - _glfwInputMonitorChange(); - return TRUE; - } - else if (wParam == DBT_DEVICEARRIVAL) + if (wParam == DBT_DEVICEARRIVAL) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh) - { - if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickConnectionWin32(); - } + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickConnectionWin32(); } else if (wParam == DBT_DEVICEREMOVECOMPLETE) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh) - { - if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickDisconnectionWin32(); - } + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickDisconnectionWin32(); } break; @@ -467,10 +544,47 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, switch (uMsg) { + case WM_MOUSEACTIVATE: + { + // HACK: Postpone cursor disabling when the window was activated by + // clicking a caption button + if (HIWORD(lParam) == WM_LBUTTONDOWN) + { + if (LOWORD(lParam) == HTCLOSE || + LOWORD(lParam) == HTMINBUTTON || + LOWORD(lParam) == HTMAXBUTTON) + { + window->win32.frameAction = GLFW_TRUE; + } + } + + break; + } + + case WM_CAPTURECHANGED: + { + // HACK: Disable the cursor once the caption button action has been + // completed or cancelled + if (lParam == 0 && window->win32.frameAction) + { + if (window->cursorMode == GLFW_CURSOR_DISABLED) + _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); + + window->win32.frameAction = GLFW_FALSE; + } + + break; + } + case WM_SETFOCUS: { _glfwInputWindowFocus(window, GLFW_TRUE); + // HACK: Do not disable cursor while the user is interacting with + // a caption button + if (window->win32.frameAction) + break; + if (window->cursorMode == GLFW_CURSOR_DISABLED) _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); @@ -519,6 +633,12 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return 0; } + case WM_INPUTLANGCHANGE: + { + _glfwUpdateKeyNamesWin32(); + break; + } + case WM_CHAR: case WM_SYSCHAR: case WM_UNICHAR: @@ -552,14 +672,15 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (action == GLFW_RELEASE && wParam == VK_SHIFT) { - // Release both Shift keys on Shift up event, as only one event - // is sent even if both keys are released + // HACK: Release both Shift keys on Shift up event, as when both + // are pressed the first release does not emit any event + // NOTE: The other half of this is in _glfwPlatformPollEvents _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods); _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods); } else if (wParam == VK_SNAPSHOT) { - // Key down is not reported for the Print Screen key + // HACK: Key down is not reported for the Print Screen key _glfwInputKey(window, key, scancode, GLFW_PRESS, mods); _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods); } @@ -578,7 +699,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_MBUTTONUP: case WM_XBUTTONUP: { - int button, action; + int i, button, action; if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) button = GLFW_MOUSE_BUTTON_LEFT; @@ -595,16 +716,30 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN) { action = GLFW_PRESS; - SetCapture(hWnd); } else - { action = GLFW_RELEASE; - ReleaseCapture(); + + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == GLFW_PRESS) + break; } + if (i > GLFW_MOUSE_BUTTON_LAST) + SetCapture(hWnd); + _glfwInputMouseClick(window, button, action, getKeyMods()); + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == GLFW_PRESS) + break; + } + + if (i > GLFW_MOUSE_BUTTON_LAST) + ReleaseCapture(); + if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) return TRUE; @@ -616,20 +751,11 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, const int x = GET_X_LPARAM(lParam); const int y = GET_Y_LPARAM(lParam); + // Disabled cursor motion input is provided by WM_INPUT if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - const int dx = x - window->win32.lastCursorPosX; - const int dy = y - window->win32.lastCursorPosY; + break; - if (_glfw.win32.disabledCursorWindow != window) - break; - - _glfwInputCursorPos(window, - window->virtualCursorPosX + dx, - window->virtualCursorPosY + dy); - } - else - _glfwInputCursorPos(window, x, y); + _glfwInputCursorPos(window, x, y); window->win32.lastCursorPosX = x; window->win32.lastCursorPosY = y; @@ -650,6 +776,56 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return 0; } + case WM_INPUT: + { + UINT size; + HRAWINPUT ri = (HRAWINPUT) lParam; + RAWINPUT* data; + int dx, dy; + + // Only process input when disabled cursor mode is applied + if (_glfw.win32.disabledCursorWindow != window) + break; + + GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); + if (size > (UINT) _glfw.win32.rawInputSize) + { + free(_glfw.win32.rawInput); + _glfw.win32.rawInput = calloc(size, 1); + _glfw.win32.rawInputSize = size; + } + + size = _glfw.win32.rawInputSize; + if (GetRawInputData(ri, RID_INPUT, + _glfw.win32.rawInput, &size, + sizeof(RAWINPUTHEADER)) == (UINT) -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to retrieve raw input data"); + break; + } + + data = _glfw.win32.rawInput; + if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) + { + dx = data->data.mouse.lLastX - window->win32.lastCursorPosX; + dy = data->data.mouse.lLastY - window->win32.lastCursorPosY; + } + else + { + dx = data->data.mouse.lLastX; + dy = data->data.mouse.lLastY; + } + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + + window->win32.lastCursorPosX += dx; + window->win32.lastCursorPosY += dy; + break; + } + case WM_MOUSELEAVE: { window->win32.cursorTracked = GLFW_FALSE; @@ -666,7 +842,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_MOUSEHWHEEL: { // This message is only sent on Windows Vista and later - // NOTE: The X-axis is inverted for consistency with OS X and X11. + // NOTE: The X-axis is inverted for consistency with macOS and X11 _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0); return 0; } @@ -674,6 +850,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_ENTERSIZEMOVE: case WM_ENTERMENULOOP: { + // HACK: Postpone cursor disabling while the user is moving or + // resizing the window or using the menu if (window->cursorMode == GLFW_CURSOR_DISABLED) _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); @@ -683,6 +861,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_EXITSIZEMOVE: case WM_EXITMENULOOP: { + // HACK: Disable the cursor once the user is done moving or + // resizing the window or using the menu if (window->cursorMode == GLFW_CURSOR_DISABLED) _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); @@ -691,36 +871,33 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_SIZE: { - const GLFWbool iconified = - !window->win32.iconified && wParam == SIZE_MINIMIZED; - const GLFWbool restored = - window->win32.iconified && - (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED); + const GLFWbool iconified = wParam == SIZE_MINIMIZED; + const GLFWbool maximized = wParam == SIZE_MAXIMIZED || + (window->win32.maximized && + wParam != SIZE_RESTORED); if (_glfw.win32.disabledCursorWindow == window) updateClipRect(window); - if (iconified) - _glfwInputWindowIconify(window, GLFW_TRUE); - else if (restored) - _glfwInputWindowIconify(window, GLFW_FALSE); + if (window->win32.iconified != iconified) + _glfwInputWindowIconify(window, iconified); + + if (window->win32.maximized != maximized) + _glfwInputWindowMaximize(window, maximized); _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam)); _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam)); - if (iconified) + if (window->monitor && window->win32.iconified != iconified) { - window->win32.iconified = GLFW_TRUE; - if (window->monitor) + if (iconified) releaseMonitor(window); - } - else if (restored) - { - window->win32.iconified = GLFW_FALSE; - if (window->monitor) + else acquireMonitor(window); } + window->win32.iconified = iconified; + window->win32.maximized = maximized; return 0; } @@ -774,6 +951,22 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, mmi->ptMaxTrackSize.y = window->maxheight + yoff; } + if (!window->decorated) + { + MONITORINFO mi; + const HMONITOR mh = MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST); + + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + GetMonitorInfo(mh, &mi); + + mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left; + mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top; + mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left; + mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top; + } + return 0; } @@ -788,6 +981,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return TRUE; } + case WM_DWMCOMPOSITIONCHANGED: + { + if (window->win32.transparent) + updateFramebufferTransparency(window); + return 0; + } + case WM_SETCURSOR: { if (LOWORD(lParam) == HTCLIENT) @@ -799,19 +999,6 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, break; } - case WM_DPICHANGED: - { - RECT* rect = (RECT*) lParam; - SetWindowPos(window->win32.handle, - HWND_TOP, - rect->left, - rect->top, - rect->right - rect->left, - rect->bottom - rect->top, - SWP_NOACTIVATE | SWP_NOZORDER); - break; - } - case WM_DROPFILES: { HDROP drop = (HDROP) wParam; @@ -853,7 +1040,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // Creates the GLFW window // static int createNativeWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig) + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig) { int xpos, ypos, fullWidth, fullHeight; WCHAR* wideTitle; @@ -866,7 +1054,7 @@ static int createNativeWindow(_GLFWwindow* window, // NOTE: This window placement is temporary and approximate, as the // correct position and size cannot be known until the monitor - // video mode has been set + // video mode has been picked in _glfwSetVideoModeWin32 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); _glfwPlatformGetVideoMode(window->monitor, &mode); fullWidth = mode.width; @@ -887,11 +1075,7 @@ static int createNativeWindow(_GLFWwindow* window, wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); if (!wideTitle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert window title to UTF-16"); return GLFW_FALSE; - } window->win32.handle = CreateWindowExW(exStyle, _GLFW_WNDCLASSNAME, @@ -908,24 +1092,31 @@ static int createNativeWindow(_GLFWwindow* window, if (!window->win32.handle) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create window"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create window"); return GLFW_FALSE; } SetPropW(window->win32.handle, L"GLFW", window); - if (_glfw_ChangeWindowMessageFilterEx) + if (IsWindows7OrGreater()) { - _glfw_ChangeWindowMessageFilterEx(window->win32.handle, - WM_DROPFILES, MSGFLT_ALLOW, NULL); - _glfw_ChangeWindowMessageFilterEx(window->win32.handle, - WM_COPYDATA, MSGFLT_ALLOW, NULL); - _glfw_ChangeWindowMessageFilterEx(window->win32.handle, - WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_DROPFILES, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYDATA, MSGFLT_ALLOW, NULL); + ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); } DragAcceptFiles(window->win32.handle, TRUE); + if (fbconfig->transparent) + { + updateFramebufferTransparency(window); + window->win32.transparent = GLFW_TRUE; + } + return GLFW_TRUE; } @@ -962,8 +1153,8 @@ GLFWbool _glfwRegisterWindowClassWin32(void) if (!RegisterClassExW(&wc)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to register window class"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to register window class"); return GLFW_FALSE; } @@ -977,6 +1168,20 @@ void _glfwUnregisterWindowClassWin32(void) UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); } +// Returns whether desktop compositing is enabled +// +GLFWbool _glfwIsCompositionEnabledWin32(void) +{ + if (IsWindowsVistaOrGreater()) + { + BOOL enabled; + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled))) + return enabled; + } + + return FALSE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -987,7 +1192,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { - if (!createNativeWindow(window, wndconfig)) + if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) @@ -999,13 +1204,20 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (window->monitor) @@ -1015,7 +1227,8 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!acquireMonitor(window)) return GLFW_FALSE; - centerCursor(window); + if (wndconfig->centerCursor) + centerCursor(window); } return GLFW_TRUE; @@ -1050,11 +1263,7 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title); if (!wideTitle) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert window title to UTF-16"); return; - } SetWindowTextW(window->win32.handle, wideTitle); free(wideTitle); @@ -1209,6 +1418,14 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, *bottom = rect.bottom - height; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + const HANDLE handle = MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST); + _glfwGetMonitorContentScaleWin32(handle, xscale, yscale); +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_MINIMIZE); @@ -1234,6 +1451,11 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) ShowWindow(window->win32.handle, SW_HIDE); } +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + FlashWindow(window->win32.handle, TRUE); +} + void _glfwPlatformFocusWindow(_GLFWwindow* window) { BringWindowToTop(window->win32.handle); @@ -1271,30 +1493,23 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) releaseMonitor(window); - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); if (monitor) { - GLFWvidmode mode; - DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); - UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS; - if (window->decorated) { + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + UINT flags = SWP_FRAMECHANGED | SWP_SHOWWINDOW | + SWP_NOACTIVATE | SWP_NOCOPYBITS | + SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE; + style &= ~WS_OVERLAPPEDWINDOW; style |= getWindowStyle(window); SetWindowLongW(window->win32.handle, GWL_STYLE, style); - - flags |= SWP_FRAMECHANGED; + SetWindowPos(window->win32.handle, HWND_TOPMOST, 0, 0, 0, 0, flags); } - _glfwPlatformGetVideoMode(monitor, &mode); - _glfwPlatformGetMonitorPos(monitor, &xpos, &ypos); - - SetWindowPos(window->win32.handle, HWND_TOPMOST, - xpos, ypos, mode.width, mode.height, - flags); - acquireMonitor(window); } else @@ -1347,6 +1562,61 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return IsZoomed(window->win32.handle); } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return window->win32.transparent && _glfwIsCompositionEnabledWin32(); +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + updateWindowStyles(window); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + updateWindowStyles(window); +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST; + SetWindowPos(window->win32.handle, after, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + BYTE alpha; + DWORD flags; + + if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) && + GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags)) + { + if (flags & LWA_ALPHA) + return alpha / 255.f; + } + + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ + if (opacity < 1.f) + { + const BYTE alpha = (BYTE) (255 * opacity); + DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + style |= WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA); + } + else + { + DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); + style &= ~WS_EX_LAYERED; + SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); + } +} + void _glfwPlatformPollEvents(void) { MSG msg; @@ -1357,9 +1627,9 @@ void _glfwPlatformPollEvents(void) { if (msg.message == WM_QUIT) { - // Treat WM_QUIT as a close on all windows - // While GLFW does not itself post WM_QUIT, other processes may post - // it to this one, for example Task Manager + // NOTE: While GLFW does not itself post WM_QUIT, other processes + // may post it to this one, for example Task Manager + // HACK: Treat WM_QUIT as a close on all windows window = _glfw.windowListHead; while (window) @@ -1378,25 +1648,28 @@ void _glfwPlatformPollEvents(void) handle = GetActiveWindow(); if (handle) { - // LSHIFT/RSHIFT fixup (keys tend to "stick" without this fix) - // This is the only async event handling in GLFW, but it solves some - // nasty problems + // NOTE: Shift keys on Windows tend to "stick" when both are pressed as + // no key up message is generated by the first key release + // The other half of this is in the handling of WM_KEYUP + // HACK: Query actual key state and synthesize release events as needed window = GetPropW(handle, L"GLFW"); if (window) { - const int mods = getAsyncKeyMods(); + const GLFWbool lshift = (GetAsyncKeyState(VK_LSHIFT) >> 15) & 1; + const GLFWbool rshift = (GetAsyncKeyState(VK_RSHIFT) >> 15) & 1; - // Get current state of left and right shift keys - const int lshiftDown = (GetAsyncKeyState(VK_LSHIFT) >> 15) & 1; - const int rshiftDown = (GetAsyncKeyState(VK_RSHIFT) >> 15) & 1; - - // See if this differs from our belief of what has happened - // (we only have to check for lost key up events) - if (!lshiftDown && window->keys[GLFW_KEY_LEFT_SHIFT] == 1) - _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, 0, GLFW_RELEASE, mods); - - if (!rshiftDown && window->keys[GLFW_KEY_RIGHT_SHIFT] == 1) - _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, 0, GLFW_RELEASE, mods); + if (!lshift && window->keys[GLFW_KEY_LEFT_SHIFT] == GLFW_PRESS) + { + const int mods = getAsyncKeyMods(); + const int scancode = _glfw.win32.scancodes[GLFW_KEY_LEFT_SHIFT]; + _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, GLFW_RELEASE, mods); + } + else if (!rshift && window->keys[GLFW_KEY_RIGHT_SHIFT] == GLFW_PRESS) + { + const int mods = getAsyncKeyMods(); + const int scancode = _glfw.win32.scancodes[GLFW_KEY_RIGHT_SHIFT]; + _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, GLFW_RELEASE, mods); + } } } @@ -1432,8 +1705,7 @@ void _glfwPlatformWaitEventsTimeout(double timeout) void _glfwPlatformPostEmptyEvent(void) { - _GLFWwindow* window = _glfw.windowListHead; - PostMessage(window->win32.handle, WM_NULL, 0, 0); + PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); } void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) @@ -1467,48 +1739,50 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { if (mode == GLFW_CURSOR_DISABLED) { + const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle }; + _glfw.win32.disabledCursorWindow = window; _glfwPlatformGetCursorPos(window, &_glfw.win32.restoreCursorPosX, &_glfw.win32.restoreCursorPosY); centerCursor(window); updateClipRect(window); + + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to register raw input device"); + } } else if (_glfw.win32.disabledCursorWindow == window) { + const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; + _glfw.win32.disabledCursorWindow = NULL; updateClipRect(NULL); _glfwPlatformSetCursorPos(window, _glfw.win32.restoreCursorPosX, _glfw.win32.restoreCursorPosY); + + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to remove raw input device"); + } } if (cursorInClientArea(window)) updateCursorImage(window); } -const char* _glfwPlatformGetKeyName(int key, int scancode) +const char* _glfwPlatformGetScancodeName(int scancode) { - WCHAR name[16]; + return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]]; +} - if (key != GLFW_KEY_UNKNOWN) - scancode = _glfw.win32.nativeKeys[key]; - - if (!_glfwIsPrintable(_glfw.win32.publicKeys[scancode])) - return NULL; - - if (!GetKeyNameTextW(scancode << 16, name, sizeof(name) / sizeof(WCHAR))) - return NULL; - - if (!WideCharToMultiByte(CP_UTF8, 0, name, -1, - _glfw.win32.keyName, - sizeof(_glfw.win32.keyName), - NULL, NULL)) - { - return NULL; - } - - return _glfw.win32.keyName; +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.win32.scancodes[key]; } int _glfwPlatformCreateCursor(_GLFWcursor* cursor, @@ -1528,8 +1802,8 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) CopyCursor(LoadCursorW(NULL, translateCursorShape(shape))); if (!cursor->win32.handle) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to create standard cursor"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to create standard cursor"); return GLFW_FALSE; } @@ -1548,7 +1822,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) updateCursorImage(window); } -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +void _glfwPlatformSetClipboardString(const char* string) { int characterCount; HANDLE object; @@ -1556,26 +1830,22 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0); if (!characterCount) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert clipboard string to UTF-16"); return; - } object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR)); if (!object) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to allocate global handle for clipboard"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to allocate global handle for clipboard"); return; } buffer = GlobalLock(object); if (!buffer) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to lock global handle"); GlobalFree(object); - - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle"); return; } @@ -1584,9 +1854,9 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) if (!OpenClipboard(_glfw.win32.helperWindowHandle)) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to open clipboard"); GlobalFree(object); - - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); return; } @@ -1595,75 +1865,60 @@ void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) CloseClipboard(); } -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +const char* _glfwPlatformGetClipboardString(void) { HANDLE object; WCHAR* buffer; if (!OpenClipboard(_glfw.win32.helperWindowHandle)) { - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to open clipboard"); return NULL; } object = GetClipboardData(CF_UNICODETEXT); if (!object) { + _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE, + "Win32: Failed to convert clipboard to string"); CloseClipboard(); - - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "Win32: Failed to convert clipboard to string"); return NULL; } buffer = GlobalLock(object); if (!buffer) { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to lock global handle"); CloseClipboard(); - - _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle"); return NULL; } free(_glfw.win32.clipboardString); - _glfw.win32.clipboardString = - _glfwCreateUTF8FromWideStringWin32(buffer); + _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer); GlobalUnlock(object); CloseClipboard(); - if (!_glfw.win32.clipboardString) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Win32: Failed to convert wide string to UTF-8"); - return NULL; - } - return _glfw.win32.clipboardString; } -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { - char** extensions; + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface) + return; - *count = 0; - - if (!_glfw.vk.KHR_win32_surface) - return NULL; - - extensions = calloc(2, sizeof(char*)); - extensions[0] = strdup("VK_KHR_surface"); - extensions[1] = strdup("VK_KHR_win32_surface"); - - *count = 2; - return extensions; + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_win32_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = + PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR + vkGetPhysicalDeviceWin32PresentationSupportKHR = (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) diff --git a/raylib/external/glfw/src/window.c b/raylib/external/glfw/src/window.c index 5e74e6e..f4468e1 100644 --- a/raylib/external/glfw/src/window.c +++ b/raylib/external/glfw/src/window.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 - www.glfw.org +// GLFW 3.3 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // Copyright (c) 2012 Torsten Walluhn // // This software is provided 'as-is', without any express or implied @@ -40,30 +40,26 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) { - if (focused) - { - if (window->callbacks.focus) - window->callbacks.focus((GLFWwindow*) window, focused); - } - else - { - int i; + if (window->callbacks.focus) + window->callbacks.focus((GLFWwindow*) window, focused); - if (window->callbacks.focus) - window->callbacks.focus((GLFWwindow*) window, focused); + if (!focused) + { + int key, button; - // Release all pressed keyboard keys - for (i = 0; i <= GLFW_KEY_LAST; i++) + for (key = 0; key <= GLFW_KEY_LAST; key++) { - if (window->keys[i] == GLFW_PRESS) - _glfwInputKey(window, i, 0, GLFW_RELEASE, 0); + if (window->keys[key] == GLFW_PRESS) + { + const int scancode = _glfwPlatformGetKeyScancode(key); + _glfwInputKey(window, key, scancode, GLFW_RELEASE, 0); + } } - // Release all pressed mouse buttons - for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + for (button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++) { - if (window->mouseButtons[i] == GLFW_PRESS) - _glfwInputMouseClick(window, i, GLFW_RELEASE, 0); + if (window->mouseButtons[button] == GLFW_PRESS) + _glfwInputMouseClick(window, button, GLFW_RELEASE, 0); } } } @@ -86,6 +82,12 @@ void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) window->callbacks.iconify((GLFWwindow*) window, iconified); } +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized) +{ + if (window->callbacks.maximize) + window->callbacks.maximize((GLFWwindow*) window, maximized); +} + void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height) { if (window->callbacks.fbsize) @@ -100,13 +102,13 @@ void _glfwInputWindowDamage(_GLFWwindow* window) void _glfwInputWindowCloseRequest(_GLFWwindow* window) { - window->closed = GLFW_TRUE; + window->shouldClose = GLFW_TRUE; if (window->callbacks.close) window->callbacks.close((GLFWwindow*) window); } -void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor) +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor) { window->monitor = monitor; } @@ -128,6 +130,8 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, _GLFWwindow* previous; assert(title != NULL); + assert(width >= 0); + assert(height >= 0); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -188,7 +192,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->denom = GLFW_DONT_CARE; // Save the currently current context so it can be restored later - previous = _glfwPlatformGetCurrentContext(); + previous = _glfwPlatformGetTls(&_glfw.contextSlot); if (ctxconfig.client != GLFW_NO_API) glfwMakeContextCurrent(NULL); @@ -233,23 +237,25 @@ void glfwDefaultWindowHints(void) { _GLFW_REQUIRE_INIT(); - memset(&_glfw.hints, 0, sizeof(_glfw.hints)); - // The default is OpenGL with minimum version 1.0 + memset(&_glfw.hints.context, 0, sizeof(_glfw.hints.context)); _glfw.hints.context.client = GLFW_OPENGL_API; _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API; _glfw.hints.context.major = 1; _glfw.hints.context.minor = 0; // The default is a focused, visible, resizable window with decorations - _glfw.hints.window.resizable = GLFW_TRUE; - _glfw.hints.window.visible = GLFW_TRUE; - _glfw.hints.window.decorated = GLFW_TRUE; - _glfw.hints.window.focused = GLFW_TRUE; - _glfw.hints.window.autoIconify = GLFW_TRUE; + memset(&_glfw.hints.window, 0, sizeof(_glfw.hints.window)); + _glfw.hints.window.resizable = GLFW_TRUE; + _glfw.hints.window.visible = GLFW_TRUE; + _glfw.hints.window.decorated = GLFW_TRUE; + _glfw.hints.window.focused = GLFW_TRUE; + _glfw.hints.window.autoIconify = GLFW_TRUE; + _glfw.hints.window.centerCursor = GLFW_TRUE; // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, // double buffered + memset(&_glfw.hints.framebuffer, 0, sizeof(_glfw.hints.framebuffer)); _glfw.hints.framebuffer.redBits = 8; _glfw.hints.framebuffer.greenBits = 8; _glfw.hints.framebuffer.blueBits = 8; @@ -260,6 +266,9 @@ void glfwDefaultWindowHints(void) // The default is to select the highest available refresh rate _glfw.hints.refreshRate = GLFW_DONT_CARE; + + // The default is to use full Retina resolution framebuffers + _glfw.hints.window.ns.retina = GLFW_TRUE; } GLFWAPI void glfwWindowHint(int hint, int value) @@ -270,107 +279,121 @@ GLFWAPI void glfwWindowHint(int hint, int value) { case GLFW_RED_BITS: _glfw.hints.framebuffer.redBits = value; - break; + return; case GLFW_GREEN_BITS: _glfw.hints.framebuffer.greenBits = value; - break; + return; case GLFW_BLUE_BITS: _glfw.hints.framebuffer.blueBits = value; - break; + return; case GLFW_ALPHA_BITS: _glfw.hints.framebuffer.alphaBits = value; - break; + return; case GLFW_DEPTH_BITS: _glfw.hints.framebuffer.depthBits = value; - break; + return; case GLFW_STENCIL_BITS: _glfw.hints.framebuffer.stencilBits = value; - break; + return; case GLFW_ACCUM_RED_BITS: _glfw.hints.framebuffer.accumRedBits = value; - break; + return; case GLFW_ACCUM_GREEN_BITS: _glfw.hints.framebuffer.accumGreenBits = value; - break; + return; case GLFW_ACCUM_BLUE_BITS: _glfw.hints.framebuffer.accumBlueBits = value; - break; + return; case GLFW_ACCUM_ALPHA_BITS: _glfw.hints.framebuffer.accumAlphaBits = value; - break; + return; case GLFW_AUX_BUFFERS: _glfw.hints.framebuffer.auxBuffers = value; - break; + return; case GLFW_STEREO: _glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_DOUBLEBUFFER: _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; + case GLFW_TRANSPARENT_FRAMEBUFFER: + _glfw.hints.framebuffer.transparent = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_SAMPLES: _glfw.hints.framebuffer.samples = value; - break; + return; case GLFW_SRGB_CAPABLE: _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_RESIZABLE: _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_DECORATED: _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_FOCUSED: _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_AUTO_ICONIFY: _glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_FLOATING: _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_MAXIMIZED: _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_VISIBLE: _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; + case GLFW_COCOA_RETINA_FRAMEBUFFER: + _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_COCOA_FRAME_AUTOSAVE: + _glfw.hints.window.ns.frame = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_COCOA_GRAPHICS_SWITCHING: + _glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE; + return; + case GLFW_CENTER_CURSOR: + _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_CLIENT_API: _glfw.hints.context.client = value; - break; + return; case GLFW_CONTEXT_CREATION_API: _glfw.hints.context.source = value; - break; + return; case GLFW_CONTEXT_VERSION_MAJOR: _glfw.hints.context.major = value; - break; + return; case GLFW_CONTEXT_VERSION_MINOR: _glfw.hints.context.minor = value; - break; + return; case GLFW_CONTEXT_ROBUSTNESS: _glfw.hints.context.robustness = value; - break; + return; case GLFW_OPENGL_FORWARD_COMPAT: _glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_OPENGL_DEBUG_CONTEXT: _glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_CONTEXT_NO_ERROR: _glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE; - break; + return; case GLFW_OPENGL_PROFILE: _glfw.hints.context.profile = value; - break; + return; case GLFW_CONTEXT_RELEASE_BEHAVIOR: _glfw.hints.context.release = value; - break; + return; case GLFW_REFRESH_RATE: _glfw.hints.refreshRate = value; - break; - default: - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint %i", hint); - break; + return; } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint); } GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) @@ -388,7 +411,7 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) // The window's context must not be current on another thread when the // window is destroyed - if (window == _glfwPlatformGetCurrentContext()) + if (window == _glfwPlatformGetTls(&_glfw.contextSlot)) glfwMakeContextCurrent(NULL); _glfwPlatformDestroyWindow(window); @@ -412,7 +435,7 @@ GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(0); - return window->closed; + return window->shouldClose; } GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) @@ -421,14 +444,13 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) assert(window != NULL); _GLFW_REQUIRE_INIT(); - window->closed = value; + window->shouldClose = value; } GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); - assert(title != NULL); _GLFW_REQUIRE_INIT(); @@ -492,6 +514,8 @@ GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); + assert(width >= 0); + assert(height >= 0); _GLFW_REQUIRE_INIT(); @@ -550,6 +574,8 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); + assert(numer != 0); + assert(denom != 0); _GLFW_REQUIRE_INIT(); @@ -607,6 +633,49 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); } +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle, + float* xscale, float* yscale) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowContentScale(window, xscale, yscale); +} + +GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(1.f); + return _glfwPlatformGetWindowOpacity(window); +} + +GLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(opacity == opacity); + assert(opacity >= 0.f); + assert(opacity <= 1.f); + + _GLFW_REQUIRE_INIT(); + + if (opacity != opacity || opacity < 0.f || opacity > 1.f) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid window opacity %f", opacity); + return; + } + + _glfwPlatformSetWindowOpacity(window, opacity); +} + GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; @@ -631,6 +700,10 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) assert(window != NULL); _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + _glfwPlatformMaximizeWindow(window); } @@ -648,6 +721,16 @@ GLFWAPI void glfwShowWindow(GLFWwindow* handle) _glfwPlatformFocusWindow(window); } +GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformRequestWindowAttention(window); +} + GLFWAPI void glfwHideWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; @@ -688,12 +771,16 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return _glfwPlatformWindowVisible(window); case GLFW_MAXIMIZED: return _glfwPlatformWindowMaximized(window); + case GLFW_TRANSPARENT_FRAMEBUFFER: + return _glfwPlatformFramebufferTransparent(window); case GLFW_RESIZABLE: return window->resizable; case GLFW_DECORATED: return window->decorated; case GLFW_FLOATING: return window->floating; + case GLFW_AUTO_ICONIFY: + return window->autoIconify; case GLFW_CLIENT_API: return window->context.client; case GLFW_CONTEXT_CREATION_API: @@ -718,10 +805,52 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return window->context.noerror; } - _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute %i", attrib); + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); return 0; } +GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + value = value ? GLFW_TRUE : GLFW_FALSE; + + if (attrib == GLFW_AUTO_ICONIFY) + window->autoIconify = value; + else if (attrib == GLFW_RESIZABLE) + { + if (window->resizable == value) + return; + + window->resizable = value; + if (!window->monitor) + _glfwPlatformSetWindowResizable(window, value); + } + else if (attrib == GLFW_DECORATED) + { + if (window->decorated == value) + return; + + window->decorated = value; + if (!window->monitor) + _glfwPlatformSetWindowDecorated(window, value); + } + else if (attrib == GLFW_FLOATING) + { + if (window->floating == value) + return; + + window->floating = value; + if (!window->monitor) + _glfwPlatformSetWindowFloating(window, value); + } + else + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); +} + GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; @@ -740,6 +869,8 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, _GLFWwindow* window = (_GLFWwindow*) wh; _GLFWmonitor* monitor = (_GLFWmonitor*) mh; assert(window != NULL); + assert(width >= 0); + assert(height >= 0); _GLFW_REQUIRE_INIT(); @@ -852,6 +983,17 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle, return cbfun; } +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* handle, + GLFWwindowmaximizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.maximize, cbfun); + return cbfun; +} + GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle, GLFWframebuffersizefun cbfun) { @@ -882,6 +1024,9 @@ GLFWAPI void glfwWaitEvents(void) GLFWAPI void glfwWaitEventsTimeout(double timeout) { _GLFW_REQUIRE_INIT(); + assert(timeout == timeout); + assert(timeout >= 0.0); + assert(timeout <= DBL_MAX); if (timeout != timeout || timeout < 0.0 || timeout > DBL_MAX) { diff --git a/raylib/external/glfw/src/wl_init.c b/raylib/external/glfw/src/wl_init.c index 44f7d0c..ec25f20 100644 --- a/raylib/external/glfw/src/wl_init.c +++ b/raylib/external/glfw/src/wl_init.c @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.2 Wayland - www.glfw.org +// GLFW 3.3 Wayland - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // @@ -26,6 +26,7 @@ #include "internal.h" +#include #include #include #include @@ -109,6 +110,8 @@ static void pointerHandleButton(void* data, if (!window) return; + _glfw.wl.pointerSerial = serial; + /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev * codes. */ glfwButton = button - BTN_LEFT; @@ -128,7 +131,7 @@ static void pointerHandleAxis(void* data, wl_fixed_t value) { _GLFWwindow* window = _glfw.wl.pointerFocus; - double scroll_factor; + double scrollFactor; double x, y; if (!window) @@ -137,19 +140,20 @@ static void pointerHandleAxis(void* data, /* Wayland scroll events are in pointer motion coordinate space (think * two finger scroll). The factor 10 is commonly used to convert to * "scroll step means 1.0. */ - scroll_factor = 1.0/10.0; + scrollFactor = 1.0/10.0; switch (axis) { case WL_POINTER_AXIS_HORIZONTAL_SCROLL: - x = wl_fixed_to_double(value) * scroll_factor; + x = wl_fixed_to_double(value) * scrollFactor; y = 0.0; break; case WL_POINTER_AXIS_VERTICAL_SCROLL: x = 0.0; - y = wl_fixed_to_double(value) * scroll_factor; + y = wl_fixed_to_double(value) * scrollFactor; break; default: + assert(GLFW_FALSE); break; } @@ -172,7 +176,14 @@ static void keyboardHandleKeymap(void* data, { struct xkb_keymap* keymap; struct xkb_state* state; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + struct xkb_compose_table* composeTable; + struct xkb_compose_state* composeState; +#endif + char* mapStr; + const char* locale; if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { @@ -186,10 +197,10 @@ static void keyboardHandleKeymap(void* data, return; } - keymap = xkb_map_new_from_string(_glfw.wl.xkb.context, - mapStr, - XKB_KEYMAP_FORMAT_TEXT_V1, - 0); + keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, + mapStr, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); munmap(mapStr, size); close(fd); @@ -205,23 +216,54 @@ static void keyboardHandleKeymap(void* data, { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create XKB state"); - xkb_map_unref(keymap); + xkb_keymap_unref(keymap); return; } + // Look up the preferred locale, falling back to "C" as default. + locale = getenv("LC_ALL"); + if (!locale) + locale = getenv("LC_CTYPE"); + if (!locale) + locale = getenv("LANG"); + if (!locale) + locale = "C"; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + composeTable = + xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (composeTable) + { + composeState = + xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(composeTable); + if (composeState) + _glfw.wl.xkb.composeState = composeState; + else + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose state"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose table"); + } +#endif + xkb_keymap_unref(_glfw.wl.xkb.keymap); xkb_state_unref(_glfw.wl.xkb.state); _glfw.wl.xkb.keymap = keymap; _glfw.wl.xkb.state = state; - _glfw.wl.xkb.control_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Control"); - _glfw.wl.xkb.alt_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); - _glfw.wl.xkb.shift_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); - _glfw.wl.xkb.super_mask = - 1 << xkb_map_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); + _glfw.wl.xkb.controlMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); + _glfw.wl.xkb.altMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); + _glfw.wl.xkb.shiftMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); + _glfw.wl.xkb.superMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); } static void keyboardHandleEnter(void* data, @@ -252,12 +294,61 @@ static void keyboardHandleLeave(void* data, static int toGLFWKeyCode(uint32_t key) { - if (key < sizeof(_glfw.wl.publicKeys) / sizeof(_glfw.wl.publicKeys[0])) - return _glfw.wl.publicKeys[key]; + if (key < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) + return _glfw.wl.keycodes[key]; return GLFW_KEY_UNKNOWN; } +#ifdef HAVE_XKBCOMMON_COMPOSE_H +static xkb_keysym_t composeSymbol(xkb_keysym_t sym) +{ + if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) + return sym; + if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) + != XKB_COMPOSE_FEED_ACCEPTED) + return sym; + switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) + { + case XKB_COMPOSE_COMPOSED: + return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return XKB_KEY_NoSymbol; + case XKB_COMPOSE_NOTHING: + default: + return sym; + } +} +#endif + +static void inputChar(_GLFWwindow* window, uint32_t key) +{ + uint32_t code, numSyms; + long cp; + const xkb_keysym_t *syms; + xkb_keysym_t sym; + + code = key + 8; + numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms); + + if (numSyms == 1) + { +#ifdef HAVE_XKBCOMMON_COMPOSE_H + sym = composeSymbol(syms[0]); +#else + sym = syms[0]; +#endif + cp = _glfwKeySym2Unicode(sym); + if (cp != -1) + { + const int mods = _glfw.wl.xkb.modifiers; + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + _glfwInputChar(window, cp, mods, plain); + } + } +} + static void keyboardHandleKey(void* data, struct wl_keyboard* keyboard, uint32_t serial, @@ -265,11 +356,8 @@ static void keyboardHandleKey(void* data, uint32_t key, uint32_t state) { - uint32_t code, num_syms; - long cp; int keyCode; int action; - const xkb_keysym_t *syms; _GLFWwindow* window = _glfw.wl.keyboardFocus; if (!window) @@ -282,19 +370,8 @@ static void keyboardHandleKey(void* data, _glfwInputKey(window, keyCode, key, action, _glfw.wl.xkb.modifiers); - code = key + 8; - num_syms = xkb_key_get_syms(_glfw.wl.xkb.state, code, &syms); - - if (num_syms == 1) - { - cp = _glfwKeySym2Unicode(syms[0]); - if (cp != -1) - { - const int mods = _glfw.wl.xkb.modifiers; - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - _glfwInputChar(window, cp, mods, plain); - } - } + if (action == GLFW_PRESS) + inputChar(window, key); } static void keyboardHandleModifiers(void* data, @@ -320,15 +397,17 @@ static void keyboardHandleModifiers(void* data, group); mask = xkb_state_serialize_mods(_glfw.wl.xkb.state, - XKB_STATE_DEPRESSED | - XKB_STATE_LATCHED); - if (mask & _glfw.wl.xkb.control_mask) + XKB_STATE_MODS_DEPRESSED | + XKB_STATE_LAYOUT_DEPRESSED | + XKB_STATE_MODS_LATCHED | + XKB_STATE_LAYOUT_LATCHED); + if (mask & _glfw.wl.xkb.controlMask) modifiers |= GLFW_MOD_CONTROL; - if (mask & _glfw.wl.xkb.alt_mask) + if (mask & _glfw.wl.xkb.altMask) modifiers |= GLFW_MOD_ALT; - if (mask & _glfw.wl.xkb.shift_mask) + if (mask & _glfw.wl.xkb.shiftMask) modifiers |= GLFW_MOD_SHIFT; - if (mask & _glfw.wl.xkb.super_mask) + if (mask & _glfw.wl.xkb.superMask) modifiers |= GLFW_MOD_SUPER; _glfw.wl.xkb.modifiers = modifiers; } @@ -380,10 +459,10 @@ static void registryHandleGlobal(void* data, { if (strcmp(interface, "wl_compositor") == 0) { - _glfw.wl.wl_compositor_version = min(3, version); + _glfw.wl.compositorVersion = min(3, version); _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, - _glfw.wl.wl_compositor_version); + _glfw.wl.compositorVersion); } else if (strcmp(interface, "wl_shm") == 0) { @@ -422,6 +501,13 @@ static void registryHandleGlobal(void* data, &zwp_pointer_constraints_v1_interface, 1); } + else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) + { + _glfw.wl.idleInhibitManager = + wl_registry_bind(registry, name, + &zwp_idle_inhibit_manager_v1_interface, + 1); + } } static void registryHandleGlobalRemove(void *data, @@ -440,124 +526,134 @@ static const struct wl_registry_listener registryListener = { // static void createKeyTables(void) { - memset(_glfw.wl.publicKeys, -1, sizeof(_glfw.wl.publicKeys)); + int scancode; - _glfw.wl.publicKeys[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; - _glfw.wl.publicKeys[KEY_1] = GLFW_KEY_1; - _glfw.wl.publicKeys[KEY_2] = GLFW_KEY_2; - _glfw.wl.publicKeys[KEY_3] = GLFW_KEY_3; - _glfw.wl.publicKeys[KEY_4] = GLFW_KEY_4; - _glfw.wl.publicKeys[KEY_5] = GLFW_KEY_5; - _glfw.wl.publicKeys[KEY_6] = GLFW_KEY_6; - _glfw.wl.publicKeys[KEY_7] = GLFW_KEY_7; - _glfw.wl.publicKeys[KEY_8] = GLFW_KEY_8; - _glfw.wl.publicKeys[KEY_9] = GLFW_KEY_9; - _glfw.wl.publicKeys[KEY_0] = GLFW_KEY_0; - _glfw.wl.publicKeys[KEY_MINUS] = GLFW_KEY_MINUS; - _glfw.wl.publicKeys[KEY_EQUAL] = GLFW_KEY_EQUAL; - _glfw.wl.publicKeys[KEY_Q] = GLFW_KEY_Q; - _glfw.wl.publicKeys[KEY_W] = GLFW_KEY_W; - _glfw.wl.publicKeys[KEY_E] = GLFW_KEY_E; - _glfw.wl.publicKeys[KEY_R] = GLFW_KEY_R; - _glfw.wl.publicKeys[KEY_T] = GLFW_KEY_T; - _glfw.wl.publicKeys[KEY_Y] = GLFW_KEY_Y; - _glfw.wl.publicKeys[KEY_U] = GLFW_KEY_U; - _glfw.wl.publicKeys[KEY_I] = GLFW_KEY_I; - _glfw.wl.publicKeys[KEY_O] = GLFW_KEY_O; - _glfw.wl.publicKeys[KEY_P] = GLFW_KEY_P; - _glfw.wl.publicKeys[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; - _glfw.wl.publicKeys[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; - _glfw.wl.publicKeys[KEY_A] = GLFW_KEY_A; - _glfw.wl.publicKeys[KEY_S] = GLFW_KEY_S; - _glfw.wl.publicKeys[KEY_D] = GLFW_KEY_D; - _glfw.wl.publicKeys[KEY_F] = GLFW_KEY_F; - _glfw.wl.publicKeys[KEY_G] = GLFW_KEY_G; - _glfw.wl.publicKeys[KEY_H] = GLFW_KEY_H; - _glfw.wl.publicKeys[KEY_J] = GLFW_KEY_J; - _glfw.wl.publicKeys[KEY_K] = GLFW_KEY_K; - _glfw.wl.publicKeys[KEY_L] = GLFW_KEY_L; - _glfw.wl.publicKeys[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; - _glfw.wl.publicKeys[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; - _glfw.wl.publicKeys[KEY_Z] = GLFW_KEY_Z; - _glfw.wl.publicKeys[KEY_X] = GLFW_KEY_X; - _glfw.wl.publicKeys[KEY_C] = GLFW_KEY_C; - _glfw.wl.publicKeys[KEY_V] = GLFW_KEY_V; - _glfw.wl.publicKeys[KEY_B] = GLFW_KEY_B; - _glfw.wl.publicKeys[KEY_N] = GLFW_KEY_N; - _glfw.wl.publicKeys[KEY_M] = GLFW_KEY_M; - _glfw.wl.publicKeys[KEY_COMMA] = GLFW_KEY_COMMA; - _glfw.wl.publicKeys[KEY_DOT] = GLFW_KEY_PERIOD; - _glfw.wl.publicKeys[KEY_SLASH] = GLFW_KEY_SLASH; - _glfw.wl.publicKeys[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; - _glfw.wl.publicKeys[KEY_ESC] = GLFW_KEY_ESCAPE; - _glfw.wl.publicKeys[KEY_TAB] = GLFW_KEY_TAB; - _glfw.wl.publicKeys[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; - _glfw.wl.publicKeys[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; - _glfw.wl.publicKeys[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; - _glfw.wl.publicKeys[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; - _glfw.wl.publicKeys[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; - _glfw.wl.publicKeys[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; - _glfw.wl.publicKeys[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; - _glfw.wl.publicKeys[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; - _glfw.wl.publicKeys[KEY_MENU] = GLFW_KEY_MENU; - _glfw.wl.publicKeys[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; - _glfw.wl.publicKeys[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; - _glfw.wl.publicKeys[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; - _glfw.wl.publicKeys[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; - _glfw.wl.publicKeys[KEY_PAUSE] = GLFW_KEY_PAUSE; - _glfw.wl.publicKeys[KEY_DELETE] = GLFW_KEY_DELETE; - _glfw.wl.publicKeys[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; - _glfw.wl.publicKeys[KEY_ENTER] = GLFW_KEY_ENTER; - _glfw.wl.publicKeys[KEY_HOME] = GLFW_KEY_HOME; - _glfw.wl.publicKeys[KEY_END] = GLFW_KEY_END; - _glfw.wl.publicKeys[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; - _glfw.wl.publicKeys[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; - _glfw.wl.publicKeys[KEY_INSERT] = GLFW_KEY_INSERT; - _glfw.wl.publicKeys[KEY_LEFT] = GLFW_KEY_LEFT; - _glfw.wl.publicKeys[KEY_RIGHT] = GLFW_KEY_RIGHT; - _glfw.wl.publicKeys[KEY_DOWN] = GLFW_KEY_DOWN; - _glfw.wl.publicKeys[KEY_UP] = GLFW_KEY_UP; - _glfw.wl.publicKeys[KEY_F1] = GLFW_KEY_F1; - _glfw.wl.publicKeys[KEY_F2] = GLFW_KEY_F2; - _glfw.wl.publicKeys[KEY_F3] = GLFW_KEY_F3; - _glfw.wl.publicKeys[KEY_F4] = GLFW_KEY_F4; - _glfw.wl.publicKeys[KEY_F5] = GLFW_KEY_F5; - _glfw.wl.publicKeys[KEY_F6] = GLFW_KEY_F6; - _glfw.wl.publicKeys[KEY_F7] = GLFW_KEY_F7; - _glfw.wl.publicKeys[KEY_F8] = GLFW_KEY_F8; - _glfw.wl.publicKeys[KEY_F9] = GLFW_KEY_F9; - _glfw.wl.publicKeys[KEY_F10] = GLFW_KEY_F10; - _glfw.wl.publicKeys[KEY_F11] = GLFW_KEY_F11; - _glfw.wl.publicKeys[KEY_F12] = GLFW_KEY_F12; - _glfw.wl.publicKeys[KEY_F13] = GLFW_KEY_F13; - _glfw.wl.publicKeys[KEY_F14] = GLFW_KEY_F14; - _glfw.wl.publicKeys[KEY_F15] = GLFW_KEY_F15; - _glfw.wl.publicKeys[KEY_F16] = GLFW_KEY_F16; - _glfw.wl.publicKeys[KEY_F17] = GLFW_KEY_F17; - _glfw.wl.publicKeys[KEY_F18] = GLFW_KEY_F18; - _glfw.wl.publicKeys[KEY_F19] = GLFW_KEY_F19; - _glfw.wl.publicKeys[KEY_F20] = GLFW_KEY_F20; - _glfw.wl.publicKeys[KEY_F21] = GLFW_KEY_F21; - _glfw.wl.publicKeys[KEY_F22] = GLFW_KEY_F22; - _glfw.wl.publicKeys[KEY_F23] = GLFW_KEY_F23; - _glfw.wl.publicKeys[KEY_F24] = GLFW_KEY_F24; - _glfw.wl.publicKeys[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; - _glfw.wl.publicKeys[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; - _glfw.wl.publicKeys[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; - _glfw.wl.publicKeys[KEY_KPPLUS] = GLFW_KEY_KP_ADD; - _glfw.wl.publicKeys[KEY_KP0] = GLFW_KEY_KP_0; - _glfw.wl.publicKeys[KEY_KP1] = GLFW_KEY_KP_1; - _glfw.wl.publicKeys[KEY_KP2] = GLFW_KEY_KP_2; - _glfw.wl.publicKeys[KEY_KP3] = GLFW_KEY_KP_3; - _glfw.wl.publicKeys[KEY_KP4] = GLFW_KEY_KP_4; - _glfw.wl.publicKeys[KEY_KP5] = GLFW_KEY_KP_5; - _glfw.wl.publicKeys[KEY_KP6] = GLFW_KEY_KP_6; - _glfw.wl.publicKeys[KEY_KP7] = GLFW_KEY_KP_7; - _glfw.wl.publicKeys[KEY_KP8] = GLFW_KEY_KP_8; - _glfw.wl.publicKeys[KEY_KP9] = GLFW_KEY_KP_9; - _glfw.wl.publicKeys[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; - _glfw.wl.publicKeys[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; - _glfw.wl.publicKeys[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes)); + memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes)); + + _glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; + _glfw.wl.keycodes[KEY_1] = GLFW_KEY_1; + _glfw.wl.keycodes[KEY_2] = GLFW_KEY_2; + _glfw.wl.keycodes[KEY_3] = GLFW_KEY_3; + _glfw.wl.keycodes[KEY_4] = GLFW_KEY_4; + _glfw.wl.keycodes[KEY_5] = GLFW_KEY_5; + _glfw.wl.keycodes[KEY_6] = GLFW_KEY_6; + _glfw.wl.keycodes[KEY_7] = GLFW_KEY_7; + _glfw.wl.keycodes[KEY_8] = GLFW_KEY_8; + _glfw.wl.keycodes[KEY_9] = GLFW_KEY_9; + _glfw.wl.keycodes[KEY_0] = GLFW_KEY_0; + _glfw.wl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; + _glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; + _glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; + _glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q; + _glfw.wl.keycodes[KEY_W] = GLFW_KEY_W; + _glfw.wl.keycodes[KEY_E] = GLFW_KEY_E; + _glfw.wl.keycodes[KEY_R] = GLFW_KEY_R; + _glfw.wl.keycodes[KEY_T] = GLFW_KEY_T; + _glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y; + _glfw.wl.keycodes[KEY_U] = GLFW_KEY_U; + _glfw.wl.keycodes[KEY_I] = GLFW_KEY_I; + _glfw.wl.keycodes[KEY_O] = GLFW_KEY_O; + _glfw.wl.keycodes[KEY_P] = GLFW_KEY_P; + _glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; + _glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; + _glfw.wl.keycodes[KEY_A] = GLFW_KEY_A; + _glfw.wl.keycodes[KEY_S] = GLFW_KEY_S; + _glfw.wl.keycodes[KEY_D] = GLFW_KEY_D; + _glfw.wl.keycodes[KEY_F] = GLFW_KEY_F; + _glfw.wl.keycodes[KEY_G] = GLFW_KEY_G; + _glfw.wl.keycodes[KEY_H] = GLFW_KEY_H; + _glfw.wl.keycodes[KEY_J] = GLFW_KEY_J; + _glfw.wl.keycodes[KEY_K] = GLFW_KEY_K; + _glfw.wl.keycodes[KEY_L] = GLFW_KEY_L; + _glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; + _glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; + _glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z; + _glfw.wl.keycodes[KEY_X] = GLFW_KEY_X; + _glfw.wl.keycodes[KEY_C] = GLFW_KEY_C; + _glfw.wl.keycodes[KEY_V] = GLFW_KEY_V; + _glfw.wl.keycodes[KEY_B] = GLFW_KEY_B; + _glfw.wl.keycodes[KEY_N] = GLFW_KEY_N; + _glfw.wl.keycodes[KEY_M] = GLFW_KEY_M; + _glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; + _glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; + _glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; + _glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; + _glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; + _glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB; + _glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; + _glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; + _glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; + _glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; + _glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; + _glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; + _glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; + _glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; + _glfw.wl.keycodes[KEY_MENU] = GLFW_KEY_MENU; + _glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; + _glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; + _glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; + _glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; + _glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; + _glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; + _glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; + _glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; + _glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME; + _glfw.wl.keycodes[KEY_END] = GLFW_KEY_END; + _glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; + _glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; + _glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; + _glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; + _glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; + _glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; + _glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP; + _glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1; + _glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2; + _glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3; + _glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4; + _glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5; + _glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6; + _glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7; + _glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8; + _glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9; + _glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10; + _glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11; + _glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12; + _glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13; + _glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14; + _glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15; + _glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16; + _glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17; + _glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18; + _glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19; + _glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20; + _glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21; + _glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22; + _glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23; + _glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24; + _glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; + _glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; + _glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; + _glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; + _glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0; + _glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1; + _glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2; + _glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3; + _glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4; + _glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5; + _glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6; + _glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7; + _glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8; + _glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9; + _glfw.wl.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; + _glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; + _glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + + for (scancode = 0; scancode < 256; scancode++) + { + if (_glfw.wl.keycodes[scancode] > 0) + _glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode; + } } @@ -567,6 +663,52 @@ static void createKeyTables(void) int _glfwPlatformInit(void) { + _glfw.wl.xkb.handle = dlopen("libxkbcommon.so.0", RTLD_LAZY | RTLD_GLOBAL); + if (!_glfw.wl.xkb.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to open libxkbcommon."); + return GLFW_FALSE; + } + + _glfw.wl.xkb.context_new = (PFN_xkb_context_new) + dlsym(_glfw.wl.xkb.handle, "xkb_context_new"); + _glfw.wl.xkb.context_unref = (PFN_xkb_context_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_context_unref"); + _glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string) + dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); + _glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref"); + _glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index) + dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); + _glfw.wl.xkb.state_new = (PFN_xkb_state_new) + dlsym(_glfw.wl.xkb.handle, "xkb_state_new"); + _glfw.wl.xkb.state_unref = (PFN_xkb_state_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_state_unref"); + _glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms) + dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); + _glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask) + dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask"); + _glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods) + dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods"); + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + _glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); + _glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); + _glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new"); + _glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); + _glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); + _glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status"); + _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); +#endif + _glfw.wl.display = wl_display_connect(NULL); if (!_glfw.wl.display) { @@ -578,9 +720,6 @@ int _glfwPlatformInit(void) _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); - _glfw.wl.monitors = calloc(4, sizeof(_GLFWmonitor*)); - _glfw.wl.monitorsSize = 4; - createKeyTables(); _glfw.wl.xkb.context = xkb_context_new(0); @@ -597,9 +736,6 @@ int _glfwPlatformInit(void) // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; @@ -625,18 +761,47 @@ void _glfwPlatformTerminate(void) { _glfwTerminateEGL(); _glfwTerminateJoysticksLinux(); - _glfwTerminateThreadLocalStoragePOSIX(); + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + xkb_compose_state_unref(_glfw.wl.xkb.composeState); +#endif + + xkb_keymap_unref(_glfw.wl.xkb.keymap); + xkb_state_unref(_glfw.wl.xkb.state); + xkb_context_unref(_glfw.wl.xkb.context); + + dlclose(_glfw.wl.xkb.handle); + _glfw.wl.xkb.handle = NULL; if (_glfw.wl.cursorTheme) wl_cursor_theme_destroy(_glfw.wl.cursorTheme); if (_glfw.wl.cursorSurface) wl_surface_destroy(_glfw.wl.cursorSurface); + if (_glfw.wl.compositor) + wl_compositor_destroy(_glfw.wl.compositor); + if (_glfw.wl.shm) + wl_shm_destroy(_glfw.wl.shm); + if (_glfw.wl.shell) + wl_shell_destroy(_glfw.wl.shell); + if (_glfw.wl.pointer) + wl_pointer_destroy(_glfw.wl.pointer); + if (_glfw.wl.keyboard) + wl_keyboard_destroy(_glfw.wl.keyboard); + if (_glfw.wl.seat) + wl_seat_destroy(_glfw.wl.seat); + if (_glfw.wl.relativePointerManager) + zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager); + if (_glfw.wl.pointerConstraints) + zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); + if (_glfw.wl.idleInhibitManager) + zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager); if (_glfw.wl.registry) wl_registry_destroy(_glfw.wl.registry); if (_glfw.wl.display) + { wl_display_flush(_glfw.wl.display); - if (_glfw.wl.display) wl_display_disconnect(_glfw.wl.display); + } } const char* _glfwPlatformGetVersionString(void) @@ -647,9 +812,7 @@ const char* _glfwPlatformGetVersionString(void) #else " gettimeofday" #endif -#if defined(__linux__) - " /dev/js" -#endif + " evdev" #if defined(_GLFW_BUILD_DLL) " shared" #endif diff --git a/raylib/external/glfw/src/wl_monitor.c b/raylib/external/glfw/src/wl_monitor.c index 27731c2..10ef0e1 100644 --- a/raylib/external/glfw/src/wl_monitor.c +++ b/raylib/external/glfw/src/wl_monitor.c @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.2 Wayland - www.glfw.org +// GLFW 3.3 Wayland - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // @@ -32,12 +32,6 @@ #include -struct _GLFWvidmodeWayland -{ - GLFWvidmode base; - uint32_t flags; -}; - static void geometry(void* data, struct wl_output* output, int32_t x, @@ -50,11 +44,15 @@ static void geometry(void* data, int32_t transform) { struct _GLFWmonitor *monitor = data; + char name[1024]; monitor->wl.x = x; monitor->wl.y = y; monitor->widthMM = physicalWidth; monitor->heightMM = physicalHeight; + + snprintf(name, sizeof(name), "%s %s", make, model); + monitor->name = strdup(name); } static void mode(void* data, @@ -65,32 +63,29 @@ static void mode(void* data, int32_t refresh) { struct _GLFWmonitor *monitor = data; - _GLFWvidmodeWayland mode = { { 0 }, }; + GLFWvidmode mode; - mode.base.width = width; - mode.base.height = height; - mode.base.refreshRate = refresh / 1000; - mode.flags = flags; + mode.width = width; + mode.height = height; + mode.redBits = 8; + mode.greenBits = 8; + mode.blueBits = 8; + mode.refreshRate = refresh / 1000; - if (monitor->wl.modesCount + 1 >= monitor->wl.modesSize) - { - int size = monitor->wl.modesSize * 2; - _GLFWvidmodeWayland* modes = - realloc(monitor->wl.modes, - size * sizeof(_GLFWvidmodeWayland)); - monitor->wl.modes = modes; - monitor->wl.modesSize = size; - } + monitor->modeCount++; + monitor->modes = + realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); + monitor->modes[monitor->modeCount - 1] = mode; - monitor->wl.modes[monitor->wl.modesCount++] = mode; + if (flags & WL_OUTPUT_MODE_CURRENT) + monitor->wl.currentMode = monitor->modeCount - 1; } -static void done(void* data, - struct wl_output* output) +static void done(void* data, struct wl_output* output) { struct _GLFWmonitor *monitor = data; - monitor->wl.done = GLFW_TRUE; + _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } static void scale(void* data, @@ -102,7 +97,7 @@ static void scale(void* data, monitor->wl.scale = factor; } -static const struct wl_output_listener output_listener = { +static const struct wl_output_listener outputListener = { geometry, mode, done, @@ -118,10 +113,6 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) { _GLFWmonitor *monitor; struct wl_output *output; - char name_str[80]; - - memset(name_str, 0, sizeof(name_str)); - snprintf(name_str, 79, "wl_output@%u", name); if (version < 2) { @@ -130,7 +121,8 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) return; } - monitor = _glfwAllocMonitor(name_str, 0, 0); + // The actual name of this output will be set in the geometry handler. + monitor = _glfwAllocMonitor(NULL, 0, 0); output = wl_registry_bind(_glfw.wl.registry, name, @@ -142,26 +134,10 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) return; } - monitor->wl.modes = calloc(4, sizeof(_GLFWvidmodeWayland)); - monitor->wl.modesSize = 4; - monitor->wl.scale = 1; - monitor->wl.output = output; - wl_output_add_listener(output, &output_listener, monitor); - if (_glfw.wl.monitorsCount + 1 >= _glfw.wl.monitorsSize) - { - _GLFWmonitor** monitors = _glfw.wl.monitors; - int size = _glfw.wl.monitorsSize * 2; - - monitors = realloc(monitors, size * sizeof(_GLFWmonitor*)); - - _glfw.wl.monitors = monitors; - _glfw.wl.monitorsSize = size; - } - - _glfw.wl.monitors[_glfw.wl.monitorsCount++] = monitor; + wl_output_add_listener(output, &outputListener, monitor); } @@ -169,42 +145,6 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) -{ - _GLFWmonitor** monitors; - _GLFWmonitor* monitor; - int i, monitorsCount = _glfw.wl.monitorsCount; - - if (_glfw.wl.monitorsCount == 0) - goto err; - - monitors = calloc(monitorsCount, sizeof(_GLFWmonitor*)); - - for (i = 0; i < monitorsCount; i++) - { - _GLFWmonitor* origMonitor = _glfw.wl.monitors[i]; - monitor = calloc(1, sizeof(_GLFWmonitor)); - - monitor->modes = - _glfwPlatformGetVideoModes(origMonitor, - &origMonitor->wl.modesCount); - *monitor = *_glfw.wl.monitors[i]; - monitors[i] = monitor; - } - - *count = monitorsCount; - return monitors; - -err: - *count = 0; - return NULL; -} - -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - return first->wl.output == second->wl.output; -} - void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { if (xpos) @@ -213,32 +153,24 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = monitor->wl.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = (float) monitor->wl.scale; + if (yscale) + *yscale = (float) monitor->wl.scale; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { - GLFWvidmode *modes; - int i, modesCount = monitor->wl.modesCount; - - modes = calloc(modesCount, sizeof(GLFWvidmode)); - - for (i = 0; i < modesCount; i++) - modes[i] = monitor->wl.modes[i].base; - - *found = modesCount; - return modes; + *found = monitor->modeCount; + return monitor->modes; } void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { - int i; - - for (i = 0; i < monitor->wl.modesCount; i++) - { - if (monitor->wl.modes[i].flags & WL_OUTPUT_MODE_CURRENT) - { - *mode = monitor->wl.modes[i].base; - return; - } - } + *mode = monitor->modes[monitor->wl.currentMode]; } void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) diff --git a/raylib/external/glfw/src/wl_platform.h b/raylib/external/glfw/src/wl_platform.h index 9e42b36..516c84b 100644 --- a/raylib/external/glfw/src/wl_platform.h +++ b/raylib/external/glfw/src/wl_platform.h @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.2 Wayland - www.glfw.org +// GLFW 3.3 Wayland - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // @@ -24,11 +24,11 @@ // //======================================================================== -#ifndef _glfw3_wayland_platform_h_ -#define _glfw3_wayland_platform_h_ - #include #include +#ifdef HAVE_XKBCOMMON_COMPOSE_H +#include +#endif #include typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; @@ -45,14 +45,16 @@ typedef struct VkWaylandSurfaceCreateInfoKHR typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); -#include "posix_tls.h" +#include "posix_thread.h" #include "posix_time.h" #include "linux_joystick.h" #include "xkb_unicode.h" #include "egl_context.h" +#include "osmesa_context.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" +#include "wayland-idle-inhibit-unstable-v1-client-protocol.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) @@ -69,10 +71,44 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #define _GLFW_PLATFORM_CONTEXT_STATE #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE +typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags); +typedef void (* PFN_xkb_context_unref)(struct xkb_context*); +typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags); +typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*); +typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*); +typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*); +typedef void (* PFN_xkb_state_unref)(struct xkb_state*); +typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**); +typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t); +typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component); +#define xkb_context_new _glfw.wl.xkb.context_new +#define xkb_context_unref _glfw.wl.xkb.context_unref +#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string +#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref +#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index +#define xkb_state_new _glfw.wl.xkb.state_new +#define xkb_state_unref _glfw.wl.xkb.state_unref +#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms +#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask +#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods + +#ifdef HAVE_XKBCOMMON_COMPOSE_H +typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags); +typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*); +typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags); +typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*); +typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t); +typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*); +typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*); +#define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale +#define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref +#define xkb_compose_state_new _glfw.wl.xkb.compose_state_new +#define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref +#define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed +#define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status +#define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym +#endif -// Wayland-specific video mode data -// -typedef struct _GLFWvidmodeWayland _GLFWvidmodeWayland; // Wayland-specific per-window data // @@ -81,9 +117,10 @@ typedef struct _GLFWwindowWayland int width, height; GLFWbool visible; GLFWbool maximized; + GLFWbool transparent; struct wl_surface* surface; struct wl_egl_window* native; - struct wl_shell_surface* shell_surface; + struct wl_shell_surface* shellSurface; struct wl_callback* callback; _GLFWcursor* currentCursor; @@ -102,6 +139,9 @@ typedef struct _GLFWwindowWayland struct zwp_relative_pointer_v1* relativePointer; struct zwp_locked_pointer_v1* lockedPointer; } pointerLock; + + struct zwp_idle_inhibitor_v1* idleInhibitor; + } _GLFWwindowWayland; // Wayland-specific global data @@ -118,28 +158,53 @@ typedef struct _GLFWlibraryWayland struct wl_keyboard* keyboard; struct zwp_relative_pointer_manager_v1* relativePointerManager; struct zwp_pointer_constraints_v1* pointerConstraints; + struct zwp_idle_inhibit_manager_v1* idleInhibitManager; - int wl_compositor_version; + int compositorVersion; struct wl_cursor_theme* cursorTheme; struct wl_surface* cursorSurface; uint32_t pointerSerial; - _GLFWmonitor** monitors; - int monitorsCount; - int monitorsSize; - - short int publicKeys[256]; + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; struct { + void* handle; struct xkb_context* context; struct xkb_keymap* keymap; struct xkb_state* state; - xkb_mod_mask_t control_mask; - xkb_mod_mask_t alt_mask; - xkb_mod_mask_t shift_mask; - xkb_mod_mask_t super_mask; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + struct xkb_compose_state* composeState; +#endif + + xkb_mod_mask_t controlMask; + xkb_mod_mask_t altMask; + xkb_mod_mask_t shiftMask; + xkb_mod_mask_t superMask; unsigned int modifiers; + + PFN_xkb_context_new context_new; + PFN_xkb_context_unref context_unref; + PFN_xkb_keymap_new_from_string keymap_new_from_string; + PFN_xkb_keymap_unref keymap_unref; + PFN_xkb_keymap_mod_get_index keymap_mod_get_index; + PFN_xkb_state_new state_new; + PFN_xkb_state_unref state_unref; + PFN_xkb_state_key_get_syms state_key_get_syms; + PFN_xkb_state_update_mask state_update_mask; + PFN_xkb_state_serialize_mods state_serialize_mods; + +#ifdef HAVE_XKBCOMMON_COMPOSE_H + PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale; + PFN_xkb_compose_table_unref compose_table_unref; + PFN_xkb_compose_state_new compose_state_new; + PFN_xkb_compose_state_unref compose_state_unref; + PFN_xkb_compose_state_feed compose_state_feed; + PFN_xkb_compose_state_get_status compose_state_get_status; + PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym; +#endif } xkb; _GLFWwindow* pointerFocus; @@ -152,15 +217,12 @@ typedef struct _GLFWlibraryWayland typedef struct _GLFWmonitorWayland { struct wl_output* output; - - _GLFWvidmodeWayland* modes; - int modesCount; - int modesSize; - GLFWbool done; + int currentMode; int x; int y; int scale; + } _GLFWmonitorWayland; // Wayland-specific per-cursor data @@ -176,4 +238,3 @@ typedef struct _GLFWcursorWayland void _glfwAddOutputWayland(uint32_t name, uint32_t version); -#endif // _glfw3_wayland_platform_h_ diff --git a/raylib/external/glfw/src/wl_window.c b/raylib/external/glfw/src/wl_window.c index cf75ec8..9759ba2 100644 --- a/raylib/external/glfw/src/wl_window.c +++ b/raylib/external/glfw/src/wl_window.c @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.2 Wayland - www.glfw.org +// GLFW 3.3 Wayland - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // @@ -105,7 +105,7 @@ static void checkScaleChange(_GLFWwindow* window) int monitorScale; // Check if we will be able to set the buffer scale or not. - if (_glfw.wl.wl_compositor_version < 3) + if (_glfw.wl.compositorVersion < 3) return; // Get the scale factor from the highest scale monitor. @@ -189,6 +189,24 @@ static void setOpaqueRegion(_GLFWwindow* window) wl_region_destroy(region); } +static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) +{ + if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager) + { + window->wl.idleInhibitor = + zwp_idle_inhibit_manager_v1_create_inhibitor( + _glfw.wl.idleInhibitManager, window->wl.surface); + if (!window->wl.idleInhibitor) + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Idle inhibitor creation failed"); + } + else if (!enable && window->wl.idleInhibitor) + { + zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); + window->wl.idleInhibitor = NULL; + } +} + static GLFWbool createSurface(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) { @@ -212,43 +230,48 @@ static GLFWbool createSurface(_GLFWwindow* window, window->wl.height = wndconfig->height; window->wl.scale = 1; - // TODO: make this optional once issue #197 is fixed. - setOpaqueRegion(window); + if (!window->wl.transparent) + setOpaqueRegion(window); return GLFW_TRUE; } static GLFWbool createShellSurface(_GLFWwindow* window) { - window->wl.shell_surface = wl_shell_get_shell_surface(_glfw.wl.shell, - window->wl.surface); - if (!window->wl.shell_surface) + window->wl.shellSurface = wl_shell_get_shell_surface(_glfw.wl.shell, + window->wl.surface); + if (!window->wl.shellSurface) return GLFW_FALSE; - wl_shell_surface_add_listener(window->wl.shell_surface, + wl_shell_surface_add_listener(window->wl.shellSurface, &shellSurfaceListener, window); if (window->wl.title) - wl_shell_surface_set_title(window->wl.shell_surface, window->wl.title); + wl_shell_surface_set_title(window->wl.shellSurface, window->wl.title); if (window->monitor) { wl_shell_surface_set_fullscreen( - window->wl.shell_surface, + window->wl.shellSurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, window->monitor->wl.output); + setIdleInhibitor(window, GLFW_TRUE); } else if (window->wl.maximized) { - wl_shell_surface_set_maximized(window->wl.shell_surface, NULL); + wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); + setIdleInhibitor(window, GLFW_FALSE); } else { - wl_shell_surface_set_toplevel(window->wl.shell_surface); + wl_shell_surface_set_toplevel(window->wl.shellSurface); + setIdleInhibitor(window, GLFW_FALSE); } + wl_surface_commit(window->wl.surface); + return GLFW_TRUE; } @@ -388,15 +411,28 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { + window->wl.transparent = fbconfig->transparent; + if (!createSurface(window, wndconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) { - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; + if (ctxconfig->source == GLFW_EGL_CONTEXT_API || + ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (wndconfig->title) @@ -411,7 +447,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, } else { - window->wl.shell_surface = NULL; + window->wl.shellSurface = NULL; window->wl.visible = GLFW_FALSE; } @@ -437,14 +473,17 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) _glfwInputWindowFocus(window, GLFW_FALSE); } + if (window->wl.idleInhibitor) + zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); + if (window->context.destroy) window->context.destroy(window); if (window->wl.native) wl_egl_window_destroy(window->wl.native); - if (window->wl.shell_surface) - wl_shell_surface_destroy(window->wl.shell_surface); + if (window->wl.shellSurface) + wl_shell_surface_destroy(window->wl.shellSurface); if (window->wl.surface) wl_surface_destroy(window->wl.surface); @@ -458,8 +497,8 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) if (window->wl.title) free(window->wl.title); window->wl.title = strdup(title); - if (window->wl.shell_surface) - wl_shell_surface_set_title(window->wl.shell_surface, title); + if (window->wl.shellSurface) + wl_shell_surface_set_title(window->wl.shellSurface, title); } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, @@ -501,7 +540,8 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) window->wl.width = width; window->wl.height = height; wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); - setOpaqueRegion(window); + if (!window->wl.transparent) + setOpaqueRegion(window); _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); } @@ -534,6 +574,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, // implemented, but for now just leave everything as 0. } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = (float) window->wl.scale; + if (yscale) + *yscale = (float) window->wl.scale; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { // TODO: move to xdg_shell instead of wl_shell. @@ -546,8 +595,8 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) // TODO: also do the same for iconified. if (window->monitor || window->wl.maximized) { - if (window->wl.shell_surface) - wl_shell_surface_set_toplevel(window->wl.shell_surface); + if (window->wl.shellSurface) + wl_shell_surface_set_toplevel(window->wl.shellSurface); window->wl.maximized = GLFW_FALSE; } @@ -557,10 +606,10 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { if (!window->monitor && !window->wl.maximized) { - if (window->wl.shell_surface) + if (window->wl.shellSurface) { // Let the compositor select the best output. - wl_shell_surface_set_maximized(window->wl.shell_surface, NULL); + wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); } window->wl.maximized = GLFW_TRUE; } @@ -570,7 +619,7 @@ void _glfwPlatformShowWindow(_GLFWwindow* window) { if (!window->monitor) { - if (!window->wl.shell_surface) + if (!window->wl.shellSurface) createShellSurface(window); window->wl.visible = GLFW_TRUE; } @@ -580,12 +629,19 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) { if (!window->monitor) { - if (window->wl.shell_surface) - wl_shell_surface_destroy(window->wl.shell_surface); + if (window->wl.shellSurface) + wl_shell_surface_destroy(window->wl.shellSurface); window->wl.visible = GLFW_FALSE; } } +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attention request not implemented yet"); +} + void _glfwPlatformFocusWindow(_GLFWwindow* window) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -601,16 +657,18 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (monitor) { wl_shell_surface_set_fullscreen( - window->wl.shell_surface, + window->wl.shellSurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, refreshRate * 1000, // Convert Hz to mHz. monitor->wl.output); + setIdleInhibitor(window, GLFW_TRUE); } else { - wl_shell_surface_set_toplevel(window->wl.shell_surface); + wl_shell_surface_set_toplevel(window->wl.shellSurface); + setIdleInhibitor(window, GLFW_FALSE); } - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); } int _glfwPlatformWindowFocused(_GLFWwindow* window) @@ -634,6 +692,41 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) return window->wl.maximized; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + return window->wl.transparent; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window attribute setting not implemented yet"); +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + return 1.f; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ +} + void _glfwPlatformPollEvents(void) { handleEvents(0); @@ -680,12 +773,17 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) _glfwPlatformSetCursor(window, window->wl.currentCursor); } -const char* _glfwPlatformGetKeyName(int key, int scancode) +const char* _glfwPlatformGetScancodeName(int scancode) { // TODO return NULL; } +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.wl.scancodes[key]; +} + int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) @@ -945,14 +1043,14 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } } -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +void _glfwPlatformSetClipboardString(const char* string) { // TODO _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Clipboard setting not implemented yet"); } -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +const char* _glfwPlatformGetClipboardString(void) { // TODO _glfwInputError(GLFW_PLATFORM_ERROR, @@ -960,28 +1058,21 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) return NULL; } -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { - char** extensions; + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) + return; - *count = 0; - - if (!_glfw.vk.KHR_wayland_surface) - return NULL; - - extensions = calloc(2, sizeof(char*)); - extensions[0] = strdup("VK_KHR_surface"); - extensions[1] = strdup("VK_KHR_wayland_surface"); - - *count = 2; - return extensions; + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_wayland_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { - PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = + PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR + vkGetPhysicalDeviceWaylandPresentationSupportKHR = (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) diff --git a/raylib/external/glfw/src/x11_init.c b/raylib/external/glfw/src/x11_init.c index f7a06c1..4c863c3 100644 --- a/raylib/external/glfw/src/x11_init.c +++ b/raylib/external/glfw/src/x11_init.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 X11 - www.glfw.org +// GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -232,13 +232,13 @@ static void createKeyTables(void) { int scancode, key; - memset(_glfw.x11.publicKeys, -1, sizeof(_glfw.x11.publicKeys)); - memset(_glfw.x11.nativeKeys, -1, sizeof(_glfw.x11.nativeKeys)); + memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes)); + memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes)); if (_glfw.x11.xkb.available) { - // Use XKB to determine physical key locations independently of the current - // keyboard layout + // Use XKB to determine physical key locations independently of the + // current keyboard layout char name[XkbKeyNameLength + 1]; XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd); @@ -305,7 +305,7 @@ static void createKeyTables(void) else key = GLFW_KEY_UNKNOWN; if ((scancode >= 0) && (scancode < 256)) - _glfw.x11.publicKeys[scancode] = key; + _glfw.x11.keycodes[scancode] = key; } XkbFreeNames(desc, XkbKeyNamesMask, True); @@ -316,12 +316,12 @@ static void createKeyTables(void) { // Translate the un-translated key codes using traditional X11 KeySym // lookups - if (_glfw.x11.publicKeys[scancode] < 0) - _glfw.x11.publicKeys[scancode] = translateKeyCode(scancode); + if (_glfw.x11.keycodes[scancode] < 0) + _glfw.x11.keycodes[scancode] = translateKeyCode(scancode); // Store the reverse translation for faster key name lookup - if (_glfw.x11.publicKeys[scancode] > 0) - _glfw.x11.nativeKeys[_glfw.x11.publicKeys[scancode]] = scancode; + if (_glfw.x11.keycodes[scancode] > 0) + _glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode; } } @@ -381,13 +381,11 @@ static void detectEWMH(void) XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", False); // Then we look for the _NET_SUPPORTING_WM_CHECK property of the root window - if (_glfwGetWindowPropertyX11(_glfw.x11.root, - supportingWmCheck, - XA_WINDOW, - (unsigned char**) &windowFromRoot) != 1) + if (!_glfwGetWindowPropertyX11(_glfw.x11.root, + supportingWmCheck, + XA_WINDOW, + (unsigned char**) &windowFromRoot)) { - if (windowFromRoot) - XFree(windowFromRoot); return; } @@ -395,14 +393,12 @@ static void detectEWMH(void) // It should be the ID of a child window (of the root) // Then we look for the same property on the child window - if (_glfwGetWindowPropertyX11(*windowFromRoot, - supportingWmCheck, - XA_WINDOW, - (unsigned char**) &windowFromChild) != 1) + if (!_glfwGetWindowPropertyX11(*windowFromRoot, + supportingWmCheck, + XA_WINDOW, + (unsigned char**) &windowFromChild)) { XFree(windowFromRoot); - if (windowFromChild) - XFree(windowFromChild); return; } @@ -442,6 +438,8 @@ static void detectEWMH(void) getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); + _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION"); _glfw.x11.NET_WM_FULLSCREEN_MONITORS = getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); _glfw.x11.NET_WM_WINDOW_TYPE = @@ -455,72 +453,176 @@ static void detectEWMH(void) _glfw.x11.NET_REQUEST_FRAME_EXTENTS = getSupportedAtom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); - XFree(supportedAtoms); + if (supportedAtoms) + XFree(supportedAtoms); } -// Initialize X11 display and look for supported X11 extensions +// Look for and initialize supported X11 extensions // static GLFWbool initExtensions(void) { -#if defined(_GLFW_HAS_XF86VM) - // Check for XF86VidMode extension - _glfw.x11.vidmode.available = - XF86VidModeQueryExtension(_glfw.x11.display, - &_glfw.x11.vidmode.eventBase, - &_glfw.x11.vidmode.errorBase); -#endif /*_GLFW_HAS_XF86VM*/ - - // Check for RandR extension - if (XRRQueryExtension(_glfw.x11.display, - &_glfw.x11.randr.eventBase, - &_glfw.x11.randr.errorBase)) + _glfw.x11.vidmode.handle = dlopen("libXxf86vm.so.1", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.vidmode.handle) { - if (XRRQueryVersion(_glfw.x11.display, - &_glfw.x11.randr.major, - &_glfw.x11.randr.minor)) + _glfw.x11.vidmode.QueryExtension = (PFN_XF86VidModeQueryExtension) + dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension"); + _glfw.x11.vidmode.GetGammaRamp = (PFN_XF86VidModeGetGammaRamp) + dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp"); + _glfw.x11.vidmode.SetGammaRamp = (PFN_XF86VidModeSetGammaRamp) + dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp"); + _glfw.x11.vidmode.GetGammaRampSize = (PFN_XF86VidModeGetGammaRampSize) + dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize"); + + _glfw.x11.vidmode.available = + XF86VidModeQueryExtension(_glfw.x11.display, + &_glfw.x11.vidmode.eventBase, + &_glfw.x11.vidmode.errorBase); + } + + _glfw.x11.xi.handle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.xi.handle) + { + _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) + dlsym(_glfw.x11.xi.handle, "XIQueryVersion"); + _glfw.x11.xi.SelectEvents = (PFN_XISelectEvents) + dlsym(_glfw.x11.xi.handle, "XISelectEvents"); + + if (XQueryExtension(_glfw.x11.display, + "XInputExtension", + &_glfw.x11.xi.majorOpcode, + &_glfw.x11.xi.eventBase, + &_glfw.x11.xi.errorBase)) { - // The GLFW RandR path requires at least version 1.3 - if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3) - _glfw.x11.randr.available = GLFW_TRUE; + _glfw.x11.xi.major = 2; + _glfw.x11.xi.minor = 0; + + if (XIQueryVersion(_glfw.x11.display, + &_glfw.x11.xi.major, + &_glfw.x11.xi.minor) == Success) + { + _glfw.x11.xi.available = GLFW_TRUE; + } } - else + } + + _glfw.x11.randr.handle = dlopen("libXrandr.so.2", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.randr.handle) + { + _glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma) + dlsym(_glfw.x11.randr.handle, "XRRAllocGamma"); + _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) + dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); + _glfw.x11.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo) + dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo"); + _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) + dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); + _glfw.x11.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo) + dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo"); + _glfw.x11.randr.FreeScreenResources = (PFN_XRRFreeScreenResources) + dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources"); + _glfw.x11.randr.GetCrtcGamma = (PFN_XRRGetCrtcGamma) + dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma"); + _glfw.x11.randr.GetCrtcGammaSize = (PFN_XRRGetCrtcGammaSize) + dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize"); + _glfw.x11.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo) + dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo"); + _glfw.x11.randr.GetOutputInfo = (PFN_XRRGetOutputInfo) + dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo"); + _glfw.x11.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary) + dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary"); + _glfw.x11.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent) + dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent"); + _glfw.x11.randr.QueryExtension = (PFN_XRRQueryExtension) + dlsym(_glfw.x11.randr.handle, "XRRQueryExtension"); + _glfw.x11.randr.QueryVersion = (PFN_XRRQueryVersion) + dlsym(_glfw.x11.randr.handle, "XRRQueryVersion"); + _glfw.x11.randr.SelectInput = (PFN_XRRSelectInput) + dlsym(_glfw.x11.randr.handle, "XRRSelectInput"); + _glfw.x11.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig) + dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig"); + _glfw.x11.randr.SetCrtcGamma = (PFN_XRRSetCrtcGamma) + dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma"); + _glfw.x11.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration) + dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration"); + + if (XRRQueryExtension(_glfw.x11.display, + &_glfw.x11.randr.eventBase, + &_glfw.x11.randr.errorBase)) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to query RandR version"); + if (XRRQueryVersion(_glfw.x11.display, + &_glfw.x11.randr.major, + &_glfw.x11.randr.minor)) + { + // The GLFW RandR path requires at least version 1.3 + if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3) + _glfw.x11.randr.available = GLFW_TRUE; + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to query RandR version"); + } } } if (_glfw.x11.randr.available) { - XRRScreenResources* sr = XRRGetScreenResources(_glfw.x11.display, - _glfw.x11.root); + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, + _glfw.x11.root); if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0])) { - // This is either a headless system or an older Nvidia binary driver - // with broken gamma support - // Flag it as useless and fall back to Xf86VidMode gamma, if - // available - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: RandR gamma ramp support seems broken"); + // This is likely an older Nvidia driver with broken gamma support + // Flag it as useless and fall back to xf86vm gamma, if available _glfw.x11.randr.gammaBroken = GLFW_TRUE; } - XRRFreeScreenResources(sr); + if (!sr->ncrtc) + { + // A system without CRTCs is likely a system with broken RandR + // Disable the RandR monitor path and fall back to core functions + _glfw.x11.randr.monitorBroken = GLFW_TRUE; + } + XRRFreeScreenResources(sr); + } + + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { XRRSelectInput(_glfw.x11.display, _glfw.x11.root, RROutputChangeNotifyMask); } - if (XineramaQueryExtension(_glfw.x11.display, - &_glfw.x11.xinerama.major, - &_glfw.x11.xinerama.minor)) + _glfw.x11.xcursor.handle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.xcursor.handle) { - if (XineramaIsActive(_glfw.x11.display)) - _glfw.x11.xinerama.available = GLFW_TRUE; + _glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate) + dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate"); + _glfw.x11.xcursor.ImageDestroy = (PFN_XcursorImageDestroy) + dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); + _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) + dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); + } + + _glfw.x11.xinerama.handle = dlopen("libXinerama.so.1", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.xinerama.handle) + { + _glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive) + dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive"); + _glfw.x11.xinerama.QueryExtension = (PFN_XineramaQueryExtension) + dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension"); + _glfw.x11.xinerama.QueryScreens = (PFN_XineramaQueryScreens) + dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens"); + + if (XineramaQueryExtension(_glfw.x11.display, + &_glfw.x11.xinerama.major, + &_glfw.x11.xinerama.minor)) + { + if (XineramaIsActive(_glfw.x11.display)) + _glfw.x11.xinerama.available = GLFW_TRUE; + } } - // Check if Xkb is supported on this display _glfw.x11.xkb.major = 1; _glfw.x11.xkb.minor = 0; _glfw.x11.xkb.available = @@ -542,13 +644,36 @@ static GLFWbool initExtensions(void) } } - _glfw.x11.x11xcb.handle = dlopen("libX11-xcb.so", RTLD_LAZY | RTLD_GLOBAL); + _glfw.x11.x11xcb.handle = dlopen("libX11-xcb.so.1", RTLD_LAZY | RTLD_GLOBAL); if (_glfw.x11.x11xcb.handle) { - _glfw.x11.x11xcb.XGetXCBConnection = (XGETXCBCONNECTION_T) + _glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection) dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); } + _glfw.x11.xrender.handle = dlopen("libXrender.so.1", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.xrender.handle) + { + _glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension) + dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension"); + _glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion) + dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion"); + _glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat) + dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat"); + + if (XRenderQueryExtension(_glfw.x11.display, + &_glfw.x11.xrender.errorBase, + &_glfw.x11.xrender.eventBase)) + { + if (XRenderQueryVersion(_glfw.x11.display, + &_glfw.x11.xrender.major, + &_glfw.x11.xrender.minor)) + { + _glfw.x11.xrender.available = GLFW_TRUE; + } + } + } + // Update the key code LUT // FIXME: We should listen to XkbMapNotify events to track changes to // the keyboard mapping. @@ -559,10 +684,7 @@ static GLFWbool initExtensions(void) // String format atoms _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); - _glfw.x11.UTF8_STRING = - XInternAtom(_glfw.x11.display, "UTF8_STRING", False); - _glfw.x11.COMPOUND_STRING = - XInternAtom(_glfw.x11.display, "COMPOUND_STRING", False); + _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False); _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); // Custom selection property atom @@ -572,6 +694,8 @@ static GLFWbool initExtensions(void) // ICCCM standard clipboard atoms _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); + _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False); + _glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False); _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); // Clipboard manager atoms @@ -587,9 +711,10 @@ static GLFWbool initExtensions(void) _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False); - _glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", False); _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False); _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False); + _glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False); + _glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False); // ICCCM, EWMH and Motif window property atoms // These can be set safely even without WM support @@ -612,24 +737,81 @@ static GLFWbool initExtensions(void) XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False); _glfw.x11.NET_WM_BYPASS_COMPOSITOR = XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False); + _glfw.x11.NET_WM_WINDOW_OPACITY = + XInternAtom(_glfw.x11.display, "_NET_WM_WINDOW_OPACITY", False); _glfw.x11.MOTIF_WM_HINTS = XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); + // The compositing manager selection name contains the screen number + { + char name[32]; + snprintf(name, sizeof(name), "_NET_WM_CM_S%u", _glfw.x11.screen); + _glfw.x11.NET_WM_CM_Sx = XInternAtom(_glfw.x11.display, name, False); + } + return GLFW_TRUE; } +// Retrieve system content scale via folklore heuristics +// +static void getSystemContentScale(float* xscale, float* yscale) +{ + // NOTE: Default to the display-wide DPI as we don't currently have a policy + // for which monitor a window is considered to be on + float xdpi = DisplayWidth(_glfw.x11.display, _glfw.x11.screen) * + 25.4f / DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); + float ydpi = DisplayHeight(_glfw.x11.display, _glfw.x11.screen) * + 25.4f / DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); + + // NOTE: Basing the scale on Xft.dpi where available should provide the most + // consistent user experience (matches Qt, Gtk, etc), although not + // always the most accurate one + char* rms = XResourceManagerString(_glfw.x11.display); + if (rms) + { + XrmDatabase db = XrmGetStringDatabase(rms); + if (db) + { + XrmValue value; + char* type = NULL; + + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) + { + if (type && strcmp(type, "String") == 0) + xdpi = ydpi = atof(value.addr); + } + + XrmDestroyDatabase(db); + } + } + + *xscale = xdpi / 96.f; + *yscale = ydpi / 96.f; +} + // Create a blank cursor for hidden and disabled cursor modes // static Cursor createHiddenCursor(void) { - unsigned char pixels[16 * 16 * 4]; + unsigned char pixels[16 * 16 * 4] = { 0 }; GLFWimage image = { 16, 16, pixels }; - - memset(pixels, 0, sizeof(pixels)); - return _glfwCreateCursorX11(&image, 0, 0); } +// Create a helper window for IPC +// +static Window createHelperWindow(void) +{ + XSetWindowAttributes wa; + wa.event_mask = PropertyChangeMask; + + return XCreateWindow(_glfw.x11.display, _glfw.x11.root, + 0, 0, 1, 1, 0, 0, + InputOnly, + DefaultVisual(_glfw.x11.display, _glfw.x11.screen), + CWEventMask, &wa); +} + // X error handler // static int errorHandler(Display *display, XErrorEvent* event) @@ -664,7 +846,7 @@ void _glfwReleaseErrorHandlerX11(void) // void _glfwInputErrorX11(int error, const char* message) { - char buffer[8192]; + char buffer[_GLFW_MESSAGE_SIZE]; XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode, buffer, sizeof(buffer)); @@ -678,6 +860,9 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) int i; Cursor cursor; + if (!_glfw.x11.xcursor.handle) + return None; + XcursorImage* native = XcursorImageCreate(image->width, image->height); if (native == NULL) return None; @@ -712,13 +897,17 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) int _glfwPlatformInit(void) { #if !defined(X_HAVE_UTF8_STRING) - // HACK: If the current locale is C, apply the environment's locale - // This is done because the C locale breaks wide character input + // HACK: If the current locale is "C" and the Xlib UTF-8 functions are + // unavailable, apply the environment's locale in the hope that it's + // both available and not "C" + // This is done because the "C" locale breaks wide character input, + // which is what we fall back on when UTF-8 support is missing if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) setlocale(LC_CTYPE, ""); #endif XInitThreads(); + XrmInitialize(); _glfw.x11.display = XOpenDisplay(NULL); if (!_glfw.x11.display) @@ -742,10 +931,13 @@ int _glfwPlatformInit(void) _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.context = XUniqueContext(); + getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY); + if (!initExtensions()) return GLFW_FALSE; - _glfw.x11.cursor = createHiddenCursor(); + _glfw.x11.helperWindowHandle = createHelperWindow(); + _glfw.x11.hiddenCursorHandle = createHiddenCursor(); if (XSupportsLocale()) { @@ -762,31 +954,38 @@ int _glfwPlatformInit(void) } } - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - +#if defined(__linux__) if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; +#endif _glfwInitTimerPOSIX(); + _glfwPollMonitorsX11(); return GLFW_TRUE; } void _glfwPlatformTerminate(void) { - if (_glfw.x11.x11xcb.handle) + if (_glfw.x11.helperWindowHandle) { - dlclose(_glfw.x11.x11xcb.handle); - _glfw.x11.x11xcb.handle = NULL; + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == + _glfw.x11.helperWindowHandle) + { + _glfwPushSelectionToManagerX11(); + } + + XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle); + _glfw.x11.helperWindowHandle = None; } - if (_glfw.x11.cursor) + if (_glfw.x11.hiddenCursorHandle) { - XFreeCursor(_glfw.x11.display, _glfw.x11.cursor); - _glfw.x11.cursor = (Cursor) 0; + XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle); + _glfw.x11.hiddenCursorHandle = (Cursor) 0; } + free(_glfw.x11.primarySelectionString); free(_glfw.x11.clipboardString); if (_glfw.x11.im) @@ -803,12 +1002,37 @@ void _glfwPlatformTerminate(void) _glfw.x11.display = NULL; } + if (_glfw.x11.x11xcb.handle) + { + dlclose(_glfw.x11.x11xcb.handle); + _glfw.x11.x11xcb.handle = NULL; + } + + if (_glfw.x11.xcursor.handle) + { + dlclose(_glfw.x11.xcursor.handle); + _glfw.x11.xcursor.handle = NULL; + } + + if (_glfw.x11.randr.handle) + { + dlclose(_glfw.x11.randr.handle); + _glfw.x11.randr.handle = NULL; + } + + if (_glfw.x11.xinerama.handle) + { + dlclose(_glfw.x11.xinerama.handle); + _glfw.x11.xinerama.handle = NULL; + } + // NOTE: This needs to be done after XCloseDisplay, as libGL registers // cleanup callbacks that get called by it _glfwTerminateGLX(); +#if defined(__linux__) _glfwTerminateJoysticksLinux(); - _glfwTerminateThreadLocalStoragePOSIX(); +#endif } const char* _glfwPlatformGetVersionString(void) @@ -820,10 +1044,7 @@ const char* _glfwPlatformGetVersionString(void) " gettimeofday" #endif #if defined(__linux__) - " /dev/js" -#endif -#if defined(_GLFW_HAS_XF86VM) - " Xf86vm" + " evdev" #endif #if defined(_GLFW_BUILD_DLL) " shared" diff --git a/raylib/external/glfw/src/x11_monitor.c b/raylib/external/glfw/src/x11_monitor.c index 2cec791..d68c588 100644 --- a/raylib/external/glfw/src/x11_monitor.c +++ b/raylib/external/glfw/src/x11_monitor.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 X11 - www.glfw.org +// GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -95,6 +95,126 @@ static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi, ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// +// Poll for changes in the set of connected monitors +// +void _glfwPollMonitorsX11(void) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + int i, j, disconnectedCount, screenCount = 0; + _GLFWmonitor** disconnected = NULL; + XineramaScreenInfo* screens = NULL; + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, + _glfw.x11.root); + RROutput primary = XRRGetOutputPrimary(_glfw.x11.display, + _glfw.x11.root); + + if (_glfw.x11.xinerama.available) + screens = XineramaQueryScreens(_glfw.x11.display, &screenCount); + + disconnectedCount = _glfw.monitorCount; + if (disconnectedCount) + { + disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); + memcpy(disconnected, + _glfw.monitors, + _glfw.monitorCount * sizeof(_GLFWmonitor*)); + } + + for (i = 0; i < sr->noutput; i++) + { + int type, widthMM, heightMM; + XRROutputInfo* oi; + XRRCrtcInfo* ci; + _GLFWmonitor* monitor; + + oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]); + if (oi->connection != RR_Connected || oi->crtc == None) + { + XRRFreeOutputInfo(oi); + continue; + } + + for (j = 0; j < disconnectedCount; j++) + { + if (disconnected[j] && + disconnected[j]->x11.output == sr->outputs[i]) + { + disconnected[j] = NULL; + break; + } + } + + if (j < disconnectedCount) + { + XRRFreeOutputInfo(oi); + continue; + } + + ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc); + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + widthMM = oi->mm_height; + heightMM = oi->mm_width; + } + else + { + widthMM = oi->mm_width; + heightMM = oi->mm_height; + } + + monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM); + monitor->x11.output = sr->outputs[i]; + monitor->x11.crtc = oi->crtc; + + for (j = 0; j < screenCount; j++) + { + if (screens[j].x_org == ci->x && + screens[j].y_org == ci->y && + screens[j].width == ci->width && + screens[j].height == ci->height) + { + monitor->x11.index = j; + break; + } + } + + if (monitor->x11.output == primary) + type = _GLFW_INSERT_FIRST; + else + type = _GLFW_INSERT_LAST; + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + } + + XRRFreeScreenResources(sr); + + if (screens) + XFree(screens); + + for (i = 0; i < disconnectedCount; i++) + { + if (disconnected[i]) + _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); + } + + free(disconnected); + } + + if (!_glfw.monitorCount) + { + const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); + const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); + + _glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM), + GLFW_CONNECTED, + _GLFW_INSERT_FIRST); + } +} + // Set the current video mode for the specified monitor // GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) @@ -114,7 +234,7 @@ GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) if (_glfwCompareVideoModes(¤t, best) == 0) return GLFW_TRUE; - sr = XRRGetScreenResources(_glfw.x11.display, _glfw.x11.root); + sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); @@ -174,7 +294,7 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) if (monitor->x11.oldMode == None) return; - sr = XRRGetScreenResources(_glfw.x11.display, _glfw.x11.root); + sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); XRRSetCrtcConfig(_glfw.x11.display, @@ -198,119 +318,6 @@ void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -_GLFWmonitor** _glfwPlatformGetMonitors(int* count) -{ - int i, j, k, found = 0; - _GLFWmonitor** monitors = NULL; - - *count = 0; - - if (_glfw.x11.randr.available) - { - int screenCount = 0; - XineramaScreenInfo* screens = NULL; - XRRScreenResources* sr = XRRGetScreenResources(_glfw.x11.display, - _glfw.x11.root); - RROutput primary = XRRGetOutputPrimary(_glfw.x11.display, - _glfw.x11.root); - - monitors = calloc(sr->noutput, sizeof(_GLFWmonitor*)); - - if (_glfw.x11.xinerama.available) - screens = XineramaQueryScreens(_glfw.x11.display, &screenCount); - - for (i = 0; i < sr->ncrtc; i++) - { - XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, - sr, sr->crtcs[i]); - - for (j = 0; j < ci->noutput; j++) - { - int widthMM, heightMM; - _GLFWmonitor* monitor; - XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, - sr, ci->outputs[j]); - if (oi->connection != RR_Connected) - { - XRRFreeOutputInfo(oi); - continue; - } - - if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) - { - widthMM = oi->mm_height; - heightMM = oi->mm_width; - } - else - { - widthMM = oi->mm_width; - heightMM = oi->mm_height; - } - - monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM); - monitor->x11.output = ci->outputs[j]; - monitor->x11.crtc = oi->crtc; - - for (k = 0; k < screenCount; k++) - { - if (screens[k].x_org == ci->x && - screens[k].y_org == ci->y && - screens[k].width == ci->width && - screens[k].height == ci->height) - { - monitor->x11.index = k; - break; - } - } - - XRRFreeOutputInfo(oi); - - found++; - monitors[found - 1] = monitor; - - if (ci->outputs[j] == primary) - _GLFW_SWAP_POINTERS(monitors[0], monitors[found - 1]); - } - - XRRFreeCrtcInfo(ci); - } - - XRRFreeScreenResources(sr); - - if (screens) - XFree(screens); - - if (found == 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: RandR monitor support seems broken"); - - _glfw.x11.randr.monitorBroken = GLFW_TRUE; - free(monitors); - monitors = NULL; - } - } - - if (!monitors) - { - monitors = calloc(1, sizeof(_GLFWmonitor*)); - monitors[0] = _glfwAllocMonitor("Display", - DisplayWidthMM(_glfw.x11.display, - _glfw.x11.screen), - DisplayHeightMM(_glfw.x11.display, - _glfw.x11.screen)); - found = 1; - } - - *count = found; - return monitors; -} - -GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) -{ - return first->x11.crtc == second->x11.crtc; -} - void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) @@ -331,6 +338,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) } } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.x11.contentScaleX; + if (yscale) + *yscale = _glfw.x11.contentScaleY; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { GLFWvidmode* result; @@ -423,13 +439,12 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) _glfwAllocGammaArrays(ramp, size); - memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); + memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); memcpy(ramp->green, gamma->green, size * sizeof(unsigned short)); - memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); + memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); XRRFreeGamma(gamma); } -#if defined(_GLFW_HAS_XF86VM) else if (_glfw.x11.vidmode.available) { int size; @@ -441,23 +456,28 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) _glfw.x11.screen, ramp->size, ramp->red, ramp->green, ramp->blue); } -#endif /*_GLFW_HAS_XF86VM*/ } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) { + if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Gamma ramp size must match current ramp size"); + return; + } + XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size); - memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); + memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short)); - memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); + memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma); XRRFreeGamma(gamma); } -#if defined(_GLFW_HAS_XF86VM) else if (_glfw.x11.vidmode.available) { XF86VidModeSetGammaRamp(_glfw.x11.display, @@ -467,7 +487,6 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) (unsigned short*) ramp->green, (unsigned short*) ramp->blue); } -#endif /*_GLFW_HAS_XF86VM*/ } diff --git a/raylib/external/glfw/src/x11_platform.h b/raylib/external/glfw/src/x11_platform.h index 3304306..c5a11cf 100644 --- a/raylib/external/glfw/src/x11_platform.h +++ b/raylib/external/glfw/src/x11_platform.h @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 X11 - www.glfw.org +// GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -25,9 +25,6 @@ // //======================================================================== -#ifndef _glfw3_x11_platform_h_ -#define _glfw3_x11_platform_h_ - #include #include #include @@ -47,15 +44,84 @@ // The Xinerama extension provides legacy monitor indices #include -#if defined(_GLFW_HAS_XF86VM) - // The Xf86VidMode extension provides fallback gamma control - #include -#endif +// The XInput extension provides raw mouse motion input +#include + +typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int); +typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*); +typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*); +typedef void (* PFN_XRRFreeOutputInfo)(XRROutputInfo*); +typedef void (* PFN_XRRFreeScreenResources)(XRRScreenResources*); +typedef XRRCrtcGamma* (* PFN_XRRGetCrtcGamma)(Display*,RRCrtc); +typedef int (* PFN_XRRGetCrtcGammaSize)(Display*,RRCrtc); +typedef XRRCrtcInfo* (* PFN_XRRGetCrtcInfo) (Display*,XRRScreenResources*,RRCrtc); +typedef XRROutputInfo* (* PFN_XRRGetOutputInfo)(Display*,XRRScreenResources*,RROutput); +typedef RROutput (* PFN_XRRGetOutputPrimary)(Display*,Window); +typedef XRRScreenResources* (* PFN_XRRGetScreenResourcesCurrent)(Display*,Window); +typedef Bool (* PFN_XRRQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XRRQueryVersion)(Display*,int*,int*); +typedef void (* PFN_XRRSelectInput)(Display*,Window,int); +typedef Status (* PFN_XRRSetCrtcConfig)(Display*,XRRScreenResources*,RRCrtc,Time,int,int,RRMode,Rotation,RROutput*,int); +typedef void (* PFN_XRRSetCrtcGamma)(Display*,RRCrtc,XRRCrtcGamma*); +typedef int (* PFN_XRRUpdateConfiguration)(XEvent*); +#define XRRAllocGamma _glfw.x11.randr.AllocGamma +#define XRRFreeCrtcInfo _glfw.x11.randr.FreeCrtcInfo +#define XRRFreeGamma _glfw.x11.randr.FreeGamma +#define XRRFreeOutputInfo _glfw.x11.randr.FreeOutputInfo +#define XRRFreeScreenResources _glfw.x11.randr.FreeScreenResources +#define XRRGetCrtcGamma _glfw.x11.randr.GetCrtcGamma +#define XRRGetCrtcGammaSize _glfw.x11.randr.GetCrtcGammaSize +#define XRRGetCrtcInfo _glfw.x11.randr.GetCrtcInfo +#define XRRGetOutputInfo _glfw.x11.randr.GetOutputInfo +#define XRRGetOutputPrimary _glfw.x11.randr.GetOutputPrimary +#define XRRGetScreenResourcesCurrent _glfw.x11.randr.GetScreenResourcesCurrent +#define XRRQueryExtension _glfw.x11.randr.QueryExtension +#define XRRQueryVersion _glfw.x11.randr.QueryVersion +#define XRRSelectInput _glfw.x11.randr.SelectInput +#define XRRSetCrtcConfig _glfw.x11.randr.SetCrtcConfig +#define XRRSetCrtcGamma _glfw.x11.randr.SetCrtcGamma +#define XRRUpdateConfiguration _glfw.x11.randr.UpdateConfiguration + +typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int); +typedef void (* PFN_XcursorImageDestroy)(XcursorImage*); +typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*); +#define XcursorImageCreate _glfw.x11.xcursor.ImageCreate +#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy +#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor + +typedef Bool (* PFN_XineramaIsActive)(Display*); +typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*); +typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*); +#define XineramaIsActive _glfw.x11.xinerama.IsActive +#define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension +#define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens typedef XID xcb_window_t; typedef XID xcb_visualid_t; typedef struct xcb_connection_t xcb_connection_t; -typedef xcb_connection_t* (* XGETXCBCONNECTION_T)(Display*); +typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*); +#define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection + +typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*); +typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); +typedef Bool (* PFN_XF86VidModeSetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); +typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*); +#define XF86VidModeQueryExtension _glfw.x11.vidmode.QueryExtension +#define XF86VidModeGetGammaRamp _glfw.x11.vidmode.GetGammaRamp +#define XF86VidModeSetGammaRamp _glfw.x11.vidmode.SetGammaRamp +#define XF86VidModeGetGammaRampSize _glfw.x11.vidmode.GetGammaRampSize + +typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*); +typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); +#define XIQueryVersion _glfw.x11.xi.QueryVersion +#define XISelectEvents _glfw.x11.xi.SelectEvents + +typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*); +typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*); +typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*); +#define XRenderQueryExtension _glfw.x11.xrender.QueryExtension +#define XRenderQueryVersion _glfw.x11.xrender.QueryVersion +#define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR; @@ -83,12 +149,17 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(V typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t); -#include "posix_tls.h" +#include "posix_thread.h" #include "posix_time.h" -#include "linux_joystick.h" #include "xkb_unicode.h" #include "glx_context.h" #include "egl_context.h" +#include "osmesa_context.h" +#if defined(__linux__) +#include "linux_joystick.h" +#else +#include "null_joystick.h" +#endif #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) @@ -112,6 +183,11 @@ typedef struct _GLFWwindowX11 XIC ic; GLFWbool overrideRedirect; + GLFWbool iconified; + GLFWbool maximized; + + // Whether the visual supports framebuffer transparency + GLFWbool transparent; // Cached position and size used to filter out duplicate events int width, height; @@ -122,8 +198,7 @@ typedef struct _GLFWwindowX11 // The last position the cursor was warped to by GLFW int warpCursorPosX, warpCursorPosY; - // The information from the last KeyPress event - unsigned int lastKeyCode; + // The time of the last KeyPress event Time lastKeyTime; } _GLFWwindowX11; @@ -136,22 +211,28 @@ typedef struct _GLFWlibraryX11 int screen; Window root; + // System content scale + float contentScaleX, contentScaleY; + // Helper window for IPC + Window helperWindowHandle; // Invisible cursor for hidden cursor mode - Cursor cursor; + Cursor hiddenCursorHandle; // Context for mapping window XIDs to _GLFWwindow pointers XContext context; // XIM input method XIM im; // Most recent error code received by X error handler int errorCode; + // Primary selection string (while the primary selection is owned) + char* primarySelectionString; // Clipboard string (while the selection is owned) char* clipboardString; // Key name string - char keyName[64]; + char keyName[5]; // X11 keycode to GLFW key LUT - short int publicKeys[256]; + short int keycodes[256]; // GLFW key to X11 keycode LUT - short int nativeKeys[GLFW_KEY_LAST + 1]; + short int scancodes[GLFW_KEY_LAST + 1]; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active @@ -173,8 +254,11 @@ typedef struct _GLFWlibraryX11 Atom NET_WM_STATE_FULLSCREEN; Atom NET_WM_STATE_MAXIMIZED_VERT; Atom NET_WM_STATE_MAXIMIZED_HORZ; + Atom NET_WM_STATE_DEMANDS_ATTENTION; Atom NET_WM_BYPASS_COMPOSITOR; Atom NET_WM_FULLSCREEN_MONITORS; + Atom NET_WM_WINDOW_OPACITY; + Atom NET_WM_CM_Sx; Atom NET_ACTIVE_WINDOW; Atom NET_FRAME_EXTENTS; Atom NET_REQUEST_FRAME_EXTENTS; @@ -187,14 +271,17 @@ typedef struct _GLFWlibraryX11 Atom XdndStatus; Atom XdndActionCopy; Atom XdndDrop; - Atom XdndLeave; Atom XdndFinished; Atom XdndSelection; + Atom XdndTypeList; + Atom text_uri_list; // Selection (clipboard) atoms Atom TARGETS; Atom MULTIPLE; + Atom INCR; Atom CLIPBOARD; + Atom PRIMARY; Atom CLIPBOARD_MANAGER; Atom SAVE_TARGETS; Atom NULL_; @@ -205,12 +292,30 @@ typedef struct _GLFWlibraryX11 struct { GLFWbool available; + void* handle; int eventBase; int errorBase; int major; int minor; GLFWbool gammaBroken; GLFWbool monitorBroken; + PFN_XRRAllocGamma AllocGamma; + PFN_XRRFreeCrtcInfo FreeCrtcInfo; + PFN_XRRFreeGamma FreeGamma; + PFN_XRRFreeOutputInfo FreeOutputInfo; + PFN_XRRFreeScreenResources FreeScreenResources; + PFN_XRRGetCrtcGamma GetCrtcGamma; + PFN_XRRGetCrtcGammaSize GetCrtcGammaSize; + PFN_XRRGetCrtcInfo GetCrtcInfo; + PFN_XRRGetOutputInfo GetOutputInfo; + PFN_XRRGetOutputPrimary GetOutputPrimary; + PFN_XRRGetScreenResourcesCurrent GetScreenResourcesCurrent; + PFN_XRRQueryExtension QueryExtension; + PFN_XRRQueryVersion QueryVersion; + PFN_XRRSelectInput SelectInput; + PFN_XRRSetCrtcConfig SetCrtcConfig; + PFN_XRRSetCrtcGamma SetCrtcGamma; + PFN_XRRUpdateConfiguration UpdateConfiguration; } randr; struct { @@ -232,27 +337,67 @@ typedef struct _GLFWlibraryX11 } saver; struct { + int version; Window source; + Atom format; } xdnd; + struct { + void* handle; + PFN_XcursorImageCreate ImageCreate; + PFN_XcursorImageDestroy ImageDestroy; + PFN_XcursorImageLoadCursor ImageLoadCursor; + } xcursor; + struct { GLFWbool available; + void* handle; int major; int minor; + PFN_XineramaIsActive IsActive; + PFN_XineramaQueryExtension QueryExtension; + PFN_XineramaQueryScreens QueryScreens; } xinerama; struct { void* handle; - XGETXCBCONNECTION_T XGetXCBConnection; + PFN_XGetXCBConnection GetXCBConnection; } x11xcb; -#if defined(_GLFW_HAS_XF86VM) struct { GLFWbool available; + void* handle; int eventBase; int errorBase; + PFN_XF86VidModeQueryExtension QueryExtension; + PFN_XF86VidModeGetGammaRamp GetGammaRamp; + PFN_XF86VidModeSetGammaRamp SetGammaRamp; + PFN_XF86VidModeGetGammaRampSize GetGammaRampSize; } vidmode; -#endif /*_GLFW_HAS_XF86VM*/ + + struct { + GLFWbool available; + void* handle; + int majorOpcode; + int eventBase; + int errorBase; + int major; + int minor; + PFN_XIQueryVersion QueryVersion; + PFN_XISelectEvents SelectEvents; + } xi; + + struct { + GLFWbool available; + void* handle; + int major; + int minor; + int eventBase; + int errorBase; + PFN_XRenderQueryExtension QueryExtension; + PFN_XRenderQueryVersion QueryVersion; + PFN_XRenderFindVisualFormat FindVisualFormat; + } xrender; } _GLFWlibraryX11; @@ -279,6 +424,7 @@ typedef struct _GLFWcursorX11 } _GLFWcursorX11; +void _glfwPollMonitorsX11(void); GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor); @@ -288,9 +434,11 @@ unsigned long _glfwGetWindowPropertyX11(Window window, Atom property, Atom type, unsigned char** value); +GLFWbool _glfwIsVisualTransparentX11(Visual* visual); void _glfwGrabErrorHandlerX11(void); void _glfwReleaseErrorHandlerX11(void); void _glfwInputErrorX11(int error, const char* message); -#endif // _glfw3_x11_platform_h_ +void _glfwPushSelectionToManagerX11(void); + diff --git a/raylib/external/glfw/src/x11_window.c b/raylib/external/glfw/src/x11_window.c index 077eebb..32fff63 100644 --- a/raylib/external/glfw/src/x11_window.c +++ b/raylib/external/glfw/src/x11_window.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 X11 - www.glfw.org +// GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -48,6 +48,8 @@ #define Button6 6 #define Button7 7 +#define _GLFW_XDND_VERSION 5 + // Wait for data to arrive using select // This avoids blocking other threads via the per-display Xlib lock that also @@ -59,16 +61,19 @@ static GLFWbool waitForEvent(double* timeout) const int fd = ConnectionNumber(_glfw.x11.display); int count = fd + 1; - FD_ZERO(&fds); - FD_SET(fd, &fds); #if defined(__linux__) - FD_SET(_glfw.linux_js.inotify, &fds); - - if (fd < _glfw.linux_js.inotify) - count = _glfw.linux_js.inotify + 1; + if (_glfw.linjs.inotify > fd) + count = _glfw.linjs.inotify + 1; #endif for (;;) { + FD_ZERO(&fds); + FD_SET(fd, &fds); +#if defined(__linux__) + if (_glfw.linjs.inotify > 0) + FD_SET(_glfw.linjs.inotify, &fds); +#endif + if (timeout) { const long seconds = (long) *timeout; @@ -130,7 +135,9 @@ static int getWindowState(_GLFWwindow* window) result = state->state; } - XFree(state); + if (state) + XFree(state); + return result; } @@ -138,6 +145,9 @@ static int getWindowState(_GLFWwindow* window) // static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer) { + if (event->xany.window != _glfw.x11.helperWindowHandle) + return False; + return event->type == SelectionRequest || event->type == SelectionNotify || event->type == SelectionClear; @@ -154,6 +164,17 @@ static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointe event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS; } +// Returns whether it is a property event for the specified selection transfer +// +static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer) +{ + XEvent* notification = (XEvent*) pointer; + return event->type == PropertyNotify && + event->xproperty.state == PropertyNewValue && + event->xproperty.window == notification->xselection.requestor && + event->xproperty.atom == notification->xselection.property; +} + // Translates a GLFW standard cursor to a font cursor shape // static int translateCursorShape(int shape) @@ -203,7 +224,7 @@ static int translateKey(int scancode) if (scancode < 0 || scancode > 255) return GLFW_KEY_UNKNOWN; - return _glfw.x11.publicKeys[scancode]; + return _glfw.x11.keycodes[scancode]; } // Return the GLFW window corresponding to the specified X11 window @@ -343,6 +364,7 @@ static void updateWindowMode(_GLFWwindow* window) } // Enable compositor bypass + if (!window->x11.transparent) { const unsigned long value = 1; @@ -381,6 +403,7 @@ static void updateWindowMode(_GLFWwindow* window) } // Disable compositor bypass + if (!window->x11.transparent) { XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_BYPASS_COMPOSITOR); @@ -407,7 +430,12 @@ static char** parseUriList(char* text, int* count) continue; if (strncmp(line, prefix, strlen(prefix)) == 0) + { line += strlen(prefix); + // TODO: Validate hostname + while (*line != '/') + line++; + } (*count)++; @@ -434,6 +462,81 @@ static char** parseUriList(char* text, int* count) return paths; } +// Encode a Unicode code point to a UTF-8 stream +// Based on cutef8 by Jeff Bezanson (Public Domain) +// +static size_t encodeUTF8(char* s, unsigned int ch) +{ + size_t count = 0; + + if (ch < 0x80) + s[count++] = (char) ch; + else if (ch < 0x800) + { + s[count++] = (ch >> 6) | 0xc0; + s[count++] = (ch & 0x3f) | 0x80; + } + else if (ch < 0x10000) + { + s[count++] = (ch >> 12) | 0xe0; + s[count++] = ((ch >> 6) & 0x3f) | 0x80; + s[count++] = (ch & 0x3f) | 0x80; + } + else if (ch < 0x110000) + { + s[count++] = (ch >> 18) | 0xf0; + s[count++] = ((ch >> 12) & 0x3f) | 0x80; + s[count++] = ((ch >> 6) & 0x3f) | 0x80; + s[count++] = (ch & 0x3f) | 0x80; + } + + return count; +} + +// Decode a Unicode code point from a UTF-8 stream +// Based on cutef8 by Jeff Bezanson (Public Domain) +// +#if defined(X_HAVE_UTF8_STRING) +static unsigned int decodeUTF8(const char** s) +{ + unsigned int ch = 0, count = 0; + static const unsigned int offsets[] = + { + 0x00000000u, 0x00003080u, 0x000e2080u, + 0x03c82080u, 0xfa082080u, 0x82082080u + }; + + do + { + ch = (ch << 6) + (unsigned char) **s; + (*s)++; + count++; + } while ((**s & 0xc0) == 0x80); + + assert(count <= 6); + return ch - offsets[count - 1]; +} +#endif /*X_HAVE_UTF8_STRING*/ + +// Convert the specified Latin-1 string to UTF-8 +// +static char* convertLatin1toUTF8(const char* source) +{ + size_t size = 1; + const char* sp; + + for (sp = source; *sp; sp++) + size += (*sp & 0x80) ? 2 : 1; + + char* target = calloc(size, 1); + char* tp = target; + + for (sp = source; *sp; sp++) + tp += encodeUTF8(tp, *sp); + + return target; +} + // Centers the cursor over the window client area // static void centerCursor(_GLFWwindow* window) @@ -458,7 +561,10 @@ static void updateCursorImage(_GLFWwindow* window) XUndefineCursor(_glfw.x11.display, window->x11.handle); } else - XDefineCursor(_glfw.x11.display, window->x11.handle, _glfw.x11.cursor); + { + XDefineCursor(_glfw.x11.display, window->x11.handle, + _glfw.x11.hiddenCursorHandle); + } } // Create the X11 window (and its colormap) @@ -473,6 +579,8 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, visual, AllocNone); + window->x11.transparent = _glfwIsVisualTransparentX11(visual); + // Create the actual window { XSetWindowAttributes wa; @@ -514,26 +622,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, } if (!wndconfig->decorated) - { - struct - { - unsigned long flags; - unsigned long functions; - unsigned long decorations; - long input_mode; - unsigned long status; - } hints; - - hints.flags = 2; // Set decorations - hints.decorations = 0; // No decorations - - XChangeProperty(_glfw.x11.display, window->x11.handle, - _glfw.x11.MOTIF_WM_HINTS, - _glfw.x11.MOTIF_WM_HINTS, 32, - PropModeReplace, - (unsigned char*) &hints, - sizeof(hints) / sizeof(long)); - } + _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); if (_glfw.x11.NET_WM_STATE && !window->monitor) { @@ -553,6 +642,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, { states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; + window->x11.maximized = GLFW_TRUE; } } @@ -578,7 +668,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, // Declare our PID { - const pid_t pid = getpid(); + const long pid = getpid(); XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_PID, XA_CARDINAL, 32, @@ -614,22 +704,33 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, updateNormalHints(window, wndconfig->width, wndconfig->height); // Set ICCCM WM_CLASS property - // HACK: Until a mechanism for specifying the application name is added, the - // initial window title is used as the window class name - if (strlen(wndconfig->title)) { XClassHint* hint = XAllocClassHint(); - hint->res_name = (char*) wndconfig->title; - hint->res_class = (char*) wndconfig->title; + + if (strlen(_glfw.hints.init.x11.className) && + strlen(_glfw.hints.init.x11.classClass)) + { + hint->res_name = (char*) _glfw.hints.init.x11.className; + hint->res_class = (char*) _glfw.hints.init.x11.classClass; + } + else if (strlen(wndconfig->title)) + { + hint->res_name = (char*) wndconfig->title; + hint->res_class = (char*) wndconfig->title; + } + else + { + hint->res_name = (char*) "glfw-application"; + hint->res_class = (char*) "GLFW-Application"; + } XSetClassHint(_glfw.x11.display, window->x11.handle, hint); XFree(hint); } - if (_glfw.x11.XdndAware) + // Announce support for Xdnd (drag and drop) { - // Announce support for Xdnd (drag and drop) - const Atom version = 5; + const Atom version = _GLFW_XDND_VERSION; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); @@ -660,11 +761,15 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, static Atom writeTargetToProperty(const XSelectionRequestEvent* request) { int i; - const Atom formats[] = { _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, - XA_STRING }; + char* selectionString = NULL; + const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING }; const int formatCount = sizeof(formats) / sizeof(formats[0]); + if (request->selection == _glfw.x11.PRIMARY) + selectionString = _glfw.x11.primarySelectionString; + else + selectionString = _glfw.x11.clipboardString; + if (request->property == None) { // The requester is a legacy client (ICCCM section 2.2) @@ -679,7 +784,6 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) const Atom targets[] = { _glfw.x11.TARGETS, _glfw.x11.MULTIPLE, _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, XA_STRING }; XChangeProperty(_glfw.x11.display, @@ -724,8 +828,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) targets[i], 8, PropModeReplace, - (unsigned char*) _glfw.x11.clipboardString, - strlen(_glfw.x11.clipboardString)); + (unsigned char *) selectionString, + strlen(selectionString)); } else targets[i + 1] = None; @@ -776,8 +880,8 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) request->target, 8, PropModeReplace, - (unsigned char*) _glfw.x11.clipboardString, - strlen(_glfw.x11.clipboardString)); + (unsigned char *) selectionString, + strlen(selectionString)); return request->property; } @@ -790,8 +894,16 @@ static Atom writeTargetToProperty(const XSelectionRequestEvent* request) static void handleSelectionClear(XEvent* event) { - free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = NULL; + if (event->xselectionclear.selection == _glfw.x11.PRIMARY) + { + free(_glfw.x11.primarySelectionString); + _glfw.x11.primarySelectionString = NULL; + } + else + { + free(_glfw.x11.clipboardString); + _glfw.x11.clipboardString = NULL; + } } static void handleSelectionRequest(XEvent* event) @@ -812,49 +924,145 @@ static void handleSelectionRequest(XEvent* event) XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply); } -static void pushSelectionToManager(_GLFWwindow* window) +static const char* getSelectionString(Atom selection) { - XConvertSelection(_glfw.x11.display, - _glfw.x11.CLIPBOARD_MANAGER, - _glfw.x11.SAVE_TARGETS, - None, - window->x11.handle, - CurrentTime); + size_t i; + char** selectionString = NULL; + const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING }; + const size_t targetCount = sizeof(targets) / sizeof(targets[0]); - for (;;) + if (selection == _glfw.x11.PRIMARY) + selectionString = &_glfw.x11.primarySelectionString; + else + selectionString = &_glfw.x11.clipboardString; + + if (XGetSelectionOwner(_glfw.x11.display, selection) == + _glfw.x11.helperWindowHandle) { - XEvent event; + // Instead of doing a large number of X round-trips just to put this + // string into a window property and then read it back, just return it + return *selectionString; + } - while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) + free(*selectionString); + *selectionString = NULL; + + for (i = 0; i < targetCount; i++) + { + char* data; + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + XEvent notification, dummy; + + XConvertSelection(_glfw.x11.display, + selection, + targets[i], + _glfw.x11.GLFW_SELECTION, + _glfw.x11.helperWindowHandle, + CurrentTime); + + while (!XCheckTypedWindowEvent(_glfw.x11.display, + _glfw.x11.helperWindowHandle, + SelectionNotify, + ¬ification)) { - switch (event.type) + waitForEvent(NULL); + } + + if (notification.xselection.property == None) + continue; + + XCheckIfEvent(_glfw.x11.display, + &dummy, + isSelPropNewValueNotify, + (XPointer) ¬ification); + + XGetWindowProperty(_glfw.x11.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + + if (actualType == _glfw.x11.INCR) + { + size_t size = 1; + char* string = NULL; + + for (;;) { - case SelectionRequest: - handleSelectionRequest(&event); - break; - - case SelectionClear: - handleSelectionClear(&event); - break; - - case SelectionNotify: + while (!XCheckIfEvent(_glfw.x11.display, + &dummy, + isSelPropNewValueNotify, + (XPointer) ¬ification)) { - if (event.xselection.target == _glfw.x11.SAVE_TARGETS) + waitForEvent(NULL); + } + + XFree(data); + XGetWindowProperty(_glfw.x11.display, + notification.xselection.requestor, + notification.xselection.property, + 0, + LONG_MAX, + True, + AnyPropertyType, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + (unsigned char**) &data); + + if (itemCount) + { + size += itemCount; + string = realloc(string, size); + string[size - itemCount - 1] = '\0'; + strcat(string, data); + } + + if (!itemCount) + { + if (targets[i] == XA_STRING) { - // This means one of two things; either the selection was - // not owned, which means there is no clipboard manager, or - // the transfer to the clipboard manager has completed - // In either case, it means we are done here - return; + *selectionString = convertLatin1toUTF8(string); + free(string); } + else + *selectionString = string; break; } } } + else if (actualType == targets[i]) + { + if (targets[i] == XA_STRING) + *selectionString = convertLatin1toUTF8(data); + else + *selectionString = strdup(data); + } - waitForEvent(NULL); + XFree(data); + + if (*selectionString) + break; } + + if (!*selectionString) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "X11: Failed to convert selection to string"); + } + + return *selectionString; } // Make the specified window and its video mode active on its monitor @@ -895,7 +1103,7 @@ static GLFWbool acquireMonitor(_GLFWwindow* window) xpos, ypos, mode.width, mode.height); } - _glfwInputMonitorWindowChange(window->monitor, window); + _glfwInputMonitorWindow(window->monitor, window); return status; } @@ -906,7 +1114,7 @@ static void releaseMonitor(_GLFWwindow* window) if (window->monitor->window != window) return; - _glfwInputMonitorWindowChange(window->monitor, NULL); + _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeX11(window->monitor); _glfw.x11.saver.count--; @@ -922,31 +1130,6 @@ static void releaseMonitor(_GLFWwindow* window) } } -// Decode a Unicode code point from a UTF-8 stream -// Based on cutef8 by Jeff Bezanson (Public Domain) -// -#if defined(X_HAVE_UTF8_STRING) -static unsigned int decodeUTF8(const char** s) -{ - unsigned int ch = 0, count = 0; - static const unsigned int offsets[] = - { - 0x00000000u, 0x00003080u, 0x000e2080u, - 0x03c82080u, 0xfa082080u, 0x82082080u - }; - - do - { - ch = (ch << 6) + (unsigned char) **s; - (*s)++; - count++; - } while ((**s & 0xc0) == 0x80); - - assert(count <= 6); - return ch - offsets[count - 1]; -} -#endif /*X_HAVE_UTF8_STRING*/ - // Process the specified X event // static void processEvent(XEvent *event) @@ -967,19 +1150,64 @@ static void processEvent(XEvent *event) if (event->type == _glfw.x11.randr.eventBase + RRNotify) { XRRUpdateConfiguration(event); - _glfwInputMonitorChange(); + _glfwPollMonitorsX11(); return; } } - if (event->type != GenericEvent) + if (event->type == GenericEvent) { - window = findWindowByHandle(event->xany.window); - if (window == NULL) + if (_glfw.x11.xi.available) { - // This is an event for a window that has already been destroyed - return; + _GLFWwindow* window = _glfw.x11.disabledCursorWindow; + + if (window && + event->xcookie.extension == _glfw.x11.xi.majorOpcode && + XGetEventData(_glfw.x11.display, &event->xcookie) && + event->xcookie.evtype == XI_RawMotion) + { + XIRawEvent* re = event->xcookie.data; + if (re->valuators.mask_len) + { + const double* values = re->raw_values; + double xpos = window->virtualCursorPosX; + double ypos = window->virtualCursorPosY; + + if (XIMaskIsSet(re->valuators.mask, 0)) + { + xpos += *values; + values++; + } + + if (XIMaskIsSet(re->valuators.mask, 1)) + ypos += *values; + + _glfwInputCursorPos(window, xpos, ypos); + } + } + + XFreeEventData(_glfw.x11.display, &event->xcookie); } + + return; + } + + if (event->type == SelectionClear) + { + handleSelectionClear(event); + return; + } + else if (event->type == SelectionRequest) + { + handleSelectionRequest(event); + return; + } + + window = findWindowByHandle(event->xany.window); + if (window == NULL) + { + // This is an event for a window that has already been destroyed + return; } switch (event->type) @@ -993,17 +1221,16 @@ static void processEvent(XEvent *event) if (window->x11.ic) { // HACK: Ignore duplicate key press events generated by ibus - // Corresponding release events are filtered out by the - // GLFW key repeat logic - if (window->x11.lastKeyCode != keycode || - window->x11.lastKeyTime != event->xkey.time) + // These have the same timestamp as the original event + // Corresponding release events are filtered out + // implicitly by the GLFW key repeat logic + if (window->x11.lastKeyTime < event->xkey.time) { if (keycode) _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); - } - window->x11.lastKeyCode = keycode; - window->x11.lastKeyTime = event->xkey.time; + window->x11.lastKeyTime = event->xkey.time; + } if (!filtered) { @@ -1040,8 +1267,10 @@ static void processEvent(XEvent *event) count = XwcLookupString(window->x11.ic, &event->xkey, - buffer, sizeof(buffer) / sizeof(wchar_t), - NULL, &status); + buffer, + sizeof(buffer) / sizeof(wchar_t), + NULL, + &status); if (status == XBufferOverflow) { @@ -1214,7 +1443,8 @@ static void processEvent(XEvent *event) const int x = event->xmotion.x; const int y = event->xmotion.y; - if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY) + if (x != window->x11.warpCursorPosX || + y != window->x11.warpCursorPosY) { // The cursor was moved by something other than GLFW @@ -1222,6 +1452,8 @@ static void processEvent(XEvent *event) { if (_glfw.x11.disabledCursorWindow != window) return; + if (_glfw.x11.xi.available) + return; const int dx = x - window->x11.lastCursorPosX; const int dy = y - window->x11.lastCursorPosY; @@ -1291,14 +1523,15 @@ static void processEvent(XEvent *event) if (protocol == _glfw.x11.WM_DELETE_WINDOW) { - // The window manager was asked to close the window, for example by - // the user pressing a 'close' window decoration button + // The window manager was asked to close the window, for + // example by the user pressing a 'close' window decoration + // button _glfwInputWindowCloseRequest(window); } else if (protocol == _glfw.x11.NET_WM_PING) { - // The window manager is pinging the application to ensure it's - // still responding to events + // The window manager is pinging the application to ensure + // it's still responding to events XEvent reply = *event; reply.xclient.window = _glfw.x11.root; @@ -1312,44 +1545,121 @@ static void processEvent(XEvent *event) else if (event->xclient.message_type == _glfw.x11.XdndEnter) { // A drag operation has entered the window - // TODO: Check if UTF-8 string is supported by the source + unsigned long i, count; + Atom* formats = NULL; + const GLFWbool list = event->xclient.data.l[1] & 1; + + _glfw.x11.xdnd.source = event->xclient.data.l[0]; + _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24; + _glfw.x11.xdnd.format = None; + + if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) + return; + + if (list) + { + count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source, + _glfw.x11.XdndTypeList, + XA_ATOM, + (unsigned char**) &formats); + } + else + { + count = 3; + formats = (Atom*) event->xclient.data.l + 2; + } + + for (i = 0; i < count; i++) + { + if (formats[i] == _glfw.x11.text_uri_list) + { + _glfw.x11.xdnd.format = _glfw.x11.text_uri_list; + break; + } + } + + if (list && formats) + XFree(formats); } else if (event->xclient.message_type == _glfw.x11.XdndDrop) { - // The drag operation has finished dropping on - // the window, ask to convert it to a UTF-8 string - _glfw.x11.xdnd.source = event->xclient.data.l[0]; - XConvertSelection(_glfw.x11.display, - _glfw.x11.XdndSelection, - _glfw.x11.UTF8_STRING, - _glfw.x11.XdndSelection, - window->x11.handle, CurrentTime); + // The drag operation has finished by dropping on the window + Time time = CurrentTime; + + if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) + return; + + if (_glfw.x11.xdnd.format) + { + if (_glfw.x11.xdnd.version >= 1) + time = event->xclient.data.l[2]; + + // Request the chosen format from the source window + XConvertSelection(_glfw.x11.display, + _glfw.x11.XdndSelection, + _glfw.x11.xdnd.format, + _glfw.x11.XdndSelection, + window->x11.handle, + time); + } + else if (_glfw.x11.xdnd.version >= 2) + { + XEvent reply; + memset(&reply, 0, sizeof(reply)); + + reply.type = ClientMessage; + reply.xclient.window = _glfw.x11.xdnd.source; + reply.xclient.message_type = _glfw.x11.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->x11.handle; + reply.xclient.data.l[1] = 0; // The drag was rejected + reply.xclient.data.l[2] = None; + + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, + False, NoEventMask, &reply); + XFlush(_glfw.x11.display); + } } else if (event->xclient.message_type == _glfw.x11.XdndPosition) { // The drag operation has moved over the window - const int absX = (event->xclient.data.l[2] >> 16) & 0xFFFF; - const int absY = (event->xclient.data.l[2]) & 0xFFFF; - int x, y; + const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff; + const int yabs = (event->xclient.data.l[2]) & 0xffff; + Window dummy; + int xpos, ypos; - _glfwPlatformGetWindowPos(window, &x, &y); - _glfwInputCursorPos(window, absX - x, absY - y); + if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) + return; + + XTranslateCoordinates(_glfw.x11.display, + _glfw.x11.root, + window->x11.handle, + xabs, yabs, + &xpos, &ypos, + &dummy); + + _glfwInputCursorPos(window, xpos, ypos); - // Reply that we are ready to copy the dragged data XEvent reply; memset(&reply, 0, sizeof(reply)); reply.type = ClientMessage; - reply.xclient.window = event->xclient.data.l[0]; + reply.xclient.window = _glfw.x11.xdnd.source; reply.xclient.message_type = _glfw.x11.XdndStatus; reply.xclient.format = 32; reply.xclient.data.l[0] = window->x11.handle; - reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle reply.xclient.data.l[2] = 0; // Specify an empty rectangle reply.xclient.data.l[3] = 0; - reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; - XSendEvent(_glfw.x11.display, event->xclient.data.l[0], + if (_glfw.x11.xdnd.format) + { + // Reply that we are ready to copy the dragged data + reply.xclient.data.l[1] = 1; // Accept with no rectangle + if (_glfw.x11.xdnd.version >= 2) + reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; + } + + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, False, NoEventMask, &reply); XFlush(_glfw.x11.display); } @@ -1359,11 +1669,11 @@ static void processEvent(XEvent *event) case SelectionNotify: { - if (event->xselection.property) + if (event->xselection.property == _glfw.x11.XdndSelection) { // The converted data from the drag operation has arrived char* data; - const int result = + const unsigned long result = _glfwGetWindowPropertyX11(event->xselection.requestor, event->xselection.property, event->xselection.target, @@ -1381,23 +1691,26 @@ static void processEvent(XEvent *event) free(paths); } - XFree(data); + if (data) + XFree(data); - XEvent reply; - memset(&reply, 0, sizeof(reply)); + if (_glfw.x11.xdnd.version >= 2) + { + XEvent reply; + memset(&reply, 0, sizeof(reply)); - reply.type = ClientMessage; - reply.xclient.window = _glfw.x11.xdnd.source; - reply.xclient.message_type = _glfw.x11.XdndFinished; - reply.xclient.format = 32; - reply.xclient.data.l[0] = window->x11.handle; - reply.xclient.data.l[1] = result; - reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; + reply.type = ClientMessage; + reply.xclient.window = _glfw.x11.xdnd.source; + reply.xclient.message_type = _glfw.x11.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->x11.handle; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; - // Reply that all is well - XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, - False, NoEventMask, &reply); - XFlush(_glfw.x11.display); + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, + False, NoEventMask, &reply); + XFlush(_glfw.x11.display); + } } return; @@ -1454,41 +1767,43 @@ static void processEvent(XEvent *event) case PropertyNotify: { - if (event->xproperty.atom == _glfw.x11.WM_STATE && - event->xproperty.state == PropertyNewValue) + if (event->xproperty.state != PropertyNewValue) + return; + + if (event->xproperty.atom == _glfw.x11.WM_STATE) { const int state = getWindowState(window); - if (state == IconicState) + if (state != IconicState && state != NormalState) + return; + + const GLFWbool iconified = (state == IconicState); + if (window->x11.iconified != iconified) { if (window->monitor) - releaseMonitor(window); + { + if (iconified) + releaseMonitor(window); + else + acquireMonitor(window); + } - _glfwInputWindowIconify(window, GLFW_TRUE); + window->x11.iconified = iconified; + _glfwInputWindowIconify(window, iconified); } - else if (state == NormalState) + } + else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE) + { + const GLFWbool maximized = _glfwPlatformWindowMaximized(window); + if (window->x11.maximized != maximized) { - if (window->monitor) - acquireMonitor(window); - - _glfwInputWindowIconify(window, GLFW_FALSE); + window->x11.maximized = maximized; + _glfwInputWindowMaximize(window, maximized); } } return; } - case SelectionClear: - { - handleSelectionClear(event); - return; - } - - case SelectionRequest: - { - handleSelectionRequest(event); - return; - } - case DestroyNotify: return; } @@ -1524,12 +1839,66 @@ unsigned long _glfwGetWindowPropertyX11(Window window, &bytesAfter, value); - if (type != AnyPropertyType && actualType != type) - return 0; - return itemCount; } +GLFWbool _glfwIsVisualTransparentX11(Visual* visual) +{ + if (!_glfw.x11.xrender.available) + return GLFW_FALSE; + + XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual); + return pf && pf->direct.alphaMask; +} + +// Push contents of our selection to clipboard manager +// +void _glfwPushSelectionToManagerX11(void) +{ + XConvertSelection(_glfw.x11.display, + _glfw.x11.CLIPBOARD_MANAGER, + _glfw.x11.SAVE_TARGETS, + None, + _glfw.x11.helperWindowHandle, + CurrentTime); + + for (;;) + { + XEvent event; + + while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) + { + switch (event.type) + { + case SelectionRequest: + handleSelectionRequest(&event); + break; + + case SelectionClear: + handleSelectionClear(&event); + break; + + case SelectionNotify: + { + if (event.xselection.target == _glfw.x11.SAVE_TARGETS) + { + // This means one of two things; either the selection + // was not owned, which means there is no clipboard + // manager, or the transfer to the clipboard manager has + // completed + // In either case, it means we are done here + return; + } + + break; + } + } + } + + waitForEvent(NULL); + } +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -1543,27 +1912,34 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, Visual* visual; int depth; - if (ctxconfig->client == GLFW_NO_API) - { - visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); - depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); - } - else + if (ctxconfig->client != GLFW_NO_API) { if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { if (!_glfwInitGLX()) return GLFW_FALSE; - if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth)) + if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; - if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth)) + if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + } + } + + if (ctxconfig->client == GLFW_NO_API || + ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); + depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); } if (!createNativeWindow(window, wndconfig, visual, depth)) @@ -1576,11 +1952,16 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) return GLFW_FALSE; } - else + else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (window->monitor) @@ -1590,7 +1971,8 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!acquireMonitor(window)) return GLFW_FALSE; - centerCursor(window); + if (wndconfig->centerCursor) + centerCursor(window); } XFlush(_glfw.x11.display); @@ -1616,12 +1998,6 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) if (window->x11.handle) { - if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == - window->x11.handle) - { - pushSelectionToManager(window); - } - XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); XUnmapWindow(_glfw.x11.display, window->x11.handle); XDestroyWindow(_glfw.x11.display, window->x11.handle); @@ -1864,6 +2240,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, XFree(extents); } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.x11.contentScaleX; + if (yscale) + *yscale = _glfw.x11.contentScaleY; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { if (window->x11.overrideRedirect) @@ -1944,6 +2329,15 @@ void _glfwPlatformHideWindow(_GLFWwindow* window) XFlush(_glfw.x11.display); } +void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) +{ + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION, + 0, 1, 0); +} + void _glfwPlatformFocusWindow(_GLFWwindow* window) { if (_glfw.x11.NET_ACTIVE_WINDOW) @@ -1983,7 +2377,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) releaseMonitor(window); - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); updateNormalHints(window, width, height); updateWindowMode(window); @@ -2044,14 +2438,161 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) } } - XFree(states); + if (states) + XFree(states); + return maximized; } +int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) +{ + if (!window->x11.transparent) + return GLFW_FALSE; + + return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None; +} + +void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); +} + +void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) +{ + if (enabled) + { + XDeleteProperty(_glfw.x11.display, + window->x11.handle, + _glfw.x11.MOTIF_WM_HINTS); + } + else + { + struct + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; + } hints; + + hints.flags = 2; // Set decorations + hints.decorations = 0; // No decorations + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.MOTIF_WM_HINTS, + _glfw.x11.MOTIF_WM_HINTS, 32, + PropModeReplace, + (unsigned char*) &hints, + sizeof(hints) / sizeof(long)); + } +} + +void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) +{ + if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE) + return; + + if (_glfwPlatformWindowVisible(window)) + { + const Atom action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + action, + _glfw.x11.NET_WM_STATE_ABOVE, + 0, 1, 0); + } + else + { + Atom* states; + unsigned long i, count; + + count = _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + if (!states) + return; + + if (enabled) + { + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) + break; + } + + if (i == count) + { + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeAppend, + (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, + 1); + } + } + else + { + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) + { + states[i] = states[count - 1]; + count--; + } + } + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &states, count); + } + + XFree(states); + } + + XFlush(_glfw.x11.display); +} + +float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) +{ + float opacity = 1.f; + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx)) + { + CARD32* value = NULL; + + if (_glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_WINDOW_OPACITY, + XA_CARDINAL, + (unsigned char**) &value)) + { + opacity = (float) (*value / (double) 0xffffffffu); + } + + if (value) + XFree(value); + } + + return opacity; +} + +void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) +{ + const CARD32 value = (CARD32) (0xffffffffu * (double) opacity); + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*) &value, 1); +} + void _glfwPlatformPollEvents(void) { - _glfwPollJoystickEvents(); + _GLFWwindow* window; +#if defined(__linux__) + _glfwDetectJoystickConnectionLinux(); +#endif int count = XPending(_glfw.x11.display); while (count--) { @@ -2060,8 +2601,20 @@ void _glfwPlatformPollEvents(void) processEvent(&event); } - if (_glfw.x11.disabledCursorWindow) - centerCursor(_glfw.x11.disabledCursorWindow); + window = _glfw.x11.disabledCursorWindow; + if (window) + { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + + // NOTE: Re-center the cursor only if it has moved since the last call, + // to avoid breaking glfwWaitEvents with MotionNotify + if (window->x11.lastCursorPosX != width / 2 || + window->x11.lastCursorPosY != height / 2) + { + _glfwPlatformSetCursorPos(window, width / 2, height / 2); + } + } XFlush(_glfw.x11.display); } @@ -2088,15 +2641,14 @@ void _glfwPlatformWaitEventsTimeout(double timeout) void _glfwPlatformPostEmptyEvent(void) { XEvent event; - _GLFWwindow* window = _glfw.windowListHead; memset(&event, 0, sizeof(event)); event.type = ClientMessage; - event.xclient.window = window->x11.handle; + event.xclient.window = _glfw.x11.helperWindowHandle; event.xclient.format = 32; // Data is 32-bit longs event.xclient.message_type = _glfw.x11.NULL_; - XSendEvent(_glfw.x11.display, window->x11.handle, False, 0, &event); + XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event); XFlush(_glfw.x11.display); } @@ -2132,6 +2684,19 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { if (mode == GLFW_CURSOR_DISABLED) { + if (_glfw.x11.xi.available) + { + XIEventMask em; + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; + + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISetMask(mask, XI_RawMotion); + + XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); + } + _glfw.x11.disabledCursorWindow = window; _glfwPlatformGetCursorPos(window, &_glfw.x11.restoreCursorPosX, @@ -2140,10 +2705,24 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) XGrabPointer(_glfw.x11.display, window->x11.handle, True, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, - window->x11.handle, _glfw.x11.cursor, CurrentTime); + window->x11.handle, + _glfw.x11.hiddenCursorHandle, + CurrentTime); } else if (_glfw.x11.disabledCursorWindow == window) { + if (_glfw.x11.xi.available) + { + XIEventMask em; + unsigned char mask[] = { 0 }; + + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + + XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); + } + _glfw.x11.disabledCursorWindow = NULL; XUngrabPointer(_glfw.x11.display, CurrentTime); _glfwPlatformSetCursorPos(window, @@ -2155,34 +2734,32 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) XFlush(_glfw.x11.display); } -const char* _glfwPlatformGetKeyName(int key, int scancode) +const char* _glfwPlatformGetScancodeName(int scancode) { - KeySym keysym; - int extra; - if (!_glfw.x11.xkb.available) return NULL; - if (key != GLFW_KEY_UNKNOWN) - scancode = _glfw.x11.nativeKeys[key]; - - if (!_glfwIsPrintable(_glfw.x11.publicKeys[scancode])) - return NULL; - - keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0); + const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0); if (keysym == NoSymbol) - return NULL; - - XkbTranslateKeySym(_glfw.x11.display, &keysym, 0, - _glfw.x11.keyName, sizeof(_glfw.x11.keyName), - &extra); - - if (!strlen(_glfw.x11.keyName)) return NULL; + const long ch = _glfwKeySym2Unicode(keysym); + if (ch == -1) + return NULL; + + const size_t count = encodeUTF8(_glfw.x11.keyName, (unsigned int) ch); + if (count == 0) + return NULL; + + _glfw.x11.keyName[count] = '\0'; return _glfw.x11.keyName; } +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.x11.scancodes[key]; +} + int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) @@ -2223,108 +2800,48 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } } -void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +void _glfwPlatformSetClipboardString(const char* string) { free(_glfw.x11.clipboardString); _glfw.x11.clipboardString = strdup(string); XSetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD, - window->x11.handle, CurrentTime); + _glfw.x11.helperWindowHandle, + CurrentTime); if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != - window->x11.handle) + _glfw.x11.helperWindowHandle) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to become owner of clipboard selection"); } } -const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +const char* _glfwPlatformGetClipboardString(void) { - size_t i; - const Atom formats[] = { _glfw.x11.UTF8_STRING, - _glfw.x11.COMPOUND_STRING, - XA_STRING }; - const size_t formatCount = sizeof(formats) / sizeof(formats[0]); - - if (findWindowByHandle(XGetSelectionOwner(_glfw.x11.display, - _glfw.x11.CLIPBOARD))) - { - // Instead of doing a large number of X round-trips just to put this - // string into a window property and then read it back, just return it - return _glfw.x11.clipboardString; - } - - free(_glfw.x11.clipboardString); - _glfw.x11.clipboardString = NULL; - - for (i = 0; i < formatCount; i++) - { - char* data; - XEvent event; - - XConvertSelection(_glfw.x11.display, - _glfw.x11.CLIPBOARD, - formats[i], - _glfw.x11.GLFW_SELECTION, - window->x11.handle, CurrentTime); - - while (!XCheckTypedEvent(_glfw.x11.display, SelectionNotify, &event)) - waitForEvent(NULL); - - if (event.xselection.property == None) - continue; - - if (_glfwGetWindowPropertyX11(event.xselection.requestor, - event.xselection.property, - event.xselection.target, - (unsigned char**) &data)) - { - _glfw.x11.clipboardString = strdup(data); - } - - XFree(data); - - XDeleteProperty(_glfw.x11.display, - event.xselection.requestor, - event.xselection.property); - - if (_glfw.x11.clipboardString) - break; - } - - if (_glfw.x11.clipboardString == NULL) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "X11: Failed to convert clipboard to string"); - } - - return _glfw.x11.clipboardString; + return getSelectionString(_glfw.x11.CLIPBOARD); } -char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { - char** extensions; - - *count = 0; + if (!_glfw.vk.KHR_surface) + return; if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) { if (!_glfw.vk.KHR_xlib_surface) - return NULL; + return; } - extensions = calloc(2, sizeof(char*)); - extensions[0] = strdup("VK_KHR_surface"); + extensions[0] = "VK_KHR_surface"; + // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but + // not correctly implementing VK_KHR_xlib_surface if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) - extensions[1] = strdup("VK_KHR_xcb_surface"); + extensions[1] = "VK_KHR_xcb_surface"; else - extensions[1] = strdup("VK_KHR_xlib_surface"); - - *count = 2; - return extensions; + extensions[1] = "VK_KHR_xlib_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, @@ -2336,7 +2853,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) { - PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = + PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR + vkGetPhysicalDeviceXcbPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) @@ -2346,8 +2864,7 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, return GLFW_FALSE; } - xcb_connection_t* connection = - _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display); + xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); if (!connection) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -2362,7 +2879,8 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, } else { - PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR + vkGetPhysicalDeviceXlibPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) @@ -2390,8 +2908,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, VkXcbSurfaceCreateInfoKHR sci; PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; - xcb_connection_t* connection = - _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display); + xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); if (!connection) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -2473,3 +2990,29 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle) return window->x11.handle; } +GLFWAPI void glfwSetX11SelectionString(const char* string) +{ + _GLFW_REQUIRE_INIT(); + + free(_glfw.x11.primarySelectionString); + _glfw.x11.primarySelectionString = strdup(string); + + XSetSelectionOwner(_glfw.x11.display, + _glfw.x11.PRIMARY, + _glfw.x11.helperWindowHandle, + CurrentTime); + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) != + _glfw.x11.helperWindowHandle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to become owner of primary selection"); + } +} + +GLFWAPI const char* glfwGetX11SelectionString(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return getSelectionString(_glfw.x11.PRIMARY); +} + diff --git a/raylib/external/glfw/src/xkb_unicode.c b/raylib/external/glfw/src/xkb_unicode.c index 3223335..ecfdc2a 100644 --- a/raylib/external/glfw/src/xkb_unicode.c +++ b/raylib/external/glfw/src/xkb_unicode.c @@ -1,8 +1,8 @@ //======================================================================== -// GLFW 3.2 X11 - www.glfw.org +// GLFW 3.3 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard -// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -826,9 +826,59 @@ static const struct codepair { { 0x13bd, 0x0153 }, { 0x13be, 0x0178 }, { 0x20ac, 0x20ac }, - // Numeric keypad with numlock on + { 0xfe50, '`' }, + { 0xfe51, 0x00b4 }, + { 0xfe52, '^' }, + { 0xfe53, '~' }, + { 0xfe54, 0x00af }, + { 0xfe55, 0x02d8 }, + { 0xfe56, 0x02d9 }, + { 0xfe57, 0x00a8 }, + { 0xfe58, 0x02da }, + { 0xfe59, 0x02dd }, + { 0xfe5a, 0x02c7 }, + { 0xfe5b, 0x00b8 }, + { 0xfe5c, 0x02db }, + { 0xfe5d, 0x037a }, + { 0xfe5e, 0x309b }, + { 0xfe5f, 0x309c }, + { 0xfe63, '/' }, + { 0xfe64, 0x02bc }, + { 0xfe65, 0x02bd }, + { 0xfe66, 0x02f5 }, + { 0xfe67, 0x02f3 }, + { 0xfe68, 0x02cd }, + { 0xfe69, 0xa788 }, + { 0xfe6a, 0x02f7 }, + { 0xfe6e, ',' }, + { 0xfe6f, 0x00a4 }, + { 0xfe80, 'a' }, // XK_dead_a + { 0xfe81, 'A' }, // XK_dead_A + { 0xfe82, 'e' }, // XK_dead_e + { 0xfe83, 'E' }, // XK_dead_E + { 0xfe84, 'i' }, // XK_dead_i + { 0xfe85, 'I' }, // XK_dead_I + { 0xfe86, 'o' }, // XK_dead_o + { 0xfe87, 'O' }, // XK_dead_O + { 0xfe88, 'u' }, // XK_dead_u + { 0xfe89, 'U' }, // XK_dead_U + { 0xfe8a, 0x0259 }, + { 0xfe8b, 0x018f }, + { 0xfe8c, 0x00b5 }, + { 0xfe90, '_' }, + { 0xfe91, 0x02c8 }, + { 0xfe92, 0x02cc }, { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, - { 0xffbd /*XKB_KEY_KP_Equal*/, '=' }, + { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, { 0xffab /*XKB_KEY_KP_Add*/, '+' }, { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, @@ -844,7 +894,8 @@ static const struct codepair { { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, - { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 } + { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } }; diff --git a/raylib/external/glfw/src/xkb_unicode.h b/raylib/external/glfw/src/xkb_unicode.h index 688374d..f95e14f 100644 --- a/raylib/external/glfw/src/xkb_unicode.h +++ b/raylib/external/glfw/src/xkb_unicode.h @@ -1,5 +1,5 @@ //======================================================================== -// GLFW 3.2 Linux - www.glfw.org +// GLFW 3.3 Linux - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // @@ -24,10 +24,5 @@ // //======================================================================== -#ifndef _glfw3_xkb_unicode_h_ -#define _glfw3_xkb_unicode_h_ - - long _glfwKeySym2Unicode(unsigned int keysym); -#endif // _glfw3_xkb_unicode_h_ From 061469c44251767e366f477f5c93ceeb5a2a4df6 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 27 Nov 2017 19:33:05 +0100 Subject: [PATCH 10/48] Don't link to Xrandr Xinerama Xi Xxf86vm Xcursor, they are dlopen-ed now --- raylib/cgo_darwin.go | 8 +++++--- raylib/cgo_linux.go | 5 +++-- raylib/cgo_windows.go | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/raylib/cgo_darwin.go b/raylib/cgo_darwin.go index 7b4541c..22893cd 100644 --- a/raylib/cgo_darwin.go +++ b/raylib/cgo_darwin.go @@ -13,12 +13,14 @@ package raylib #include "external/glfw/src/cocoa_init.m" #include "external/glfw/src/cocoa_joystick.m" #include "external/glfw/src/cocoa_monitor.m" -#include "external/glfw/src/cocoa_window.m" #include "external/glfw/src/cocoa_time.c" -#include "external/glfw/src/posix_tls.c" +#include "external/glfw/src/cocoa_window.m" +#include "external/glfw/src/posix_thread.c" +#include "external/glfw/src/posix_time.c" #include "external/glfw/src/nsgl_context.m" +#include "external/glfw/src/osmesa_context.c" -#cgo darwin LDFLAGS: -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo +#cgo darwin LDFLAGS: -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo -framework CoreFoundation #cgo darwin CFLAGS: -x objective-c -Iexternal/glfw/include -D_GLFW_COCOA -D_GLFW_USE_CHDIR -D_GLFW_USE_MENUBAR -D_GLFW_USE_RETINA -Wno-deprecated-declarations -DPLATFORM_DESKTOP #cgo darwin,!noaudio LDFLAGS: -framework OpenAL diff --git a/raylib/cgo_linux.go b/raylib/cgo_linux.go index 9046e14..fa8f340 100644 --- a/raylib/cgo_linux.go +++ b/raylib/cgo_linux.go @@ -25,14 +25,15 @@ package raylib #endif #include "external/glfw/src/linux_joystick.c" +#include "external/glfw/src/posix_thread.c" #include "external/glfw/src/posix_time.c" -#include "external/glfw/src/posix_tls.c" #include "external/glfw/src/xkb_unicode.c" #include "external/glfw/src/egl_context.c" +#include "external/glfw/src/osmesa_context.c" #cgo linux CFLAGS: -Iexternal/glfw/include -DPLATFORM_DESKTOP -#cgo linux,!wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor +#cgo linux,!wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lX11 #cgo linux,wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon #cgo linux,!wayland CFLAGS: -D_GLFW_X11 diff --git a/raylib/cgo_windows.go b/raylib/cgo_windows.go index cc11a55..2d215f7 100644 --- a/raylib/cgo_windows.go +++ b/raylib/cgo_windows.go @@ -13,11 +13,12 @@ package raylib #include "external/glfw/src/win32_init.c" #include "external/glfw/src/win32_joystick.c" #include "external/glfw/src/win32_monitor.c" +#include "external/glfw/src/win32_thread.c" #include "external/glfw/src/win32_time.c" -#include "external/glfw/src/win32_tls.c" #include "external/glfw/src/win32_window.c" #include "external/glfw/src/wgl_context.c" #include "external/glfw/src/egl_context.c" +#include "external/glfw/src/osmesa_context.c" #cgo windows LDFLAGS: -lopengl32 -lgdi32 -lwinmm -lole32 #cgo windows CFLAGS: -D_GLFW_WIN32 -Iexternal/glfw/include -Iexternal/glfw/deps/mingw -DPLATFORM_DESKTOP From 0ac55844c97d0aa8275a97f42674ffbe570e9a02 Mon Sep 17 00:00:00 2001 From: Roland Singer Date: Tue, 28 Nov 2017 12:09:23 +0100 Subject: [PATCH 11/48] added wayland protocol generation script and updated wayland protocol files --- ...idle-inhibit-unstable-v1-client-protocol.c | 58 +++++ ...idle-inhibit-unstable-v1-client-protocol.h | 230 ++++++++++++++++++ ...-constraints-unstable-v1-client-protocol.c | 2 +- ...-constraints-unstable-v1-client-protocol.h | 6 +- ...tive-pointer-unstable-v1-client-protocol.c | 2 +- ...tive-pointer-unstable-v1-client-protocol.h | 4 +- raylib/external/glfw/src/wl_platform.h | 1 - .../external/scripts/glfw-generate-wayland.sh | 17 ++ 8 files changed, 312 insertions(+), 8 deletions(-) create mode 100644 raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c create mode 100644 raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.h create mode 100755 raylib/external/scripts/glfw-generate-wayland.sh diff --git a/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c b/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c new file mode 100644 index 0000000..506e3aa --- /dev/null +++ b/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c @@ -0,0 +1,58 @@ +/* Generated by wayland-scanner 1.14.0 */ + +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface zwp_idle_inhibitor_v1_interface; + +static const struct wl_interface *types[] = { + &zwp_idle_inhibitor_v1_interface, + &wl_surface_interface, +}; + +static const struct wl_message zwp_idle_inhibit_manager_v1_requests[] = { + { "destroy", "", types + 0 }, + { "create_inhibitor", "no", types + 0 }, +}; + +WL_EXPORT const struct wl_interface zwp_idle_inhibit_manager_v1_interface = { + "zwp_idle_inhibit_manager_v1", 1, + 2, zwp_idle_inhibit_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwp_idle_inhibitor_v1_requests[] = { + { "destroy", "", types + 0 }, +}; + +WL_EXPORT const struct wl_interface zwp_idle_inhibitor_v1_interface = { + "zwp_idle_inhibitor_v1", 1, + 1, zwp_idle_inhibitor_v1_requests, + 0, NULL, +}; + diff --git a/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.h b/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.h new file mode 100644 index 0000000..b53d706 --- /dev/null +++ b/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.h @@ -0,0 +1,230 @@ +/* Generated by wayland-scanner 1.14.0 */ + +#ifndef IDLE_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define IDLE_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_idle_inhibit_unstable_v1 The idle_inhibit_unstable_v1 protocol + * @section page_ifaces_idle_inhibit_unstable_v1 Interfaces + * - @subpage page_iface_zwp_idle_inhibit_manager_v1 - control behavior when display idles + * - @subpage page_iface_zwp_idle_inhibitor_v1 - context object for inhibiting idle behavior + * @section page_copyright_idle_inhibit_unstable_v1 Copyright + *
+ *
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_surface; +struct zwp_idle_inhibit_manager_v1; +struct zwp_idle_inhibitor_v1; + +/** + * @page page_iface_zwp_idle_inhibit_manager_v1 zwp_idle_inhibit_manager_v1 + * @section page_iface_zwp_idle_inhibit_manager_v1_desc Description + * + * This interface permits inhibiting the idle behavior such as screen + * blanking, locking, and screensaving. The client binds the idle manager + * globally, then creates idle-inhibitor objects for each surface. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + * @section page_iface_zwp_idle_inhibit_manager_v1_api API + * See @ref iface_zwp_idle_inhibit_manager_v1. + */ +/** + * @defgroup iface_zwp_idle_inhibit_manager_v1 The zwp_idle_inhibit_manager_v1 interface + * + * This interface permits inhibiting the idle behavior such as screen + * blanking, locking, and screensaving. The client binds the idle manager + * globally, then creates idle-inhibitor objects for each surface. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + */ +extern const struct wl_interface zwp_idle_inhibit_manager_v1_interface; +/** + * @page page_iface_zwp_idle_inhibitor_v1 zwp_idle_inhibitor_v1 + * @section page_iface_zwp_idle_inhibitor_v1_desc Description + * + * An idle inhibitor prevents the output that the associated surface is + * visible on from being set to a state where it is not visually usable due + * to lack of user interaction (e.g. blanked, dimmed, locked, set to power + * save, etc.) Any screensaver processes are also blocked from displaying. + * + * If the surface is destroyed, unmapped, becomes occluded, loses + * visibility, or otherwise becomes not visually relevant for the user, the + * idle inhibitor will not be honored by the compositor; if the surface + * subsequently regains visibility the inhibitor takes effect once again. + * Likewise, the inhibitor isn't honored if the system was already idled at + * the time the inhibitor was established, although if the system later + * de-idles and re-idles the inhibitor will take effect. + * @section page_iface_zwp_idle_inhibitor_v1_api API + * See @ref iface_zwp_idle_inhibitor_v1. + */ +/** + * @defgroup iface_zwp_idle_inhibitor_v1 The zwp_idle_inhibitor_v1 interface + * + * An idle inhibitor prevents the output that the associated surface is + * visible on from being set to a state where it is not visually usable due + * to lack of user interaction (e.g. blanked, dimmed, locked, set to power + * save, etc.) Any screensaver processes are also blocked from displaying. + * + * If the surface is destroyed, unmapped, becomes occluded, loses + * visibility, or otherwise becomes not visually relevant for the user, the + * idle inhibitor will not be honored by the compositor; if the surface + * subsequently regains visibility the inhibitor takes effect once again. + * Likewise, the inhibitor isn't honored if the system was already idled at + * the time the inhibitor was established, although if the system later + * de-idles and re-idles the inhibitor will take effect. + */ +extern const struct wl_interface zwp_idle_inhibitor_v1_interface; + +#define ZWP_IDLE_INHIBIT_MANAGER_V1_DESTROY 0 +#define ZWP_IDLE_INHIBIT_MANAGER_V1_CREATE_INHIBITOR 1 + + +/** + * @ingroup iface_zwp_idle_inhibit_manager_v1 + */ +#define ZWP_IDLE_INHIBIT_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_idle_inhibit_manager_v1 + */ +#define ZWP_IDLE_INHIBIT_MANAGER_V1_CREATE_INHIBITOR_SINCE_VERSION 1 + +/** @ingroup iface_zwp_idle_inhibit_manager_v1 */ +static inline void +zwp_idle_inhibit_manager_v1_set_user_data(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_idle_inhibit_manager_v1, user_data); +} + +/** @ingroup iface_zwp_idle_inhibit_manager_v1 */ +static inline void * +zwp_idle_inhibit_manager_v1_get_user_data(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_idle_inhibit_manager_v1); +} + +static inline uint32_t +zwp_idle_inhibit_manager_v1_get_version(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_idle_inhibit_manager_v1); +} + +/** + * @ingroup iface_zwp_idle_inhibit_manager_v1 + * + * Destroy the inhibit manager. + */ +static inline void +zwp_idle_inhibit_manager_v1_destroy(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_idle_inhibit_manager_v1, + ZWP_IDLE_INHIBIT_MANAGER_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) zwp_idle_inhibit_manager_v1); +} + +/** + * @ingroup iface_zwp_idle_inhibit_manager_v1 + * + * Create a new inhibitor object associated with the given surface. + */ +static inline struct zwp_idle_inhibitor_v1 * +zwp_idle_inhibit_manager_v1_create_inhibitor(struct zwp_idle_inhibit_manager_v1 *zwp_idle_inhibit_manager_v1, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_constructor((struct wl_proxy *) zwp_idle_inhibit_manager_v1, + ZWP_IDLE_INHIBIT_MANAGER_V1_CREATE_INHIBITOR, &zwp_idle_inhibitor_v1_interface, NULL, surface); + + return (struct zwp_idle_inhibitor_v1 *) id; +} + +#define ZWP_IDLE_INHIBITOR_V1_DESTROY 0 + + +/** + * @ingroup iface_zwp_idle_inhibitor_v1 + */ +#define ZWP_IDLE_INHIBITOR_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_zwp_idle_inhibitor_v1 */ +static inline void +zwp_idle_inhibitor_v1_set_user_data(struct zwp_idle_inhibitor_v1 *zwp_idle_inhibitor_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_idle_inhibitor_v1, user_data); +} + +/** @ingroup iface_zwp_idle_inhibitor_v1 */ +static inline void * +zwp_idle_inhibitor_v1_get_user_data(struct zwp_idle_inhibitor_v1 *zwp_idle_inhibitor_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_idle_inhibitor_v1); +} + +static inline uint32_t +zwp_idle_inhibitor_v1_get_version(struct zwp_idle_inhibitor_v1 *zwp_idle_inhibitor_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_idle_inhibitor_v1); +} + +/** + * @ingroup iface_zwp_idle_inhibitor_v1 + * + * Remove the inhibitor effect from the associated wl_surface. + */ +static inline void +zwp_idle_inhibitor_v1_destroy(struct zwp_idle_inhibitor_v1 *zwp_idle_inhibitor_v1) +{ + wl_proxy_marshal((struct wl_proxy *) zwp_idle_inhibitor_v1, + ZWP_IDLE_INHIBITOR_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy *) zwp_idle_inhibitor_v1); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c index 48bc432..9592de9 100644 --- a/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c +++ b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.12.0 */ +/* Generated by wayland-scanner 1.14.0 */ /* * Copyright © 2014 Jonas Ã…dahl diff --git a/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h index 91408c4..37e0c3a 100644 --- a/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h +++ b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.12.0 */ +/* Generated by wayland-scanner 1.14.0 */ #ifndef POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H #define POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H @@ -410,7 +410,7 @@ struct zwp_locked_pointer_v1_listener { }; /** - * @ingroup zwp_locked_pointer_v1_iface + * @ingroup iface_zwp_locked_pointer_v1 */ static inline int zwp_locked_pointer_v1_add_listener(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, @@ -549,7 +549,7 @@ struct zwp_confined_pointer_v1_listener { }; /** - * @ingroup zwp_confined_pointer_v1_iface + * @ingroup iface_zwp_confined_pointer_v1 */ static inline int zwp_confined_pointer_v1_add_listener(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1, diff --git a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c index 3483ca8..0220868 100644 --- a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c +++ b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.12.0 */ +/* Generated by wayland-scanner 1.14.0 */ /* * Copyright © 2014 Jonas Ã…dahl diff --git a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h index 4c3a2ef..7c74dbe 100644 --- a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h +++ b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.12.0 */ +/* Generated by wayland-scanner 1.14.0 */ #ifndef RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H #define RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H @@ -234,7 +234,7 @@ struct zwp_relative_pointer_v1_listener { }; /** - * @ingroup zwp_relative_pointer_v1_iface + * @ingroup iface_zwp_relative_pointer_v1 */ static inline int zwp_relative_pointer_v1_add_listener(struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, diff --git a/raylib/external/glfw/src/wl_platform.h b/raylib/external/glfw/src/wl_platform.h index 516c84b..6f017fa 100644 --- a/raylib/external/glfw/src/wl_platform.h +++ b/raylib/external/glfw/src/wl_platform.h @@ -237,4 +237,3 @@ typedef struct _GLFWcursorWayland void _glfwAddOutputWayland(uint32_t name, uint32_t version); - diff --git a/raylib/external/scripts/glfw-generate-wayland.sh b/raylib/external/scripts/glfw-generate-wayland.sh new file mode 100755 index 0000000..290ef93 --- /dev/null +++ b/raylib/external/scripts/glfw-generate-wayland.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +TMPDIR="/tmp" +GLGLFW_PATH="`pwd`/../glfw/src" + +cd $TMPDIR +git clone https://github.com/wayland-project/wayland-protocols +cd wayland-protocols + +wayland-scanner code ./unstable/pointer-constraints/pointer-constraints-unstable-v1.xml "$GLGLFW_PATH"/wayland-pointer-constraints-unstable-v1-client-protocol.c +wayland-scanner client-header ./unstable/pointer-constraints/pointer-constraints-unstable-v1.xml "$GLGLFW_PATH"/wayland-pointer-constraints-unstable-v1-client-protocol.h + +wayland-scanner code ./unstable/relative-pointer/relative-pointer-unstable-v1.xml "$GLGLFW_PATH"/wayland-relative-pointer-unstable-v1-client-protocol.c +wayland-scanner client-header ./unstable/relative-pointer/relative-pointer-unstable-v1.xml "$GLGLFW_PATH"/wayland-relative-pointer-unstable-v1-client-protocol.h + +wayland-scanner code ./unstable/idle-inhibit/idle-inhibit-unstable-v1.xml "$GLGLFW_PATH"/wayland-idle-inhibit-unstable-v1-client-protocol.c +wayland-scanner client-header ./unstable/idle-inhibit/idle-inhibit-unstable-v1.xml "$GLGLFW_PATH"/wayland-idle-inhibit-unstable-v1-client-protocol.h From 726ebb1718b71ad411cf6b0df1e05025bc6447a3 Mon Sep 17 00:00:00 2001 From: Roland Singer Date: Tue, 28 Nov 2017 12:33:35 +0100 Subject: [PATCH 12/48] fixed wayland cgo compilation bug --- ...land-relative-pointer-unstable-v1-client-protocol.c | 10 +++++----- raylib/external/scripts/glfw-generate-wayland.sh | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c index 0220868..1a77873 100644 --- a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c +++ b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c @@ -31,7 +31,7 @@ extern const struct wl_interface wl_pointer_interface; extern const struct wl_interface zwp_relative_pointer_v1_interface; -static const struct wl_interface *types[] = { +static const struct wl_interface *types2[] = { NULL, NULL, NULL, @@ -43,8 +43,8 @@ static const struct wl_interface *types[] = { }; static const struct wl_message zwp_relative_pointer_manager_v1_requests[] = { - { "destroy", "", types + 0 }, - { "get_relative_pointer", "no", types + 6 }, + { "destroy", "", types2 + 0 }, + { "get_relative_pointer", "no", types2 + 6 }, }; WL_EXPORT const struct wl_interface zwp_relative_pointer_manager_v1_interface = { @@ -54,11 +54,11 @@ WL_EXPORT const struct wl_interface zwp_relative_pointer_manager_v1_interface = }; static const struct wl_message zwp_relative_pointer_v1_requests[] = { - { "destroy", "", types + 0 }, + { "destroy", "", types2 + 0 }, }; static const struct wl_message zwp_relative_pointer_v1_events[] = { - { "relative_motion", "uuffff", types + 0 }, + { "relative_motion", "uuffff", types2 + 0 }, }; WL_EXPORT const struct wl_interface zwp_relative_pointer_v1_interface = { diff --git a/raylib/external/scripts/glfw-generate-wayland.sh b/raylib/external/scripts/glfw-generate-wayland.sh index 290ef93..d15567d 100755 --- a/raylib/external/scripts/glfw-generate-wayland.sh +++ b/raylib/external/scripts/glfw-generate-wayland.sh @@ -7,6 +7,10 @@ cd $TMPDIR git clone https://github.com/wayland-project/wayland-protocols cd wayland-protocols +rm -f "$GLGLFW_PATH"/wayland-pointer-constraints-unstable-v1-client-protocol.{h,c} +rm -f "$GLGLFW_PATH"/wayland-relative-pointer-unstable-v1-client-protocol.{h,c} +rm -f "$GLGLFW_PATH"/wayland-idle-inhibit-unstable-v1-client-protocol.{h,c} + wayland-scanner code ./unstable/pointer-constraints/pointer-constraints-unstable-v1.xml "$GLGLFW_PATH"/wayland-pointer-constraints-unstable-v1-client-protocol.c wayland-scanner client-header ./unstable/pointer-constraints/pointer-constraints-unstable-v1.xml "$GLGLFW_PATH"/wayland-pointer-constraints-unstable-v1-client-protocol.h @@ -15,3 +19,6 @@ wayland-scanner client-header ./unstable/relative-pointer/relative-pointer-unsta wayland-scanner code ./unstable/idle-inhibit/idle-inhibit-unstable-v1.xml "$GLGLFW_PATH"/wayland-idle-inhibit-unstable-v1-client-protocol.c wayland-scanner client-header ./unstable/idle-inhibit/idle-inhibit-unstable-v1.xml "$GLGLFW_PATH"/wayland-idle-inhibit-unstable-v1-client-protocol.h + +# Patch for cgo +sed -i "s|types|types2|g" "$GLGLFW_PATH"/wayland-relative-pointer-unstable-v1-client-protocol.c From 4eb7bface362a5a8e01c329b47c3cce36d5e0d6f Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 28 Nov 2017 13:41:25 +0100 Subject: [PATCH 13/48] Fix build for Wayland --- raylib/core.c | 4 +- raylib/platform_wayland.go | 93 -------------------------------------- 2 files changed, 2 insertions(+), 95 deletions(-) delete mode 100644 raylib/platform_wayland.go diff --git a/raylib/core.c b/raylib/core.c index 173b219..01febd6 100644 --- a/raylib/core.c +++ b/raylib/core.c @@ -716,7 +716,7 @@ int GetScreenHeight(void) void ShowCursor() { #if defined(PLATFORM_DESKTOP) - #if defined(__linux__) + #if defined(__linux__) && defined(_GLFW_X11) XUndefineCursor(glfwGetX11Display(), glfwGetX11Window(window)); #else glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); @@ -729,7 +729,7 @@ void ShowCursor() void HideCursor() { #if defined(PLATFORM_DESKTOP) - #if defined(__linux__) + #if defined(__linux__) && defined(_GLFW_X11) XColor col; const char nil[] = {0}; diff --git a/raylib/platform_wayland.go b/raylib/platform_wayland.go deleted file mode 100644 index 0582daf..0000000 --- a/raylib/platform_wayland.go +++ /dev/null @@ -1,93 +0,0 @@ -// +build wayland - -package raylib - -/* -#include "raylib.h" -#include -*/ -import "C" - -import ( - "os" - "unsafe" -) - -// InitWindow - Initialize Window and OpenGL Graphics -func InitWindow(width int32, height int32, t interface{}) { - cwidth := (C.int)(width) - cheight := (C.int)(height) - - title, ok := t.(string) - if ok { - ctitle := C.CString(title) - cptitle := unsafe.Pointer(ctitle) - defer C.free(cptitle) - C.InitWindow(cwidth, cheight, cptitle) - } -} - -// SetCallbackFunc - Sets callback function -func SetCallbackFunc(func(unsafe.Pointer)) { - return -} - -// ShowCursor - Shows cursor -func ShowCursor() { - return -} - -// HideCursor - Hides cursor -func HideCursor() { - return -} - -// IsCursorHidden - Returns true if cursor is not visible -func IsCursorHidden() bool { - return false -} - -// EnableCursor - Enables cursor -func EnableCursor() { - C.EnableCursor() -} - -// DisableCursor - Disables cursor -func DisableCursor() { - C.DisableCursor() -} - -// IsFileDropped - Check if a file have been dropped into window -func IsFileDropped() bool { - ret := C.IsFileDropped() - v := bool(int(ret) == 1) - return v -} - -// GetDroppedFiles - Retrieve dropped files into window -func GetDroppedFiles(count *int32) []string { - ccount := (*C.int)(unsafe.Pointer(count)) - ret := C.GetDroppedFiles(ccount) - - tmpslice := (*[1 << 24]*C.char)(unsafe.Pointer(ret))[:*count:*count] - gostrings := make([]string, *count) - for i, s := range tmpslice { - gostrings[i] = C.GoString(s) - } - - return gostrings -} - -// ClearDroppedFiles - Clear dropped files paths buffer -func ClearDroppedFiles() { - C.ClearDroppedFiles() -} - -// OpenAsset - Open asset -func OpenAsset(name string) (Asset, error) { - f, err := os.Open(name) - if err != nil { - return nil, err - } - return f, nil -} From 7af11fba36c0252b12436ad5de21490b4f74f2a9 Mon Sep 17 00:00:00 2001 From: Roland Singer Date: Tue, 28 Nov 2017 13:57:22 +0100 Subject: [PATCH 14/48] added missing include --- raylib/cgo_linux.go | 1 + 1 file changed, 1 insertion(+) diff --git a/raylib/cgo_linux.go b/raylib/cgo_linux.go index fa8f340..62ef966 100644 --- a/raylib/cgo_linux.go +++ b/raylib/cgo_linux.go @@ -16,6 +16,7 @@ package raylib #include "external/glfw/src/wl_window.c" #include "external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c" #include "external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c" +#include "external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c" #endif #ifdef _GLFW_X11 #include "external/glfw/src/x11_init.c" From ca8e3a675d265058d8c52f0a39cfb86f80cca752 Mon Sep 17 00:00:00 2001 From: Roland Singer Date: Tue, 28 Nov 2017 13:59:58 +0100 Subject: [PATCH 15/48] fixed cgo wayland patch --- ...idle-inhibit-unstable-v1-client-protocol.c | 8 +++--- ...-constraints-unstable-v1-client-protocol.c | 26 +++++++++---------- ...tive-pointer-unstable-v1-client-protocol.c | 10 +++---- .../external/scripts/glfw-generate-wayland.sh | 4 ++- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c b/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c index 506e3aa..04c32c2 100644 --- a/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c +++ b/raylib/external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c @@ -30,14 +30,14 @@ extern const struct wl_interface wl_surface_interface; extern const struct wl_interface zwp_idle_inhibitor_v1_interface; -static const struct wl_interface *types[] = { +static const struct wl_interface *wl_ii_types[] = { &zwp_idle_inhibitor_v1_interface, &wl_surface_interface, }; static const struct wl_message zwp_idle_inhibit_manager_v1_requests[] = { - { "destroy", "", types + 0 }, - { "create_inhibitor", "no", types + 0 }, + { "destroy", "", wl_ii_types + 0 }, + { "create_inhibitor", "no", wl_ii_types + 0 }, }; WL_EXPORT const struct wl_interface zwp_idle_inhibit_manager_v1_interface = { @@ -47,7 +47,7 @@ WL_EXPORT const struct wl_interface zwp_idle_inhibit_manager_v1_interface = { }; static const struct wl_message zwp_idle_inhibitor_v1_requests[] = { - { "destroy", "", types + 0 }, + { "destroy", "", wl_ii_types + 0 }, }; WL_EXPORT const struct wl_interface zwp_idle_inhibitor_v1_interface = { diff --git a/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c index 9592de9..c46152e 100644 --- a/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c +++ b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c @@ -34,7 +34,7 @@ extern const struct wl_interface wl_surface_interface; extern const struct wl_interface zwp_confined_pointer_v1_interface; extern const struct wl_interface zwp_locked_pointer_v1_interface; -static const struct wl_interface *types[] = { +static const struct wl_interface *wl_pc_types[] = { NULL, NULL, &zwp_locked_pointer_v1_interface, @@ -52,9 +52,9 @@ static const struct wl_interface *types[] = { }; static const struct wl_message zwp_pointer_constraints_v1_requests[] = { - { "destroy", "", types + 0 }, - { "lock_pointer", "noo?ou", types + 2 }, - { "confine_pointer", "noo?ou", types + 7 }, + { "destroy", "", wl_pc_types + 0 }, + { "lock_pointer", "noo?ou", wl_pc_types + 2 }, + { "confine_pointer", "noo?ou", wl_pc_types + 7 }, }; WL_EXPORT const struct wl_interface zwp_pointer_constraints_v1_interface = { @@ -64,14 +64,14 @@ WL_EXPORT const struct wl_interface zwp_pointer_constraints_v1_interface = { }; static const struct wl_message zwp_locked_pointer_v1_requests[] = { - { "destroy", "", types + 0 }, - { "set_cursor_position_hint", "ff", types + 0 }, - { "set_region", "?o", types + 12 }, + { "destroy", "", wl_pc_types + 0 }, + { "set_cursor_position_hint", "ff", wl_pc_types + 0 }, + { "set_region", "?o", wl_pc_types + 12 }, }; static const struct wl_message zwp_locked_pointer_v1_events[] = { - { "locked", "", types + 0 }, - { "unlocked", "", types + 0 }, + { "locked", "", wl_pc_types + 0 }, + { "unlocked", "", wl_pc_types + 0 }, }; WL_EXPORT const struct wl_interface zwp_locked_pointer_v1_interface = { @@ -81,13 +81,13 @@ WL_EXPORT const struct wl_interface zwp_locked_pointer_v1_interface = { }; static const struct wl_message zwp_confined_pointer_v1_requests[] = { - { "destroy", "", types + 0 }, - { "set_region", "?o", types + 13 }, + { "destroy", "", wl_pc_types + 0 }, + { "set_region", "?o", wl_pc_types + 13 }, }; static const struct wl_message zwp_confined_pointer_v1_events[] = { - { "confined", "", types + 0 }, - { "unconfined", "", types + 0 }, + { "confined", "", wl_pc_types + 0 }, + { "unconfined", "", wl_pc_types + 0 }, }; WL_EXPORT const struct wl_interface zwp_confined_pointer_v1_interface = { diff --git a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c index 1a77873..0852c9a 100644 --- a/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c +++ b/raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c @@ -31,7 +31,7 @@ extern const struct wl_interface wl_pointer_interface; extern const struct wl_interface zwp_relative_pointer_v1_interface; -static const struct wl_interface *types2[] = { +static const struct wl_interface *wl_rp_types[] = { NULL, NULL, NULL, @@ -43,8 +43,8 @@ static const struct wl_interface *types2[] = { }; static const struct wl_message zwp_relative_pointer_manager_v1_requests[] = { - { "destroy", "", types2 + 0 }, - { "get_relative_pointer", "no", types2 + 6 }, + { "destroy", "", wl_rp_types + 0 }, + { "get_relative_pointer", "no", wl_rp_types + 6 }, }; WL_EXPORT const struct wl_interface zwp_relative_pointer_manager_v1_interface = { @@ -54,11 +54,11 @@ WL_EXPORT const struct wl_interface zwp_relative_pointer_manager_v1_interface = }; static const struct wl_message zwp_relative_pointer_v1_requests[] = { - { "destroy", "", types2 + 0 }, + { "destroy", "", wl_rp_types + 0 }, }; static const struct wl_message zwp_relative_pointer_v1_events[] = { - { "relative_motion", "uuffff", types2 + 0 }, + { "relative_motion", "uuffff", wl_rp_types + 0 }, }; WL_EXPORT const struct wl_interface zwp_relative_pointer_v1_interface = { diff --git a/raylib/external/scripts/glfw-generate-wayland.sh b/raylib/external/scripts/glfw-generate-wayland.sh index d15567d..fd4ee07 100755 --- a/raylib/external/scripts/glfw-generate-wayland.sh +++ b/raylib/external/scripts/glfw-generate-wayland.sh @@ -21,4 +21,6 @@ wayland-scanner code ./unstable/idle-inhibit/idle-inhibit-unstable-v1.xml "$GLGL wayland-scanner client-header ./unstable/idle-inhibit/idle-inhibit-unstable-v1.xml "$GLGLFW_PATH"/wayland-idle-inhibit-unstable-v1-client-protocol.h # Patch for cgo -sed -i "s|types|types2|g" "$GLGLFW_PATH"/wayland-relative-pointer-unstable-v1-client-protocol.c +sed -i "s|types|wl_pc_types|g" "$GLGLFW_PATH"/wayland-pointer-constraints-unstable-v1-client-protocol.c +sed -i "s|types|wl_rp_types|g" "$GLGLFW_PATH"/wayland-relative-pointer-unstable-v1-client-protocol.c +sed -i "s|types|wl_ii_types|g" "$GLGLFW_PATH"/wayland-idle-inhibit-unstable-v1-client-protocol.c From bcb8a7b7811cb718f979dc2b7b5e3dae8cee17a1 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 28 Nov 2017 14:16:06 +0100 Subject: [PATCH 16/48] Platform for Wayland --- raylib/platform_desktop.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raylib/platform_desktop.go b/raylib/platform_desktop.go index 0528477..3994cd8 100644 --- a/raylib/platform_desktop.go +++ b/raylib/platform_desktop.go @@ -1,4 +1,4 @@ -// +build !android,!arm,!wayland +// +build !android,!arm package raylib From f2835b88ff5790d2bc9cd0113489ada0436d96b3 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 28 Nov 2017 15:00:52 +0100 Subject: [PATCH 17/48] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index abd3dae..b58d630 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,14 @@ On Windows, build binary in MSYS2 shell. go get -v -u github.com/gen2brain/raylib-go/raylib +### Build tags + +* `noaudio` - disables audio functions and doesn't link against OpenAL libraries +* `opengl21` - use OpenGL 2.1 backend (default is 3.3 on desktop) +* `opengl11` - use OpenGL 1.1 backend (pseudo OpenGL 1.1 style) +* `wayland` - builds against Wayland libraries +* `static` - link against OpenAL static libraries + ### Documentation Documentation on [GoDoc](https://godoc.org/github.com/gen2brain/raylib-go/raylib). Also check raylib [cheatsheet](http://www.raylib.com/cheatsheet/cheatsheet.html). From 01a664983d1686d2579c54b8312ac7199e1aa03e Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 28 Nov 2017 15:01:57 +0100 Subject: [PATCH 18/48] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b58d630..2e5a70d 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,10 @@ On Windows, build binary in MSYS2 shell. ### Build tags -* `noaudio` - disables audio functions and doesn't link against OpenAL libraries +* `noaudio` - disable audio functions and doesn't link against OpenAL libraries * `opengl21` - use OpenGL 2.1 backend (default is 3.3 on desktop) * `opengl11` - use OpenGL 1.1 backend (pseudo OpenGL 1.1 style) -* `wayland` - builds against Wayland libraries +* `wayland` - build against Wayland libraries * `static` - link against OpenAL static libraries ### Documentation From 1ec6d282057daa59267d00ffc7572974bb5ac168 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 28 Nov 2017 15:03:04 +0100 Subject: [PATCH 19/48] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2e5a70d..387cd86 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,11 @@ On Windows, build binary in MSYS2 shell. ### Build tags -* `noaudio` - disable audio functions and doesn't link against OpenAL libraries -* `opengl21` - use OpenGL 2.1 backend (default is 3.3 on desktop) -* `opengl11` - use OpenGL 1.1 backend (pseudo OpenGL 1.1 style) -* `wayland` - build against Wayland libraries -* `static` - link against OpenAL static libraries +* `noaudio` - disables audio functions and doesn't link against OpenAL libraries +* `opengl21` - uses OpenGL 2.1 backend (default is 3.3 on desktop) +* `opengl11` - uses OpenGL 1.1 backend (pseudo OpenGL 1.1 style) +* `wayland` - builds against Wayland libraries +* `static` - links against OpenAL static libraries ### Documentation From 8cd17bd7a985c2a53a0bbd17bd0b9c4be5da7e89 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 28 Nov 2017 19:11:48 +0100 Subject: [PATCH 20/48] Refactor rrem to functions --- rres/cmd/rrem/main.go | 587 ++++++++++++++++++++++++------------------ 1 file changed, 333 insertions(+), 254 deletions(-) diff --git a/rres/cmd/rrem/main.go b/rres/cmd/rrem/main.go index 0c7400d..443790e 100644 --- a/rres/cmd/rrem/main.go +++ b/rres/cmd/rrem/main.go @@ -35,7 +35,7 @@ import ( "github.com/klauspost/compress/flate" "github.com/moutend/go-wav" "github.com/pierrec/lz4" - "github.com/rootlch/encrypt" + xor "github.com/rootlch/encrypt" "github.com/ulikunitz/xz" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/xtea" @@ -62,25 +62,12 @@ func main() { os.Exit(1) } - switch *comp { - case rres.CompNone: - case rres.CompDeflate: - case rres.CompLZ4: - case rres.CompLZMA2: - case rres.CompBZIP2: - default: + if !validComp(*comp) { fmt.Printf("compression type %d not implemented\n", *comp) os.Exit(1) } - switch *enc { - case rres.CryptoNone: - case rres.CryptoXOR: - case rres.CryptoAES: - case rres.Crypto3DES: - case rres.CryptoBlowfish: - case rres.CryptoXTEA: - default: + if !validEnc(*enc) { fmt.Printf("encryption type %d not implemented\n", *enc) os.Exit(1) } @@ -102,8 +89,6 @@ func main() { os.Exit(1) } - defer rresFile.Close() - var headerFile *os.File if *header { headerFile, err = os.Create(fmt.Sprintf("%s.h", *base)) @@ -175,198 +160,27 @@ func main() { infoHeader.PartsCount = uint8(1) // Params - switch infoHeader.DataType { - case rres.TypeImage: - img, _, err := image.Decode(bytes.NewReader(data)) - if err != nil { - fmt.Printf("%s: %v\n", filename, err) - continue - } - - rect := img.Bounds() - width, height := rect.Dx(), rect.Dy() - - infoHeader.Param1 = uint32(width) - infoHeader.Param2 = uint32(height) - - switch img.ColorModel() { - case color.GrayModel: - infoHeader.Param3 = rres.ImUncompGrayscale - - i := image.NewGray(rect) - draw.Draw(i, rect, img, rect.Min, draw.Src) - data = i.Pix - case color.Gray16Model: - infoHeader.Param3 = rres.ImUncompGrayAlpha - - i := image.NewGray16(rect) - draw.Draw(i, rect, img, rect.Min, draw.Src) - data = i.Pix - default: - infoHeader.Param3 = rres.ImUncompR8g8b8a8 - - i := image.NewNRGBA(rect) - draw.Draw(i, rect, img, rect.Min, draw.Src) - data = i.Pix - } - - case rres.TypeWave: - a := &wav.File{} - err := wav.Unmarshal(data, a) - if err != nil { - fmt.Printf("%s: %v\n", filename, err) - } - - data, err = ioutil.ReadAll(a) - if err != nil { - fmt.Printf("%s: %v\n", filename, err) - } - - infoHeader.Param1 = uint32(a.Samples()) - infoHeader.Param2 = uint32(a.SamplesPerSec()) - infoHeader.Param3 = uint32(a.BitsPerSample()) - infoHeader.Param4 = uint32(a.Channels()) - case rres.TypeVorbis: - r, err := oggvorbis.NewReader(bytes.NewReader(data)) - if err != nil { - fmt.Printf("%s: %v\n", filename, err) - } - - d, _, err := oggvorbis.ReadAll(bytes.NewReader(data)) - if err != nil { - fmt.Printf("%s: %v\n", filename, err) - } - - // Convert []float32 to []byte - header := *(*reflect.SliceHeader)(unsafe.Pointer(&d)) - header.Len *= 4 - header.Cap *= 4 - data = *(*[]byte)(unsafe.Pointer(&header)) - - infoHeader.Param1 = uint32(r.SampleRate()) - infoHeader.Param2 = uint32(r.Bitrate().Nominal) - infoHeader.Param3 = uint32(r.Channels()) - case rres.TypeVertex: - // TODO https://github.com/sheenobu/go-obj - case rres.TypeText, rres.TypeRaw: + data, infoHeader.Param1, infoHeader.Param2, infoHeader.Param3, infoHeader.Param4, err = params(data, int(infoHeader.DataType)) + if err != nil { + fmt.Printf("%s: %v\n", filename, err) } // Encryption - switch infoHeader.CryptoType { - case rres.CryptoXOR: - c, err := encrypt.NewXor(*key) - if err != nil { - fmt.Printf("%v\n", err) - } - - b := c.Encode(data) - data = b - case rres.CryptoAES: - b, err := encryptAES([]byte(*key), data) - if err != nil { - fmt.Printf("%v\n", err) - } - - data = b - case rres.Crypto3DES: - b, err := encrypt3DES([]byte(*key), data) - if err != nil { - fmt.Printf("%v\n", err) - } - - data = b - case rres.CryptoBlowfish: - b, err := encryptBlowfish([]byte(*key), data) - if err != nil { - fmt.Printf("%v\n", err) - } - - data = b - case rres.CryptoXTEA: - b, err := encryptXTEA([]byte(*key), data) - if err != nil { - fmt.Printf("%v\n", err) - } - - data = b + data, err = encrypt([]byte(*key), data, int(infoHeader.CryptoType)) + if err != nil { + fmt.Printf("%v\n", err) } infoHeader.UncompSize = uint32(len(data)) // Compression - switch infoHeader.CompType { - case rres.CompNone: - infoHeader.DataSize = uint32(len(data)) - case rres.CompDeflate: - buf := new(bytes.Buffer) - - w, err := flate.NewWriter(buf, flate.BestCompression) - if err != nil { - fmt.Printf("%v\n", err) - } - - _, err = w.Write(data) - if err != nil { - fmt.Printf("%v\n", err) - } - - w.Close() - - infoHeader.DataSize = uint32(len(buf.Bytes())) - data = buf.Bytes() - case rres.CompLZ4: - buf := new(bytes.Buffer) - - w := lz4.NewWriter(buf) - if err != nil { - fmt.Printf("%v\n", err) - } - - _, err = w.Write(data) - if err != nil { - fmt.Printf("%v\n", err) - } - - w.Close() - - infoHeader.DataSize = uint32(len(buf.Bytes())) - data = buf.Bytes() - case rres.CompLZMA2: - buf := new(bytes.Buffer) - - w, err := xz.NewWriter(buf) - if err != nil { - fmt.Printf("%v\n", err) - } - - _, err = w.Write(data) - if err != nil { - fmt.Printf("%v\n", err) - } - - w.Close() - - infoHeader.DataSize = uint32(len(buf.Bytes())) - data = buf.Bytes() - case rres.CompBZIP2: - buf := new(bytes.Buffer) - - w, err := bzip2.NewWriter(buf, &bzip2.WriterConfig{Level: bzip2.BestCompression}) - if err != nil { - fmt.Printf("%v\n", err) - } - - _, err = w.Write(data) - if err != nil { - fmt.Printf("%v\n", err) - } - - w.Close() - - infoHeader.DataSize = uint32(len(buf.Bytes())) - data = buf.Bytes() + data, err = compress(data, int(infoHeader.CompType)) + if err != nil { + fmt.Printf("%v\n", err) } + infoHeader.DataSize = uint32(len(data)) + // Write resource info and parameters err = binary.Write(rresFile, binary.LittleEndian, &infoHeader) if err != nil { @@ -381,80 +195,47 @@ func main() { fmt.Printf("%v\n", err) } - var typeName string - switch infoHeader.DataType { - case rres.TypeImage: - typeName = "IMAGE" - case rres.TypeWave: - typeName = "WAVE" - case rres.TypeVorbis: - typeName = "VORBIS" - case rres.TypeText: - typeName = "TEXT" - default: - typeName = "RAW" - } - - fmt.Printf("%s %d // Embedded as %s\n", filepath.Base(filename), id, typeName) + fmt.Printf("%s %d // Embedded as %s\n", filepath.Base(filename), id, typeName(int(infoHeader.DataType))) if *header { - headerFile.Write([]byte(fmt.Sprintf("#define RES_%s 0x%08x\t\t// Embedded as %s\n", filepath.Base(filename), id, typeName))) + headerFile.Write([]byte(fmt.Sprintf("#define RES_%s 0x%08x\t\t// Embedded as %s\n", filepath.Base(filename), id, typeName(int(infoHeader.DataType))))) } } + err = rresFile.Sync() + if err != nil { + fmt.Printf("%v\n", err) + } + + err = rresFile.Close() + if err != nil { + fmt.Printf("%v\n", err) + } + // Generate C source if *source { - rresFile.Seek(0, 0) - d, err := ioutil.ReadAll(rresFile) + fname := fmt.Sprintf("%s.rres", *base) + file, err := os.Open(fname) if err != nil { - fmt.Printf("%v\n", err) + fmt.Printf("%s: %v\n", fname, err) } - _, err = sourceFile.Write([]byte("// This file has been automatically generated by rREM - raylib Resource Embedder\n\n")) + d, err := ioutil.ReadAll(file) if err != nil { - fmt.Printf("%v\n", err) + fmt.Printf("%s: %v\n", fname, err) } - _, err = sourceFile.Write([]byte(fmt.Sprintf("const unsigned char data[%d] = {\n ", len(d)))) - if err != nil { - fmt.Printf("%v\n", err) - } + file.Close() - blCounter := 0 // break line counter - - for i := 0; i < len(d)-1; i++ { - blCounter++ - - _, err = sourceFile.Write([]byte(fmt.Sprintf("0x%.2x, ", d[i]))) - if err != nil { - fmt.Printf("%v\n", err) - } - - if blCounter >= 24 { - _, err = sourceFile.Write([]byte("\n ")) - if err != nil { - fmt.Printf("%v\n", err) - } - - blCounter = 0 - } - } - - _, err = sourceFile.Write([]byte(fmt.Sprintf("0x%.2x };\n", d[len(d)-1]))) + err = genSource(sourceFile, d) if err != nil { fmt.Printf("%v\n", err) } } - // Generate bindata + // Generate Go bindata if *bin { - cfg := bindata.NewConfig() - cfg.NoCompress = true - cfg.Output = fmt.Sprintf("%s.go", *base) - cfg.Input = make([]bindata.InputConfig, 1) - cfg.Input[0] = bindata.InputConfig{Path: fmt.Sprintf("%s.rres", *base), Recursive: false} - - err := bindata.Translate(cfg) + err = genBin(*base) if err != nil { fmt.Printf("%v\n", err) } @@ -479,6 +260,304 @@ func fileType(f string) int { } } +// typeName returns data type name +func typeName(dataType int) string { + switch dataType { + case rres.TypeImage: + return "IMAGE" + case rres.TypeWave: + return "WAVE" + case rres.TypeVorbis: + return "VORBIS" + case rres.TypeText: + return "TEXT" + default: + return "RAW" + } +} + +// validEnc checks if encryption type is valid +func validEnc(encType int) bool { + switch encType { + case rres.CryptoNone, rres.CryptoXOR: + return true + case rres.CryptoAES, rres.Crypto3DES: + return true + case rres.CryptoBlowfish, rres.CryptoXTEA: + return true + } + return false +} + +// validComp checks if compression type is valid +func validComp(compType int) bool { + switch compType { + case rres.CompNone, rres.CompDeflate: + return true + case rres.CompLZ4, rres.CompLZMA2: + return true + case rres.CompBZIP2: + return true + } + return false +} + +// params returns data params +func params(data []byte, dataType int) (d []byte, p1, p2, p3, p4 uint32, err error) { + switch dataType { + case rres.TypeImage: + var img image.Image + + img, _, err = image.Decode(bytes.NewReader(data)) + if err != nil { + return + } + + rect := img.Bounds() + width, height := rect.Dx(), rect.Dy() + + p1 = uint32(width) + p2 = uint32(height) + + switch img.ColorModel() { + case color.GrayModel: + p3 = rres.ImUncompGrayscale + + i := image.NewGray(rect) + draw.Draw(i, rect, img, rect.Min, draw.Src) + d = i.Pix + return + case color.Gray16Model: + p3 = rres.ImUncompGrayAlpha + + i := image.NewGray16(rect) + draw.Draw(i, rect, img, rect.Min, draw.Src) + d = i.Pix + return + default: + p3 = rres.ImUncompR8g8b8a8 + + i := image.NewNRGBA(rect) + draw.Draw(i, rect, img, rect.Min, draw.Src) + d = i.Pix + return + } + + case rres.TypeWave: + a := &wav.File{} + err = wav.Unmarshal(data, a) + if err != nil { + return + } + + d, err = ioutil.ReadAll(a) + if err != nil { + return + } + + p1 = uint32(a.Samples()) + p2 = uint32(a.SamplesPerSec()) + p3 = uint32(a.BitsPerSample()) + p4 = uint32(a.Channels()) + return + case rres.TypeVorbis: + r, e := oggvorbis.NewReader(bytes.NewReader(data)) + if e != nil { + err = e + return + } + + o, _, e := oggvorbis.ReadAll(bytes.NewReader(data)) + if e != nil { + err = e + return + } + + // Convert []float32 to []byte + header := *(*reflect.SliceHeader)(unsafe.Pointer(&o)) + header.Len *= 4 + header.Cap *= 4 + d = *(*[]byte)(unsafe.Pointer(&header)) + + p1 = uint32(r.SampleRate()) + p2 = uint32(r.Bitrate().Nominal) + p3 = uint32(r.Channels()) + return + case rres.TypeVertex: + // TODO https://github.com/sheenobu/go-obj + case rres.TypeText, rres.TypeRaw: + } + + return +} + +// encrypt data +func encrypt(key, data []byte, cryptoType int) ([]byte, error) { + switch cryptoType { + case rres.CryptoXOR: + c, err := xor.NewXor(string(key)) + if err != nil { + return nil, err + } + + return c.Encode(data), nil + case rres.CryptoAES: + b, err := encryptAES(key, data) + if err != nil { + return nil, err + } + + return b, nil + case rres.Crypto3DES: + b, err := encrypt3DES(key, data) + if err != nil { + return nil, err + } + + return b, nil + case rres.CryptoBlowfish: + b, err := encryptBlowfish(key, data) + if err != nil { + return nil, err + } + + return b, nil + case rres.CryptoXTEA: + b, err := encryptXTEA(key, data) + if err != nil { + fmt.Printf("%v\n", err) + } + + return b, nil + default: + return data, nil + } +} + +// compress data +func compress(data []byte, compType int) ([]byte, error) { + switch compType { + case rres.CompNone: + return data, nil + case rres.CompDeflate: + buf := new(bytes.Buffer) + + w, err := flate.NewWriter(buf, flate.BestCompression) + if err != nil { + return nil, err + } + + _, err = w.Write(data) + if err != nil { + return nil, err + } + + w.Close() + + return buf.Bytes(), nil + case rres.CompLZ4: + buf := new(bytes.Buffer) + + w := lz4.NewWriter(buf) + + _, err := w.Write(data) + if err != nil { + return nil, err + } + + w.Close() + + return buf.Bytes(), nil + case rres.CompLZMA2: + buf := new(bytes.Buffer) + + w, err := xz.NewWriter(buf) + if err != nil { + return nil, err + } + + _, err = w.Write(data) + if err != nil { + return nil, err + } + + w.Close() + + return buf.Bytes(), nil + case rres.CompBZIP2: + buf := new(bytes.Buffer) + + w, err := bzip2.NewWriter(buf, &bzip2.WriterConfig{Level: bzip2.BestCompression}) + if err != nil { + return nil, err + } + + _, err = w.Write(data) + if err != nil { + return nil, err + } + + w.Close() + + return buf.Bytes(), nil + default: + return data, nil + } +} + +// genSource generates C source file +func genSource(w io.Writer, data []byte) error { + length := len(data) + + _, err := w.Write([]byte("// This file has been automatically generated by rREM - raylib Resource Embedder\n\n")) + if err != nil { + return err + } + + _, err = w.Write([]byte(fmt.Sprintf("const unsigned char data[%d] = {\n ", length))) + if err != nil { + fmt.Printf("%v\n", err) + } + + blCounter := 0 // break line counter + + for i := 0; i < len(data)-1; i++ { + blCounter++ + + _, err = w.Write([]byte(fmt.Sprintf("0x%.2x, ", data[i]))) + if err != nil { + return err + } + + if blCounter >= 24 { + _, err = w.Write([]byte("\n ")) + if err != nil { + return err + } + + blCounter = 0 + } + } + + _, err = w.Write([]byte(fmt.Sprintf("0x%.2x };\n", data[length-1]))) + if err != nil { + return err + } + + return nil +} + +//genBin generates go-bindata file +func genBin(base string) error { + cfg := bindata.NewConfig() + cfg.NoCompress = true + cfg.Output = fmt.Sprintf("%s.go", base) + cfg.Input = make([]bindata.InputConfig, 1) + cfg.Input[0] = bindata.InputConfig{Path: fmt.Sprintf("%s.rres", base), Recursive: false} + + return bindata.Translate(cfg) +} + // pad to block size func pad(src []byte, blockSize int) []byte { padding := blockSize - len(src)%blockSize From e9123b8b252ad86ac098b759e81b91e915ffbb52 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Wed, 29 Nov 2017 11:58:55 +0100 Subject: [PATCH 21/48] Refactor rres --- raylib/rres.go | 166 +++++++++++++++++++++++------------------- rres/cmd/rrem/main.go | 2 +- 2 files changed, 94 insertions(+), 74 deletions(-) diff --git a/raylib/rres.go b/raylib/rres.go index dffb904..db61f3b 100644 --- a/raylib/rres.go +++ b/raylib/rres.go @@ -68,84 +68,18 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) reader.Read(b) // Uncompress data - switch infoHeader.CompType { - case rres.CompNone: - data.Data = b - case rres.CompDeflate: - r := flate.NewReader(bytes.NewReader(b)) - - u := make([]byte, infoHeader.UncompSize) - r.Read(u) - - data.Data = u - - r.Close() - case rres.CompLZ4: - r := lz4.NewReader(bytes.NewReader(b)) - - u := make([]byte, infoHeader.UncompSize) - r.Read(u) - - data.Data = u - case rres.CompLZMA2: - r, err := xz.NewReader(bytes.NewReader(b)) - if err != nil { - TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) - } - - u := make([]byte, infoHeader.UncompSize) - r.Read(u) - - data.Data = u - case rres.CompBZIP2: - r, err := bzip2.NewReader(bytes.NewReader(b), &bzip2.ReaderConfig{}) - if err != nil { - TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) - } - - u := make([]byte, infoHeader.UncompSize) - r.Read(u) - - data.Data = u + data.Data, err = uncompress(b, int(infoHeader.CompType), int(infoHeader.UncompSize)) + if err != nil { + TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) } // Decrypt data - switch infoHeader.CryptoType { - case rres.CryptoXOR: - c, err := encrypt.NewXor(string(key)) - if err != nil { - TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) - } - - b := c.Encode(data.Data) - data.Data = b - case rres.CryptoAES: - b, err := decryptAES(key, data.Data) - if err != nil { - TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) - } - data.Data = b - case rres.Crypto3DES: - b, err := decrypt3DES(key, data.Data) - if err != nil { - TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) - } - data.Data = b - case rres.CryptoBlowfish: - b, err := decryptBlowfish(key, data.Data) - if err != nil { - TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) - } - data.Data = b - case rres.CryptoXTEA: - b, err := decryptXTEA(key, data.Data) - if err != nil { - TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) - } - data.Data = b + data.Data, err = decrypt(key, data.Data, int(infoHeader.CryptoType)) + if err != nil { + TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) } - if data.Data != nil { + if data.Data != nil && len(data.Data) == int(infoHeader.UncompSize) { TraceLog(LogInfo, "[ID %d] Resource data loaded successfully", infoHeader.ID) } } else { @@ -161,6 +95,92 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) return } +// decrypt data +func decrypt(key, data []byte, cryptoType int) ([]byte, error) { + switch cryptoType { + case rres.CryptoXOR: + c, err := encrypt.NewXor(string(key)) + if err != nil { + return nil, err + } + + b := c.Encode(data) + return b, nil + case rres.CryptoAES: + b, err := decryptAES(key, data) + if err != nil { + return nil, err + } + return b, nil + case rres.Crypto3DES: + b, err := decrypt3DES(key, data) + if err != nil { + return nil, err + } + return b, nil + case rres.CryptoBlowfish: + b, err := decryptBlowfish(key, data) + if err != nil { + return nil, err + } + return b, nil + case rres.CryptoXTEA: + b, err := decryptXTEA(key, data) + if err != nil { + return nil, err + } + return b, nil + default: + return data, nil + } +} + +// uncompress data +func uncompress(data []byte, compType, uncompSize int) ([]byte, error) { + switch compType { + case rres.CompNone: + return data, nil + case rres.CompDeflate: + r := flate.NewReader(bytes.NewReader(data)) + + u := make([]byte, uncompSize) + r.Read(u) + + r.Close() + + return u, nil + case rres.CompLZ4: + r := lz4.NewReader(bytes.NewReader(data)) + + u := make([]byte, uncompSize) + r.Read(u) + + return u, nil + case rres.CompLZMA2: + r, err := xz.NewReader(bytes.NewReader(data)) + if err != nil { + return nil, err + } + + u := make([]byte, uncompSize) + r.Read(u) + + return u, nil + case rres.CompBZIP2: + r, err := bzip2.NewReader(bytes.NewReader(data), &bzip2.ReaderConfig{}) + if err != nil { + return nil, err + } + + u := make([]byte, uncompSize) + r.Read(u) + + return u, nil + default: + return data, nil + } +} + // unpad func unpad(src []byte) ([]byte, error) { length := len(src) diff --git a/rres/cmd/rrem/main.go b/rres/cmd/rrem/main.go index 443790e..2b072b6 100644 --- a/rres/cmd/rrem/main.go +++ b/rres/cmd/rrem/main.go @@ -442,7 +442,7 @@ func compress(data []byte, compType int) ([]byte, error) { case rres.CompDeflate: buf := new(bytes.Buffer) - w, err := flate.NewWriter(buf, flate.BestCompression) + w, err := flate.NewWriter(buf, flate.DefaultCompression) if err != nil { return nil, err } From bfe9d6817fca681e08d707095074899eb5bbd5d3 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Wed, 29 Nov 2017 12:45:36 +0100 Subject: [PATCH 22/48] Fix deflate issue and make it default --- raylib/rres.go | 29 +++++++++++++++++++---------- rres/cmd/rrem/main.go | 2 +- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/raylib/rres.go b/raylib/rres.go index db61f3b..6e9f403 100644 --- a/raylib/rres.go +++ b/raylib/rres.go @@ -8,6 +8,7 @@ import ( "encoding/binary" "fmt" "io" + "io/ioutil" "os" "unsafe" @@ -68,7 +69,7 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) reader.Read(b) // Uncompress data - data.Data, err = uncompress(b, int(infoHeader.CompType), int(infoHeader.UncompSize)) + data.Data, err = uncompress(b, int(infoHeader.CompType)) if err != nil { TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) } @@ -136,15 +137,17 @@ func decrypt(key, data []byte, cryptoType int) ([]byte, error) { } // uncompress data -func uncompress(data []byte, compType, uncompSize int) ([]byte, error) { +func uncompress(data []byte, compType int) ([]byte, error) { switch compType { case rres.CompNone: return data, nil case rres.CompDeflate: r := flate.NewReader(bytes.NewReader(data)) - u := make([]byte, uncompSize) - r.Read(u) + u, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } r.Close() @@ -152,8 +155,10 @@ func uncompress(data []byte, compType, uncompSize int) ([]byte, error) { case rres.CompLZ4: r := lz4.NewReader(bytes.NewReader(data)) - u := make([]byte, uncompSize) - r.Read(u) + u, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } return u, nil case rres.CompLZMA2: @@ -162,8 +167,10 @@ func uncompress(data []byte, compType, uncompSize int) ([]byte, error) { return nil, err } - u := make([]byte, uncompSize) - r.Read(u) + u, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } return u, nil case rres.CompBZIP2: @@ -172,8 +179,10 @@ func uncompress(data []byte, compType, uncompSize int) ([]byte, error) { return nil, err } - u := make([]byte, uncompSize) - r.Read(u) + u, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } return u, nil default: diff --git a/rres/cmd/rrem/main.go b/rres/cmd/rrem/main.go index 2b072b6..d64aeff 100644 --- a/rres/cmd/rrem/main.go +++ b/rres/cmd/rrem/main.go @@ -49,7 +49,7 @@ func init() { func main() { base := flag.String("base", "data", "Resources file basename") - comp := flag.Int("comp", rres.CompLZMA2, "Compression type, 0=NONE, 1=DEFLATE, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2") + comp := flag.Int("comp", rres.CompDeflate, "Compression type, 0=NONE, 1=DEFLATE, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2") enc := flag.Int("enc", rres.CryptoNone, "Encryption type, 0=NONE, 1=XOR, 2=AES, 3=3DES, 4=Blowfish, 5=XTEA") key := flag.String("key", "", "Encryption key") header := flag.Bool("header", false, "Generate C header (.h file)") From 4564e18b328bceef0d8b87802b7ea2dd45739028 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 1 Dec 2017 16:12:23 +0100 Subject: [PATCH 23/48] Add rlib --- raylib/rres.go | 226 +-------------------- rres/cmd/rrem/main.go | 217 +------------------- rres/rlib/cgo/Makefile | 4 + rres/rlib/cgo/make.bash | 18 ++ rres/rlib/cgo/rlib.go | 69 +++++++ rres/rlib/rlib.go | 431 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 529 insertions(+), 436 deletions(-) create mode 100644 rres/rlib/cgo/Makefile create mode 100755 rres/rlib/cgo/make.bash create mode 100644 rres/rlib/cgo/rlib.go create mode 100644 rres/rlib/rlib.go diff --git a/raylib/rres.go b/raylib/rres.go index 6e9f403..aecbc68 100644 --- a/raylib/rres.go +++ b/raylib/rres.go @@ -1,26 +1,14 @@ package raylib import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/des" "encoding/binary" "fmt" "io" - "io/ioutil" "os" "unsafe" - "github.com/dsnet/compress/bzip2" - "github.com/klauspost/compress/flate" - "github.com/pierrec/lz4" - "github.com/rootlch/encrypt" - "github.com/ulikunitz/xz" - "golang.org/x/crypto/blowfish" - "golang.org/x/crypto/xtea" - "github.com/gen2brain/raylib-go/rres" + "github.com/gen2brain/raylib-go/rres/rlib" ) // LoadResource - Load resource from file by id @@ -68,14 +56,14 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) b := make([]byte, infoHeader.DataSize) reader.Read(b) - // Uncompress data - data.Data, err = uncompress(b, int(infoHeader.CompType)) + // Decompress data + data.Data, err = rlib.Decompress(b, int(infoHeader.CompType)) if err != nil { TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) } // Decrypt data - data.Data, err = decrypt(key, data.Data, int(infoHeader.CryptoType)) + data.Data, err = rlib.Decrypt(key, data.Data, int(infoHeader.CryptoType)) if err != nil { TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) } @@ -95,209 +83,3 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) return } - -// decrypt data -func decrypt(key, data []byte, cryptoType int) ([]byte, error) { - switch cryptoType { - case rres.CryptoXOR: - c, err := encrypt.NewXor(string(key)) - if err != nil { - return nil, err - } - - b := c.Encode(data) - return b, nil - case rres.CryptoAES: - b, err := decryptAES(key, data) - if err != nil { - return nil, err - } - return b, nil - case rres.Crypto3DES: - b, err := decrypt3DES(key, data) - if err != nil { - return nil, err - } - return b, nil - case rres.CryptoBlowfish: - b, err := decryptBlowfish(key, data) - if err != nil { - return nil, err - } - return b, nil - case rres.CryptoXTEA: - b, err := decryptXTEA(key, data) - if err != nil { - return nil, err - } - return b, nil - default: - return data, nil - } -} - -// uncompress data -func uncompress(data []byte, compType int) ([]byte, error) { - switch compType { - case rres.CompNone: - return data, nil - case rres.CompDeflate: - r := flate.NewReader(bytes.NewReader(data)) - - u, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - - r.Close() - - return u, nil - case rres.CompLZ4: - r := lz4.NewReader(bytes.NewReader(data)) - - u, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - - return u, nil - case rres.CompLZMA2: - r, err := xz.NewReader(bytes.NewReader(data)) - if err != nil { - return nil, err - } - - u, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - - return u, nil - case rres.CompBZIP2: - r, err := bzip2.NewReader(bytes.NewReader(data), &bzip2.ReaderConfig{}) - if err != nil { - return nil, err - } - - u, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - - return u, nil - default: - return data, nil - } -} - -// unpad -func unpad(src []byte) ([]byte, error) { - length := len(src) - unpadding := int(src[length-1]) - - if unpadding > length { - return nil, fmt.Errorf("unpad error. This can happen when incorrect encryption key is used.") - } - - return src[:(length - unpadding)], nil -} - -// decryptAES -func decryptAES(key, text []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - if (len(text) % aes.BlockSize) != 0 { - return nil, fmt.Errorf("blocksize must be multiple of decoded message length") - } - - iv := text[:aes.BlockSize] - msg := text[aes.BlockSize:] - - cfb := cipher.NewCFBDecrypter(block, iv) - cfb.XORKeyStream(msg, msg) - - unpadMsg, err := unpad(msg) - if err != nil { - return nil, err - } - - return unpadMsg, nil -} - -// decrypt3DES -func decrypt3DES(key, text []byte) ([]byte, error) { - block, err := des.NewCipher(key) - if err != nil { - return nil, err - } - - if (len(text) % des.BlockSize) != 0 { - return nil, fmt.Errorf("blocksize must be multiple of decoded message length") - } - - iv := text[:des.BlockSize] - msg := text[des.BlockSize:] - - cbc := cipher.NewCBCDecrypter(block, iv) - cbc.CryptBlocks(msg, msg) - - unpadMsg, err := unpad(msg) - if err != nil { - return nil, err - } - - return unpadMsg, nil -} - -// decryptBlowfish -func decryptBlowfish(key, text []byte) ([]byte, error) { - block, err := blowfish.NewCipher(key) - if err != nil { - return nil, err - } - - if (len(text) % blowfish.BlockSize) != 0 { - return nil, fmt.Errorf("blocksize must be multiple of decoded message length") - } - - iv := text[:blowfish.BlockSize] - msg := text[blowfish.BlockSize:] - - cbc := cipher.NewCBCDecrypter(block, iv) - cbc.CryptBlocks(msg, msg) - - unpadMsg, err := unpad(msg) - if err != nil { - return nil, err - } - - return unpadMsg, nil -} - -// decryptXTEA -func decryptXTEA(key, text []byte) ([]byte, error) { - block, err := xtea.NewCipher(key) - if err != nil { - return nil, err - } - - if (len(text) % xtea.BlockSize) != 0 { - return nil, fmt.Errorf("blocksize must be multiple of decoded message length") - } - - iv := text[:xtea.BlockSize] - msg := text[xtea.BlockSize:] - - cbc := cipher.NewCBCDecrypter(block, iv) - cbc.CryptBlocks(msg, msg) - - unpadMsg, err := unpad(msg) - if err != nil { - return nil, err - } - - return unpadMsg, nil -} diff --git a/rres/cmd/rrem/main.go b/rres/cmd/rrem/main.go index d64aeff..3e356e8 100644 --- a/rres/cmd/rrem/main.go +++ b/rres/cmd/rrem/main.go @@ -3,10 +3,6 @@ package main import ( "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/des" - "crypto/rand" "encoding/binary" "flag" "fmt" @@ -29,18 +25,12 @@ import ( _ "github.com/jbuchbinder/gopnm" _ "golang.org/x/image/bmp" - "github.com/dsnet/compress/bzip2" "github.com/jfreymuth/oggvorbis" "github.com/jteeuwen/go-bindata" - "github.com/klauspost/compress/flate" "github.com/moutend/go-wav" - "github.com/pierrec/lz4" - xor "github.com/rootlch/encrypt" - "github.com/ulikunitz/xz" - "golang.org/x/crypto/blowfish" - "golang.org/x/crypto/xtea" "github.com/gen2brain/raylib-go/rres" + "github.com/gen2brain/raylib-go/rres/rlib" ) func init() { @@ -166,7 +156,7 @@ func main() { } // Encryption - data, err = encrypt([]byte(*key), data, int(infoHeader.CryptoType)) + data, err = rlib.Encrypt([]byte(*key), data, int(infoHeader.CryptoType)) if err != nil { fmt.Printf("%v\n", err) } @@ -174,7 +164,7 @@ func main() { infoHeader.UncompSize = uint32(len(data)) // Compression - data, err = compress(data, int(infoHeader.CompType)) + data, err = rlib.Compress(data, int(infoHeader.CompType)) if err != nil { fmt.Printf("%v\n", err) } @@ -391,120 +381,6 @@ func params(data []byte, dataType int) (d []byte, p1, p2, p3, p4 uint32, err err return } -// encrypt data -func encrypt(key, data []byte, cryptoType int) ([]byte, error) { - switch cryptoType { - case rres.CryptoXOR: - c, err := xor.NewXor(string(key)) - if err != nil { - return nil, err - } - - return c.Encode(data), nil - case rres.CryptoAES: - b, err := encryptAES(key, data) - if err != nil { - return nil, err - } - - return b, nil - case rres.Crypto3DES: - b, err := encrypt3DES(key, data) - if err != nil { - return nil, err - } - - return b, nil - case rres.CryptoBlowfish: - b, err := encryptBlowfish(key, data) - if err != nil { - return nil, err - } - - return b, nil - case rres.CryptoXTEA: - b, err := encryptXTEA(key, data) - if err != nil { - fmt.Printf("%v\n", err) - } - - return b, nil - default: - return data, nil - } -} - -// compress data -func compress(data []byte, compType int) ([]byte, error) { - switch compType { - case rres.CompNone: - return data, nil - case rres.CompDeflate: - buf := new(bytes.Buffer) - - w, err := flate.NewWriter(buf, flate.DefaultCompression) - if err != nil { - return nil, err - } - - _, err = w.Write(data) - if err != nil { - return nil, err - } - - w.Close() - - return buf.Bytes(), nil - case rres.CompLZ4: - buf := new(bytes.Buffer) - - w := lz4.NewWriter(buf) - - _, err := w.Write(data) - if err != nil { - return nil, err - } - - w.Close() - - return buf.Bytes(), nil - case rres.CompLZMA2: - buf := new(bytes.Buffer) - - w, err := xz.NewWriter(buf) - if err != nil { - return nil, err - } - - _, err = w.Write(data) - if err != nil { - return nil, err - } - - w.Close() - - return buf.Bytes(), nil - case rres.CompBZIP2: - buf := new(bytes.Buffer) - - w, err := bzip2.NewWriter(buf, &bzip2.WriterConfig{Level: bzip2.BestCompression}) - if err != nil { - return nil, err - } - - _, err = w.Write(data) - if err != nil { - return nil, err - } - - w.Close() - - return buf.Bytes(), nil - default: - return data, nil - } -} - // genSource generates C source file func genSource(w io.Writer, data []byte) error { length := len(data) @@ -557,90 +433,3 @@ func genBin(base string) error { return bindata.Translate(cfg) } - -// pad to block size -func pad(src []byte, blockSize int) []byte { - padding := blockSize - len(src)%blockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(src, padtext...) -} - -// encryptAES -func encryptAES(key, text []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - msg := pad(text, aes.BlockSize) - ciphertext := make([]byte, aes.BlockSize+len(msg)) - iv := ciphertext[:aes.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return nil, err - } - - cfb := cipher.NewCFBEncrypter(block, iv) - cfb.XORKeyStream(ciphertext[aes.BlockSize:], msg) - - return ciphertext, nil -} - -// encrypt3DES -func encrypt3DES(key, text []byte) ([]byte, error) { - block, err := des.NewTripleDESCipher(key) - if err != nil { - return nil, err - } - - msg := pad(text, des.BlockSize) - ciphertext := make([]byte, des.BlockSize+len(msg)) - iv := ciphertext[:des.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return nil, err - } - - cbc := cipher.NewCBCEncrypter(block, iv) - cbc.CryptBlocks(ciphertext[des.BlockSize:], msg) - - return ciphertext, nil -} - -// encryptBlowfish -func encryptBlowfish(key, text []byte) ([]byte, error) { - block, err := blowfish.NewCipher(key) - if err != nil { - return nil, err - } - - msg := pad(text, blowfish.BlockSize) - ciphertext := make([]byte, blowfish.BlockSize+len(msg)) - iv := ciphertext[:blowfish.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return nil, err - } - - cbc := cipher.NewCBCEncrypter(block, iv) - cbc.CryptBlocks(ciphertext[blowfish.BlockSize:], msg) - - return ciphertext, nil -} - -// encryptXTEA -func encryptXTEA(key, text []byte) ([]byte, error) { - block, err := xtea.NewCipher(key) - if err != nil { - return nil, err - } - - msg := pad(text, xtea.BlockSize) - ciphertext := make([]byte, xtea.BlockSize+len(msg)) - iv := ciphertext[:xtea.BlockSize] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return nil, err - } - - cbc := cipher.NewCBCEncrypter(block, iv) - cbc.CryptBlocks(ciphertext[xtea.BlockSize:], msg) - - return ciphertext, nil -} diff --git a/rres/rlib/cgo/Makefile b/rres/rlib/cgo/Makefile new file mode 100644 index 0000000..d7e1300 --- /dev/null +++ b/rres/rlib/cgo/Makefile @@ -0,0 +1,4 @@ +all: build + +build: + go build -buildmode=c-archive -o rlib.a diff --git a/rres/rlib/cgo/make.bash b/rres/rlib/cgo/make.bash new file mode 100755 index 0000000..84ba576 --- /dev/null +++ b/rres/rlib/cgo/make.bash @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +CHROOT="/usr/x86_64-pc-linux-gnu-static" +MINGW="/usr/i686-w64-mingw32" +RPI="/usr/armv6j-hardfloat-linux-gnueabi" +ANDROID="/opt/android-toolchain-arm7" + +CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -buildmode=c-archive -o release/linux/rlib.a -ldflags "-s -w" + +CC="i686-w64-mingw32-gcc" CXX="i686-w64-mingw32-g++" \ +CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -buildmode=c-archive -o release/win32/rlib.a -ldflags "-s -w" + +CC="armv6j-hardfloat-linux-gnueabi-gcc" CXX="armv6j-hardfloat-linux-gnueabi-g++" \ +CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -buildmode=c-archive -o release/rpi/rlib.a -ldflags "-s -w" + +PATH="$PATH:$ANDROID/bin" \ +CC="arm-linux-androideabi-gcc" CXX="arm-linux-androideabi-g++" \ +CGO_ENABLED=1 GOOS=android GOARCH=arm go build -buildmode=c-shared -o release/android/armeabi-v7a/rlib.so -ldflags "-s -w" diff --git a/rres/rlib/cgo/rlib.go b/rres/rlib/cgo/rlib.go new file mode 100644 index 0000000..b7a876c --- /dev/null +++ b/rres/rlib/cgo/rlib.go @@ -0,0 +1,69 @@ +package main + +import "C" + +import ( + "unsafe" + + "github.com/gen2brain/raylib-go/rres/rlib" +) + +//export Compress +func Compress(compType C.int, data *C.uchar, uncompSize C.ulong, outCompSize *C.ulong) *C.uchar { + d := C.GoBytes(unsafe.Pointer(data), C.int(uncompSize)) + + c, err := rlib.Compress(d, int(compType)) + if err != nil { + return nil + } + + *outCompSize = C.ulong(len(c)) + return (*C.uchar)(unsafe.Pointer(&c[0])) +} + +//export Decompress +func Decompress(compType C.int, data *C.uchar, uncompSize C.ulong, compSize C.ulong) *C.uchar { + d := C.GoBytes(unsafe.Pointer(data), C.int(compSize)) + + c, err := rlib.Decompress(d, int(compType)) + if err != nil { + return nil + } + + if len(c) != int(uncompSize) { + return nil + } + + return (*C.uchar)(unsafe.Pointer(&c[0])) +} + +//export Encrypt +func Encrypt(cryptoType C.int, key *C.char, data *C.uchar, dataSize C.ulong, outDataSize *C.ulong) *C.uchar { + k := []byte(C.GoString(key)) + d := C.GoBytes(unsafe.Pointer(data), C.int(dataSize)) + + c, err := rlib.Encrypt(k, d, int(cryptoType)) + if err != nil { + return nil + } + + *outDataSize = C.ulong(len(c)) + return (*C.uchar)(unsafe.Pointer(&c[0])) +} + +//export Decrypt +func Decrypt(cryptoType C.int, key *C.char, data *C.uchar, dataSize C.ulong) *C.uchar { + k := []byte(C.GoString(key)) + d := C.GoBytes(unsafe.Pointer(data), C.int(dataSize)) + + c, err := rlib.Decrypt(k, d, int(cryptoType)) + if err != nil { + return nil + } + + return (*C.uchar)(unsafe.Pointer(&c[0])) +} + +// We need the main() so we can compile as C library +func main() { +} diff --git a/rres/rlib/rlib.go b/rres/rlib/rlib.go new file mode 100644 index 0000000..5b2a3b6 --- /dev/null +++ b/rres/rlib/rlib.go @@ -0,0 +1,431 @@ +// Package rlib provides Encrypt/Decrypt and Compress/Uncompress functions +package rlib + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rand" + "fmt" + "io" + "io/ioutil" + + "golang.org/x/crypto/blowfish" + "golang.org/x/crypto/xtea" + + "github.com/dsnet/compress/bzip2" + "github.com/klauspost/compress/flate" + "github.com/pierrec/lz4" + xor "github.com/rootlch/encrypt" + "github.com/ulikunitz/xz" + + "github.com/gen2brain/raylib-go/rres" +) + +// Encrypt data +func Encrypt(key, data []byte, cryptoType int) ([]byte, error) { + switch cryptoType { + case rres.CryptoXOR: + c, err := xor.NewXor(string(key)) + if err != nil { + return nil, err + } + + return c.Encode(data), nil + case rres.CryptoAES: + b, err := encryptAES(key, data) + if err != nil { + return nil, err + } + + return b, nil + case rres.Crypto3DES: + b, err := encrypt3DES(key, data) + if err != nil { + return nil, err + } + + return b, nil + case rres.CryptoBlowfish: + b, err := encryptBlowfish(key, data) + if err != nil { + return nil, err + } + + return b, nil + case rres.CryptoXTEA: + b, err := encryptXTEA(key, data) + if err != nil { + fmt.Printf("%v\n", err) + } + + return b, nil + default: + return data, nil + } +} + +// Decrypt data +func Decrypt(key, data []byte, cryptoType int) ([]byte, error) { + switch cryptoType { + case rres.CryptoXOR: + c, err := xor.NewXor(string(key)) + if err != nil { + return nil, err + } + + b := c.Encode(data) + return b, nil + case rres.CryptoAES: + b, err := decryptAES(key, data) + if err != nil { + return nil, err + } + return b, nil + case rres.Crypto3DES: + b, err := decrypt3DES(key, data) + if err != nil { + return nil, err + } + return b, nil + case rres.CryptoBlowfish: + b, err := decryptBlowfish(key, data) + if err != nil { + return nil, err + } + return b, nil + case rres.CryptoXTEA: + b, err := decryptXTEA(key, data) + if err != nil { + return nil, err + } + return b, nil + default: + return data, nil + } +} + +// Compress data +func Compress(data []byte, compType int) ([]byte, error) { + switch compType { + case rres.CompNone: + return data, nil + case rres.CompDeflate: + buf := new(bytes.Buffer) + + w, err := flate.NewWriter(buf, flate.DefaultCompression) + if err != nil { + return nil, err + } + + _, err = w.Write(data) + if err != nil { + return nil, err + } + + w.Close() + + return buf.Bytes(), nil + case rres.CompLZ4: + buf := new(bytes.Buffer) + + w := lz4.NewWriter(buf) + + _, err := w.Write(data) + if err != nil { + return nil, err + } + + w.Close() + + return buf.Bytes(), nil + case rres.CompLZMA2: + buf := new(bytes.Buffer) + + w, err := xz.NewWriter(buf) + if err != nil { + return nil, err + } + + _, err = w.Write(data) + if err != nil { + return nil, err + } + + w.Close() + + return buf.Bytes(), nil + case rres.CompBZIP2: + buf := new(bytes.Buffer) + + w, err := bzip2.NewWriter(buf, &bzip2.WriterConfig{Level: bzip2.BestCompression}) + if err != nil { + return nil, err + } + + _, err = w.Write(data) + if err != nil { + return nil, err + } + + w.Close() + + return buf.Bytes(), nil + default: + return data, nil + } +} + +// Decompress data +func Decompress(data []byte, compType int) ([]byte, error) { + switch compType { + case rres.CompNone: + return data, nil + case rres.CompDeflate: + r := flate.NewReader(bytes.NewReader(data)) + + u, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + r.Close() + + return u, nil + case rres.CompLZ4: + r := lz4.NewReader(bytes.NewReader(data)) + + u, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + return u, nil + case rres.CompLZMA2: + r, err := xz.NewReader(bytes.NewReader(data)) + if err != nil { + return nil, err + } + + u, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + return u, nil + case rres.CompBZIP2: + r, err := bzip2.NewReader(bytes.NewReader(data), &bzip2.ReaderConfig{}) + if err != nil { + return nil, err + } + + u, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + return u, nil + default: + return data, nil + } +} + +// pad to block size +func pad(src []byte, blockSize int) []byte { + padding := blockSize - len(src)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(src, padtext...) +} + +// unpad +func unpad(src []byte) ([]byte, error) { + length := len(src) + unpadding := int(src[length-1]) + + if unpadding > length { + return nil, fmt.Errorf("unpad error. This can happen when incorrect encryption key is used.") + } + + return src[:(length - unpadding)], nil +} + +// encryptAES +func encryptAES(key, text []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + msg := pad(text, aes.BlockSize) + ciphertext := make([]byte, aes.BlockSize+len(msg)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + + cfb := cipher.NewCFBEncrypter(block, iv) + cfb.XORKeyStream(ciphertext[aes.BlockSize:], msg) + + return ciphertext, nil +} + +// decryptAES +func decryptAES(key, text []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + if (len(text) % aes.BlockSize) != 0 { + return nil, fmt.Errorf("blocksize must be multiple of decoded message length") + } + + iv := text[:aes.BlockSize] + msg := text[aes.BlockSize:] + + cfb := cipher.NewCFBDecrypter(block, iv) + cfb.XORKeyStream(msg, msg) + + unpadMsg, err := unpad(msg) + if err != nil { + return nil, err + } + + return unpadMsg, nil +} + +// encrypt3DES +func encrypt3DES(key, text []byte) ([]byte, error) { + block, err := des.NewTripleDESCipher(key) + if err != nil { + return nil, err + } + + msg := pad(text, des.BlockSize) + ciphertext := make([]byte, des.BlockSize+len(msg)) + iv := ciphertext[:des.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + + cbc := cipher.NewCBCEncrypter(block, iv) + cbc.CryptBlocks(ciphertext[des.BlockSize:], msg) + + return ciphertext, nil +} + +// decrypt3DES +func decrypt3DES(key, text []byte) ([]byte, error) { + block, err := des.NewCipher(key) + if err != nil { + return nil, err + } + + if (len(text) % des.BlockSize) != 0 { + return nil, fmt.Errorf("blocksize must be multiple of decoded message length") + } + + iv := text[:des.BlockSize] + msg := text[des.BlockSize:] + + cbc := cipher.NewCBCDecrypter(block, iv) + cbc.CryptBlocks(msg, msg) + + unpadMsg, err := unpad(msg) + if err != nil { + return nil, err + } + + return unpadMsg, nil +} + +// encryptBlowfish +func encryptBlowfish(key, text []byte) ([]byte, error) { + block, err := blowfish.NewCipher(key) + if err != nil { + return nil, err + } + + msg := pad(text, blowfish.BlockSize) + ciphertext := make([]byte, blowfish.BlockSize+len(msg)) + iv := ciphertext[:blowfish.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + + cbc := cipher.NewCBCEncrypter(block, iv) + cbc.CryptBlocks(ciphertext[blowfish.BlockSize:], msg) + + return ciphertext, nil +} + +// decryptBlowfish +func decryptBlowfish(key, text []byte) ([]byte, error) { + block, err := blowfish.NewCipher(key) + if err != nil { + return nil, err + } + + if (len(text) % blowfish.BlockSize) != 0 { + return nil, fmt.Errorf("blocksize must be multiple of decoded message length") + } + + iv := text[:blowfish.BlockSize] + msg := text[blowfish.BlockSize:] + + cbc := cipher.NewCBCDecrypter(block, iv) + cbc.CryptBlocks(msg, msg) + + unpadMsg, err := unpad(msg) + if err != nil { + return nil, err + } + + return unpadMsg, nil +} + +// encryptXTEA +func encryptXTEA(key, text []byte) ([]byte, error) { + block, err := xtea.NewCipher(key) + if err != nil { + return nil, err + } + + msg := pad(text, xtea.BlockSize) + ciphertext := make([]byte, xtea.BlockSize+len(msg)) + iv := ciphertext[:xtea.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + + cbc := cipher.NewCBCEncrypter(block, iv) + cbc.CryptBlocks(ciphertext[xtea.BlockSize:], msg) + + return ciphertext, nil +} + +// decryptXTEA +func decryptXTEA(key, text []byte) ([]byte, error) { + block, err := xtea.NewCipher(key) + if err != nil { + return nil, err + } + + if (len(text) % xtea.BlockSize) != 0 { + return nil, fmt.Errorf("blocksize must be multiple of decoded message length") + } + + iv := text[:xtea.BlockSize] + msg := text[xtea.BlockSize:] + + cbc := cipher.NewCBCDecrypter(block, iv) + cbc.CryptBlocks(msg, msg) + + unpadMsg, err := unpad(msg) + if err != nil { + return nil, err + } + + return unpadMsg, nil +} From 2914dc5cc88e26a4a481d4dd150abd202e548722 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 1 Dec 2017 16:22:06 +0100 Subject: [PATCH 24/48] Add snappy compression --- rres/cmd/rrem/main.go | 4 ++-- rres/rlib/rlib.go | 23 +++++++++++++++++++++++ rres/rres.go | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/rres/cmd/rrem/main.go b/rres/cmd/rrem/main.go index 3e356e8..7436918 100644 --- a/rres/cmd/rrem/main.go +++ b/rres/cmd/rrem/main.go @@ -39,7 +39,7 @@ func init() { func main() { base := flag.String("base", "data", "Resources file basename") - comp := flag.Int("comp", rres.CompDeflate, "Compression type, 0=NONE, 1=DEFLATE, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2") + comp := flag.Int("comp", rres.CompDeflate, "Compression type, 0=NONE, 1=DEFLATE, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2, 7=Snappy") enc := flag.Int("enc", rres.CryptoNone, "Encryption type, 0=NONE, 1=XOR, 2=AES, 3=3DES, 4=Blowfish, 5=XTEA") key := flag.String("key", "", "Encryption key") header := flag.Bool("header", false, "Generate C header (.h file)") @@ -286,7 +286,7 @@ func validComp(compType int) bool { return true case rres.CompLZ4, rres.CompLZMA2: return true - case rres.CompBZIP2: + case rres.CompBZIP2, rres.CompSnappy: return true } return false diff --git a/rres/rlib/rlib.go b/rres/rlib/rlib.go index 5b2a3b6..b64f7f4 100644 --- a/rres/rlib/rlib.go +++ b/rres/rlib/rlib.go @@ -15,6 +15,7 @@ import ( "golang.org/x/crypto/xtea" "github.com/dsnet/compress/bzip2" + "github.com/golang/snappy" "github.com/klauspost/compress/flate" "github.com/pierrec/lz4" xor "github.com/rootlch/encrypt" @@ -171,6 +172,19 @@ func Compress(data []byte, compType int) ([]byte, error) { w.Close() + return buf.Bytes(), nil + case rres.CompSnappy: + buf := new(bytes.Buffer) + + w := snappy.NewWriter(buf) + + _, err := w.Write(data) + if err != nil { + return nil, err + } + + w.Close() + return buf.Bytes(), nil default: return data, nil @@ -225,6 +239,15 @@ func Decompress(data []byte, compType int) ([]byte, error) { return nil, err } + return u, nil + case rres.CompSnappy: + r := snappy.NewReader(bytes.NewReader(data)) + + u, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + return u, nil default: return data, nil diff --git a/rres/rres.go b/rres/rres.go index 1a13d67..f3dee7b 100644 --- a/rres/rres.go +++ b/rres/rres.go @@ -82,6 +82,8 @@ const ( CompLZMA2 // BZIP2 compression CompBZIP2 + // Snappy compression + CompSnappy ) // Encryption types From 020ab54435838fdc6b6193dbed4bcb2387dd583c Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 1 Dec 2017 17:18:41 +0100 Subject: [PATCH 25/48] Update README.md --- rres/cmd/rrem/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rres/cmd/rrem/README.md b/rres/cmd/rrem/README.md index e55622b..6e8b957 100644 --- a/rres/cmd/rrem/README.md +++ b/rres/cmd/rrem/README.md @@ -11,7 +11,7 @@ Usage of ./rrem: -bin Generate Go bindata (.go file) -comp int - Compression type, 0=NONE, 1=DEFLATE, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2 (default 5) + Compression type, 0=NONE, 1=DEFLATE, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2, 7=Snappy (default 1) -enc int Encryption type, 0=NONE, 1=XOR, 2=AES, 3=3DES, 4=Blowfish, 5=XTEA -header From 275bcd3efc133a7c8a818667f4c25e1af340f097 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 5 Dec 2017 16:38:08 +0100 Subject: [PATCH 26/48] Rename rlib to rreslib --- raylib/rres.go | 6 +++--- rres/cmd/rrem/main.go | 6 +++--- rres/rlib/cgo/Makefile | 4 ---- rres/rreslib/cgo/Makefile | 4 ++++ rres/{rlib => rreslib}/cgo/make.bash | 8 ++++---- rres/{rlib/cgo/rlib.go => rreslib/cgo/rreslib.go} | 10 +++++----- rres/{rlib/rlib.go => rreslib/rreslib.go} | 4 ++-- 7 files changed, 21 insertions(+), 21 deletions(-) delete mode 100644 rres/rlib/cgo/Makefile create mode 100644 rres/rreslib/cgo/Makefile rename rres/{rlib => rreslib}/cgo/make.bash (76%) rename rres/{rlib/cgo/rlib.go => rreslib/cgo/rreslib.go} (84%) rename rres/{rlib/rlib.go => rreslib/rreslib.go} (98%) diff --git a/raylib/rres.go b/raylib/rres.go index aecbc68..8b89c1e 100644 --- a/raylib/rres.go +++ b/raylib/rres.go @@ -8,7 +8,7 @@ import ( "unsafe" "github.com/gen2brain/raylib-go/rres" - "github.com/gen2brain/raylib-go/rres/rlib" + "github.com/gen2brain/raylib-go/rres/rreslib" ) // LoadResource - Load resource from file by id @@ -57,13 +57,13 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) reader.Read(b) // Decompress data - data.Data, err = rlib.Decompress(b, int(infoHeader.CompType)) + data.Data, err = rreslib.Decompress(b, int(infoHeader.CompType)) if err != nil { TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) } // Decrypt data - data.Data, err = rlib.Decrypt(key, data.Data, int(infoHeader.CryptoType)) + data.Data, err = rreslib.Decrypt(key, data.Data, int(infoHeader.CryptoType)) if err != nil { TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) } diff --git a/rres/cmd/rrem/main.go b/rres/cmd/rrem/main.go index 7436918..8d69d16 100644 --- a/rres/cmd/rrem/main.go +++ b/rres/cmd/rrem/main.go @@ -30,7 +30,7 @@ import ( "github.com/moutend/go-wav" "github.com/gen2brain/raylib-go/rres" - "github.com/gen2brain/raylib-go/rres/rlib" + "github.com/gen2brain/raylib-go/rres/rreslib" ) func init() { @@ -156,7 +156,7 @@ func main() { } // Encryption - data, err = rlib.Encrypt([]byte(*key), data, int(infoHeader.CryptoType)) + data, err = rreslib.Encrypt([]byte(*key), data, int(infoHeader.CryptoType)) if err != nil { fmt.Printf("%v\n", err) } @@ -164,7 +164,7 @@ func main() { infoHeader.UncompSize = uint32(len(data)) // Compression - data, err = rlib.Compress(data, int(infoHeader.CompType)) + data, err = rreslib.Compress(data, int(infoHeader.CompType)) if err != nil { fmt.Printf("%v\n", err) } diff --git a/rres/rlib/cgo/Makefile b/rres/rlib/cgo/Makefile deleted file mode 100644 index d7e1300..0000000 --- a/rres/rlib/cgo/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -all: build - -build: - go build -buildmode=c-archive -o rlib.a diff --git a/rres/rreslib/cgo/Makefile b/rres/rreslib/cgo/Makefile new file mode 100644 index 0000000..e3d3c38 --- /dev/null +++ b/rres/rreslib/cgo/Makefile @@ -0,0 +1,4 @@ +all: build + +build: + go build -buildmode=c-archive -o rreslib.a diff --git a/rres/rlib/cgo/make.bash b/rres/rreslib/cgo/make.bash similarity index 76% rename from rres/rlib/cgo/make.bash rename to rres/rreslib/cgo/make.bash index 84ba576..082bf0e 100755 --- a/rres/rlib/cgo/make.bash +++ b/rres/rreslib/cgo/make.bash @@ -5,14 +5,14 @@ MINGW="/usr/i686-w64-mingw32" RPI="/usr/armv6j-hardfloat-linux-gnueabi" ANDROID="/opt/android-toolchain-arm7" -CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -buildmode=c-archive -o release/linux/rlib.a -ldflags "-s -w" +CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -buildmode=c-archive -o release/linux/rreslib.a -ldflags "-s -w" CC="i686-w64-mingw32-gcc" CXX="i686-w64-mingw32-g++" \ -CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -buildmode=c-archive -o release/win32/rlib.a -ldflags "-s -w" +CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -buildmode=c-archive -o release/win32/rreslib.a -ldflags "-s -w" CC="armv6j-hardfloat-linux-gnueabi-gcc" CXX="armv6j-hardfloat-linux-gnueabi-g++" \ -CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -buildmode=c-archive -o release/rpi/rlib.a -ldflags "-s -w" +CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -buildmode=c-archive -o release/rpi/rreslib.a -ldflags "-s -w" PATH="$PATH:$ANDROID/bin" \ CC="arm-linux-androideabi-gcc" CXX="arm-linux-androideabi-g++" \ -CGO_ENABLED=1 GOOS=android GOARCH=arm go build -buildmode=c-shared -o release/android/armeabi-v7a/rlib.so -ldflags "-s -w" +CGO_ENABLED=1 GOOS=android GOARCH=arm go build -buildmode=c-shared -o release/android/armeabi-v7a/rreslib.so -ldflags "-s -w" diff --git a/rres/rlib/cgo/rlib.go b/rres/rreslib/cgo/rreslib.go similarity index 84% rename from rres/rlib/cgo/rlib.go rename to rres/rreslib/cgo/rreslib.go index b7a876c..51c4996 100644 --- a/rres/rlib/cgo/rlib.go +++ b/rres/rreslib/cgo/rreslib.go @@ -5,14 +5,14 @@ import "C" import ( "unsafe" - "github.com/gen2brain/raylib-go/rres/rlib" + "github.com/gen2brain/raylib-go/rres/rreslib" ) //export Compress func Compress(compType C.int, data *C.uchar, uncompSize C.ulong, outCompSize *C.ulong) *C.uchar { d := C.GoBytes(unsafe.Pointer(data), C.int(uncompSize)) - c, err := rlib.Compress(d, int(compType)) + c, err := rreslib.Compress(d, int(compType)) if err != nil { return nil } @@ -25,7 +25,7 @@ func Compress(compType C.int, data *C.uchar, uncompSize C.ulong, outCompSize *C. func Decompress(compType C.int, data *C.uchar, uncompSize C.ulong, compSize C.ulong) *C.uchar { d := C.GoBytes(unsafe.Pointer(data), C.int(compSize)) - c, err := rlib.Decompress(d, int(compType)) + c, err := rreslib.Decompress(d, int(compType)) if err != nil { return nil } @@ -42,7 +42,7 @@ func Encrypt(cryptoType C.int, key *C.char, data *C.uchar, dataSize C.ulong, out k := []byte(C.GoString(key)) d := C.GoBytes(unsafe.Pointer(data), C.int(dataSize)) - c, err := rlib.Encrypt(k, d, int(cryptoType)) + c, err := rreslib.Encrypt(k, d, int(cryptoType)) if err != nil { return nil } @@ -56,7 +56,7 @@ func Decrypt(cryptoType C.int, key *C.char, data *C.uchar, dataSize C.ulong) *C. k := []byte(C.GoString(key)) d := C.GoBytes(unsafe.Pointer(data), C.int(dataSize)) - c, err := rlib.Decrypt(k, d, int(cryptoType)) + c, err := rreslib.Decrypt(k, d, int(cryptoType)) if err != nil { return nil } diff --git a/rres/rlib/rlib.go b/rres/rreslib/rreslib.go similarity index 98% rename from rres/rlib/rlib.go rename to rres/rreslib/rreslib.go index b64f7f4..4547864 100644 --- a/rres/rlib/rlib.go +++ b/rres/rreslib/rreslib.go @@ -1,5 +1,5 @@ -// Package rlib provides Encrypt/Decrypt and Compress/Uncompress functions -package rlib +// Package rreslib provides Encrypt/Decrypt and Compress/Uncompress functions +package rreslib import ( "bytes" From 1923507276d0cba0b00f47ef73c517ffe43d3610 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 5 Dec 2017 19:45:13 +0100 Subject: [PATCH 27/48] OpenAL is no more --- .appveyor.yml | 3 - .travis.yml | 2 +- README.md | 21 +- examples/android/example/README.md | 4 +- examples/android/example/bootstrap.sh | 48 - examples/rpi/basic_window/bootstrap.sh | 20 - raylib/audio.c | 934 +- raylib/audio.go | 16 +- raylib/audio.h | 4 +- raylib/cgo_android.go | 4 +- raylib/cgo_darwin.go | 6 +- raylib/cgo_linux.go | 8 +- raylib/cgo_linux_arm.go | 6 +- raylib/cgo_windows.go | 7 +- raylib/core.c | 43 +- raylib/external/dr_flac.h | 4429 +++++---- raylib/external/dr_wav.h | 3455 +++++++ raylib/external/mini_al.h | 11601 +++++++++++++++++++++++ raylib/gestures.h | 32 +- raylib/mini_al.c | 4 + raylib/raylib.h | 45 +- raylib/raymath.h | 20 +- raylib/rlgl.c | 4 +- raylib/text.c | 3 +- raylib/textures.c | 42 +- 25 files changed, 18868 insertions(+), 1893 deletions(-) create mode 100644 raylib/external/dr_wav.h create mode 100644 raylib/external/mini_al.h create mode 100644 raylib/mini_al.c diff --git a/.appveyor.yml b/.appveyor.yml index 300979e..3a08426 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -19,8 +19,5 @@ install: - go version - go env -before_build: - - bash -lc "pacman --noconfirm --needed -Sy mingw-w64-i686-openal" - build_script: - bash -lc "cd /c/gopath/src/github.com/gen2brain/raylib-go && go get -t ./... && make" diff --git a/.travis.yml b/.travis.yml index f757d1c..d8aa708 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,5 @@ go: install: - sudo apt-get update -y - - sudo apt-get install libopenal-dev libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev -y + - sudo apt-get install libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev -y - go get -t ./... diff --git a/README.md b/README.md index 387cd86..4fd2650 100644 --- a/README.md +++ b/README.md @@ -12,28 +12,24 @@ Golang bindings for [raylib](http://www.raylib.com/), a simple and easy-to-use l ### Requirements -* [OpenAL Soft](http://kcat.strangesoft.net/openal.html) -NOTE: if you don't need audio you can use `-tags noaudio` during build, OpenAL will not be linked to binary, though none of the audio functions will be available. - * [GLFW](http://www.glfw.org/) is included as part of the Go package, but you need to make sure you have dependencies installed, see below. ##### Ubuntu - apt-get install libopenal-dev libgl1-mesa-dev libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev + apt-get install libgl1-mesa-dev libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev ##### Fedora - dnf install openal-soft-devel mesa-libGL-devel libXi-devel libXcursor-devel libXrandr-devel libXinerama-devel + dnf install mesa-libGL-devel libXi-devel libXcursor-devel libXrandr-devel libXinerama-devel -##### OS X +##### macOS -On OS X system OpenAL framework is used, you need Xcode or Command Line Tools for Xcode. +On macOS you need Xcode or Command Line Tools for Xcode. -##### Windows ([MSYS2](https://msys2.github.io/)) +##### Windows - pacman -S mingw-w64-x86_64-openal mingw-w64-x86_64-gcc mingw-w64-x86_64-go git - -On Windows, build binary in MSYS2 shell. +On Windows you need C compiler, like [https://mingw-w64.org](Mingw-w64) or [http://tdm-gcc.tdragon.net/](TDM-GCC). +You can also build binary in [MSYS2](https://msys2.github.io/) shell. ##### Android @@ -49,11 +45,10 @@ On Windows, build binary in MSYS2 shell. ### Build tags -* `noaudio` - disables audio functions and doesn't link against OpenAL libraries +* `noaudio` - disables audio functions * `opengl21` - uses OpenGL 2.1 backend (default is 3.3 on desktop) * `opengl11` - uses OpenGL 1.1 backend (pseudo OpenGL 1.1 style) * `wayland` - builds against Wayland libraries -* `static` - links against OpenAL static libraries ### Documentation diff --git a/examples/android/example/README.md b/examples/android/example/README.md index d79cde6..a0a17c2 100644 --- a/examples/android/example/README.md +++ b/examples/android/example/README.md @@ -4,13 +4,13 @@ To compile example to shared library you will need [Android NDK](https://develop To build Android apk you will need [Android SDK](http://developer.android.com/sdk/index.html#Other). Download and unpack archives somewhere. -Go must be cross compiled for android. There is a bootstrap.sh script that you can use to compile Go and OpenAL for android/arm and android/arm64. +Go must be cross compiled for android. There is a bootstrap.sh script that you can use to compile Go for android/arm and android/arm64. Export path to Android NDK, point to location where you have unpacked archive: export ANDROID_NDK_HOME=/opt/android-ndk -Compile Go, OpenAL and android_native_app_glue, /usr/local is prefix where Go and Android toolchains will be installed: +Compile Go and android_native_app_glue, /usr/local is prefix where Go and Android toolchains will be installed: ./bootstrap.sh /usr/local diff --git a/examples/android/example/bootstrap.sh b/examples/android/example/bootstrap.sh index 033aa9f..01f0525 100755 --- a/examples/android/example/bootstrap.sh +++ b/examples/android/example/bootstrap.sh @@ -37,10 +37,6 @@ if [[ -z "$GO_VERSION" ]]; then # go1.9.2 GO_VERSION=`curl -s https://golang.org/dl/ | grep 'id="go' | head -n1 | awk -F'"' '{print $4}'` fi -if [[ -z "$OPENAL_VERSION" ]]; then - # 1.18.2 - OPENAL_VERSION=`curl -s http://kcat.strangesoft.net/openal.html | grep 'tar.bz2' | awk -F'"' '{print $2}' | awk -F'-' '{print $4}' | sed 's/.tar.bz2//'` -fi if [[ -z "$NDK_VERSION" ]]; then # r15c NDK_VERSION=`curl -s https://developer.android.com/ndk/downloads/index.html | grep 'id="stable-downloads"' | awk -F'(' '{print $2}' | awk -F')' '{print $1}'` @@ -114,50 +110,6 @@ cp -r -f ${BUILD_DIR}/go ${INSTALL_PREFIX} ################################################### -echo; echo "##### Download OpenAL ${OPENAL_VERSION}" - -cd ${BUILD_DIR} && curl -L --progress-bar http://kcat.strangesoft.net/openal-releases/openal-soft-${OPENAL_VERSION}.tar.bz2 | tar -xj || exit 1 - -echo; echo "##### Compile OpenAL ${OPENAL_VERSION}" - -cat << EOF > ${BUILD_DIR}/openal-soft-${OPENAL_VERSION}/android-arm.cmake -set(CMAKE_SYSTEM_NAME Android) -set(CMAKE_ANDROID_ARCH arm) -set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a) -set(TOOLCHAIN_PREFIX arm-linux-androideabi) -set(CMAKE_C_COMPILER \${TOOLCHAIN_PREFIX}-${MYCC}) -set(CMAKE_CXX_COMPILER \${TOOLCHAIN_PREFIX}-${MYCXX}) -set(CMAKE_FIND_ROOT_PATH \${INSTALL_PREFIX}/android-arm) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -EOF - -cat << EOF > ${BUILD_DIR}/openal-soft-${OPENAL_VERSION}/android-arm64.cmake -set(CMAKE_SYSTEM_NAME Android) -set(CMAKE_ANDROID_ARCH arm64) -set(CMAKE_ANDROID_ARCH_ABI arm64-v8a) -set(TOOLCHAIN_PREFIX aarch64-linux-android) -set(CMAKE_C_COMPILER \${TOOLCHAIN_PREFIX}-${MYCC}) -set(CMAKE_CXX_COMPILER \${TOOLCHAIN_PREFIX}-${MYCXX}) -set(CMAKE_FIND_ROOT_PATH \${INSTALL_PREFIX}/android-arm64) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -EOF - -mkdir -p ${BUILD_DIR}/openal-soft-${OPENAL_VERSION}/build-arm -cd ${BUILD_DIR}/openal-soft-${OPENAL_VERSION}/build-arm -cmake -DLIBTYPE=STATIC -DCMAKE_TOOLCHAIN_FILE=../android-arm.cmake -DCMAKE_C_FLAGS="-DANDROID -D__ANDROID_API__=${API_VERSION_ARM}" -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX}/android-arm -DCMAKE_ANDROID_STANDALONE_TOOLCHAIN=${INSTALL_PREFIX}/android-arm -DALSOFT_NO_CONFIG_UTIL=ON -DALSOFT_UTILS=OFF -DALSOFT_EXAMPLES=OFF -DALSOFT_TESTS=OFF -DALSOFT_CONFIG=OFF -DALSOFT_HRTF_DEFS=OFF -DALSOFT_AMBDEC_PRESETS=OFF .. || exit 1 -make -j $(nproc) VERBOSE=1 && make install || exit 1 - -mkdir -p ${BUILD_DIR}/openal-soft-${OPENAL_VERSION}/build-arm64 -cd ${BUILD_DIR}/openal-soft-${OPENAL_VERSION}/build-arm64 -cmake -DLIBTYPE=STATIC -DCMAKE_TOOLCHAIN_FILE=../android-arm64.cmake -DCMAKE_C_FLAGS="-DANDROID -D__ANDROID_API__=${API_VERSION_ARM64}" -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX}/android-arm64 -DCMAKE_ANDROID_STANDALONE_TOOLCHAIN=${INSTALL_PREFIX}/android-arm64 -DALSOFT_NO_CONFIG_UTIL=ON -DALSOFT_UTILS=OFF -DALSOFT_EXAMPLES=OFF -DALSOFT_TESTS=OFF -DALSOFT_CONFIG=OFF -DALSOFT_HRTF_DEFS=OFF -DALSOFT_AMBDEC_PRESETS=OFF .. || exit 1 -make -j $(nproc) VERBOSE=1 && make install || exit 1 - -################################################### - echo; echo "##### Compile android_native_app_glue" mkdir -p ${BUILD_DIR}/native_app_glue/jni cp -r ${ANDROID_NDK_HOME}/sources/android/native_app_glue/* ${BUILD_DIR}/native_app_glue/jni/ diff --git a/examples/rpi/basic_window/bootstrap.sh b/examples/rpi/basic_window/bootstrap.sh index 024c5c1..4355e0f 100755 --- a/examples/rpi/basic_window/bootstrap.sh +++ b/examples/rpi/basic_window/bootstrap.sh @@ -9,8 +9,6 @@ GO_OS="linux" GO_ARCH="amd64" GO_VERSION=`curl -s https://golang.org/dl/ | grep 'id="go' | head -n1 | awk -F'"' '{print $4}'` -OPENAL_VERSION="1.17.2" - INSTALL_PREFIX="$1" export PATH=${INSTALL_PREFIX}/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:${PATH} @@ -39,23 +37,5 @@ GOROOT_BOOTSTRAP=${BUILD_DIR}/bootstrap/go CC_FOR_TARGET=arm-linux-gnueabihf-gcc cp -r -f ${BUILD_DIR}/go ${INSTALL_PREFIX} -echo "##### Compile OpenAL" - -cd ${BUILD_DIR} && curl -s -L http://kcat.strangesoft.net/openal-releases/openal-soft-${OPENAL_VERSION}.tar.bz2 | tar -xj - -cat << EOF > ${BUILD_DIR}/openal-soft-${OPENAL_VERSION}/linux-rpi.cmake -set(TOOLCHAIN_PREFIX arm-linux-gnueabihf) -set(CMAKE_C_COMPILER \${TOOLCHAIN_PREFIX}-gcc) -set(CMAKE_FIND_ROOT_PATH \${INSTALL_PREFIX}/gcc-linaro-arm-linux-gnueabihf-raspbian-x64) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -EOF - -mkdir -p ${BUILD_DIR}/openal-soft-${OPENAL_VERSION}/build-rpi -cd ${BUILD_DIR}/openal-soft-${OPENAL_VERSION}/build-rpi -cmake -DLIBTYPE=STATIC -DCMAKE_TOOLCHAIN_FILE=../linux-rpi.cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX}/gcc-linaro-arm-linux-gnueabihf-raspbian-x64 .. -make -j $(nproc) && make install - echo "##### Remove build directory" rm -rf ${BUILD_DIR} diff --git a/raylib/audio.c b/raylib/audio.c index a7cd665..5efca74 100644 --- a/raylib/audio.c +++ b/raylib/audio.c @@ -74,6 +74,10 @@ #define SUPPORT_FILEFORMAT_MOD //------------------------------------------------- +#ifndef USE_MINI_AL +#define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL. +#endif + #if defined(AUDIO_STANDALONE) #include "audio.h" #include // Required for: va_list, va_start(), vfprintf(), va_end() @@ -82,17 +86,21 @@ #include "utils.h" // Required for: fopen() Android mapping #endif -#if defined(__APPLE__) - #include "OpenAL/al.h" // OpenAL basic header - #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) -#else - #include "AL/al.h" // OpenAL basic header - #include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) - //#include "AL/alext.h" // OpenAL extensions header, required for AL_EXT_FLOAT32 and AL_EXT_MCFORMATS -#endif +#include "external/mini_al.h" // Implemented in mini_al.c. Cannot implement this here because it conflicts with Win32 APIs such as CloseWindow(), etc. -// OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples -// OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1) +#if !defined(USE_MINI_AL) || USE_MINI_AL == 0 + #if defined(__APPLE__) + #include "OpenAL/al.h" // OpenAL basic header + #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) + #else + #include "AL/al.h" // OpenAL basic header + #include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) + //#include "AL/alext.h" // OpenAL extensions header, required for AL_EXT_FLOAT32 and AL_EXT_MCFORMATS + #endif + + // OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples + // OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1) +#endif #include // Required for: malloc(), free() #include // Required for: strcmp(), strncmp() @@ -202,10 +210,242 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Device initialization and Closing //---------------------------------------------------------------------------------- +#if USE_MINI_AL +#define DEVICE_FORMAT mal_format_f32 +#define DEVICE_CHANNELS 2 +#define DEVICE_SAMPLE_RATE 44100 + +typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioBufferUsage; + +typedef struct AudioBuffer AudioBuffer; +struct AudioBuffer +{ + mal_dsp dsp; // For format conversion. + float volume; + float pitch; + bool playing; + bool paused; + bool looping; // Always true for AudioStreams. + AudioBufferUsage usage; // Slightly different logic is used when feeding data to the playback device depending on whether or not data is streamed. + bool isSubBufferProcessed[2]; + unsigned int frameCursorPos; + unsigned int bufferSizeInFrames; + AudioBuffer* next; + AudioBuffer* prev; + unsigned char buffer[1]; +}; + +void StopAudioBuffer(AudioBuffer* audioBuffer); + + +static mal_context context; +static mal_device device; +static mal_bool32 isAudioInitialized = MAL_FALSE; +static float masterVolume = 1; +static mal_mutex audioLock; +static AudioBuffer* firstAudioBuffer = NULL; // Audio buffers are tracked in a linked list. +static AudioBuffer* lastAudioBuffer = NULL; + +static void TrackAudioBuffer(AudioBuffer* audioBuffer) +{ + mal_mutex_lock(&audioLock); + { + if (firstAudioBuffer == NULL) { + firstAudioBuffer = audioBuffer; + } else { + lastAudioBuffer->next = audioBuffer; + audioBuffer->prev = lastAudioBuffer; + } + + lastAudioBuffer = audioBuffer; + } + mal_mutex_unlock(&audioLock); +} + +static void UntrackAudioBuffer(AudioBuffer* audioBuffer) +{ + mal_mutex_lock(&audioLock); + { + if (audioBuffer->prev == NULL) { + firstAudioBuffer = audioBuffer->next; + } else { + audioBuffer->prev->next = audioBuffer->next; + } + + if (audioBuffer->next == NULL) { + lastAudioBuffer = audioBuffer->prev; + } else { + audioBuffer->next->prev = audioBuffer->prev; + } + + audioBuffer->prev = NULL; + audioBuffer->next = NULL; + } + mal_mutex_unlock(&audioLock); +} + +static void OnLog_MAL(mal_context* pContext, mal_device* pDevice, const char* message) +{ + (void)pContext; + (void)pDevice; + TraceLog(LOG_ERROR, message); // All log messages from mini_al are errors. +} + +// This is the main mixing function. Mixing is pretty simple in this project - it's just an accumulation. +// +// framesOut is both an input and an output. It will be initially filled with zeros outside of this function. +static void MixFrames(float* framesOut, const float* framesIn, mal_uint32 frameCount, float localVolume) +{ + for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { + for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) { + float* frameOut = framesOut + (iFrame * device.channels); + const float* frameIn = framesIn + (iFrame * device.channels); + + frameOut[iChannel] += frameIn[iChannel] * masterVolume * localVolume; + } + } +} + +static mal_uint32 OnSendAudioDataToDevice(mal_device* pDevice, mal_uint32 frameCount, void* pFramesOut) +{ + // This is where all of the mixing takes place. + (void)pDevice; + + // Mixing is basically just an accumulation. We need to initialize the output buffer to 0. + memset(pFramesOut, 0, frameCount*pDevice->channels*mal_get_sample_size_in_bytes(pDevice->format)); + + // Using a mutex here for thread-safety which makes things not real-time. This is unlikely to be necessary for this project, but may + // want to consider how you might want to avoid this. + mal_mutex_lock(&audioLock); + { + for (AudioBuffer* audioBuffer = firstAudioBuffer; audioBuffer != NULL; audioBuffer = audioBuffer->next) + { + // Ignore stopped or paused sounds. + if (!audioBuffer->playing || audioBuffer->paused) { + continue; + } + + mal_uint32 framesRead = 0; + for (;;) { + if (framesRead > frameCount) { + TraceLog(LOG_DEBUG, "Mixed too many frames from audio buffer"); + break; + } + if (framesRead == frameCount) { + break; + } + + // Just read as much data as we can from the stream. + mal_uint32 framesToRead = (frameCount - framesRead); + while (framesToRead > 0) { + float tempBuffer[1024]; // 512 frames for stereo. + + mal_uint32 framesToReadRightNow = framesToRead; + if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS) { + framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS; + } + + // If we're not looping, we need to make sure we flush the internal buffers of the DSP pipeline to ensure we get the + // last few samples. + mal_bool32 flushDSP = !audioBuffer->looping; + + mal_uint32 framesJustRead = mal_dsp_read_frames_ex(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, flushDSP); + if (framesJustRead > 0) { + float* framesOut = (float*)pFramesOut + (framesRead * device.channels); + float* framesIn = tempBuffer; + MixFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume); + + framesToRead -= framesJustRead; + framesRead += framesJustRead; + } + + // If we weren't able to read all the frames we requested, break. + if (framesJustRead < framesToReadRightNow) { + if (!audioBuffer->looping) { + StopAudioBuffer(audioBuffer); + break; + } else { + // Should never get here, but just for safety, move the cursor position back to the start and continue the loop. + audioBuffer->frameCursorPos = 0; + continue; + } + } + } + + // If for some reason we weren't able to read every frame we'll need to break from the loop. Not doing this could + // theoretically put us into an infinite loop. + if (framesToRead > 0) { + break; + } + } + } + } + mal_mutex_unlock(&audioLock); + + return frameCount; // We always output the same number of frames that were originally requested. +} +#endif // Initialize audio device void InitAudioDevice(void) { +#if USE_MINI_AL + // Context. + mal_context_config contextConfig = mal_context_config_init(OnLog_MAL); + mal_result result = mal_context_init(NULL, 0, &contextConfig, &context); + if (result != MAL_SUCCESS) + { + TraceLog(LOG_ERROR, "Failed to initialize audio context"); + return; + } + + // Device. Using the default device. Format is floating point because it simplifies mixing. + mal_device_config deviceConfig = mal_device_config_init(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, OnSendAudioDataToDevice); + + // Special case for PLATFORM_RPI. +//#if defined(PLATFORM_RPI) +// deviceConfig.alsa.noMMap = MAL_TRUE; +// deviceConfig.bufferSizeInFrames = 2048; +//#endif + + result = mal_device_init(&context, mal_device_type_playback, NULL, &deviceConfig, NULL, &device); + if (result != MAL_SUCCESS) + { + TraceLog(LOG_ERROR, "Failed to initialize audio playback device"); + mal_context_uninit(&context); + return; + } + + // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running + // while there's at least one sound being played. + result = mal_device_start(&device); + if (result != MAL_SUCCESS) + { + TraceLog(LOG_ERROR, "Failed to start audio playback device"); + mal_device_uninit(&device); + mal_context_uninit(&context); + return; + } + + // Mixing happens on a seperate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may + // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. + if (mal_mutex_init(&context, &audioLock) != MAL_SUCCESS) + { + TraceLog(LOG_ERROR, "Failed to create mutex for audio mixing"); + mal_device_uninit(&device); + mal_context_uninit(&context); + return; + } + + TraceLog(LOG_INFO, "Audio device initialized successfully: %s", device.name); + TraceLog(LOG_INFO, "Audio backend: mini_al / %s", mal_get_backend_name(context.backend)); + TraceLog(LOG_INFO, "Audio format: %s -> %s", mal_get_format_name(device.format), mal_get_format_name(device.internalFormat)); + TraceLog(LOG_INFO, "Audio channels: %d -> %d", device.channels, device.internalChannels); + TraceLog(LOG_INFO, "Audio sample rate: %d -> %d", device.sampleRate, device.internalSampleRate); + TraceLog(LOG_INFO, "Audio buffer size: %d", device.bufferSizeInFrames); + + isAudioInitialized = MAL_TRUE; +#else // Open and initialize a device with default settings ALCdevice *device = alcOpenDevice(NULL); @@ -232,13 +472,30 @@ void InitAudioDevice(void) alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f); alListenerf(AL_GAIN, 1.0f); + + if (alIsExtensionPresent("AL_EXT_float32")) { + TraceLog(LOG_INFO, "AL_EXT_float32 supported"); + } else { + TraceLog(LOG_INFO, "AL_EXT_float32 not supported"); + } } } +#endif } // Close the audio device for all contexts void CloseAudioDevice(void) { +#if USE_MINI_AL + if (!isAudioInitialized) { + TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized"); + return; + } + + mal_mutex_uninit(&audioLock); + mal_device_uninit(&device); + mal_context_uninit(&context); +#else ALCdevice *device; ALCcontext *context = alcGetCurrentContext(); @@ -249,6 +506,7 @@ void CloseAudioDevice(void) alcMakeContextCurrent(NULL); alcDestroyContext(context); alcCloseDevice(device); +#endif TraceLog(LOG_INFO, "Audio device closed successfully"); } @@ -256,6 +514,9 @@ void CloseAudioDevice(void) // Check if device has been initialized successfully bool IsAudioDeviceReady(void) { +#if USE_MINI_AL + return isAudioInitialized; +#else ALCcontext *context = alcGetCurrentContext(); if (context == NULL) return false; @@ -266,6 +527,7 @@ bool IsAudioDeviceReady(void) if (device == NULL) return false; else return true; } +#endif } // Set master volume (listener) @@ -273,10 +535,270 @@ void SetMasterVolume(float volume) { if (volume < 0.0f) volume = 0.0f; else if (volume > 1.0f) volume = 1.0f; - + +#if USE_MINI_AL + masterVolume = 1; +#else alListenerf(AL_GAIN, volume); +#endif } + +//---------------------------------------------------------------------------------- +// Audio Buffer +//---------------------------------------------------------------------------------- +#if USE_MINI_AL +static mal_uint32 AudioBuffer_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + AudioBuffer* audioBuffer = (AudioBuffer*)pUserData; + + mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames / 2; + mal_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos / subBufferSizeInFrames; + if (currentSubBufferIndex > 1) { + TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream"); + return 0; + } + + // Another thread can update the processed state of buffers so we just take a copy here to try and avoid potential synchronization problems. + bool isSubBufferProcessed[2]; + isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; + isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; + + mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn) * audioBuffer->dsp.config.channelsIn; + + // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0. + mal_uint32 framesRead = 0; + for (;;) + { + // We break from this loop differently depending on the buffer's usage. For static buffers, we simply fill as much data as we can. For + // streaming buffers we only fill the halves of the buffer that are processed. Unprocessed halves must keep their audio data in-tact. + if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) { + if (framesRead >= frameCount) { + break; + } + } else { + if (isSubBufferProcessed[currentSubBufferIndex]) { + break; + } + } + + mal_uint32 totalFramesRemaining = (frameCount - framesRead); + if (totalFramesRemaining == 0) { + break; + } + + mal_uint32 framesRemainingInOutputBuffer; + if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) { + framesRemainingInOutputBuffer = audioBuffer->bufferSizeInFrames - audioBuffer->frameCursorPos; + } else { + mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex; + framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer); + } + + + + mal_uint32 framesToRead = totalFramesRemaining; + if (framesToRead > framesRemainingInOutputBuffer) { + framesToRead = framesRemainingInOutputBuffer; + } + + memcpy((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->buffer + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); + audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead) % audioBuffer->bufferSizeInFrames; + framesRead += framesToRead; + + // If we've read to the end of the buffer, mark it as processed. + if (framesToRead == framesRemainingInOutputBuffer) { + audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true; + isSubBufferProcessed[currentSubBufferIndex] = true; + + currentSubBufferIndex = (currentSubBufferIndex + 1) % 2; + + // We need to break from this loop if we're not looping. + if (!audioBuffer->looping) { + StopAudioBuffer(audioBuffer); + break; + } + } + } + + // Zero-fill excess. + mal_uint32 totalFramesRemaining = (frameCount - framesRead); + if (totalFramesRemaining > 0) { + memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); + + // For static buffers we can fill the remaining frames with silence for safety, but we don't want + // to report those frames as "read". The reason for this is that the caller uses the return value + // to know whether or not a non-looping sound has finished playback. + if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) { + framesRead += totalFramesRemaining; + } + } + + return framesRead; +} + +// Create a new audio buffer. Initially filled with silence. +AudioBuffer* CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage) +{ + AudioBuffer* audioBuffer = (AudioBuffer*)calloc(sizeof(*audioBuffer) + (bufferSizeInFrames*channels*mal_get_sample_size_in_bytes(format)), 1); + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to allocate memory for audio buffer"); + return NULL; + } + + // We run audio data through a format converter. + mal_dsp_config dspConfig; + memset(&dspConfig, 0, sizeof(dspConfig)); + dspConfig.formatIn = format; + dspConfig.formatOut = DEVICE_FORMAT; + dspConfig.channelsIn = channels; + dspConfig.channelsOut = DEVICE_CHANNELS; + dspConfig.sampleRateIn = sampleRate; + dspConfig.sampleRateOut = DEVICE_SAMPLE_RATE; + mal_result resultMAL = mal_dsp_init(&dspConfig, AudioBuffer_OnDSPRead, audioBuffer, &audioBuffer->dsp); + if (resultMAL != MAL_SUCCESS) { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create data conversion pipeline"); + free(audioBuffer); + return NULL; + } + + audioBuffer->volume = 1; + audioBuffer->pitch = 1; + audioBuffer->playing = 0; + audioBuffer->paused = 0; + audioBuffer->looping = 0; + audioBuffer->usage = usage; + audioBuffer->bufferSizeInFrames = bufferSizeInFrames; + audioBuffer->frameCursorPos = 0; + + // Buffers should be marked as processed by default so that a call to UpdateAudioStream() immediately after initialization works correctly. + audioBuffer->isSubBufferProcessed[0] = true; + audioBuffer->isSubBufferProcessed[1] = true; + + TrackAudioBuffer(audioBuffer); + + return audioBuffer; +} + +// Delete an audio buffer. +void DeleteAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + UntrackAudioBuffer(audioBuffer); + free(audioBuffer); +} + +// Check if an audio buffer is playing. +bool IsAudioBufferPlaying(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return false; + } + + return audioBuffer->playing && !audioBuffer->paused; +} + +// Play an audio buffer. +// +// This will restart the buffer from the start. Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position +// should be maintained. +void PlayAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->playing = true; + audioBuffer->paused = false; + audioBuffer->frameCursorPos = 0; +} + +// Stop an audio buffer. +void StopAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + // Don't do anything if the audio buffer is already stopped. + if (!IsAudioBufferPlaying(audioBuffer)) + { + return; + } + + audioBuffer->playing = false; + audioBuffer->paused = false; + audioBuffer->frameCursorPos = 0; + audioBuffer->isSubBufferProcessed[0] = true; + audioBuffer->isSubBufferProcessed[1] = true; +} + +// Pause an audio buffer. +void PauseAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->paused = true; +} + +// Resume an audio buffer. +void ResumeAudioBuffer(AudioBuffer* audioBuffer) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->paused = false; +} + +// Set volume for an audio buffer. +void SetAudioBufferVolume(AudioBuffer* audioBuffer, float volume) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->volume = volume; +} + +// Set pitch for an audio buffer. +void SetAudioBufferPitch(AudioBuffer* audioBuffer, float pitch) +{ + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayAudioBuffer() : No audio buffer"); + return; + } + + audioBuffer->pitch = pitch; + + // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches + // will make the sound faster; lower pitches make it slower. + mal_uint32 newOutputSampleRate = (mal_uint32)((((float)audioBuffer->dsp.config.sampleRateOut / (float)audioBuffer->dsp.config.sampleRateIn) / pitch) * audioBuffer->dsp.config.sampleRateIn); + mal_dsp_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate); +} +#endif + //---------------------------------------------------------------------------------- // Module Functions Definition - Sounds loading and playing (.WAV) //---------------------------------------------------------------------------------- @@ -351,6 +873,39 @@ Sound LoadSoundFromWave(Wave wave) if (wave.data != NULL) { +#if USE_MINI_AL + // When using mini_al we need to do our own mixing. To simplify this we need convert the format of each sound to be consistent with + // the format used to open the playback device. We can do this two ways: + // + // 1) Convert the whole sound in one go at load time (here). + // 2) Convert the audio data in chunks at mixing time. + // + // I have decided on the first option because it offloads work required for the format conversion to the to the loading stage. The + // downside to this is that it uses more memory if the original sound is u8 or s16. + mal_format formatIn = ((wave.sampleSize == 8) ? mal_format_u8 : ((wave.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); + mal_uint32 frameCountIn = wave.sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. + + mal_uint32 frameCount = mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn); + if (frameCount == 0) { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to get frame count for format conversion"); + } + + + AudioBuffer* audioBuffer = CreateAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC); + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create audio buffer"); + } + + + frameCount = mal_convert_frames(audioBuffer->buffer, audioBuffer->dsp.config.formatIn, audioBuffer->dsp.config.channelsIn, audioBuffer->dsp.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn); + if (frameCount == 0) + { + TraceLog(LOG_ERROR, "LoadSoundFromWave() : Format conversion failed"); + } + + sound.audioBuffer = audioBuffer; +#else ALenum format = 0; // The OpenAL format is worked out by looking at the number of channels and the sample size (bits per sample) @@ -404,6 +959,7 @@ Sound LoadSoundFromWave(Wave wave) sound.source = source; sound.buffer = buffer; sound.format = format; +#endif } return sound; @@ -420,10 +976,14 @@ void UnloadWave(Wave wave) // Unload sound void UnloadSound(Sound sound) { +#if USE_MINI_AL + DeleteAudioBuffer((AudioBuffer*)sound.audioBuffer); +#else alSourceStop(sound.source); alDeleteSources(1, &sound.source); alDeleteBuffers(1, &sound.buffer); +#endif TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer); } @@ -432,6 +992,19 @@ void UnloadSound(Sound sound) // NOTE: data must match sound.format void UpdateSound(Sound sound, const void *data, int samplesCount) { +#if USE_MINI_AL + AudioBuffer* audioBuffer = (AudioBuffer*)sound.audioBuffer; + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer"); + return; + } + + StopAudioBuffer(audioBuffer); + + // TODO: May want to lock/unlock this since this data buffer is read at mixing time. + memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.config.channelsIn*mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn)); +#else ALint sampleRate, sampleSize, channels; alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); alGetBufferi(sound.buffer, AL_BITS, &sampleSize); // It could also be retrieved from sound.format @@ -453,12 +1026,17 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) // Attach sound buffer to source again alSourcei(sound.source, AL_BUFFER, sound.buffer); +#endif } // Play a sound void PlaySound(Sound sound) { +#if USE_MINI_AL + PlayAudioBuffer((AudioBuffer*)sound.audioBuffer); +#else alSourcePlay(sound.source); // Play the sound +#endif //TraceLog(LOG_INFO, "Playing sound"); @@ -479,28 +1057,43 @@ void PlaySound(Sound sound) // Pause a sound void PauseSound(Sound sound) { +#if USE_MINI_AL + PauseAudioBuffer((AudioBuffer*)sound.audioBuffer); +#else alSourcePause(sound.source); +#endif } // Resume a paused sound void ResumeSound(Sound sound) { +#if USE_MINI_AL + ResumeAudioBuffer((AudioBuffer*)sound.audioBuffer); +#else ALenum state; alGetSourcei(sound.source, AL_SOURCE_STATE, &state); if (state == AL_PAUSED) alSourcePlay(sound.source); +#endif } // Stop reproducing a sound void StopSound(Sound sound) { +#if USE_MINI_AL + StopAudioBuffer((AudioBuffer*)sound.audioBuffer); +#else alSourceStop(sound.source); +#endif } // Check if a sound is playing bool IsSoundPlaying(Sound sound) { +#if USE_MINI_AL + return IsAudioBufferPlaying((AudioBuffer*)sound.audioBuffer); +#else bool playing = false; ALint state; @@ -508,23 +1101,59 @@ bool IsSoundPlaying(Sound sound) if (state == AL_PLAYING) playing = true; return playing; +#endif } // Set volume for a sound void SetSoundVolume(Sound sound, float volume) { +#if USE_MINI_AL + SetAudioBufferVolume((AudioBuffer*)sound.audioBuffer, volume); +#else alSourcef(sound.source, AL_GAIN, volume); +#endif } // Set pitch for a sound void SetSoundPitch(Sound sound, float pitch) { +#if USE_MINI_AL + SetAudioBufferPitch((AudioBuffer*)sound.audioBuffer, pitch); +#else alSourcef(sound.source, AL_PITCH, pitch); +#endif } // Convert wave data to desired format void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) { + mal_format formatIn = ((wave->sampleSize == 8) ? mal_format_u8 : ((wave->sampleSize == 16) ? mal_format_s16 : mal_format_f32)); + mal_format formatOut = (( sampleSize == 8) ? mal_format_u8 : (( sampleSize == 16) ? mal_format_s16 : mal_format_f32)); + + mal_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. + + mal_uint32 frameCount = mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn); + if (frameCount == 0) { + TraceLog(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion."); + return; + } + + void* data = malloc(frameCount * channels * (sampleSize/8)); + + frameCount = mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn); + if (frameCount == 0) { + TraceLog(LOG_ERROR, "WaveFormat() : Format conversion failed."); + return; + } + + wave->sampleCount = frameCount; + wave->sampleSize = sampleSize; + wave->sampleRate = sampleRate; + wave->channels = channels; + free(wave->data); + wave->data = data; + +#if 0 // Format sample rate // NOTE: Only supported 22050 <--> 44100 if (wave->sampleRate != sampleRate) @@ -603,6 +1232,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) free(wave->data); wave->data = data; } +#endif } // Copy a wave to a new wave @@ -785,18 +1415,44 @@ void UnloadMusicStream(Music music) // Start music playing (open stream) void PlayMusicStream(Music music) { +#if USE_MINI_AL + AudioBuffer* audioBuffer = (AudioBuffer*)music->stream.audioBuffer; + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); + return; + } + + // For music streams, we need to make sure we maintain the frame cursor position. This is hack for this section of code in UpdateMusicStream() + // // NOTE: In case window is minimized, music stream is stopped, + // // just make sure to play again on window restore + // if (IsMusicPlaying(music)) PlayMusicStream(music); + mal_uint32 frameCursorPos = audioBuffer->frameCursorPos; + { + PlayAudioStream(music->stream); // <-- This resets the cursor position. + } + audioBuffer->frameCursorPos = frameCursorPos; +#else alSourcePlay(music->stream.source); +#endif } // Pause music playing void PauseMusicStream(Music music) { +#if USE_MINI_AL + PauseAudioStream(music->stream); +#else alSourcePause(music->stream.source); +#endif } // Resume music playing void ResumeMusicStream(Music music) { +#if USE_MINI_AL + ResumeAudioStream(music->stream); +#else ALenum state; alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); @@ -805,12 +1461,16 @@ void ResumeMusicStream(Music music) TraceLog(LOG_INFO, "[AUD ID %i] Resume music stream playing", music->stream.source); alSourcePlay(music->stream.source); } +#endif } // Stop music playing (close stream) // TODO: To clear a buffer, make sure they have been already processed! void StopMusicStream(Music music) { +#if USE_MINI_AL + StopAudioStream(music->stream); +#else alSourceStop(music->stream.source); /* @@ -826,6 +1486,7 @@ void StopMusicStream(Music music) free(pcm); */ +#endif // Restart music context switch (music->ctxType) @@ -850,6 +1511,86 @@ void StopMusicStream(Music music) // TODO: Make sure buffers are ready for update... check music state void UpdateMusicStream(Music music) { +#if USE_MINI_AL + bool streamEnding = false; + + unsigned int subBufferSizeInFrames = ((AudioBuffer*)music->stream.audioBuffer)->bufferSizeInFrames / 2; + + // NOTE: Using dynamic allocation because it could require more than 16KB + void *pcm = calloc(subBufferSizeInFrames*music->stream.sampleSize/8*music->stream.channels, 1); + + int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts + + while (IsAudioBufferProcessed(music->stream)) + { + if (music->samplesLeft >= subBufferSizeInFrames) samplesCount = subBufferSizeInFrames; + else samplesCount = music->samplesLeft; + + // TODO: Really don't like ctxType thingy... + switch (music->ctxType) + { + case MUSIC_AUDIO_OGG: + { + // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) + int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount*music->stream.channels); + + } break; + #if defined(SUPPORT_FILEFORMAT_FLAC) + case MUSIC_AUDIO_FLAC: + { + // NOTE: Returns the number of samples to process + unsigned int numSamplesFlac = (unsigned int)drflac_read_s16(music->ctxFlac, samplesCount*music->stream.channels, (short *)pcm); + + } break; + #endif + #if defined(SUPPORT_FILEFORMAT_XM) + case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break; + #endif + #if defined(SUPPORT_FILEFORMAT_MOD) + case MUSIC_MODULE_MOD: jar_mod_fillbuffer(&music->ctxMod, pcm, samplesCount, 0); break; + #endif + default: break; + } + + UpdateAudioStream(music->stream, pcm, samplesCount); + music->samplesLeft -= samplesCount; + + if (music->samplesLeft <= 0) + { + streamEnding = true; + break; + } + } + + // Free allocated pcm data + free(pcm); + + // Reset audio stream for looping + if (streamEnding) + { + StopMusicStream(music); // Stop music (and reset) + + // Decrease loopCount to stop when required + if (music->loopCount > 0) + { + music->loopCount--; // Decrease loop count + PlayMusicStream(music); // Play again + } + else + { + if (music->loopCount == -1) + { + PlayMusicStream(music); + } + } + } + else + { + // NOTE: In case window is minimized, music stream is stopped, + // just make sure to play again on window restore + if (IsMusicPlaying(music)) PlayMusicStream(music); + } +#else ALenum state; ALint processed = 0; @@ -922,6 +1663,13 @@ void UpdateMusicStream(Music music) music->loopCount--; // Decrease loop count PlayMusicStream(music); // Play again } + else + { + if (music->loopCount == -1) + { + PlayMusicStream(music); + } + } } else { @@ -930,11 +1678,15 @@ void UpdateMusicStream(Music music) if (state != AL_PLAYING) PlayMusicStream(music); } } +#endif } // Check if any music is playing bool IsMusicPlaying(Music music) { +#if USE_MINI_AL + return IsAudioStreamPlaying(music->stream); +#else bool playing = false; ALint state; @@ -943,23 +1695,32 @@ bool IsMusicPlaying(Music music) if (state == AL_PLAYING) playing = true; return playing; +#endif } // Set volume for music void SetMusicVolume(Music music, float volume) { +#if USE_MINI_AL + SetAudioStreamVolume(music->stream, volume); +#else alSourcef(music->stream.source, AL_GAIN, volume); +#endif } // Set pitch for music void SetMusicPitch(Music music, float pitch) { +#if USE_MINI_AL + SetAudioStreamPitch(music->stream, pitch); +#else alSourcef(music->stream.source, AL_PITCH, pitch); +#endif } // Set music loop count (loop repeats) // NOTE: If set to -1, means infinite loop -void SetMusicLoopCount(Music music, float count) +void SetMusicLoopCount(Music music, int count) { music->loopCount = count; } @@ -983,6 +1744,7 @@ float GetMusicTimePlayed(Music music) return secondsPlayed; } + // Init audio stream (to stream audio pcm data) AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels) { @@ -999,6 +1761,27 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un stream.channels = 1; // Fallback to mono channel } + +#if USE_MINI_AL + mal_format formatIn = ((stream.sampleSize == 8) ? mal_format_u8 : ((stream.sampleSize == 16) ? mal_format_s16 : mal_format_f32)); + + // The size of a streaming buffer must be at least double the size of a period. + unsigned int periodSize = device.bufferSizeInFrames / device.periods; + unsigned int subBufferSize = AUDIO_BUFFER_SIZE; + if (subBufferSize < periodSize) { + subBufferSize = periodSize; + } + + AudioBuffer* audioBuffer = CreateAudioBuffer(formatIn, stream.channels, stream.sampleRate, subBufferSize*2, AUDIO_BUFFER_USAGE_STREAM); + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "InitAudioStream() : Failed to create audio buffer"); + return stream; + } + + audioBuffer->looping = true; // Always loop for streaming buffers. + stream.audioBuffer = audioBuffer; +#else // Setup OpenAL format if (stream.channels == 1) { @@ -1043,6 +1826,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un free(pcm); alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers); +#endif TraceLog(LOG_INFO, "[AUD ID %i] Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.source, stream.sampleRate, stream.sampleSize, (stream.channels == 1) ? "Mono" : "Stereo"); @@ -1052,6 +1836,9 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un // Close audio stream and free memory void CloseAudioStream(AudioStream stream) { +#if USE_MINI_AL + DeleteAudioBuffer((AudioBuffer*)stream.audioBuffer); +#else // Stop playing channel alSourceStop(stream.source); @@ -1070,7 +1857,8 @@ void CloseAudioStream(AudioStream stream) // Delete source and buffers alDeleteSources(1, &stream.source); alDeleteBuffers(MAX_STREAM_BUFFERS, stream.buffers); - +#endif + TraceLog(LOG_INFO, "[AUD ID %i] Unloaded audio stream data", stream.source); } @@ -1079,6 +1867,63 @@ void CloseAudioStream(AudioStream stream) // NOTE 2: To unqueue a buffer it needs to be processed: IsAudioBufferProcessed() void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { +#if USE_MINI_AL + AudioBuffer* audioBuffer = (AudioBuffer*)stream.audioBuffer; + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "UpdateAudioStream() : No audio buffer"); + return; + } + + if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]) + { + mal_uint32 subBufferToUpdate; + if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1]) + { + // Both buffers are available for updating. Update the first one and make sure the cursor is moved back to the front. + subBufferToUpdate = 0; + audioBuffer->frameCursorPos = 0; + } + else + { + // Just update whichever sub-buffer is processed. + subBufferToUpdate = (audioBuffer->isSubBufferProcessed[0]) ? 0 : 1; + } + + mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; + unsigned char *subBuffer = audioBuffer->buffer + ((subBufferSizeInFrames * stream.channels * (stream.sampleSize/8)) * subBufferToUpdate); + + // Does this API expect a whole buffer to be updated in one go? Assuming so, but if not will need to change this logic. + if (subBufferSizeInFrames >= (mal_uint32)samplesCount) + { + mal_uint32 framesToWrite = subBufferSizeInFrames; + if (framesToWrite > (mal_uint32)samplesCount) { + framesToWrite = (mal_uint32)samplesCount; + } + + mal_uint32 bytesToWrite = framesToWrite * stream.channels * (stream.sampleSize/8); + memcpy(subBuffer, data, bytesToWrite); + + // Any leftover frames should be filled with zeros. + mal_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; + if (leftoverFrameCount > 0) { + memset(subBuffer + bytesToWrite, 0, leftoverFrameCount * stream.channels * (stream.sampleSize/8)); + } + + audioBuffer->isSubBufferProcessed[subBufferToUpdate] = false; + } + else + { + TraceLog(LOG_ERROR, "UpdateAudioStream() : Attempting to write too many frames to buffer"); + return; + } + } + else + { + TraceLog(LOG_ERROR, "Audio buffer not available for updating"); + return; + } +#else ALuint buffer = 0; alSourceUnqueueBuffers(stream.source, 1, &buffer); @@ -1089,44 +1934,107 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) alSourceQueueBuffers(stream.source, 1, &buffer); } else TraceLog(LOG_WARNING, "[AUD ID %i] Audio buffer not available for unqueuing", stream.source); +#endif } // Check if any audio stream buffers requires refill bool IsAudioBufferProcessed(AudioStream stream) { +#if USE_MINI_AL + AudioBuffer* audioBuffer = (AudioBuffer*)stream.audioBuffer; + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "IsAudioBufferProcessed() : No audio buffer"); + return false; + } + + return audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]; +#else ALint processed = 0; // Determine if music stream is ready to be written alGetSourcei(stream.source, AL_BUFFERS_PROCESSED, &processed); return (processed > 0); +#endif } // Play audio stream void PlayAudioStream(AudioStream stream) { +#if USE_MINI_AL + PlayAudioBuffer((AudioBuffer*)stream.audioBuffer); +#else alSourcePlay(stream.source); +#endif } // Play audio stream void PauseAudioStream(AudioStream stream) { +#if USE_MINI_AL + PauseAudioBuffer((AudioBuffer*)stream.audioBuffer); +#else alSourcePause(stream.source); +#endif } // Resume audio stream playing void ResumeAudioStream(AudioStream stream) { +#if USE_MINI_AL + ResumeAudioBuffer((AudioBuffer*)stream.audioBuffer); +#else ALenum state; alGetSourcei(stream.source, AL_SOURCE_STATE, &state); if (state == AL_PAUSED) alSourcePlay(stream.source); +#endif +} + +// Check if audio stream is playing. +bool IsAudioStreamPlaying(AudioStream stream) +{ +#if USE_MINI_AL + return IsAudioBufferPlaying((AudioBuffer*)stream.audioBuffer); +#else + bool playing = false; + ALint state; + + alGetSourcei(stream.source, AL_SOURCE_STATE, &state); + + if (state == AL_PLAYING) playing = true; + + return playing; +#endif } // Stop audio stream void StopAudioStream(AudioStream stream) { +#if USE_MINI_AL + StopAudioBuffer((AudioBuffer*)stream.audioBuffer); +#else alSourceStop(stream.source); +#endif +} + +void SetAudioStreamVolume(AudioStream stream, float volume) +{ +#if USE_MINI_AL + SetAudioBufferVolume((AudioBuffer*)stream.audioBuffer, volume); +#else + alSourcef(stream.source, AL_GAIN, volume); +#endif +} + +void SetAudioStreamPitch(AudioStream stream, float pitch) +{ +#if USE_MINI_AL + SetAudioBufferPitch((AudioBuffer*)stream.audioBuffer, pitch); +#else + alSourcef(stream.source, AL_PITCH, pitch); +#endif } //---------------------------------------------------------------------------------- diff --git a/raylib/audio.go b/raylib/audio.go index 486f725..989eceb 100644 --- a/raylib/audio.go +++ b/raylib/audio.go @@ -42,11 +42,11 @@ func NewWaveFromPointer(ptr unsafe.Pointer) Wave { // Sound source type type Sound struct { - // OpenAL audio source id + // Audio source id Source uint32 - // OpenAL audio buffer id + // Audio buffer id Buffer uint32 - // OpenAL audio format specifier + // Audio format specifier Format int32 } @@ -77,11 +77,11 @@ type AudioStream struct { SampleSize uint32 // Number of channels (1-mono, 2-stereo) Channels uint32 - // OpenAL audio format specifier + // Audio format specifier Format int32 - // OpenAL audio source id + // Audio source id Source uint32 - // OpenAL audio buffers (double buffering) + // Audio buffers (double buffering) Buffers [2]uint32 } @@ -334,9 +334,9 @@ func SetMusicPitch(music Music, pitch float32) { // SetMusicLoopCount - Set music loop count (loop repeats) // NOTE: If set to -1, means infinite loop -func SetMusicLoopCount(music Music, count float32) { +func SetMusicLoopCount(music Music, count int32) { cmusic := *(*C.Music)(unsafe.Pointer(&music)) - ccount := (C.float)(count) + ccount := (C.int)(count) C.SetMusicLoopCount(cmusic, ccount) } diff --git a/raylib/audio.h b/raylib/audio.h index f135fda..48ef740 100644 --- a/raylib/audio.h +++ b/raylib/audio.h @@ -1,5 +1,3 @@ -// +build !noaudio - /********************************************************************************************** * * raylib.audio - Basic funtionality to work with audio @@ -169,4 +167,4 @@ void StopAudioStream(AudioStream stream); // Stop audio st } #endif -#endif // AUDIO_H +#endif // AUDIO_H \ No newline at end of file diff --git a/raylib/cgo_android.go b/raylib/cgo_android.go index 39c86d4..e56b130 100644 --- a/raylib/cgo_android.go +++ b/raylib/cgo_android.go @@ -4,8 +4,6 @@ package raylib /* #cgo android LDFLAGS: -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lm -landroid_native_app_glue -u ANativeActivity_onCreate -#cgo android CFLAGS: -DPLATFORM_ANDROID -DGRAPHICS_API_OPENGL_ES2 - -#cgo android,!noaudio LDFLAGS: -lopenal +#cgo android CFLAGS: -DPLATFORM_ANDROID -DGRAPHICS_API_OPENGL_ES2 -Iexternal */ import "C" diff --git a/raylib/cgo_darwin.go b/raylib/cgo_darwin.go index 22893cd..8b7bf07 100644 --- a/raylib/cgo_darwin.go +++ b/raylib/cgo_darwin.go @@ -16,14 +16,12 @@ package raylib #include "external/glfw/src/cocoa_time.c" #include "external/glfw/src/cocoa_window.m" #include "external/glfw/src/posix_thread.c" -#include "external/glfw/src/posix_time.c" #include "external/glfw/src/nsgl_context.m" +#include "external/glfw/src/egl_context.c" #include "external/glfw/src/osmesa_context.c" #cgo darwin LDFLAGS: -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo -framework CoreFoundation -#cgo darwin CFLAGS: -x objective-c -Iexternal/glfw/include -D_GLFW_COCOA -D_GLFW_USE_CHDIR -D_GLFW_USE_MENUBAR -D_GLFW_USE_RETINA -Wno-deprecated-declarations -DPLATFORM_DESKTOP - -#cgo darwin,!noaudio LDFLAGS: -framework OpenAL +#cgo darwin CFLAGS: -x objective-c -Iexternal -Iexternal/glfw/include -D_GLFW_COCOA -D_GLFW_USE_CHDIR -D_GLFW_USE_MENUBAR -D_GLFW_USE_RETINA -Wno-deprecated-declarations -DPLATFORM_DESKTOP -DMAL_NO_COREAUDIO #cgo darwin,opengl11 CFLAGS: -DGRAPHICS_API_OPENGL_11 #cgo darwin,opengl21 CFLAGS: -DGRAPHICS_API_OPENGL_21 diff --git a/raylib/cgo_linux.go b/raylib/cgo_linux.go index 62ef966..5297216 100644 --- a/raylib/cgo_linux.go +++ b/raylib/cgo_linux.go @@ -16,7 +16,6 @@ package raylib #include "external/glfw/src/wl_window.c" #include "external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c" #include "external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c" -#include "external/glfw/src/wayland-idle-inhibit-unstable-v1-client-protocol.c" #endif #ifdef _GLFW_X11 #include "external/glfw/src/x11_init.c" @@ -32,7 +31,7 @@ package raylib #include "external/glfw/src/egl_context.c" #include "external/glfw/src/osmesa_context.c" -#cgo linux CFLAGS: -Iexternal/glfw/include -DPLATFORM_DESKTOP +#cgo linux CFLAGS: -Iexternal -Iexternal/glfw/include -DPLATFORM_DESKTOP #cgo linux,!wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lX11 #cgo linux,wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon @@ -40,11 +39,6 @@ package raylib #cgo linux,!wayland CFLAGS: -D_GLFW_X11 #cgo linux,wayland CFLAGS: -D_GLFW_WAYLAND -#cgo linux,!noaudio LDFLAGS: -lopenal - -#cgo linux,!static CFLAGS: -DSHARED_OPENAL -#cgo linux,static CFLAGS: -DAL_LIBTYPE_STATIC - #cgo linux,opengl11 CFLAGS: -DGRAPHICS_API_OPENGL_11 #cgo linux,opengl21 CFLAGS: -DGRAPHICS_API_OPENGL_21 #cgo linux,!opengl11,!opengl21 CFLAGS: -DGRAPHICS_API_OPENGL_33 diff --git a/raylib/cgo_linux_arm.go b/raylib/cgo_linux_arm.go index cbdb4d0..3326212 100644 --- a/raylib/cgo_linux_arm.go +++ b/raylib/cgo_linux_arm.go @@ -3,9 +3,7 @@ package raylib /* -#cgo linux,arm LDFLAGS: -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -lvcos -lvchiq_arm -#cgo linux,arm CFLAGS: -DPLATFORM_RPI -DGRAPHICS_API_OPENGL_ES2 -I/opt/vc/include -I/opt/vc/include/interface/vcos -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads - -#cgo linux,arm,!noaudio LDFLAGS: -lopenal +#cgo linux,arm LDFLAGS: -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -lvcos -lvchiq_arm -ldl +#cgo linux,arm CFLAGS: -DPLATFORM_RPI -DGRAPHICS_API_OPENGL_ES2 -Iexternal -I/opt/vc/include -I/opt/vc/include/interface/vcos -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads */ import "C" diff --git a/raylib/cgo_windows.go b/raylib/cgo_windows.go index 2d215f7..6615cc2 100644 --- a/raylib/cgo_windows.go +++ b/raylib/cgo_windows.go @@ -21,12 +21,7 @@ package raylib #include "external/glfw/src/osmesa_context.c" #cgo windows LDFLAGS: -lopengl32 -lgdi32 -lwinmm -lole32 -#cgo windows CFLAGS: -D_GLFW_WIN32 -Iexternal/glfw/include -Iexternal/glfw/deps/mingw -DPLATFORM_DESKTOP - -#cgo windows,!noaudio LDFLAGS: -lopenal - -#cgo windows,!static CFLAGS: -DSHARED_OPENAL -#cgo windows,static CFLAGS: -DAL_LIBTYPE_STATIC +#cgo windows CFLAGS: -D_GLFW_WIN32 -Iexternal -Iexternal/glfw/include -Iexternal/glfw/deps/mingw -DPLATFORM_DESKTOP #cgo windows,opengl11 CFLAGS: -DGRAPHICS_API_OPENGL_11 #cgo windows,opengl21 CFLAGS: -DGRAPHICS_API_OPENGL_21 diff --git a/raylib/core.c b/raylib/core.c index 01febd6..36b9de6 100644 --- a/raylib/core.c +++ b/raylib/core.c @@ -2,19 +2,20 @@ * * raylib.core - Basic functions to manage windows, OpenGL context and input on multiple platforms * -* PLATFORMS SUPPORTED: -* - Windows (Win32, Win64) -* - Linux (tested on Ubuntu) -* - FreeBSD -* - OSX/macOS -* - Android (ARM, ARM64) -* - Raspberry Pi (Raspbian) -* - HTML5 (Chrome, Firefox) +* PLATFORMS SUPPORTED: +* PLATFORM_DESKTOP: Windows (Win32, Win64) +* PLATFORM_DESKTOP: Linux (32 and 64 bit) +* PLATFORM_DESKTOP: OSX/macOS +* PLATFORM_DESKTOP: FreeBSD +* PLATFORM_ANDROID: Android (ARM, ARM64) +* PLATFORM_RPI: Raspberry Pi (Raspbian) +* PLATFORM_WEB: HTML5 (Chrome, Firefox) +* PLATFORM_UWP: Universal Windows Platform * * CONFIGURATION: * * #define PLATFORM_DESKTOP -* Windowing and input system configured for desktop platforms: Windows, Linux, OSX, FreeBSD (managed by GLFW3 library) +* Windowing and input system configured for desktop platforms: Windows, Linux, OSX, FreeBSD * NOTE: Oculus Rift CV1 requires PLATFORM_DESKTOP for mirror rendering - View [rlgl] module to enable it * * #define PLATFORM_ANDROID @@ -22,8 +23,8 @@ * NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL * * #define PLATFORM_RPI -* Windowing and input system configured for Raspberry Pi (tested on Raspbian), graphic device is managed by EGL -* and inputs are processed is raw mode, reading from /dev/input/ +* Windowing and input system configured for Raspberry Pi i native mode (no X.org required, tested on Raspbian), +* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ * * #define PLATFORM_WEB * Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js @@ -49,15 +50,15 @@ * Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() * * DEPENDENCIES: -* GLFW3 - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX) -* raymath - 3D math functionality (Vector3, Matrix, Quaternion) +* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD) +* raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) * camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person) * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2017 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -88,6 +89,11 @@ #include "raylib.h" +#if (defined(__linux__) || defined(PLATFORM_WEB)) && _POSIX_S_SOURCE < 199309L + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. +#endif + #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 #include "utils.h" // Required for: fopen() Android mapping @@ -110,10 +116,6 @@ #include "external/rgif.h" // Support GIF recording #endif -#if defined(__linux__) || defined(PLATFORM_WEB) - /*#define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.*/ -#endif - #include // Standard input / output lib #include // Required for: malloc(), free(), rand(), atexit() #include // Required for: typedef unsigned long long int uint64_t, used by hi-res timer @@ -151,7 +153,6 @@ //#define GLFW_DLL // Using GLFW DLL on Windows -> No, we use static version! #if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32) - // NOTE: Those functions require linking with winmm library __stdcall unsigned int timeBeginPeriod(unsigned int uPeriod); __stdcall unsigned int timeEndPeriod(unsigned int uPeriod); #endif @@ -413,7 +414,7 @@ static void *GamepadThread(void *arg); // Mouse reading thread // NOTE: data parameter could be used to pass any kind of required data to the initialization void InitWindow(int width, int height, void *data) { - TraceLog(LOG_INFO, "Initializing raylib (v1.8.0)"); + TraceLog(LOG_INFO, "Initializing raylib (v1.9-dev)"); // Input data is window title char data windowTitle = (char *)data; @@ -477,7 +478,7 @@ void InitWindow(int width, int height, void *data) // NOTE: data parameter could be used to pass any kind of required data to the initialization void InitWindow(int width, int height, void *data) { - TraceLog(LOG_INFO, "Initializing raylib (v1.8.0)"); + TraceLog(LOG_INFO, "Initializing raylib (v1.9-dev)"); screenWidth = width; screenHeight = height; diff --git a/raylib/external/dr_flac.h b/raylib/external/dr_flac.h index 5388330..60f57ec 100644 --- a/raylib/external/dr_flac.h +++ b/raylib/external/dr_flac.h @@ -1,13 +1,13 @@ // FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file. -// dr_flac - v0.4c - 2016-12-26 +// dr_flac - v0.8d - 2017-09-22 // // David Reid - mackron@gmail.com // USAGE // // dr_flac is a single-file library. To use it, do something like the following in one .c file. -// #define DR_FLAC_IMPLEMENTATION -// #include "dr_flac.h" +// #define DR_FLAC_IMPLEMENTATION +// #include "dr_flac.h" // // You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, // do something like the following: @@ -17,8 +17,8 @@ // // Failed to open FLAC file // } // -// int32_t* pSamples = malloc(pFlac->totalSampleCount * sizeof(int32_t)); -// uint64_t numberOfInterleavedSamplesActuallyRead = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSamples); +// drflac_int32* pSamples = malloc(pFlac->totalSampleCount * sizeof(drflac_int32)); +// drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSamples); // // The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of // channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are @@ -43,8 +43,8 @@ // // unsigned int channels; // unsigned int sampleRate; -// uint64_t totalSampleCount; -// int32_t* pSampleData = drflac_open_and_decode_file("MySong.flac", &channels, &sampleRate, &totalSampleCount); +// drflac_uint64 totalSampleCount; +// drflac_int32* pSampleData = drflac_open_and_decode_file_s32("MySong.flac", &channels, &sampleRate, &totalSampleCount); // if (pSampleData == NULL) { // // Failed to open and decode FLAC file. // } @@ -54,13 +54,25 @@ // drflac_free(pSampleData); // // +// You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs +// respectively, but note that these should be considered lossy. +// +// // If you need access to metadata (album art, etc.), use drflac_open_with_metadata(), drflac_open_file_with_metdata() or // drflac_open_memory_with_metadata(). The rationale for keeping these APIs separate is that they're slightly slower than the // normal versions and also just a little bit harder to use. // // dr_flac reports metadata to the application through the use of a callback, and every metadata block is reported before -// drflac_open_with_metdata() returns. See https://github.com/mackron/dr_libs_tests/blob/master/dr_flac/dr_flac_test_2.c for -// an example on how to read metadata. +// drflac_open_with_metdata() returns. +// +// +// The main opening APIs (drflac_open(), etc.) will fail if the header is not present. The presents a problem in certain +// scenarios such as broadcast style streams like internet radio where the header may not be present because the user has +// started playback mid-stream. To handle this, use the relaxed APIs: drflac_open_relaxed() and drflac_open_with_metadata_relaxed(). +// +// It is not recommended to use these APIs for file based streams because a missing header would usually indicate a +// corrupted or perverse file. In addition, these APIs can take a long time to initialize because they may need to spend +// a lot of time finding the first frame. // // // @@ -84,54 +96,53 @@ // returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of // onRead(), or increase it if it's very inefficient. Must be a multiple of 8. // +// #define DR_FLAC_NO_CRC +// Disables CRC checks. This will offer a performance boost when CRC is unnecessary. +// +// #define DR_FLAC_NO_SIMD +// Disables SIMD optimizations (SSE on x86/x64 architectures). Use this if you are having compatibility issues with your +// compiler. +// // // // QUICK NOTES -// - Based on my tests, the performance of the 32-bit build is at about parity with the reference implementation. The 64-bit build -// is slightly faster. -// - dr_flac does not currently do any CRC checks. -// - dr_flac should work fine with valid native FLAC files, but for broadcast streams it won't work if the header and STREAMINFO -// block is unavailable. // - Audio data is output as signed 32-bit PCM, regardless of the bits per sample the FLAC stream is encoded as. // - This has not been tested on big-endian architectures. // - Rice codes in unencoded binary form (see https://xiph.org/flac/format.html#rice_partition) has not been tested. If anybody // knows where I can find some test files for this, let me know. -// - Perverse and erroneous files have not been tested. Again, if you know where I can get some test files let me know. // - dr_flac is not thread-safe, but it's APIs can be called from any thread so long as you do your own synchronization. +// - When using Ogg encapsulation, a corrupted metadata block will result in drflac_open_with_metadata() and drflac_open() +// returning inconsistent samples. #ifndef dr_flac_h #define dr_flac_h -#include #include -#ifndef DR_SIZED_TYPES_DEFINED -#define DR_SIZED_TYPES_DEFINED #if defined(_MSC_VER) && _MSC_VER < 1600 -typedef signed char dr_int8; -typedef unsigned char dr_uint8; -typedef signed short dr_int16; -typedef unsigned short dr_uint16; -typedef signed int dr_int32; -typedef unsigned int dr_uint32; -typedef signed __int64 dr_int64; -typedef unsigned __int64 dr_uint64; +typedef signed char drflac_int8; +typedef unsigned char drflac_uint8; +typedef signed short drflac_int16; +typedef unsigned short drflac_uint16; +typedef signed int drflac_int32; +typedef unsigned int drflac_uint32; +typedef signed __int64 drflac_int64; +typedef unsigned __int64 drflac_uint64; #else #include -typedef int8_t dr_int8; -typedef uint8_t dr_uint8; -typedef int16_t dr_int16; -typedef uint16_t dr_uint16; -typedef int32_t dr_int32; -typedef uint32_t dr_uint32; -typedef int64_t dr_int64; -typedef uint64_t dr_uint64; -#endif -typedef dr_int8 dr_bool8; -typedef dr_int32 dr_bool32; -#define DR_TRUE 1 -#define DR_FALSE 0 +typedef int8_t drflac_int8; +typedef uint8_t drflac_uint8; +typedef int16_t drflac_int16; +typedef uint16_t drflac_uint16; +typedef int32_t drflac_int32; +typedef uint32_t drflac_uint32; +typedef int64_t drflac_int64; +typedef uint64_t drflac_uint64; #endif +typedef drflac_uint8 drflac_bool8; +typedef drflac_uint32 drflac_bool32; +#define DRFLAC_TRUE 1 +#define DRFLAC_FALSE 0 // As data is read from the client it is placed into an internal buffer for fast access. This controls the // size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing @@ -156,9 +167,9 @@ extern "C" { #endif #ifdef DRFLAC_64BIT -typedef uint64_t drflac_cache_t; +typedef drflac_uint64 drflac_cache_t; #else -typedef uint32_t drflac_cache_t; +typedef drflac_uint32 drflac_cache_t; #endif // The various metadata block types. @@ -197,7 +208,8 @@ typedef uint32_t drflac_cache_t; typedef enum { drflac_container_native, - drflac_container_ogg + drflac_container_ogg, + drflac_container_unknown } drflac_container; typedef enum @@ -210,29 +222,29 @@ typedef enum #pragma pack(2) typedef struct { - uint64_t firstSample; - uint64_t frameOffset; // The offset from the first byte of the header of the first frame. - uint16_t sampleCount; + drflac_uint64 firstSample; + drflac_uint64 frameOffset; // The offset from the first byte of the header of the first frame. + drflac_uint16 sampleCount; } drflac_seekpoint; #pragma pack() typedef struct { - uint16_t minBlockSize; - uint16_t maxBlockSize; - uint32_t minFrameSize; - uint32_t maxFrameSize; - uint32_t sampleRate; - uint8_t channels; - uint8_t bitsPerSample; - uint64_t totalSampleCount; - uint8_t md5[16]; + drflac_uint16 minBlockSize; + drflac_uint16 maxBlockSize; + drflac_uint32 minFrameSize; + drflac_uint32 maxFrameSize; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalSampleCount; + drflac_uint8 md5[16]; } drflac_streaminfo; typedef struct { // The metadata type. Use this to know how to interpret the data below. - uint32_t type; + drflac_uint32 type; // A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to // not modify the contents of this buffer. Use the structures below for more meaningful and structured @@ -240,7 +252,7 @@ typedef struct const void* pRawData; // The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. - uint32_t rawDataSize; + drflac_uint32 rawDataSize; union { @@ -253,50 +265,49 @@ typedef struct struct { - uint32_t id; + drflac_uint32 id; const void* pData; - uint32_t dataSize; + drflac_uint32 dataSize; } application; struct { - uint32_t seekpointCount; + drflac_uint32 seekpointCount; const drflac_seekpoint* pSeekpoints; } seektable; struct { - uint32_t vendorLength; + drflac_uint32 vendorLength; const char* vendor; - uint32_t commentCount; + drflac_uint32 commentCount; const char* comments; } vorbis_comment; struct { char catalog[128]; - uint64_t leadInSampleCount; - dr_bool32 isCD; - uint8_t trackCount; - const uint8_t* pTrackData; + drflac_uint64 leadInSampleCount; + drflac_bool32 isCD; + drflac_uint8 trackCount; + const drflac_uint8* pTrackData; } cuesheet; struct { - uint32_t type; - uint32_t mimeLength; + drflac_uint32 type; + drflac_uint32 mimeLength; const char* mime; - uint32_t descriptionLength; + drflac_uint32 descriptionLength; const char* description; - uint32_t width; - uint32_t height; - uint32_t colorDepth; - uint32_t indexColorCount; - uint32_t pictureDataSize; - const uint8_t* pPictureData; + drflac_uint32 width; + drflac_uint32 height; + drflac_uint32 colorDepth; + drflac_uint32 indexColorCount; + drflac_uint32 pictureDataSize; + const drflac_uint8* pPictureData; } picture; } data; - } drflac_metadata; @@ -307,6 +318,9 @@ typedef struct // bytesToRead [in] The number of bytes to read. // // Returns the number of bytes actually read. +// +// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until +// either the entire bytesToRead is filled or you have reached the end of the stream. typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); // Callback for when data needs to be seeked. @@ -319,7 +333,7 @@ typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t by // // The offset will never be negative. Whether or not it is relative to the beginning or current position is determined // by the "origin" parameter which will be either drflac_seek_origin_start or drflac_seek_origin_current. -typedef dr_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); +typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); // Callback for when a metadata block is read. // @@ -333,7 +347,7 @@ typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); // Structure for internal use. Only used for decoders opened with drflac_open_memory. typedef struct { - const uint8_t* data; + const drflac_uint8* data; size_t dataSize; size_t currentReadPos; } drflac__memory_stream; @@ -360,64 +374,67 @@ typedef struct drflac_cache_t unalignedCache; // The index of the next valid cache line in the "L2" cache. - size_t nextL2Line; + drflac_uint32 nextL2Line; // The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. - size_t consumedBits; + drflac_uint32 consumedBits; // The cached data which was most recently read from the client. There are two levels of cache. Data flows as such: // Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions. drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; drflac_cache_t cache; + // CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this + // is reset to 0 at the beginning of each frame. + drflac_uint16 crc16; + drflac_cache_t crc16Cache; // A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. + drflac_uint32 crc16CacheIgnoredBytes; // The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. } drflac_bs; typedef struct { // The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. - uint8_t subframeType; + drflac_uint8 subframeType; // The number of wasted bits per sample as specified by the sub-frame header. - uint8_t wastedBitsPerSample; + drflac_uint8 wastedBitsPerSample; // The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. - uint8_t lpcOrder; + drflac_uint8 lpcOrder; // The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because // an extra bit is required for side channels when interchannel decorrelation is being used. - uint32_t bitsPerSample; - - // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData, or - // NULL if the heap is not being used. Note that it's a signed 32-bit integer for each value. - int32_t* pDecodedSamples; + drflac_uint32 bitsPerSample; + // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. Note that + // it's a signed 32-bit integer for each value. + drflac_int32* pDecodedSamples; } drflac_subframe; typedef struct { // If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will // always be set to 0. - uint64_t sampleNumber; + drflac_uint64 sampleNumber; // If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. - uint32_t frameNumber; + drflac_uint32 frameNumber; // The sample rate of this frame. - uint32_t sampleRate; + drflac_uint32 sampleRate; // The number of samples in each sub-frame within this frame. - uint16_t blockSize; + drflac_uint16 blockSize; // The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this // will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. - uint8_t channelAssignment; + drflac_uint8 channelAssignment; // The number of bits per sample within this frame. - uint8_t bitsPerSample; - - // The frame's CRC. This is set, but unused at the moment. - uint8_t crc8; + drflac_uint8 bitsPerSample; + // The frame's CRC. + drflac_uint8 crc8; } drflac_frame_header; typedef struct @@ -427,11 +444,10 @@ typedef struct // The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples // are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. - uint32_t samplesRemaining; + drflac_uint32 samplesRemaining; // The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. drflac_subframe subframes[8]; - } drflac_frame; typedef struct @@ -444,22 +460,22 @@ typedef struct // The sample rate. Will be set to something like 44100. - uint32_t sampleRate; + drflac_uint32 sampleRate; // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the // value specified in the STREAMINFO block. - uint8_t channels; + drflac_uint8 channels; // The bits per sample. Will be set to somthing like 16, 24, etc. - uint8_t bitsPerSample; + drflac_uint8 bitsPerSample; // The maximum block size, in samples. This number represents the number of samples in each channel (not combined). - uint16_t maxBlockSize; + drflac_uint16 maxBlockSize; // The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels, // with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a // valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio. - uint64_t totalSampleCount; + drflac_uint64 totalSampleCount; // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. @@ -467,34 +483,34 @@ typedef struct // The position of the seektable in the file. - uint64_t seektablePos; + drflac_uint64 seektablePos; // The size of the seektable. - uint32_t seektableSize; + drflac_uint32 seektableSize; // Information about the frame the decoder is currently sitting on. drflac_frame currentFrame; // The position of the first frame in the stream. This is only ever used for seeking. - uint64_t firstFramePos; + drflac_uint64 firstFramePos; // A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). drflac__memory_stream memoryStream; - // A pointer to the decoded sample data. This is an offset of pExtraData. - int32_t* pDecodedSamples; + drflac_int32* pDecodedSamples; + // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. + void* _oggbs; // The bit streamer. The raw FLAC data is fed through this object. drflac_bs bs; - // Variable length extra data. We attach this to the end of the object so we avoid unnecessary mallocs. - uint8_t pExtraData[1]; - + // Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. + drflac_uint8 pExtraData[1]; } drflac; @@ -515,11 +531,22 @@ typedef struct // This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory() // to open the stream from a file or from a block of memory respectively. // -// The STREAMINFO block must be present for this to succeed. +// The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where +// the header may not be present. // // See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close() drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData); +// The same as drflac_open(), except attempts to open the stream even when a header block is not present. +// +// Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do +// not set this to drflac_container_unknown - that is for internal use only. +// +// Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never +// found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an +// indicator that the end of the stream was found. +drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData); + // Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). // // onRead [in] The function to call when data needs to be read from the client. @@ -531,15 +558,29 @@ drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUse // // Close the decoder with drflac_close(). // -// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a malloc() -// and free() for every metadata block except for STREAMINFO and PADDING blocks. +// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a DRFLAC_MALLOC() +// and DRFLAC_FREE() for every metadata block except for STREAMINFO and PADDING blocks. // -// The caller is notified of the metadata via the onMeta callback. All metadata blocks with be handled before the function +// The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function // returns. // +// The STREAMINFO block must be present for this to succeed. Use drflac_open_with_metadata_relaxed() to open a FLAC +// stream where the header may not be present. +// +// Note that this will behave inconsistently with drflac_open() if the stream is an Ogg encapsulated stream and a metadata +// block is corrupted. This is due to the way the Ogg stream recovers from corrupted pages. When drflac_open_with_metadata() +// is being used, the open routine will try to read the contents of the metadata block, whereas drflac_open() will simply +// seek past it (for the sake of efficiency). This inconsistency can result in different samples being returned depending on +// whether or not the stream is being opened with metadata. +// // See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close() drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData); +// The same as drflac_open_with_metadata(), except attemps to open the stream even when a header block is not present. +// +// See also: drflac_open_with_metadata(), drflac_open_relaxed() +drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData); + // Closes the given FLAC decoder. // // pFlac [in] The decoder to close. @@ -558,25 +599,50 @@ void drflac_close(drflac* pFlac); // // pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples // seeked. -uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* pBufferOut); +drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* pBufferOut); -// Same as drflac_read_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit. Note -// that this is lossey. -uint64_t drflac_read_s16(drflac* pFlac, uint64_t samplesToRead, int16_t* pBufferOut); +// Same as drflac_read_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit. +// +// pFlac [in] The decoder. +// samplesToRead [in] The number of samples to read. +// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. +// +// Returns the number of samples actually read. +// +// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples +// seeked. +// +// Note that this is lossy for streams where the bits per sample is larger than 16. +drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut); + +// Same as drflac_read_s32(), except outputs samples as 32-bit floating-point PCM. +// +// pFlac [in] The decoder. +// samplesToRead [in] The number of samples to read. +// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. +// +// Returns the number of samples actually read. +// +// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples +// seeked. +// +// Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly +// represent every possible number. +drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut); // Seeks to the sample at the given index. // // pFlac [in] The decoder. // sampleIndex [in] The index of the sample to seek to. See notes below. // -// Returns DR_TRUE if successful; DR_FALSE otherwise. +// Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise. // // The sample index is based on interleaving. In a stereo stream, for example, the sample at index 0 is the first sample // in the left channel; the sample at index 1 is the first sample on the right channel, and so on. // // When seeking, you will likely want to ensure it's rounded to a multiple of the channel count. You can do this with // something like drflac_seek_to_sample(pFlac, (mySampleIndex + (mySampleIndex % pFlac->channels))) -dr_bool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex); +drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex); @@ -618,43 +684,58 @@ drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drfl //// High Level APIs //// // Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a -// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with drflac_free(). +// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with DRFLAC_FREE(). // // Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously // read samples into a dynamically sized buffer on the heap until no samples are left. // // Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). -int32_t* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); -int16_t* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); +drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_s32(), except returns signed 16-bit integer samples. +drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_s32(), except returns 32-bit floating-point samples. +float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); #ifndef DR_FLAC_NO_STDIO // Same as drflac_open_and_decode_s32() except opens the decoder from a file. -int32_t* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); -int16_t* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); +drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_file_s32(), except returns signed 16-bit integer samples. +drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_file_f32(), except returns 32-bit floating-point samples. +float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); #endif // Same as drflac_open_and_decode_s32() except opens the decoder from a block of memory. -int32_t* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); -int16_t* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); +drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); -// Frees data returned by drflac_open_and_decode_*(). -void drflac_free(void* pSampleDataReturnedByOpenAndDecode); +// Same as drflac_open_and_decode_memory_s32(), except returns signed 16-bit integer samples. +drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_memory_s32(), except returns 32-bit floating-point samples. +float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Frees memory that was allocated internally by dr_flac. +void drflac_free(void* p); // Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. typedef struct { - uint32_t countRemaining; + drflac_uint32 countRemaining; const char* pRunningData; } drflac_vorbis_comment_iterator; // Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT // metadata block. -void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, uint32_t commentCount, const char* pComments); +void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments); // Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The // returned string is NOT null terminated. -const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, uint32_t* pCommentLengthOut); +const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); @@ -672,24 +753,119 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, ui #ifdef DR_FLAC_IMPLEMENTATION #include #include -#include -#ifdef _MSC_VER -#include // For _byteswap_ulong and _byteswap_uint64 +// CPU architecture. +#if defined(__x86_64__) || defined(_M_X64) +#define DRFLAC_X64 +#elif defined(__i386) || defined(_M_IX86) +#define DRFLAC_X86 #endif +// Compile-time CPU feature support. +#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) + #ifdef _MSC_VER + #if _MSC_VER >= 1400 + #include + static void drflac__cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define DRFLAC_NO_CPUID + #endif + #else + #if defined(__GNUC__) || defined(__clang__) + static void drflac__cpuid(int info[4], int fid) + { + asm ( + "movl %[fid], %%eax\n\t" + "cpuid\n\t" + "movl %%eax, %[info0]\n\t" + "movl %%ebx, %[info1]\n\t" + "movl %%ecx, %[info2]\n\t" + "movl %%edx, %[info3]\n\t" + : [info0] "=rm"(info[0]), + [info1] "=rm"(info[1]), + [info2] "=rm"(info[2]), + [info3] "=rm"(info[3]) + : [fid] "rm"(fid) + : "eax", "ebx", "ecx", "edx" + ); + } + #else + #define DRFLAC_NO_CPUID + #endif + #endif +#else +#define DRFLAC_NO_CPUID +#endif + + #ifdef __linux__ -#ifndef _BSD_SOURCE #define _BSD_SOURCE -#endif #include #endif +#if defined(_MSC_VER) && _MSC_VER >= 1500 +#define DRFLAC_HAS_LZCNT_INTRINSIC +#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) +#define DRFLAC_HAS_LZCNT_INTRINSIC +#elif defined(__clang__) + #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) + #define DRFLAC_HAS_LZCNT_INTRINSIC + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1300 +#define DRFLAC_HAS_BYTESWAP_INTRINSIC +#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +#define DRFLAC_HAS_BYTESWAP_INTRINSIC +#elif defined(__clang__) + #if __has_builtin(__builtin_bswap16) && __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64) + #define DRFLAC_HAS_BYTESWAP_INTRINSIC + #endif +#endif + + +// Standard library stuff. +#ifndef DRFLAC_ASSERT +#include +#define DRFLAC_ASSERT(expression) assert(expression) +#endif +#ifndef DRFLAC_MALLOC +#define DRFLAC_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRFLAC_REALLOC +#define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRFLAC_FREE +#define DRFLAC_FREE(p) free((p)) +#endif +#ifndef DRFLAC_COPY_MEMORY +#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRFLAC_ZERO_MEMORY +#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif + +#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future. + #ifdef _MSC_VER #define DRFLAC_INLINE __forceinline #else +#ifdef __GNUC__ +#define DRFLAC_INLINE inline __attribute__((always_inline)) +#else #define DRFLAC_INLINE inline #endif +#endif + +typedef drflac_int32 drflac_result; +#define DRFLAC_SUCCESS 0 +#define DRFLAC_ERROR -1 // A generic error. +#define DRFLAC_INVALID_ARGS -2 +#define DRFLAC_END_OF_STREAM -128 +#define DRFLAC_CRC_MISMATCH -129 #define DRFLAC_SUBFRAME_CONSTANT 0 #define DRFLAC_SUBFRAME_VERBATIM 1 @@ -706,31 +882,68 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, ui #define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 -//// Endian Management //// -static DRFLAC_INLINE dr_bool32 drflac__is_little_endian() +#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define drflac_assert DRFLAC_ASSERT +#define drflac_copy_memory DRFLAC_COPY_MEMORY +#define drflac_zero_memory DRFLAC_ZERO_MEMORY + + +// CPU caps. +static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; +#ifndef DRFLAC_NO_CPUID +static drflac_bool32 drflac__gIsSSE42Supported = DRFLAC_FALSE; +static void drflac__init_cpu_caps() { + int info[4] = {0}; + + // LZCNT + drflac__cpuid(info, 0x80000001); + drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; + + // SSE4.2 + drflac__cpuid(info, 1); + drflac__gIsSSE42Supported = (info[2] & (1 << 19)) != 0; +} +#endif + + +//// Endian Management //// +static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian() +{ +#if defined(DRFLAC_X86) || defined(DRFLAC_X64) + return DRFLAC_TRUE; +#else int n = 1; return (*(char*)&n) == 1; +#endif } -static DRFLAC_INLINE uint16_t drflac__swap_endian_uint16(uint16_t n) +static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) { -#ifdef _MSC_VER - return _byteswap_ushort(n); -#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - return __builtin_bswap16(n); +#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif #else return ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); #endif } -static DRFLAC_INLINE uint32_t drflac__swap_endian_uint32(uint32_t n) +static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) { -#ifdef _MSC_VER - return _byteswap_ulong(n); -#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - return __builtin_bswap32(n); +#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap32(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif #else return ((n & 0xFF000000) >> 24) | ((n & 0x00FF0000) >> 8) | @@ -739,25 +952,30 @@ static DRFLAC_INLINE uint32_t drflac__swap_endian_uint32(uint32_t n) #endif } -static DRFLAC_INLINE uint64_t drflac__swap_endian_uint64(uint64_t n) +static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) { -#ifdef _MSC_VER - return _byteswap_uint64(n); -#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - return __builtin_bswap64(n); +#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif #else - return ((n & 0xFF00000000000000ULL) >> 56) | - ((n & 0x00FF000000000000ULL) >> 40) | - ((n & 0x0000FF0000000000ULL) >> 24) | - ((n & 0x000000FF00000000ULL) >> 8) | - ((n & 0x00000000FF000000ULL) << 8) | - ((n & 0x0000000000FF0000ULL) << 24) | - ((n & 0x000000000000FF00ULL) << 40) | - ((n & 0x00000000000000FFULL) << 56); + return ((n & (drflac_uint64)0xFF00000000000000) >> 56) | + ((n & (drflac_uint64)0x00FF000000000000) >> 40) | + ((n & (drflac_uint64)0x0000FF0000000000) >> 24) | + ((n & (drflac_uint64)0x000000FF00000000) >> 8) | + ((n & (drflac_uint64)0x00000000FF000000) << 8) | + ((n & (drflac_uint64)0x0000000000FF0000) << 24) | + ((n & (drflac_uint64)0x000000000000FF00) << 40) | + ((n & (drflac_uint64)0x00000000000000FF) << 56); #endif } -static DRFLAC_INLINE uint16_t drflac__be2host_16(uint16_t n) + +static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) { #ifdef __linux__ return be16toh(n); @@ -770,7 +988,7 @@ static DRFLAC_INLINE uint16_t drflac__be2host_16(uint16_t n) #endif } -static DRFLAC_INLINE uint32_t drflac__be2host_32(uint32_t n) +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) { #ifdef __linux__ return be32toh(n); @@ -783,7 +1001,7 @@ static DRFLAC_INLINE uint32_t drflac__be2host_32(uint32_t n) #endif } -static DRFLAC_INLINE uint64_t drflac__be2host_64(uint64_t n) +static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) { #ifdef __linux__ return be64toh(n); @@ -797,7 +1015,7 @@ static DRFLAC_INLINE uint64_t drflac__be2host_64(uint64_t n) } -static DRFLAC_INLINE uint32_t drflac__le2host_32(uint32_t n) +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) { #ifdef __linux__ return le32toh(n); @@ -811,13 +1029,242 @@ static DRFLAC_INLINE uint32_t drflac__le2host_32(uint32_t n) } +static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) +{ + drflac_uint32 result = 0; + result |= (n & 0x7F000000) >> 3; + result |= (n & 0x007F0000) >> 2; + result |= (n & 0x00007F00) >> 1; + result |= (n & 0x0000007F) >> 0; + + return result; +} + + + +// The CRC code below is based on this document: http://zlib.net/crc_v3.txt +static drflac_uint8 drflac__crc8_table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +static drflac_uint16 drflac__crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; + +static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) +{ + return drflac__crc8_table[crc ^ data]; +} + +static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) +{ + drflac_assert(count <= 32); + +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") + drflac_uint8 p = 0x07; + for (int i = count-1; i >= 0; --i) { + drflac_uint8 bit = (data & (1 << i)) >> i; + if (crc & 0x80) { + crc = ((crc << 1) | bit) ^ p; + } else { + crc = ((crc << 1) | bit); + } + } + return crc; +#else + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) +{ + return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) +{ + switch (byteCount) + { +#ifdef DRFLAC_64BIT + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#endif + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + } + + return crc; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) +{ + drflac_assert(count <= 64); + +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") + drflac_uint16 p = 0x8005; + for (int i = count-1; i >= 0; --i) { + drflac_uint16 bit = (data & (1ULL << i)) >> i; + if (r & 0x8000) { + r = ((r << 1) | bit) ^ p; + } else { + r = ((r << 1) | bit); + } + } + + return crc; +#else + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) +{ + drflac_assert(count <= 64); + +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0xFF00000000000000 << leftoverBits)) >> (56 + leftoverBits))); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00FF000000000000 << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000FF0000000000 << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000FF00000000 << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000FF000000 << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000000000FF0000 << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000000000FF00 << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000000000FF << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +} + + +static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) +{ +#ifdef DRFLAC_64BIT + return drflac_crc16__64bit(crc, data, count); +#else + return drflac_crc16__32bit(crc, data, count); +#endif +} + + #ifdef DRFLAC_64BIT #define drflac__be2host__cache_line drflac__be2host_64 #else #define drflac__be2host__cache_line drflac__be2host_32 #endif - // BIT READING ATTEMPT #2 // // This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting @@ -829,9 +1276,9 @@ static DRFLAC_INLINE uint32_t drflac__le2host_32(uint32_t n) #define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) #define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - ((bs)->consumedBits)) #ifdef DRFLAC_64BIT -#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((uint64_t)-1LL) >> (_bitCount))) +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((drflac_uint64)-1LL) >> (_bitCount))) #else -#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((uint32_t)-1) >> (_bitCount))) +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((drflac_uint32)-1) >> (_bitCount))) #endif #define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) #define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) @@ -840,18 +1287,54 @@ static DRFLAC_INLINE uint32_t drflac__le2host_32(uint32_t n) #define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) #define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) -static DRFLAC_INLINE dr_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) + +#ifndef DR_FLAC_NO_CRC +static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) +{ + bs->crc16 = 0; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +} + +static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) +{ + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16CacheIgnoredBytes = 0; +} + +static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) +{ + // We should never be flushing in a situation where we are not aligned on a byte boundary. + drflac_assert((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + + // The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined + // by the number of bits that have been consumed. + if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + drflac__update_crc16(bs); + } else { + // We only accumulate the consumed bits. + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + + // The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated + // so we can handle that later. + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; + } + + return bs->crc16; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) { // Fast path. Try loading straight from L2. if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DR_TRUE; + return DRFLAC_TRUE; } // If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's // any left. if (bs->unalignedByteCount > 0) { - return DR_FALSE; // If we have any unaligned bytes it means there's not more aligned bytes left in the client. + return DRFLAC_FALSE; // If we have any unaligned bytes it means there's no more aligned bytes left in the client. } size_t bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); @@ -859,7 +1342,7 @@ static DRFLAC_INLINE dr_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) bs->nextL2Line = 0; if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DR_TRUE; + return DRFLAC_TRUE; } @@ -872,35 +1355,39 @@ static DRFLAC_INLINE dr_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) // We need to keep track of any unaligned bytes for later use. bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); if (bs->unalignedByteCount > 0) { - bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; + bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; } - if (alignedL1LineCount > 0) - { + if (alignedL1LineCount > 0) { size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; for (size_t i = alignedL1LineCount; i > 0; --i) { bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; } - bs->nextL2Line = offset; + bs->nextL2Line = (drflac_uint32)offset; bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DR_TRUE; - } - else - { + return DRFLAC_TRUE; + } else { // If we get into this branch it means we weren't able to load any L1-aligned data. bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); - return DR_FALSE; + return DRFLAC_FALSE; } } -static dr_bool32 drflac__reload_cache(drflac_bs* bs) +static drflac_bool32 drflac__reload_cache(drflac_bs* bs) { +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); +#endif + // Fast path. Try just moving the next value in the L2 cache to the L1 cache. if (drflac__reload_l1_cache_from_l2(bs)) { bs->cache = drflac__be2host__cache_line(bs->cache); bs->consumedBits = 0; - return DR_TRUE; +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + return DRFLAC_TRUE; } // Slow path. @@ -910,15 +1397,21 @@ static dr_bool32 drflac__reload_cache(drflac_bs* bs) // data from the unaligned cache. size_t bytesRead = bs->unalignedByteCount; if (bytesRead == 0) { - return DR_FALSE; + return DRFLAC_FALSE; } - assert(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); - bs->consumedBits = (DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + drflac_assert(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; bs->cache = drflac__be2host__cache_line(bs->unalignedCache); bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs->consumedBits); // <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. - return DR_TRUE; + bs->unalignedByteCount = 0; // <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache >> bs->consumedBits; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +#endif + return DRFLAC_TRUE; } static void drflac__reset_cache(drflac_bs* bs) @@ -928,69 +1421,24 @@ static void drflac__reset_cache(drflac_bs* bs) bs->cache = 0; bs->unalignedByteCount = 0; // <-- This clears the trailing unaligned bytes. bs->unalignedCache = 0; + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = 0; + bs->crc16CacheIgnoredBytes = 0; +#endif } -static dr_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) + +static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) { - if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += bitsToSeek; - bs->cache <<= bitsToSeek; - return DR_TRUE; - } else { - // It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. - bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); - bs->cache = 0; - - size_t wholeBytesRemaining = bitsToSeek/8; - if (wholeBytesRemaining > 0) - { - // The next bytes to seek will be located in the L2 cache. The problem is that the L2 cache is not byte aligned, - // but rather DRFLAC_CACHE_L1_SIZE_BYTES aligned (usually 4 or 8). If, for example, the number of bytes to seek is - // 3, we'll need to handle it in a special way. - size_t wholeCacheLinesRemaining = wholeBytesRemaining / DRFLAC_CACHE_L1_SIZE_BYTES(bs); - if (wholeCacheLinesRemaining < DRFLAC_CACHE_L2_LINES_REMAINING(bs)) - { - wholeBytesRemaining -= wholeCacheLinesRemaining * DRFLAC_CACHE_L1_SIZE_BYTES(bs); - bitsToSeek -= wholeCacheLinesRemaining * DRFLAC_CACHE_L1_SIZE_BITS(bs); - bs->nextL2Line += wholeCacheLinesRemaining; - } - else - { - wholeBytesRemaining -= DRFLAC_CACHE_L2_LINES_REMAINING(bs) * DRFLAC_CACHE_L1_SIZE_BYTES(bs); - bitsToSeek -= DRFLAC_CACHE_L2_LINES_REMAINING(bs) * DRFLAC_CACHE_L1_SIZE_BITS(bs); - bs->nextL2Line += DRFLAC_CACHE_L2_LINES_REMAINING(bs); - - if (wholeBytesRemaining > 0) { - bs->onSeek(bs->pUserData, (int)wholeBytesRemaining, drflac_seek_origin_current); - bitsToSeek -= wholeBytesRemaining*8; - } - } - } - - - if (bitsToSeek > 0) { - if (!drflac__reload_cache(bs)) { - return DR_FALSE; - } - - return drflac__seek_bits(bs, bitsToSeek); - } - - return DR_TRUE; - } -} - -static dr_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, uint32_t* pResultOut) -{ - assert(bs != NULL); - assert(pResultOut != NULL); - assert(bitCount > 0); - assert(bitCount <= 32); + drflac_assert(bs != NULL); + drflac_assert(pResultOut != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 32); if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { if (!drflac__reload_cache(bs)) { - return DR_FALSE; + return DRFLAC_FALSE; } } @@ -1000,165 +1448,259 @@ static dr_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, uint3 bs->consumedBits += bitCount; bs->cache <<= bitCount; } else { - *pResultOut = (uint32_t)bs->cache; + *pResultOut = (drflac_uint32)bs->cache; bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; } - return DR_TRUE; + return DRFLAC_TRUE; } else { // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. - size_t bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); - size_t bitCountLo = bitCount - bitCountHi; - uint32_t resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); + drflac_uint32 bitCountLo = bitCount - bitCountHi; + drflac_uint32 resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); if (!drflac__reload_cache(bs)) { - return DR_FALSE; + return DRFLAC_FALSE; } *pResultOut = (resultHi << bitCountLo) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; - return DR_TRUE; + return DRFLAC_TRUE; } } -static dr_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, int32_t* pResult) +static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) { - assert(bs != NULL); - assert(pResult != NULL); - assert(bitCount > 0); - assert(bitCount <= 32); + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 32); - uint32_t result; + drflac_uint32 result; if (!drflac__read_uint32(bs, bitCount, &result)) { - return DR_FALSE; + return DRFLAC_FALSE; } - uint32_t signbit = ((result >> (bitCount-1)) & 0x01); + drflac_uint32 signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; - *pResult = (int32_t)result; - return DR_TRUE; + *pResult = (drflac_int32)result; + return DRFLAC_TRUE; } -static dr_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, uint64_t* pResultOut) +static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) { - assert(bitCount <= 64); - assert(bitCount > 32); + drflac_assert(bitCount <= 64); + drflac_assert(bitCount > 32); - uint32_t resultHi; + drflac_uint32 resultHi; if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { - return DR_FALSE; + return DRFLAC_FALSE; } - uint32_t resultLo; + drflac_uint32 resultLo; if (!drflac__read_uint32(bs, 32, &resultLo)) { - return DR_FALSE; + return DRFLAC_FALSE; } - *pResultOut = (((uint64_t)resultHi) << 32) | ((uint64_t)resultLo); - return DR_TRUE; + *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); + return DRFLAC_TRUE; } // Function below is unused, but leaving it here in case I need to quickly add it again. #if 0 -static dr_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, int64_t* pResultOut) +static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) { - assert(bitCount <= 64); + drflac_assert(bitCount <= 64); - uint64_t result; + drflac_uint64 result; if (!drflac__read_uint64(bs, bitCount, &result)) { - return DR_FALSE; + return DRFLAC_FALSE; } - uint64_t signbit = ((result >> (bitCount-1)) & 0x01); + drflac_uint64 signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; - *pResultOut = (int64_t)result; - return DR_TRUE; + *pResultOut = (drflac_int64)result; + return DRFLAC_TRUE; } #endif -static dr_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, uint16_t* pResult) +static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) { - assert(bs != NULL); - assert(pResult != NULL); - assert(bitCount > 0); - assert(bitCount <= 16); + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 16); - uint32_t result; + drflac_uint32 result; if (!drflac__read_uint32(bs, bitCount, &result)) { - return DR_FALSE; + return DRFLAC_FALSE; } - *pResult = (uint16_t)result; - return DR_TRUE; + *pResult = (drflac_uint16)result; + return DRFLAC_TRUE; } -static dr_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, int16_t* pResult) +#if 0 +static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) { - assert(bs != NULL); - assert(pResult != NULL); - assert(bitCount > 0); - assert(bitCount <= 16); + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 16); - int32_t result; + drflac_int32 result; if (!drflac__read_int32(bs, bitCount, &result)) { - return DR_FALSE; + return DRFLAC_FALSE; } - *pResult = (int16_t)result; - return DR_TRUE; + *pResult = (drflac_int16)result; + return DRFLAC_TRUE; } +#endif -static dr_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, uint8_t* pResult) +static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) { - assert(bs != NULL); - assert(pResult != NULL); - assert(bitCount > 0); - assert(bitCount <= 8); + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 8); - uint32_t result; + drflac_uint32 result; if (!drflac__read_uint32(bs, bitCount, &result)) { - return DR_FALSE; + return DRFLAC_FALSE; } - *pResult = (uint8_t)result; - return DR_TRUE; + *pResult = (drflac_uint8)result; + return DRFLAC_TRUE; } -static dr_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, int8_t* pResult) +static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) { - assert(bs != NULL); - assert(pResult != NULL); - assert(bitCount > 0); - assert(bitCount <= 8); + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 8); - int32_t result; + drflac_int32 result; if (!drflac__read_int32(bs, bitCount, &result)) { - return DR_FALSE; + return DRFLAC_FALSE; } - *pResult = (int8_t)result; - return DR_TRUE; + *pResult = (drflac_int8)result; + return DRFLAC_TRUE; } -static inline dr_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) { - unsigned int zeroCounter = 0; - while (bs->cache == 0) { - zeroCounter += (unsigned int)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) { - return DR_FALSE; + if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (drflac_uint32)bitsToSeek; + bs->cache <<= bitsToSeek; + return DRFLAC_TRUE; + } else { + // It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. + bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->cache = 0; + + // Simple case. Seek in groups of the same number as bits that fit within a cache line. +#ifdef DRFLAC_64BIT + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint64 bin; + if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#else + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint32 bin; + if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#endif + + // Whole leftover bytes. + while (bitsToSeek >= 8) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, 8, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= 8; + } + + // Leftover bits. + if (bitsToSeek > 0) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek = 0; // <-- Necessary for the assert below. + } + + drflac_assert(bitsToSeek == 0); + return DRFLAC_TRUE; + } +} + + +// This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. +static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) +{ + drflac_assert(bs != NULL); + + // The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first + // thing to do is align to the next byte. + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + + for (;;) { +#ifndef DR_FLAC_NO_CRC + drflac__reset_crc16(bs); +#endif + + drflac_uint8 hi; + if (!drflac__read_uint8(bs, 8, &hi)) { + return DRFLAC_FALSE; + } + + if (hi == 0xFF) { + drflac_uint8 lo; + if (!drflac__read_uint8(bs, 6, &lo)) { + return DRFLAC_FALSE; + } + + if (lo == 0x3E) { + return DRFLAC_TRUE; + } else { + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + } } } - // At this point the cache should not be zero, in which case we know the first set bit should be somewhere in here. There is - // no need for us to perform any cache reloading logic here which should make things much faster. - assert(bs->cache != 0); + // Should never get here. + //return DRFLAC_FALSE; +} - unsigned int bitOffsetTable[] = { + +#if !defined(DR_FLAC_NO_SIMD) && defined(DRFLAC_HAS_LZCNT_INTRINSIC) +#define DRFLAC_IMPLEMENT_CLZ_LZCNT +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#define DRFLAC_IMPLEMENT_CLZ_MSVC +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) +{ + static drflac_uint32 clz_table_4[] = { 0, 4, 3, 3, @@ -1166,91 +1708,169 @@ static inline dr_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned i 1, 1, 1, 1, 1, 1, 1, 1 }; - unsigned int setBitOffsetPlus1 = bitOffsetTable[DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, 4)]; - if (setBitOffsetPlus1 == 0) { - if (bs->cache == 1) { - setBitOffsetPlus1 = DRFLAC_CACHE_L1_SIZE_BITS(bs); - } else { - setBitOffsetPlus1 = 5; - for (;;) - { - if ((bs->cache & DRFLAC_CACHE_L1_SELECT(bs, setBitOffsetPlus1))) { - break; - } + drflac_uint32 n = clz_table_4[x >> (sizeof(x)*8 - 4)]; + if (n == 0) { +#ifdef DRFLAC_64BIT + if ((x & 0xFFFFFFFF00000000ULL) == 0) { n = 32; x <<= 32; } + if ((x & 0xFFFF000000000000ULL) == 0) { n += 16; x <<= 16; } + if ((x & 0xFF00000000000000ULL) == 0) { n += 8; x <<= 8; } + if ((x & 0xF000000000000000ULL) == 0) { n += 4; x <<= 4; } +#else + if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } + if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } + if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } +#endif + n += clz_table_4[x >> (sizeof(x)*8 - 4)]; + } - setBitOffsetPlus1 += 1; - } + return n - 1; +} + +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT +static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported() +{ + // If the compiler itself does not support the intrinsic then we'll need to return false. +#ifdef DRFLAC_HAS_LZCNT_INTRINSIC + return drflac__gIsLZCNTSupported; +#else + return DRFLAC_FALSE; +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) +{ +#ifdef _MSC_VER + #ifdef DRFLAC_64BIT + return (drflac_uint32)__lzcnt64(x); + #else + return (drflac_uint32)__lzcnt(x); + #endif +#else + #if defined(__GNUC__) || defined(__clang__) + #ifdef DRFLAC_64BIT + return (drflac_uint32)__builtin_clzll((unsigned long long)x); + #else + return (drflac_uint32)__builtin_clzl((unsigned long)x); + #endif + #else + // Unsupported compiler. + #error "This compiler does not support the lzcnt intrinsic." + #endif +#endif +} +#endif + +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC +static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) +{ + drflac_uint32 n; +#ifdef DRFLAC_64BIT + _BitScanReverse64((unsigned long*)&n, x); +#else + _BitScanReverse((unsigned long*)&n, x); +#endif + return sizeof(x)*8 - n - 1; +} +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) +{ + // This function assumes at least one bit is set. Checking for 0 needs to be done at a higher level, outside this function. +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT + if (drflac__is_lzcnt_supported()) { + return drflac__clz_lzcnt(x); + } else +#endif + { + #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC + return drflac__clz_msvc(x); + #else + return drflac__clz_software(x); + #endif + } +} + + +static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +{ + drflac_uint32 zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; } } + drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; + bs->consumedBits += setBitOffsetPlus1; bs->cache <<= setBitOffsetPlus1; *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return DR_TRUE; + return DRFLAC_TRUE; } -static dr_bool32 drflac__seek_to_byte(drflac_bs* bs, uint64_t offsetFromStart) +static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) { - assert(bs != NULL); - assert(offsetFromStart > 0); + drflac_assert(bs != NULL); + drflac_assert(offsetFromStart > 0); // Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which // is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit. // To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder. - if (offsetFromStart > 0x7FFFFFFF) - { - uint64_t bytesRemaining = offsetFromStart; + if (offsetFromStart > 0x7FFFFFFF) { + drflac_uint64 bytesRemaining = offsetFromStart; if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { - return DR_FALSE; + return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; - while (bytesRemaining > 0x7FFFFFFF) { if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { - return DR_FALSE; + return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; } - if (bytesRemaining > 0) { if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { - return DR_FALSE; + return DRFLAC_FALSE; } } - } - else - { + } else { if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { - return DR_FALSE; + return DRFLAC_FALSE; } } - // The cache should be reset to force a reload of fresh data from the client. drflac__reset_cache(bs); - return DR_TRUE; + return DRFLAC_TRUE; } -static dr_bool32 drflac__read_utf8_coded_number(drflac_bs* bs, uint64_t* pNumberOut) +static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) { - assert(bs != NULL); - assert(pNumberOut != NULL); + drflac_assert(bs != NULL); + drflac_assert(pNumberOut != NULL); + + drflac_uint8 crc = *pCRCOut; unsigned char utf8[7] = {0}; if (!drflac__read_uint8(bs, 8, utf8)) { *pNumberOut = 0; - return DR_FALSE; + return DRFLAC_END_OF_STREAM; } + crc = drflac_crc8(crc, utf8[0], 8); if ((utf8[0] & 0x80) == 0) { *pNumberOut = utf8[0]; - return DR_TRUE; + *pCRCOut = crc; + return DRFLAC_SUCCESS; } int byteCount = 1; @@ -1268,57 +1888,43 @@ static dr_bool32 drflac__read_utf8_coded_number(drflac_bs* bs, uint64_t* pNumber byteCount = 7; } else { *pNumberOut = 0; - return DR_FALSE; // Bad UTF-8 encoding. + return DRFLAC_CRC_MISMATCH; // Bad UTF-8 encoding. } // Read extra bytes. - assert(byteCount > 1); + drflac_assert(byteCount > 1); - uint64_t result = (uint64_t)(utf8[0] & (0xFF >> (byteCount + 1))); + drflac_uint64 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); for (int i = 1; i < byteCount; ++i) { if (!drflac__read_uint8(bs, 8, utf8 + i)) { *pNumberOut = 0; - return DR_FALSE; + return DRFLAC_END_OF_STREAM; } + crc = drflac_crc8(crc, utf8[i], 8); result = (result << 6) | (utf8[i] & 0x3F); } *pNumberOut = result; - return DR_TRUE; + *pCRCOut = crc; + return DRFLAC_SUCCESS; } -static DRFLAC_INLINE dr_bool32 drflac__read_and_seek_rice(drflac_bs* bs, uint8_t m) -{ - unsigned int unused; - if (!drflac__seek_past_next_set_bit(bs, &unused)) { - return DR_FALSE; - } - - if (m > 0) { - if (!drflac__seek_bits(bs, m)) { - return DR_FALSE; - } - } - - return DR_TRUE; -} - // The next two functions are responsible for calculating the prediction. // // When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's // safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. -static DRFLAC_INLINE int32_t drflac__calculate_prediction_32(uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pDecodedSamples) +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { - assert(order <= 32); + drflac_assert(order <= 32); // 32-bit version. // VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. - int32_t prediction = 0; + drflac_int32 prediction = 0; switch (order) { @@ -1356,137 +1962,137 @@ static DRFLAC_INLINE int32_t drflac__calculate_prediction_32(uint32_t order, int case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; } - return (int32_t)(prediction >> shift); + return (drflac_int32)(prediction >> shift); } -static DRFLAC_INLINE int32_t drflac__calculate_prediction_64(uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pDecodedSamples) +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { - assert(order <= 32); + drflac_assert(order <= 32); // 64-bit version. // This method is faster on the 32-bit build when compiling with VC++. See note below. #ifndef DRFLAC_64BIT - int64_t prediction; + drflac_int64 prediction; if (order == 8) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; - prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; - prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; - prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; - prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; - prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; } else if (order == 7) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; - prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; - prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; - prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; - prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; } else if (order == 3) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; } else if (order == 6) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; - prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; - prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; - prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; } else if (order == 5) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; - prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; - prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; } else if (order == 4) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; - prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; } else if (order == 12) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; - prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; - prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; - prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; - prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; - prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; - prediction += coefficients[8] * (int64_t)pDecodedSamples[-9]; - prediction += coefficients[9] * (int64_t)pDecodedSamples[-10]; - prediction += coefficients[10] * (int64_t)pDecodedSamples[-11]; - prediction += coefficients[11] * (int64_t)pDecodedSamples[-12]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; } else if (order == 2) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; } else if (order == 1) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; } else if (order == 10) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; - prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; - prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; - prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; - prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; - prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; - prediction += coefficients[8] * (int64_t)pDecodedSamples[-9]; - prediction += coefficients[9] * (int64_t)pDecodedSamples[-10]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; } else if (order == 9) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; - prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; - prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; - prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; - prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; - prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; - prediction += coefficients[8] * (int64_t)pDecodedSamples[-9]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; } else if (order == 11) { - prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; - prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; - prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; - prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; - prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; - prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; - prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; - prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; - prediction += coefficients[8] * (int64_t)pDecodedSamples[-9]; - prediction += coefficients[9] * (int64_t)pDecodedSamples[-10]; - prediction += coefficients[10] * (int64_t)pDecodedSamples[-11]; + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; } else { prediction = 0; for (int j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (int64_t)pDecodedSamples[-j-1]; + prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; } } #endif @@ -1494,190 +2100,268 @@ static DRFLAC_INLINE int32_t drflac__calculate_prediction_64(uint32_t order, int // VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some // reason. The ugly version above is faster so we'll just switch between the two depending on the target platform. #ifdef DRFLAC_64BIT - int64_t prediction = 0; + drflac_int64 prediction = 0; switch (order) { - case 32: prediction += coefficients[31] * (int64_t)pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * (int64_t)pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * (int64_t)pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * (int64_t)pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * (int64_t)pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * (int64_t)pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * (int64_t)pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * (int64_t)pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * (int64_t)pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * (int64_t)pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * (int64_t)pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * (int64_t)pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * (int64_t)pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * (int64_t)pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * (int64_t)pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * (int64_t)pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * (int64_t)pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * (int64_t)pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * (int64_t)pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * (int64_t)pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * (int64_t)pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * (int64_t)pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * (int64_t)pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * (int64_t)pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * (int64_t)pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * (int64_t)pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * (int64_t)pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * (int64_t)pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * (int64_t)pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * (int64_t)pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * (int64_t)pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * (int64_t)pDecodedSamples[- 1]; + case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; } #endif - return (int32_t)(prediction >> shift); + return (drflac_int32)(prediction >> shift); } - -// Reads and decodes a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. -// -// This is the most frequently called function in the library. It does both the Rice decoding and the prediction in a single loop -// iteration. The prediction is done at the end, and there's an annoying branch I'd like to avoid so the main function is defined -// as a #define - sue me! -#define DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(funcName, predictionFunc) \ -static dr_bool32 funcName (drflac_bs* bs, uint32_t count, uint8_t riceParam, uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pSamplesOut) \ -{ \ - assert(bs != NULL); \ - assert(count > 0); \ - assert(pSamplesOut != NULL); \ - \ - static unsigned int bitOffsetTable[] = { \ - 0, \ - 4, \ - 3, 3, \ - 2, 2, 2, 2, \ - 1, 1, 1, 1, 1, 1, 1, 1 \ - }; \ - \ - drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); \ - drflac_cache_t resultHiShift = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParam; \ - \ - for (int i = 0; i < (int)count; ++i) \ - { \ - unsigned int zeroCounter = 0; \ - while (bs->cache == 0) { \ - zeroCounter += (unsigned int)DRFLAC_CACHE_L1_BITS_REMAINING(bs); \ - if (!drflac__reload_cache(bs)) { \ - return DR_FALSE; \ - } \ - } \ - \ - /* At this point the cache should not be zero, in which case we know the first set bit should be somewhere in here. There is \ - no need for us to perform any cache reloading logic here which should make things much faster. */ \ - assert(bs->cache != 0); \ - unsigned int decodedRice; \ - \ - unsigned int setBitOffsetPlus1 = bitOffsetTable[DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, 4)]; \ - if (setBitOffsetPlus1 > 0) { \ - decodedRice = (zeroCounter + (setBitOffsetPlus1-1)) << riceParam; \ - } else { \ - if (bs->cache == 1) { \ - setBitOffsetPlus1 = DRFLAC_CACHE_L1_SIZE_BITS(bs); \ - decodedRice = (zeroCounter + (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1)) << riceParam; \ - } else { \ - setBitOffsetPlus1 = 5; \ - for (;;) \ - { \ - if ((bs->cache & DRFLAC_CACHE_L1_SELECT(bs, setBitOffsetPlus1))) { \ - decodedRice = (zeroCounter + (setBitOffsetPlus1-1)) << riceParam; \ - break; \ - } \ - \ - setBitOffsetPlus1 += 1; \ - } \ - } \ - } \ - \ - \ - unsigned int bitsLo = 0; \ - unsigned int riceLength = setBitOffsetPlus1 + riceParam; \ - if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) \ - { \ - bitsLo = (unsigned int)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> (DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceLength)); \ - \ - bs->consumedBits += riceLength; \ - bs->cache <<= riceLength; \ - } \ - else \ - { \ - bs->consumedBits += riceLength; \ - bs->cache <<= setBitOffsetPlus1; \ - \ - /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ \ - size_t bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); \ - drflac_cache_t resultHi = bs->cache & riceParamMask; /* <-- This mask is OK because all bits after the first bits are always zero. */ \ - \ - \ - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { \ - bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); \ - } else { \ - /* Slow path. We need to fetch more data from the client. */ \ - if (!drflac__reload_cache(bs)) { \ - return DR_FALSE; \ - } \ - } \ - \ - bitsLo = (unsigned int)((resultHi >> resultHiShift) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo)); \ - bs->consumedBits = bitCountLo; \ - bs->cache <<= bitCountLo; \ - } \ - \ - decodedRice |= bitsLo; \ - decodedRice = (decodedRice >> 1) ^ (~(decodedRice & 0x01) + 1); /* <-- Ah, much faster! :) */ \ - /* \ - if ((decodedRice & 0x01)) { \ - decodedRice = ~(decodedRice >> 1); \ - } else { \ - decodedRice = (decodedRice >> 1); \ - } \ - */ \ - \ - /* In order to properly calculate the prediction when the bits per sample is >16 we need to do it using 64-bit arithmetic. We can assume this \ - is probably going to be slower on 32-bit systems so we'll do a more optimized 32-bit version when the bits per sample is low enough.*/ \ - pSamplesOut[i] = ((int)decodedRice + predictionFunc(order, shift, coefficients, pSamplesOut + i)); \ - } \ - \ - return DR_TRUE; \ -} \ - -DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(drflac__decode_samples_with_residual__rice_64, drflac__calculate_prediction_64) -DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(drflac__decode_samples_with_residual__rice_32, drflac__calculate_prediction_32) - - -// Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. -static dr_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, uint32_t count, uint8_t riceParam) +#if 0 +// Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the +// sake of readability and should only be used as a reference. +static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { - assert(bs != NULL); - assert(count > 0); + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(pSamplesOut != NULL); - for (uint32_t i = 0; i < count; ++i) { - if (!drflac__read_and_seek_rice(bs, riceParam)) { - return DR_FALSE; + for (drflac_uint32 i = 0; i < count; ++i) { + drflac_uint32 zeroCounter = 0; + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + drflac_uint32 decodedRice; + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + decodedRice |= (zeroCounter << riceParam); + if ((decodedRice & 0x01)) { + decodedRice = ~(decodedRice >> 1); + } else { + decodedRice = (decodedRice >> 1); + } + + + if (bitsPerSample > 16) { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); } } - return DR_TRUE; + return DRFLAC_TRUE; +} +#endif + +#if 0 +static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_uint32 zeroCounter = 0; + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + drflac_uint32 decodedRice; + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = decodedRice; + return DRFLAC_TRUE; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); + drflac_cache_t resultHiShift = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParam; + + + drflac_uint32 zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; + + + drflac_uint32 riceParamPart; + drflac_uint32 riceLength = setBitOffsetPlus1 + riceParam; + if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> (DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceLength)); + + bs->consumedBits += riceLength; + bs->cache <<= riceLength; + } else { + bs->consumedBits += riceLength; + if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + bs->cache <<= setBitOffsetPlus1; + } + + // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. + drflac_uint32 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); + drflac_cache_t resultHi = bs->cache & riceParamMask; // <-- This mask is OK because all bits after the first bits are always zero. + + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->consumedBits = 0; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; + #endif + } else { + // Slow path. We need to fetch more data from the client. + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + riceParamPart = (drflac_uint32)((resultHi >> resultHiShift) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo)); + + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + } + + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = riceParamPart; + return DRFLAC_TRUE; } -static dr_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, uint32_t bitsPerSample, uint32_t count, uint8_t unencodedBitsPerSample, uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pSamplesOut) -{ - assert(bs != NULL); - assert(count > 0); - assert(unencodedBitsPerSample > 0 && unencodedBitsPerSample <= 32); - assert(pSamplesOut != NULL); - for (unsigned int i = 0; i < count; ++i) - { +static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(pSamplesOut != NULL); + + drflac_uint32 zeroCountPart; + drflac_uint32 riceParamPart; + + drflac_uint32 i = 0; + while (i < count) { + // Rice extraction. + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { + return DRFLAC_FALSE; + } + + // Rice reconstruction. + static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + riceParamPart |= (zeroCountPart << riceParam); + riceParamPart = (riceParamPart >> 1) ^ t[riceParamPart & 0x01]; + //riceParamPart = (riceParamPart >> 1) ^ (~(riceParamPart & 0x01) + 1); + + // Sample reconstruction. + if (bitsPerSample > 16) { + pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + } + + i += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ +#if 0 + return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); +#else + return drflac__decode_samples_with_residual__rice__simple(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); +#endif +} + +// Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. +static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + + for (drflac_uint32 i = 0; i < count; ++i) { + drflac_uint32 zeroCountPart; + drflac_uint32 riceParamPart; + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(unencodedBitsPerSample > 0 && unencodedBitsPerSample <= 32); + drflac_assert(pSamplesOut != NULL); + + for (unsigned int i = 0; i < count; ++i) { if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (bitsPerSample > 16) { @@ -1687,53 +2371,52 @@ static dr_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, } } - return DR_TRUE; + return DRFLAC_TRUE; } // Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called // when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The // and parameters are used to determine how many residual values need to be decoded. -static dr_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, uint32_t bitsPerSample, uint32_t blockSize, uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pDecodedSamples) +static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { - assert(bs != NULL); - assert(blockSize != 0); - assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? + drflac_assert(bs != NULL); + drflac_assert(blockSize != 0); + drflac_assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? - uint8_t residualMethod; + drflac_uint8 residualMethod; if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DR_FALSE; // Unknown or unsupported residual coding method. + return DRFLAC_FALSE; // Unknown or unsupported residual coding method. } // Ignore the first values. pDecodedSamples += order; - uint8_t partitionOrder; + drflac_uint8 partitionOrder; if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DR_FALSE; + return DRFLAC_FALSE; } - uint32_t samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - uint32_t partitionsRemaining = (1 << partitionOrder); - for (;;) - { - uint8_t riceParam = 0; + drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + drflac_uint32 partitionsRemaining = (1 << partitionOrder); + for (;;) { + drflac_uint8 riceParam = 0; if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (riceParam == 16) { riceParam = 0xFF; } } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (riceParam == 32) { riceParam = 0xFF; @@ -1741,23 +2424,17 @@ static dr_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, uint32_t bi } if (riceParam != 0xFF) { - if (bitsPerSample > 16) { - if (!drflac__decode_samples_with_residual__rice_64(bs, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { - return DR_FALSE; - } - } else { - if (!drflac__decode_samples_with_residual__rice_32(bs, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { - return DR_FALSE; - } + if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; } } else { unsigned char unencodedBitsPerSample = 0; if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) { - return DR_FALSE; + return DRFLAC_FALSE; } } @@ -1772,46 +2449,46 @@ static dr_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, uint32_t bi samplesInPartition = blockSize / (1 << partitionOrder); } - return DR_TRUE; + return DRFLAC_TRUE; } // Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called // when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be set to 0. The // and parameters are used to determine how many residual values need to be decoded. -static dr_bool32 drflac__read_and_seek_residual(drflac_bs* bs, uint32_t blockSize, uint32_t order) +static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) { - assert(bs != NULL); - assert(blockSize != 0); + drflac_assert(bs != NULL); + drflac_assert(blockSize != 0); - uint8_t residualMethod; + drflac_uint8 residualMethod; if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DR_FALSE; // Unknown or unsupported residual coding method. + return DRFLAC_FALSE; // Unknown or unsupported residual coding method. } - uint8_t partitionOrder; + drflac_uint8 partitionOrder; if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DR_FALSE; + return DRFLAC_FALSE; } - uint32_t samplesInPartition = (blockSize / (1 << partitionOrder)) - order; - uint32_t partitionsRemaining = (1 << partitionOrder); + drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + drflac_uint32 partitionsRemaining = (1 << partitionOrder); for (;;) { - uint8_t riceParam = 0; + drflac_uint8 riceParam = 0; if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (riceParam == 16) { riceParam = 0xFF; } } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (riceParam == 32) { riceParam = 0xFF; @@ -1820,16 +2497,16 @@ static dr_bool32 drflac__read_and_seek_residual(drflac_bs* bs, uint32_t blockSiz if (riceParam != 0xFF) { if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return DR_FALSE; + return DRFLAC_FALSE; } } else { unsigned char unencodedBitsPerSample = 0; if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return DR_FALSE; + return DRFLAC_FALSE; } } @@ -1842,44 +2519,44 @@ static dr_bool32 drflac__read_and_seek_residual(drflac_bs* bs, uint32_t blockSiz samplesInPartition = blockSize / (1 << partitionOrder); } - return DR_TRUE; + return DRFLAC_TRUE; } -static dr_bool32 drflac__decode_samples__constant(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, int32_t* pDecodedSamples) +static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples) { // Only a single sample needs to be decoded here. - int32_t sample; + drflac_int32 sample; if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DR_FALSE; + return DRFLAC_FALSE; } // We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely) // we'll want to look at a more efficient way. - for (uint32_t i = 0; i < blockSize; ++i) { + for (drflac_uint32 i = 0; i < blockSize; ++i) { pDecodedSamples[i] = sample; } - return DR_TRUE; + return DRFLAC_TRUE; } -static dr_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, int32_t* pDecodedSamples) +static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples) { - for (uint32_t i = 0; i < blockSize; ++i) { - int32_t sample; + for (drflac_uint32 i = 0; i < blockSize; ++i) { + drflac_int32 sample; if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DR_FALSE; + return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } - return DR_TRUE; + return DRFLAC_TRUE; } -static dr_bool32 drflac__decode_samples__fixed(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, uint8_t lpcOrder, int32_t* pDecodedSamples) +static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { - short lpcCoefficientsTable[5][4] = { + drflac_int32 lpcCoefficientsTable[5][4] = { {0, 0, 0, 0}, {1, 0, 0, 0}, {2, -1, 0, 0}, @@ -1888,10 +2565,10 @@ static dr_bool32 drflac__decode_samples__fixed(drflac_bs* bs, uint32_t blockSize }; // Warm up samples and coefficients. - for (uint32_t i = 0; i < lpcOrder; ++i) { - int32_t sample; + for (drflac_uint32 i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DR_FALSE; + return DRFLAC_FALSE; } pDecodedSamples[i] = sample; @@ -1899,195 +2576,224 @@ static dr_bool32 drflac__decode_samples__fixed(drflac_bs* bs, uint32_t blockSize if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { - return DR_FALSE; + return DRFLAC_FALSE; } - return DR_TRUE; + return DRFLAC_TRUE; } -static dr_bool32 drflac__decode_samples__lpc(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, uint8_t lpcOrder, int32_t* pDecodedSamples) +static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { + drflac_uint8 i; + // Warm up samples. - for (uint8_t i = 0; i < lpcOrder; ++i) { - int32_t sample; + for (i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DR_FALSE; + return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } - uint8_t lpcPrecision; + drflac_uint8 lpcPrecision; if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (lpcPrecision == 15) { - return DR_FALSE; // Invalid. + return DRFLAC_FALSE; // Invalid. } lpcPrecision += 1; - int8_t lpcShift; + drflac_int8 lpcShift; if (!drflac__read_int8(bs, 5, &lpcShift)) { - return DR_FALSE; + return DRFLAC_FALSE; } - int16_t coefficients[32]; - for (uint8_t i = 0; i < lpcOrder; ++i) { - if (!drflac__read_int16(bs, lpcPrecision, coefficients + i)) { - return DR_FALSE; + drflac_int32 coefficients[32]; + for (i = 0; i < lpcOrder; ++i) { + if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { + return DRFLAC_FALSE; } } if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) { - return DR_FALSE; + return DRFLAC_FALSE; } - return DR_TRUE; + return DRFLAC_TRUE; } -static dr_bool32 drflac__read_next_frame_header(drflac_bs* bs, uint8_t streaminfoBitsPerSample, drflac_frame_header* header) +static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) { - assert(bs != NULL); - assert(header != NULL); + drflac_assert(bs != NULL); + drflac_assert(header != NULL); - // At the moment the sync code is as a form of basic validation. The CRC is stored, but is unused at the moment. This - // should probably be handled better in the future. + const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; // -1 = reserved. - const uint32_t sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const uint8_t bitsPerSampleTable[8] = {0, 8, 12, (uint8_t)-1, 16, 20, 24, (uint8_t)-1}; // -1 = reserved. - - uint16_t syncCode = 0; - if (!drflac__read_uint16(bs, 14, &syncCode)) { - return DR_FALSE; - } - - if (syncCode != 0x3FFE) { - // TODO: Try and recover by attempting to seek to and read the next frame? - return DR_FALSE; - } - - uint8_t reserved; - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DR_FALSE; - } - - uint8_t blockingStrategy = 0; - if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { - return DR_FALSE; - } - - - - uint8_t blockSize = 0; - if (!drflac__read_uint8(bs, 4, &blockSize)) { - return DR_FALSE; - } - - uint8_t sampleRate = 0; - if (!drflac__read_uint8(bs, 4, &sampleRate)) { - return DR_FALSE; - } - - uint8_t channelAssignment = 0; - if (!drflac__read_uint8(bs, 4, &channelAssignment)) { - return DR_FALSE; - } - - uint8_t bitsPerSample = 0; - if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { - return DR_FALSE; - } - - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DR_FALSE; - } - - - dr_bool32 isVariableBlockSize = blockingStrategy == 1; - if (isVariableBlockSize) { - uint64_t sampleNumber; - if (!drflac__read_utf8_coded_number(bs, &sampleNumber)) { - return DR_FALSE; + // Keep looping until we find a valid sync code. + for (;;) { + if (!drflac__find_and_seek_to_next_sync_code(bs)) { + return DRFLAC_FALSE; } - header->frameNumber = 0; - header->sampleNumber = sampleNumber; - } else { - uint64_t frameNumber = 0; - if (!drflac__read_utf8_coded_number(bs, &frameNumber)) { - return DR_FALSE; + + drflac_uint8 crc8 = 0xCE; // 0xCE = drflac_crc8(0, 0x3FFE, 14); + + drflac_uint8 reserved = 0; + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; } - header->frameNumber = (uint32_t)frameNumber; // <-- Safe cast. - header->sampleNumber = 0; + crc8 = drflac_crc8(crc8, reserved, 1); + + + drflac_uint8 blockingStrategy = 0; + if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, blockingStrategy, 1); + + + drflac_uint8 blockSize = 0; + if (!drflac__read_uint8(bs, 4, &blockSize)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, blockSize, 4); + + + drflac_uint8 sampleRate = 0; + if (!drflac__read_uint8(bs, 4, &sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, sampleRate, 4); + + + drflac_uint8 channelAssignment = 0; + if (!drflac__read_uint8(bs, 4, &channelAssignment)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, channelAssignment, 4); + + + drflac_uint8 bitsPerSample = 0; + if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, bitsPerSample, 3); + + + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, reserved, 1); + + + drflac_bool32 isVariableBlockSize = blockingStrategy == 1; + if (isVariableBlockSize) { + drflac_uint64 sampleNumber; + drflac_result result = drflac__read_utf8_coded_number(bs, &sampleNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_END_OF_STREAM) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->frameNumber = 0; + header->sampleNumber = sampleNumber; + } else { + drflac_uint64 frameNumber = 0; + drflac_result result = drflac__read_utf8_coded_number(bs, &frameNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_END_OF_STREAM) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->frameNumber = (drflac_uint32)frameNumber; // <-- Safe cast. + header->sampleNumber = 0; + } + + + if (blockSize == 1) { + header->blockSize = 192; + } else if (blockSize >= 2 && blockSize <= 5) { + header->blockSize = 576 * (1 << (blockSize - 2)); + } else if (blockSize == 6) { + if (!drflac__read_uint16(bs, 8, &header->blockSize)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSize, 8); + header->blockSize += 1; + } else if (blockSize == 7) { + if (!drflac__read_uint16(bs, 16, &header->blockSize)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSize, 16); + header->blockSize += 1; + } else { + header->blockSize = 256 * (1 << (blockSize - 8)); + } + + + if (sampleRate <= 11) { + header->sampleRate = sampleRateTable[sampleRate]; + } else if (sampleRate == 12) { + if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 8); + header->sampleRate *= 1000; + } else if (sampleRate == 13) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + } else if (sampleRate == 14) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + header->sampleRate *= 10; + } else { + continue; // Invalid. Assume an invalid block. + } + + + header->channelAssignment = channelAssignment; + + header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; + if (header->bitsPerSample == 0) { + header->bitsPerSample = streaminfoBitsPerSample; + } + + if (!drflac__read_uint8(bs, 8, &header->crc8)) { + return DRFLAC_FALSE; + } + + #ifndef DR_FLAC_NO_CRC + if (header->crc8 != crc8) { + continue; // CRC mismatch. Loop back to the top and find the next sync code. + } + #endif + return DRFLAC_TRUE; } - - - if (blockSize == 1) { - header->blockSize = 192; - } else if (blockSize >= 2 && blockSize <= 5) { - header->blockSize = 576 * (1 << (blockSize - 2)); - } else if (blockSize == 6) { - if (!drflac__read_uint16(bs, 8, &header->blockSize)) { - return DR_FALSE; - } - header->blockSize += 1; - } else if (blockSize == 7) { - if (!drflac__read_uint16(bs, 16, &header->blockSize)) { - return DR_FALSE; - } - header->blockSize += 1; - } else { - header->blockSize = 256 * (1 << (blockSize - 8)); - } - - - if (sampleRate <= 11) { - header->sampleRate = sampleRateTable[sampleRate]; - } else if (sampleRate == 12) { - if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { - return DR_FALSE; - } - header->sampleRate *= 1000; - } else if (sampleRate == 13) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DR_FALSE; - } - } else if (sampleRate == 14) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DR_FALSE; - } - header->sampleRate *= 10; - } else { - return DR_FALSE; // Invalid. - } - - - header->channelAssignment = channelAssignment; - - header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; - if (header->bitsPerSample == 0) { - header->bitsPerSample = streaminfoBitsPerSample; - } - - if (drflac__read_uint8(bs, 8, &header->crc8) != 1) { - return DR_FALSE; - } - - return DR_TRUE; } -static dr_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) +static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) { - uint8_t header; + drflac_uint8 header; if (!drflac__read_uint8(bs, 8, &header)) { - return DR_FALSE; + return DRFLAC_FALSE; } // First bit should always be 0. if ((header & 0x80) != 0) { - return DR_FALSE; + return DRFLAC_FALSE; } int type = (header & 0x7E) >> 1; @@ -2112,7 +2818,7 @@ static dr_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pS } if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { - return DR_FALSE; + return DRFLAC_FALSE; } // Wasted bits per sample. @@ -2120,22 +2826,22 @@ static dr_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pS if ((header & 0x01) == 1) { unsigned int wastedBitsPerSample; if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return DR_FALSE; + return DRFLAC_FALSE; } pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1; } - return DR_TRUE; + return DRFLAC_TRUE; } -static dr_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, int32_t* pDecodedSamplesOut) +static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) { - assert(bs != NULL); - assert(frame != NULL); + drflac_assert(bs != NULL); + drflac_assert(frame != NULL); drflac_subframe* pSubframe = frame->subframes + subframeIndex; if (!drflac__read_subframe_header(bs, pSubframe)) { - return DR_FALSE; + return DRFLAC_FALSE; } // Side channels require an extra bit per sample. Took a while to figure that one out... @@ -2172,20 +2878,20 @@ static dr_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int drflac__decode_samples__lpc(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples); } break; - default: return DR_FALSE; + default: return DRFLAC_FALSE; } - return DR_TRUE; + return DRFLAC_TRUE; } -static dr_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) +static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) { - assert(bs != NULL); - assert(frame != NULL); + drflac_assert(bs != NULL); + drflac_assert(frame != NULL); drflac_subframe* pSubframe = frame->subframes + subframeIndex; if (!drflac__read_subframe_header(bs, pSubframe)) { - return DR_FALSE; + return DRFLAC_FALSE; } // Side channels require an extra bit per sample. Took a while to figure that one out... @@ -2199,14 +2905,13 @@ static dr_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int s // Need to handle wasted bits per sample. pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pDecodedSamples = NULL; - //pSubframe->pDecodedSamples = pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * subframeIndex); switch (pSubframe->subframeType) { case DRFLAC_SUBFRAME_CONSTANT: { if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) { - return DR_FALSE; + return DRFLAC_FALSE; } } break; @@ -2214,7 +2919,7 @@ static dr_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int s { unsigned int bitsToSeek = frame->header.blockSize * pSubframe->bitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { - return DR_FALSE; + return DRFLAC_FALSE; } } break; @@ -2222,11 +2927,11 @@ static dr_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int s { unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { - return DR_FALSE; + return DRFLAC_FALSE; } } break; @@ -2234,217 +2939,253 @@ static dr_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int s { unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { - return DR_FALSE; + return DRFLAC_FALSE; } unsigned char lpcPrecision; if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (lpcPrecision == 15) { - return DR_FALSE; // Invalid. + return DRFLAC_FALSE; // Invalid. } lpcPrecision += 1; bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; // +5 for shift. if (!drflac__seek_bits(bs, bitsToSeek)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { - return DR_FALSE; + return DRFLAC_FALSE; } } break; - default: return DR_FALSE; + default: return DRFLAC_FALSE; } - return DR_TRUE; + return DRFLAC_TRUE; } -static DRFLAC_INLINE uint8_t drflac__get_channel_count_from_channel_assignment(int8_t channelAssignment) +static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) { - assert(channelAssignment <= 10); + drflac_assert(channelAssignment <= 10); - uint8_t lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; return lookup[channelAssignment]; } -static dr_bool32 drflac__decode_frame(drflac* pFlac) +static drflac_result drflac__decode_frame(drflac* pFlac) { // This function should be called while the stream is sitting on the first byte after the frame header. - memset(pFlac->currentFrame.subframes, 0, sizeof(pFlac->currentFrame.subframes)); + drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes)); int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - for (int i = 0; i < channelCount; ++i) - { + for (int i = 0; i < channelCount; ++i) { if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) { - return DR_FALSE; + return DRFLAC_ERROR; } } - // At the end of the frame sits the padding and CRC. We don't use these so we can just seek past. - if (!drflac__seek_bits(&pFlac->bs, (DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7) + 16)) { - return DR_FALSE; + drflac_uint8 paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7; + if (paddingSizeInBits > 0) { + drflac_uint8 padding = 0; + if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return DRFLAC_END_OF_STREAM; + } } +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + drflac_uint16 desiredCRC16; + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_END_OF_STREAM; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; // CRC mismatch. + } +#endif pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount; - return DR_TRUE; + return DRFLAC_SUCCESS; } -static dr_bool32 drflac__seek_frame(drflac* pFlac) +static drflac_result drflac__seek_frame(drflac* pFlac) { int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - for (int i = 0; i < channelCount; ++i) - { + for (int i = 0; i < channelCount; ++i) { if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) { - return DR_FALSE; + return DRFLAC_ERROR; } } - // Padding and CRC. - return drflac__seek_bits(&pFlac->bs, (DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7) + 16); -} - -static dr_bool32 drflac__read_and_decode_next_frame(drflac* pFlac) -{ - assert(pFlac != NULL); - - if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { - return DR_FALSE; + // Padding. + if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return DRFLAC_ERROR; } - return drflac__decode_frame(pFlac); + // CRC. +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + drflac_uint16 desiredCRC16; + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_END_OF_STREAM; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; // CRC mismatch. + } +#endif + + return DRFLAC_SUCCESS; +} + +static drflac_bool32 drflac__read_and_decode_next_frame(drflac* pFlac) +{ + drflac_assert(pFlac != NULL); + + for (;;) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + + drflac_result result = drflac__decode_frame(pFlac); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Skip to the next frame. + } else { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; + } } -static void drflac__get_current_frame_sample_range(drflac* pFlac, uint64_t* pFirstSampleInFrameOut, uint64_t* pLastSampleInFrameOut) +static void drflac__get_current_frame_sample_range(drflac* pFlac, drflac_uint64* pFirstSampleInFrameOut, drflac_uint64* pLastSampleInFrameOut) { - assert(pFlac != NULL); + drflac_assert(pFlac != NULL); unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - uint64_t firstSampleInFrame = pFlac->currentFrame.header.sampleNumber; + drflac_uint64 firstSampleInFrame = pFlac->currentFrame.header.sampleNumber; if (firstSampleInFrame == 0) { firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount; } - uint64_t lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount); + drflac_uint64 lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount); if (lastSampleInFrame > 0) { lastSampleInFrame -= 1; // Needs to be zero based. } - - if (pFirstSampleInFrameOut) { - *pFirstSampleInFrameOut = firstSampleInFrame; - } - if (pLastSampleInFrameOut) { - *pLastSampleInFrameOut = lastSampleInFrame; - } + if (pFirstSampleInFrameOut) *pFirstSampleInFrameOut = firstSampleInFrame; + if (pLastSampleInFrameOut) *pLastSampleInFrameOut = lastSampleInFrame; } -static dr_bool32 drflac__seek_to_first_frame(drflac* pFlac) +static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) { - assert(pFlac != NULL); + drflac_assert(pFlac != NULL); - dr_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); + drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); - memset(&pFlac->currentFrame, 0, sizeof(pFlac->currentFrame)); + drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame)); return result; } -static DRFLAC_INLINE dr_bool32 drflac__seek_to_next_frame(drflac* pFlac) +static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac) { // This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. - assert(pFlac != NULL); + drflac_assert(pFlac != NULL); return drflac__seek_frame(pFlac); } -static dr_bool32 drflac__seek_to_frame_containing_sample(drflac* pFlac, uint64_t sampleIndex) +static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex) { - assert(pFlac != NULL); - + // We need to find the frame that contains the sample. To do this, we iterate over each frame and inspect it's header. If based on the + // header we can determine that the frame contains the sample, we do a full decode of that frame. if (!drflac__seek_to_first_frame(pFlac)) { - return DR_FALSE; + return DRFLAC_FALSE; } - uint64_t firstSampleInFrame = 0; - uint64_t lastSampleInFrame = 0; - for (;;) - { - // We need to read the frame's header in order to determine the range of samples it contains. + drflac_uint64 runningSampleCount = 0; + for (;;) { if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { - return DR_FALSE; + return DRFLAC_FALSE; } + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); - if (sampleIndex >= firstSampleInFrame && sampleIndex <= lastSampleInFrame) { - break; // The sample is in this frame. - } - if (!drflac__seek_to_next_frame(pFlac)) { - return DR_FALSE; + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // it never existed and keep iterating. + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. + if (samplesToDecode == 0) { + return DRFLAC_TRUE; + } + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + // frame never existed and leave the running sample count untouched. + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } } } - - // If we get here we should be right at the start of the frame containing the sample. - return DR_TRUE; -} - -static dr_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, uint64_t sampleIndex) -{ - if (!drflac__seek_to_frame_containing_sample(pFlac, sampleIndex)) { - return DR_FALSE; - } - - // At this point we should be sitting on the first byte of the frame containing the sample. We need to decode every sample up to (but - // not including) the sample we're seeking to. - uint64_t firstSampleInFrame = 0; - drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, NULL); - - assert(firstSampleInFrame <= sampleIndex); - size_t samplesToDecode = (size_t)(sampleIndex - firstSampleInFrame); // <-- Safe cast because the maximum number of samples in a frame is 65535. - if (samplesToDecode == 0) { - return DR_TRUE; - } - - // At this point we are just sitting on the byte after the frame header. We need to decode the frame before reading anything from it. - if (!drflac__decode_frame(pFlac)) { - return DR_FALSE; - } - - return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; } -static dr_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, uint64_t sampleIndex) +static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_uint64 sampleIndex) { - assert(pFlac != NULL); + drflac_assert(pFlac != NULL); if (pFlac->seektablePos == 0) { - return DR_FALSE; + return DRFLAC_FALSE; } if (!drflac__seek_to_byte(&pFlac->bs, pFlac->seektablePos)) { - return DR_FALSE; + return DRFLAC_FALSE; } // The number of seek points is derived from the size of the SEEKTABLE block. - uint32_t seekpointCount = pFlac->seektableSize / 18; // 18 = the size of each seek point. + drflac_uint32 seekpointCount = pFlac->seektableSize / 18; // 18 = the size of each seek point. if (seekpointCount == 0) { - return DR_FALSE; // Would this ever happen? + return DRFLAC_FALSE; // Would this ever happen? } drflac_seekpoint closestSeekpoint = {0, 0, 0}; - uint32_t seekpointsRemaining = seekpointCount; - while (seekpointsRemaining > 0) - { + drflac_uint32 seekpointsRemaining = seekpointCount; + while (seekpointsRemaining > 0) { drflac_seekpoint seekpoint; if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) { break; @@ -2456,7 +3197,9 @@ static dr_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, uint64_t samp break; } - if (seekpoint.firstSample * pFlac->channels > sampleIndex) { + // Note that the seekpoint sample is based on a single channel. The input sample (sampleIndex) is based on interleaving, thus + // we need to multiple the seekpoint's sample by the channel count. + if (seekpoint.firstSample*pFlac->channels > sampleIndex) { break; } @@ -2467,53 +3210,68 @@ static dr_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, uint64_t samp // At this point we should have found the seekpoint closest to our sample. We need to seek to it using basically the same // technique as we use with the brute force method. if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + closestSeekpoint.frameOffset)) { - return DR_FALSE; + return DRFLAC_FALSE; } - - uint64_t firstSampleInFrame = 0; - uint64_t lastSampleInFrame = 0; - for (;;) - { - // We need to read the frame's header in order to determine the range of samples it contains. + drflac_uint64 runningSampleCount = closestSeekpoint.firstSample*pFlac->channels; + for (;;) { if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { - return DR_FALSE; + return DRFLAC_FALSE; } + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); - if (sampleIndex >= firstSampleInFrame && sampleIndex <= lastSampleInFrame) { - break; // The sample is in this frame. - } - if (!drflac__seek_to_next_frame(pFlac)) { - return DR_FALSE; + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // it never existed and keep iterating. + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. + if (samplesToDecode == 0) { + return DRFLAC_TRUE; + } + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + // frame never existed and leave the running sample count untouched. + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } } } - - assert(firstSampleInFrame <= sampleIndex); - - // At this point we are just sitting on the byte after the frame header. We need to decode the frame before reading anything from it. - if (!drflac__decode_frame(pFlac)) { - return DR_FALSE; - } - - size_t samplesToDecode = (size_t)(sampleIndex - firstSampleInFrame); // <-- Safe cast because the maximum number of samples in a frame is 65535. - return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; } #ifndef DR_FLAC_NO_OGG typedef struct { - uint8_t capturePattern[4]; // Should be "OggS" - uint8_t structureVersion; // Always 0. - uint8_t headerType; - uint64_t granulePosition; - uint32_t serialNumber; - uint32_t sequenceNumber; - uint32_t checksum; - uint8_t segmentCount; - uint8_t segmentTable[255]; + drflac_uint8 capturePattern[4]; // Should be "OggS" + drflac_uint8 structureVersion; // Always 0. + drflac_uint8 headerType; + drflac_uint64 granulePosition; + drflac_uint32 serialNumber; + drflac_uint32 sequenceNumber; + drflac_uint32 checksum; + drflac_uint8 segmentCount; + drflac_uint8 segmentTable[255]; } drflac_ogg_page_header; #endif @@ -2522,25 +3280,28 @@ typedef struct drflac_read_proc onRead; drflac_seek_proc onSeek; drflac_meta_proc onMeta; + drflac_container container; void* pUserData; void* pUserDataMD; - drflac_container container; - uint32_t sampleRate; - uint8_t channels; - uint8_t bitsPerSample; - uint64_t totalSampleCount; - uint16_t maxBlockSize; - uint64_t runningFilePos; - dr_bool32 hasMetadataBlocks; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalSampleCount; + drflac_uint16 maxBlockSize; + drflac_uint64 runningFilePos; + drflac_bool32 hasStreamInfoBlock; + drflac_bool32 hasMetadataBlocks; + drflac_bs bs; // <-- A bit streamer is required for loading data during initialization. + drflac_frame_header firstFrameHeader; // <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. #ifndef DR_FLAC_NO_OGG - uint32_t oggSerial; - uint64_t oggFirstBytePos; + drflac_uint32 oggSerial; + drflac_uint64 oggFirstBytePos; drflac_ogg_page_header oggBosHeader; #endif } drflac_init_info; -static DRFLAC_INLINE void drflac__decode_block_header(uint32_t blockHeader, uint8_t* isLastBlock, uint8_t* blockType, uint32_t* blockSize) +static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) { blockHeader = drflac__be2host_32(blockHeader); *isLastBlock = (blockHeader & (0x01 << 31)) >> 31; @@ -2548,41 +3309,41 @@ static DRFLAC_INLINE void drflac__decode_block_header(uint32_t blockHeader, uint *blockSize = (blockHeader & 0xFFFFFF); } -static DRFLAC_INLINE dr_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, uint8_t* isLastBlock, uint8_t* blockType, uint32_t* blockSize) +static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) { - uint32_t blockHeader; + drflac_uint32 blockHeader; if (onRead(pUserData, &blockHeader, 4) != 4) { - return DR_FALSE; + return DRFLAC_FALSE; } drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); - return DR_TRUE; + return DRFLAC_TRUE; } -dr_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) { // min/max block size. - uint32_t blockSizes; + drflac_uint32 blockSizes; if (onRead(pUserData, &blockSizes, 4) != 4) { - return DR_FALSE; + return DRFLAC_FALSE; } // min/max frame size. - uint64_t frameSizes = 0; + drflac_uint64 frameSizes = 0; if (onRead(pUserData, &frameSizes, 6) != 6) { - return DR_FALSE; + return DRFLAC_FALSE; } // Sample rate, channels, bits per sample and total sample count. - uint64_t importantProps; + drflac_uint64 importantProps; if (onRead(pUserData, &importantProps, 8) != 8) { - return DR_FALSE; + return DRFLAC_FALSE; } // MD5 - uint8_t md5[16]; + drflac_uint8 md5[16]; if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { - return DR_FALSE; + return DRFLAC_FALSE; } blockSizes = drflac__be2host_32(blockSizes); @@ -2591,34 +3352,33 @@ dr_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drfl pStreamInfo->minBlockSize = (blockSizes & 0xFFFF0000) >> 16; pStreamInfo->maxBlockSize = blockSizes & 0x0000FFFF; - pStreamInfo->minFrameSize = (uint32_t)((frameSizes & 0xFFFFFF0000000000ULL) >> 40ULL); - pStreamInfo->maxFrameSize = (uint32_t)((frameSizes & 0x000000FFFFFF0000ULL) >> 16ULL); - pStreamInfo->sampleRate = (uint32_t)((importantProps & 0xFFFFF00000000000ULL) >> 44ULL); - pStreamInfo->channels = (uint8_t )((importantProps & 0x00000E0000000000ULL) >> 41ULL) + 1; - pStreamInfo->bitsPerSample = (uint8_t )((importantProps & 0x000001F000000000ULL) >> 36ULL) + 1; - pStreamInfo->totalSampleCount = (importantProps & 0x0000000FFFFFFFFFULL) * pStreamInfo->channels; - memcpy(pStreamInfo->md5, md5, sizeof(md5)); + pStreamInfo->minFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0xFFFFFF0000000000) >> 40); + pStreamInfo->maxFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0x000000FFFFFF0000) >> 16); + pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (drflac_uint64)0xFFFFF00000000000) >> 44); + pStreamInfo->channels = (drflac_uint8 )((importantProps & (drflac_uint64)0x00000E0000000000) >> 41) + 1; + pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (drflac_uint64)0x000001F000000000) >> 36) + 1; + pStreamInfo->totalSampleCount = (importantProps & (drflac_uint64)0x0000000FFFFFFFFF) * pStreamInfo->channels; + drflac_copy_memory(pStreamInfo->md5, md5, sizeof(md5)); - return DR_TRUE; + return DRFLAC_TRUE; } -dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) +drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) { - assert(pFlac != NULL); + drflac_assert(pFlac != NULL); // We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that // we'll be sitting on byte 42. - uint64_t runningFilePos = 42; - uint64_t seektablePos = 0; - uint32_t seektableSize = 0; + drflac_uint64 runningFilePos = 42; + drflac_uint64 seektablePos = 0; + drflac_uint32 seektableSize = 0; - for (;;) - { - uint8_t isLastBlock = 0; - uint8_t blockType; - uint32_t blockSize; + for (;;) { + drflac_uint8 isLastBlock = 0; + drflac_uint8 blockType; + drflac_uint32 blockSize; if (!drflac__read_and_decode_block_header(pFlac->bs.onRead, pFlac->bs.pUserData, &isLastBlock, &blockType, &blockSize)) { - return DR_FALSE; + return DRFLAC_FALSE; } runningFilePos += 4; @@ -2633,24 +3393,24 @@ dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: { if (pFlac->onMeta) { - void* pRawData = malloc(blockSize); + void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { - return DR_FALSE; + return DRFLAC_FALSE; } if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - free(pRawData); - return DR_FALSE; + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; - metadata.data.application.id = drflac__be2host_32(*(uint32_t*)pRawData); - metadata.data.application.pData = (const void*)((uint8_t*)pRawData + sizeof(uint32_t)); - metadata.data.application.dataSize = blockSize - sizeof(uint32_t); + metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); + metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); pFlac->onMeta(pFlac->pUserDataMD, &metadata); - free(pRawData); + DRFLAC_FREE(pRawData); } } break; @@ -2660,14 +3420,14 @@ dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) seektableSize = blockSize; if (pFlac->onMeta) { - void* pRawData = malloc(blockSize); + void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { - return DR_FALSE; + return DRFLAC_FALSE; } if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - free(pRawData); - return DR_FALSE; + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; @@ -2676,7 +3436,7 @@ dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; // Endian swap. - for (uint32_t iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { + for (drflac_uint32 iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample); pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset); @@ -2685,96 +3445,96 @@ dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) pFlac->onMeta(pFlac->pUserDataMD, &metadata); - free(pRawData); + DRFLAC_FREE(pRawData); } } break; case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: { if (pFlac->onMeta) { - void* pRawData = malloc(blockSize); + void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { - return DR_FALSE; + return DRFLAC_FALSE; } if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - free(pRawData); - return DR_FALSE; + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; const char* pRunningData = (const char*)pRawData; - metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(uint32_t*)pRunningData); pRunningData += 4; - metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.vorbis_comment.comments = pRunningData; pFlac->onMeta(pFlac->pUserDataMD, &metadata); - free(pRawData); + DRFLAC_FREE(pRawData); } } break; case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: { if (pFlac->onMeta) { - void* pRawData = malloc(blockSize); + void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { - return DR_FALSE; + return DRFLAC_FALSE; } if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - free(pRawData); - return DR_FALSE; + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; const char* pRunningData = (const char*)pRawData; - memcpy(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(uint64_t*)pRunningData); pRunningData += 4; - metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; - metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; - metadata.data.cuesheet.pTrackData = (const uint8_t*)pRunningData; + drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(drflac_uint64*)pRunningData); pRunningData += 4; + metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; + metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; + metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData; pFlac->onMeta(pFlac->pUserDataMD, &metadata); - free(pRawData); + DRFLAC_FREE(pRawData); } } break; case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: { if (pFlac->onMeta) { - void* pRawData = malloc(blockSize); + void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { - return DR_FALSE; + return DRFLAC_FALSE; } if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - free(pRawData); - return DR_FALSE; + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; const char* pRunningData = (const char*)pRawData; - metadata.data.picture.type = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; - metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.picture.type = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.picture.description = pRunningData; - metadata.data.picture.width = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; - metadata.data.picture.height = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const uint8_t*)pRunningData; + metadata.data.picture.width = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; pFlac->onMeta(pFlac->pUserDataMD, &metadata); - free(pRawData); + DRFLAC_FREE(pRawData); } } break; @@ -2783,12 +3543,12 @@ dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) if (pFlac->onMeta) { metadata.data.padding.unused = 0; - // Padding doesn't have anything meaningful in it, so just skip over it. + // Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - return DR_FALSE; + isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. + } else { + pFlac->onMeta(pFlac->pUserDataMD, &metadata); } - - pFlac->onMeta(pFlac->pUserDataMD, &metadata); } } break; @@ -2797,7 +3557,7 @@ dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) // Invalid chunk. Just skip over this one. if (pFlac->onMeta) { if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - return DR_FALSE; + isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. } } } @@ -2807,29 +3567,29 @@ dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we // can at the very least report the chunk to the application and let it look at the raw data. if (pFlac->onMeta) { - void* pRawData = malloc(blockSize); + void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { - return DR_FALSE; + return DRFLAC_FALSE; } if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { - free(pRawData); - return DR_FALSE; + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pFlac->onMeta(pFlac->pUserDataMD, &metadata); - free(pRawData); + DRFLAC_FREE(pRawData); } } break; } // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. - if (pFlac->onMeta == NULL) { + if (pFlac->onMeta == NULL && blockSize > 0) { if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - return DR_FALSE; + isLastBlock = DRFLAC_TRUE; } } @@ -2843,10 +3603,10 @@ dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) pFlac->seektableSize = seektableSize; pFlac->firstFramePos = runningFilePos; - return DR_TRUE; + return DRFLAC_TRUE; } -dr_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD) +drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { (void)onSeek; @@ -2855,56 +3615,193 @@ dr_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc pInit->container = drflac_container_native; // The first metadata block should be the STREAMINFO block. - uint8_t isLastBlock; - uint8_t blockType; - uint32_t blockSize; + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return DR_FALSE; // Invalid block type. First block must be the STREAMINFO block. + if (!relaxed) { + // We're opening in strict mode and the first block is not the STREAMINFO block. Error. + return DRFLAC_FALSE; + } else { + // Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined + // for that frame. + pInit->hasStreamInfoBlock = DRFLAC_FALSE; + pInit->hasMetadataBlocks = DRFLAC_FALSE; + + if (!drflac__read_next_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return DRFLAC_FALSE; // Couldn't find a frame. + } + + if (pInit->firstFrameHeader.bitsPerSample == 0) { + return DRFLAC_FALSE; // Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. + } + + pInit->sampleRate = pInit->firstFrameHeader.sampleRate; + pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; + pInit->maxBlockSize = 65535; // <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo + return DRFLAC_TRUE; + } + } else { + drflac_streaminfo streaminfo; + if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return DRFLAC_FALSE; + } + + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalSampleCount = streaminfo.totalSampleCount; + pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation). + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + + return DRFLAC_TRUE; } - - - drflac_streaminfo streaminfo; - if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { - return DR_FALSE; - } - - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalSampleCount = streaminfo.totalSampleCount; - pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation). - - if (onMeta) { - drflac_metadata metadata; - metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; - metadata.pRawData = NULL; - metadata.rawDataSize = 0; - metadata.data.streaminfo = streaminfo; - onMeta(pUserDataMD, &metadata); - } - - pInit->hasMetadataBlocks = !isLastBlock; - return DR_TRUE; } #ifndef DR_FLAC_NO_OGG -static DRFLAC_INLINE dr_bool32 drflac_ogg__is_capture_pattern(uint8_t pattern[4]) +#define DRFLAC_OGG_MAX_PAGE_SIZE 65307 +#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 // CRC-32 of "OggS". + +typedef enum +{ + drflac_ogg_recover_on_crc_mismatch, + drflac_ogg_fail_on_crc_mismatch +} drflac_ogg_crc_mismatch_recovery; + + +static drflac_uint32 drflac__crc32_table[] = { + 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, + 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, + 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, + 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, + 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, + 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, + 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, + 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, + 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, + 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, + 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, + 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, + 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, + 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, + 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, + 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, + 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, + 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, + 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, + 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, + 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, + 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, + 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, + 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, + 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, + 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, + 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, + 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, + 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, + 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, + 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, + 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, + 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, + 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, + 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, + 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, + 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, + 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, + 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, + 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, + 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, + 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, + 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, + 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, + 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, + 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, + 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, + 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, + 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, + 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, + 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, + 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) +{ +#ifndef DR_FLAC_NO_CRC + return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#else + (void)data; + return crc32; +#endif +} + +#if 0 +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) +{ + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); + return crc32; +} + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) +{ + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); + return crc32; +} +#endif + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) +{ + // This can be optimized. + for (drflac_uint32 i = 0; i < dataSize; ++i) { + crc32 = drflac_crc32_byte(crc32, pData[i]); + } + return crc32; +} + + +static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) { return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; } -static DRFLAC_INLINE uint32_t drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) { return 27 + pHeader->segmentCount; } -static DRFLAC_INLINE uint32_t drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) { - uint32_t pageBodySize = 0; + drflac_uint32 pageBodySize = 0; for (int i = 0; i < pHeader->segmentCount; ++i) { pageBodySize += pHeader->segmentTable[i]; } @@ -2912,49 +3809,84 @@ static DRFLAC_INLINE uint32_t drflac_ogg__get_page_body_size(drflac_ogg_page_hea return pageBodySize; } -dr_bool32 drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, uint32_t* pHeaderSize) +drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { - if (onRead(pUserData, &pHeader->structureVersion, 1) != 1 || pHeader->structureVersion != 0) { - return DR_FALSE; // Unknown structure version. Possibly corrupt stream. + drflac_assert(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); + + drflac_uint8 data[23]; + if (onRead(pUserData, data, 23) != 23) { + return DRFLAC_END_OF_STREAM; } - if (onRead(pUserData, &pHeader->headerType, 1) != 1) { - return DR_FALSE; - } - if (onRead(pUserData, &pHeader->granulePosition, 8) != 8) { - return DR_FALSE; - } - if (onRead(pUserData, &pHeader->serialNumber, 4) != 4) { - return DR_FALSE; - } - if (onRead(pUserData, &pHeader->sequenceNumber, 4) != 4) { - return DR_FALSE; - } - if (onRead(pUserData, &pHeader->checksum, 4) != 4) { - return DR_FALSE; - } - if (onRead(pUserData, &pHeader->segmentCount, 1) != 1 || pHeader->segmentCount == 0) { - return DR_FALSE; // Should not have a segment count of 0. - } - if (onRead(pUserData, &pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return DR_FALSE; + *pBytesRead += 23; + + pHeader->structureVersion = data[0]; + pHeader->headerType = data[1]; + drflac_copy_memory(&pHeader->granulePosition, &data[ 2], 8); + drflac_copy_memory(&pHeader->serialNumber, &data[10], 4); + drflac_copy_memory(&pHeader->sequenceNumber, &data[14], 4); + drflac_copy_memory(&pHeader->checksum, &data[18], 4); + pHeader->segmentCount = data[22]; + + // Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. + data[18] = 0; + data[19] = 0; + data[20] = 0; + data[21] = 0; + + drflac_uint32 i; + for (i = 0; i < 23; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); } - if (pHeaderSize) *pHeaderSize = (27 + pHeader->segmentCount); - return DR_TRUE; + + if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += pHeader->segmentCount; + + for (i = 0; i < pHeader->segmentCount; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + } + + return DRFLAC_SUCCESS; } -dr_bool32 drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, uint32_t* pHeaderSize) +drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { - uint8_t id[4]; + *pBytesRead = 0; + + drflac_uint8 id[4]; if (onRead(pUserData, id, 4) != 4) { - return DR_FALSE; + return DRFLAC_END_OF_STREAM; } + *pBytesRead += 4; - if (id[0] != 'O' || id[1] != 'g' || id[2] != 'g' || id[3] != 'S') { - return DR_FALSE; + // We need to read byte-by-byte until we find the OggS capture pattern. + for (;;) { + if (drflac_ogg__is_capture_pattern(id)) { + *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + + drflac_result result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == DRFLAC_SUCCESS) { + return DRFLAC_SUCCESS; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; + } else { + return result; + } + } + } else { + // The first 4 bytes did not equal the capture pattern. Read the next byte and try again. + id[0] = id[1]; + id[1] = id[2]; + id[2] = id[3]; + if (onRead(pUserData, &id[3], 1) != 1) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += 1; + } } - - return drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pHeaderSize); } @@ -2968,12 +3900,14 @@ typedef struct drflac_read_proc onRead; // The original onRead callback from drflac_open() and family. drflac_seek_proc onSeek; // The original onSeek callback from drflac_open() and family. void* pUserData; // The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. - uint64_t currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. - uint64_t firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. - uint32_t serialNumber; // The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. + drflac_uint64 currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. + drflac_uint64 firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. + drflac_uint32 serialNumber; // The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. drflac_ogg_page_header bosPageHeader; // Used for seeking. drflac_ogg_page_header currentPageHeader; - uint32_t bytesRemainingInPage; + drflac_uint32 bytesRemainingInPage; + drflac_uint32 pageDataSize; + drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; } drflac_oggbs; // oggbs = Ogg Bitstream static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) @@ -2984,82 +3918,103 @@ static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, return bytesActuallyRead; } -static dr_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, uint64_t offset, drflac_seek_origin origin) +static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) { - if (origin == drflac_seek_origin_start) - { + if (origin == drflac_seek_origin_start) { if (offset <= 0x7FFFFFFF) { if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { - return DR_FALSE; + return DRFLAC_FALSE; } oggbs->currentBytePos = offset; - return DR_TRUE; + return DRFLAC_TRUE; } else { if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { - return DR_FALSE; + return DRFLAC_FALSE; } oggbs->currentBytePos = offset; return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); } - } - else - { + } else { while (offset > 0x7FFFFFFF) { if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { - return DR_FALSE; + return DRFLAC_FALSE; } oggbs->currentBytePos += 0x7FFFFFFF; offset -= 0x7FFFFFFF; } if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { // <-- Safe cast thanks to the loop above. - return DR_FALSE; + return DRFLAC_FALSE; } oggbs->currentBytePos += offset; - return DR_TRUE; + return DRFLAC_TRUE; } } -static dr_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs) +static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) { drflac_ogg_page_header header; - for (;;) - { - uint32_t headerSize; - if (!drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &headerSize)) { - return DR_FALSE; + for (;;) { + drflac_uint32 crc32 = 0; + drflac_uint32 bytesRead; + if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; } - oggbs->currentBytePos += headerSize; + oggbs->currentBytePos += bytesRead; - - uint32_t pageBodySize = drflac_ogg__get_page_body_size(&header); - - if (header.serialNumber == oggbs->serialNumber) { - oggbs->currentPageHeader = header; - oggbs->bytesRemainingInPage = pageBodySize; - return DR_TRUE; + drflac_uint32 pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { + continue; // Invalid page size. Assume it's corrupted and just move to the next page. } - // If we get here it means the page is not a FLAC page - skip it. - if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { // <-- Safe cast - maximum size of a page is way below that of an int. - return DR_FALSE; + if (header.serialNumber != oggbs->serialNumber) { + // It's not a FLAC page. Skip it. + if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + continue; } + + + // We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. + if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return DRFLAC_FALSE; + } + oggbs->pageDataSize = pageBodySize; + +#ifndef DR_FLAC_NO_CRC + drflac_uint32 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); + if (actualCRC32 != header.checksum) { + if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { + continue; // CRC mismatch. Skip this page. + } else { + // Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we + // go to the next valid page to ensure we're in a good state, but return false to let the caller know that the + // seek did not fully complete. + drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); + return DRFLAC_FALSE; + } + } +#endif + + oggbs->currentPageHeader = header; + oggbs->bytesRemainingInPage = pageBodySize; + return DRFLAC_TRUE; } } // Function below is unused at the moment, but I might be re-adding it later. #if 0 -static uint8_t drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, uint8_t* pBytesRemainingInSeg) +static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) { - uint32_t bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; - uint8_t iSeg = 0; - uint32_t iByte = 0; - while (iByte < bytesConsumedInPage) - { - uint8_t segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + drflac_uint8 iSeg = 0; + drflac_uint32 iByte = 0; + while (iByte < bytesConsumedInPage) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (iByte + segmentSize > bytesConsumedInPage) { break; } else { @@ -3068,26 +4023,25 @@ static uint8_t drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, uint } } - *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (uint8_t)(bytesConsumedInPage - iByte); + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); return iSeg; } -static dr_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) +static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) { // The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. - for (;;) // <-- Loop over pages. - { - dr_bool32 atEndOfPage = DR_FALSE; + for (;;) { + drflac_bool32 atEndOfPage = DRFLAC_FALSE; - uint8_t bytesRemainingInSeg; - uint8_t iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + drflac_uint8 bytesRemainingInSeg; + drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); - uint32_t bytesToEndOfPacketOrPage = bytesRemainingInSeg; - for (uint8_t iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { - uint8_t segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (segmentSize < 255) { if (iSeg == oggbs->currentPageHeader.segmentCount-1) { - atEndOfPage = DR_TRUE; + atEndOfPage = DRFLAC_TRUE; } break; @@ -3097,32 +4051,29 @@ static dr_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) } // At this point we will have found either the packet or the end of the page. If were at the end of the page we'll - // want to load the next page and keep searching for the end of the frame. + // want to load the next page and keep searching for the end of the packet. drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; - if (atEndOfPage) - { + if (atEndOfPage) { // We're potentially at the next packet, but we need to check the next page first to be sure because the packet may // straddle pages. if (!drflac_oggbs__goto_next_page(oggbs)) { - return DR_FALSE; + return DRFLAC_FALSE; } // If it's a fresh packet it most likely means we're at the next packet. if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - return DR_TRUE; + return DRFLAC_TRUE; } - } - else - { - // We're at the next frame. - return DR_TRUE; + } else { + // We're at the next packet. + return DRFLAC_TRUE; } } } -static dr_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) +static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) { // The bitstream should be sitting on the first byte just after the header of the frame. @@ -3134,76 +4085,67 @@ static dr_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) { drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; - assert(oggbs != NULL); + drflac_assert(oggbs != NULL); - uint8_t* pRunningBufferOut = (uint8_t*)bufferOut; + drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; // Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. size_t bytesRead = 0; - while (bytesRead < bytesToRead) - { + while (bytesRead < bytesToRead) { size_t bytesRemainingToRead = bytesToRead - bytesRead; if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - bytesRead += oggbs->onRead(oggbs->pUserData, pRunningBufferOut, bytesRemainingToRead); - oggbs->bytesRemainingInPage -= (uint32_t)bytesRemainingToRead; + drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + bytesRead += bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; break; } // If we get here it means some of the requested data is contained in the next pages. if (oggbs->bytesRemainingInPage > 0) { - size_t bytesJustRead = oggbs->onRead(oggbs->pUserData, pRunningBufferOut, oggbs->bytesRemainingInPage); - bytesRead += bytesJustRead; - pRunningBufferOut += bytesJustRead; - - if (bytesJustRead != oggbs->bytesRemainingInPage) { - break; // Ran out of data. - } + drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + bytesRead += oggbs->bytesRemainingInPage; + pRunningBufferOut += oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; } - assert(bytesRemainingToRead > 0); - if (!drflac_oggbs__goto_next_page(oggbs)) { - break; // Failed to go to the next chunk. Might have simply hit the end of the stream. + drflac_assert(bytesRemainingToRead > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + break; // Failed to go to the next page. Might have simply hit the end of the stream. } } - oggbs->currentBytePos += bytesRead; return bytesRead; } -static dr_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) +static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) { drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; - assert(oggbs != NULL); - assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + drflac_assert(oggbs != NULL); + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); // Seeking is always forward which makes things a lot simpler. if (origin == drflac_seek_origin_start) { - int startBytePos = (int)oggbs->firstBytePos + (79-42); // 79 = size of bos page; 42 = size of FLAC header data. Seek up to the first byte of the native FLAC data. - if (!drflac_oggbs__seek_physical(oggbs, startBytePos, drflac_seek_origin_start)) { - return DR_FALSE; + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; } - oggbs->currentPageHeader = oggbs->bosPageHeader; - oggbs->bytesRemainingInPage = 42; // 42 = size of the native FLAC header data. That's our start point for seeking. + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + return DRFLAC_FALSE; + } return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); } - assert(origin == drflac_seek_origin_current); + drflac_assert(origin == drflac_seek_origin_current); int bytesSeeked = 0; - while (bytesSeeked < offset) - { + while (bytesSeeked < offset) { int bytesRemainingToSeek = offset - bytesSeeked; - assert(bytesRemainingToSeek >= 0); + drflac_assert(bytesRemainingToSeek >= 0); if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - if (!drflac_oggbs__seek_physical(oggbs, bytesRemainingToSeek, drflac_seek_origin_current)) { - return DR_FALSE; - } - bytesSeeked += bytesRemainingToSeek; oggbs->bytesRemainingInPage -= bytesRemainingToSeek; break; @@ -3211,45 +4153,42 @@ static dr_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_or // If we get here it means some of the requested data is contained in the next pages. if (oggbs->bytesRemainingInPage > 0) { - if (!drflac_oggbs__seek_physical(oggbs, oggbs->bytesRemainingInPage, drflac_seek_origin_current)) { - return DR_FALSE; - } - bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; } - assert(bytesRemainingToSeek > 0); - if (!drflac_oggbs__goto_next_page(oggbs)) { - break; // Failed to go to the next chunk. Might have simply hit the end of the stream. + drflac_assert(bytesRemainingToSeek > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + // Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. + return DRFLAC_FALSE; } } - return DR_TRUE; + return DRFLAC_TRUE; } -dr_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, uint64_t sample) +drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) { - drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels); + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - uint64_t originalBytePos = oggbs->currentBytePos; // For recovery. + drflac_uint64 originalBytePos = oggbs->currentBytePos; // For recovery. // First seek to the first frame. if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) { - return DR_FALSE; + return DRFLAC_FALSE; } oggbs->bytesRemainingInPage = 0; - uint64_t runningGranulePosition = 0; - uint64_t runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier. - for (;;) - { - if (!drflac_oggbs__goto_next_page(oggbs)) { + drflac_uint64 runningGranulePosition = 0; + drflac_uint64 runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier. + for (;;) { + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); - return DR_FALSE; // Never did find that sample... + return DRFLAC_FALSE; // Never did find that sample... } - runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader); - if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sample) { + runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) { break; // The sample is somewhere in the previous page. } @@ -3258,28 +4197,17 @@ dr_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, uint64_t sample) // disregard any pages that do not begin a fresh packet. if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { // <-- Is it a fresh page? if (oggbs->currentPageHeader.segmentTable[0] >= 2) { - uint8_t firstBytesInPage[2]; - if (drflac_oggbs__read_physical(oggbs, firstBytesInPage, 2) != 2) { - drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); - return DR_FALSE; - } + drflac_uint8 firstBytesInPage[2]; + firstBytesInPage[0] = oggbs->pageData[0]; + firstBytesInPage[1] = oggbs->pageData[1]; + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { // <-- Does the page begin with a frame's sync code? runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels; } - if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->bytesRemainingInPage-2, drflac_seek_origin_current)) { - drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); - return DR_FALSE; - } - continue; } } - - if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->bytesRemainingInPage, drflac_seek_origin_current)) { - drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); - return DR_FALSE; - } } @@ -3288,71 +4216,85 @@ dr_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, uint64_t sample) // a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until // we find the one containing the target sample. if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { - return DR_FALSE; + return DRFLAC_FALSE; } - if (!drflac_oggbs__goto_next_page(oggbs)) { - return DR_FALSE; + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + return DRFLAC_FALSE; } // At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep // looping over these frames until we find the one containing the sample we're after. - uint64_t firstSampleInFrame = runningGranulePosition; - for (;;) - { - // NOTE for later: When using Ogg's page/segment based seeking later on we can't use this function (or any drflac__* - // reading functions) because otherwise it will pull extra data for use in it's own internal caches which will then - // break the positioning of the read pointer for the Ogg bitstream. + drflac_uint64 runningSampleCount = runningGranulePosition; + for (;;) { + // There are two ways to find the sample and seek past irrelevant frames: + // 1) Use the native FLAC decoder. + // 2) Use Ogg's framing system. + // + // Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to + // do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code + // duplication for the decoding of frame headers. + // + // Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg + // bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the + // standard drflac__*() APIs because that will read in extra data for it's own internal caching which in turn breaks + // the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read + // using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to + // avoid the use of the drflac_bs object. + // + // Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: + // 1) Seeking is already partially accellerated using Ogg's paging system in the code block above. + // 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. + // 3) Simplicity. if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { - return DR_FALSE; + return DRFLAC_FALSE; } - int channels = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - uint64_t lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channels); - lastSampleInFrame -= 1; // <-- Zero based. + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); - if (sample >= firstSampleInFrame && sample <= lastSampleInFrame) { - break; // The sample is in this frame. + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // it never existed and keep iterating. + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. + if (samplesToDecode == 0) { + return DRFLAC_TRUE; + } + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + // frame never existed and leave the running sample count untouched. + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } } - - - // If we get here it means the sample is not in this frame so we need to move to the next one. Now the cool thing - // with Ogg is that we can efficiently seek past the frame by looking at the lacing values of each segment in - // the page. - firstSampleInFrame = lastSampleInFrame+1; - -#if 1 - // Slow way. This uses the native FLAC decoder to seek past the frame. This is slow because it needs to do a partial - // decode of the frame. Although this is how the native version works, we can use Ogg's framing system to make it - // more efficient. Leaving this here for reference and to use as a basis for debugging purposes. - if (!drflac__seek_to_next_frame(pFlac)) { - return DR_FALSE; - } -#else - // TODO: This is not yet complete. See note at the top of this loop body. - - // Fast(er) way. This uses Ogg's framing system to seek past the frame. This should be much more efficient than the - // native FLAC seeking. - if (!drflac_oggbs__seek_to_next_frame(oggbs)) { - return DR_FALSE; - } -#endif } - - assert(firstSampleInFrame <= sample); - - if (!drflac__decode_frame(pFlac)) { - return DR_FALSE; - } - - size_t samplesToDecode = (size_t)(sample - firstSampleInFrame); // <-- Safe cast because the maximum number of samples in a frame is 65535. - return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; } -dr_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD) +drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { // Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern. + (void)relaxed; pInit->container = drflac_container_ogg; pInit->oggFirstBytePos = 0; @@ -3362,88 +4304,85 @@ dr_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc on // any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. drflac_ogg_page_header header; - uint32_t headerSize; - if (!drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &headerSize)) { - return DR_FALSE; + drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + drflac_uint32 bytesRead = 0; + if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; } - pInit->runningFilePos = headerSize; + pInit->runningFilePos += bytesRead; - for (;;) - { + for (;;) { // Break if we're past the beginning of stream page. if ((header.headerType & 0x02) == 0) { - return DR_FALSE; + return DRFLAC_FALSE; } // Check if it's a FLAC header. int pageBodySize = drflac_ogg__get_page_body_size(&header); - if (pageBodySize == 51) // 51 = the lacing value of the FLAC header packet. - { + if (pageBodySize == 51) { // 51 = the lacing value of the FLAC header packet. // It could be a FLAC page... - uint32_t bytesRemainingInPage = pageBodySize; + drflac_uint32 bytesRemainingInPage = pageBodySize; - uint8_t packetType; + drflac_uint8 packetType; if (onRead(pUserData, &packetType, 1) != 1) { - return DR_FALSE; + return DRFLAC_FALSE; } bytesRemainingInPage -= 1; - if (packetType == 0x7F) - { + if (packetType == 0x7F) { // Increasingly more likely to be a FLAC page... - uint8_t sig[4]; + drflac_uint8 sig[4]; if (onRead(pUserData, sig, 4) != 4) { - return DR_FALSE; + return DRFLAC_FALSE; } bytesRemainingInPage -= 4; - if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') - { + if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { // Almost certainly a FLAC page... - uint8_t mappingVersion[2]; + drflac_uint8 mappingVersion[2]; if (onRead(pUserData, mappingVersion, 2) != 2) { - return DR_FALSE; + return DRFLAC_FALSE; } if (mappingVersion[0] != 1) { - return DR_FALSE; // Only supporting version 1.x of the Ogg mapping. + return DRFLAC_FALSE; // Only supporting version 1.x of the Ogg mapping. } // The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to // be handling it in a generic way based on the serial number and packet types. if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { - return DR_FALSE; + return DRFLAC_FALSE; } // Expecting the native FLAC signature "fLaC". if (onRead(pUserData, sig, 4) != 4) { - return DR_FALSE; + return DRFLAC_FALSE; } - if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') - { + if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { // The remaining data in the page should be the STREAMINFO block. - uint8_t isLastBlock; - uint8_t blockType; - uint32_t blockSize; + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return DR_FALSE; + return DRFLAC_FALSE; } if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return DR_FALSE; // Invalid block type. First block must be the STREAMINFO block. + return DRFLAC_FALSE; // Invalid block type. First block must be the STREAMINFO block. } drflac_streaminfo streaminfo; - if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) - { + if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { // Success! - pInit->sampleRate = streaminfo.sampleRate; - pInit->channels = streaminfo.channels; - pInit->bitsPerSample = streaminfo.bitsPerSample; - pInit->totalSampleCount = streaminfo.totalSampleCount; - pInit->maxBlockSize = streaminfo.maxBlockSize; + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalSampleCount = streaminfo.totalSampleCount; + pInit->maxBlockSize = streaminfo.maxBlockSize; + pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { drflac_metadata metadata; @@ -3459,39 +4398,29 @@ dr_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc on pInit->oggSerial = header.serialNumber; pInit->oggBosHeader = header; break; - } - else - { + } else { // Failed to read STREAMINFO block. Aww, so close... - return DR_FALSE; + return DRFLAC_FALSE; } - } - else - { + } else { // Invalid file. - return DR_FALSE; + return DRFLAC_FALSE; } - } - else - { + } else { // Not a FLAC header. Skip it. if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DR_FALSE; + return DRFLAC_FALSE; } } - } - else - { + } else { // Not a FLAC header. Seek past the entire page and move on to the next. if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DR_FALSE; + return DRFLAC_FALSE; } } - } - else - { + } else { if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { - return DR_FALSE; + return DRFLAC_FALSE; } } @@ -3499,85 +4428,153 @@ dr_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc on // Read the header of the next page. - if (!drflac_ogg__read_page_header(onRead, pUserData, &header, &headerSize)) { - return DR_FALSE; + if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; } - pInit->runningFilePos += headerSize; + pInit->runningFilePos += bytesRead; } // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialiation phase for Ogg is to create the // Ogg bistream object. - pInit->hasMetadataBlocks = DR_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block. - return DR_TRUE; + pInit->hasMetadataBlocks = DRFLAC_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block. + return DRFLAC_TRUE; } #endif -dr_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD) +drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { if (pInit == NULL || onRead == NULL || onSeek == NULL) { - return DR_FALSE; + return DRFLAC_FALSE; } - pInit->onRead = onRead; - pInit->onSeek = onSeek; - pInit->onMeta = onMeta; - pInit->pUserData = pUserData; - pInit->pUserDataMD = pUserDataMD; + drflac_zero_memory(pInit, sizeof(*pInit)); + pInit->onRead = onRead; + pInit->onSeek = onSeek; + pInit->onMeta = onMeta; + pInit->container = container; + pInit->pUserData = pUserData; + pInit->pUserDataMD = pUserDataMD; - uint8_t id[4]; - if (onRead(pUserData, id, 4) != 4) { - return DR_FALSE; + pInit->bs.onRead = onRead; + pInit->bs.onSeek = onSeek; + pInit->bs.pUserData = pUserData; + drflac__reset_cache(&pInit->bs); + + + // If the container is explicitly defined then we can try opening in relaxed mode. + drflac_bool32 relaxed = container != drflac_container_unknown; + + drflac_uint8 id[4]; + + // Skip over any ID3 tags. + for (;;) { + if (onRead(pUserData, id, 4) != 4) { + return DRFLAC_FALSE; // Ran out of data. + } + pInit->runningFilePos += 4; + + if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { + drflac_uint8 header[6]; + if (onRead(pUserData, header, 6) != 6) { + return DRFLAC_FALSE; // Ran out of data. + } + pInit->runningFilePos += 6; + + drflac_uint8 flags = header[1]; + drflac_uint32 headerSize; + drflac_copy_memory(&headerSize, header+2, 4); + headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); + if (flags & 0x10) { + headerSize += 10; + } + + if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; // Failed to seek past the tag. + } + pInit->runningFilePos += headerSize; + } else { + break; + } } if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { - return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD); + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } - #ifndef DR_FLAC_NO_OGG if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { - return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD); + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif + // If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. + if (relaxed) { + if (container == drflac_container_native) { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef DR_FLAC_NO_OGG + if (container == drflac_container_ogg) { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + } + // Unsupported container. - return DR_FALSE; + return DRFLAC_FALSE; } void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit) { - assert(pFlac != NULL); - assert(pInit != NULL); - - memset(pFlac, 0, sizeof(*pFlac)); - pFlac->bs.onRead = pInit->onRead; - pFlac->bs.onSeek = pInit->onSeek; - pFlac->bs.pUserData = pInit->pUserData; - pFlac->bs.nextL2Line = sizeof(pFlac->bs.cacheL2) / sizeof(pFlac->bs.cacheL2[0]); // <-- Initialize to this to force a client-side data retrieval right from the start. - pFlac->bs.consumedBits = sizeof(pFlac->bs.cache)*8; + drflac_assert(pFlac != NULL); + drflac_assert(pInit != NULL); + drflac_zero_memory(pFlac, sizeof(*pFlac)); + pFlac->bs = pInit->bs; pFlac->onMeta = pInit->onMeta; pFlac->pUserDataMD = pInit->pUserDataMD; pFlac->maxBlockSize = pInit->maxBlockSize; pFlac->sampleRate = pInit->sampleRate; - pFlac->channels = (uint8_t)pInit->channels; - pFlac->bitsPerSample = (uint8_t)pInit->bitsPerSample; + pFlac->channels = (drflac_uint8)pInit->channels; + pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; pFlac->totalSampleCount = pInit->totalSampleCount; pFlac->container = pInit->container; } -drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD) +drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { +#ifndef DRFLAC_NO_CPUID + // CPU support first. + drflac__init_cpu_caps(); +#endif + drflac_init_info init; - if (!drflac__init_private(&init, onRead, onSeek, onMeta, pUserData, pUserDataMD)) { + if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { return NULL; } - size_t allocationSize = sizeof(drflac); - allocationSize += init.maxBlockSize * init.channels * sizeof(int32_t); - //allocationSize += init.seektableSize; + // The size of the allocation for the drflac object needs to be large enough to fit the following: + // 1) The main members of the drflac structure + // 2) A block of memory large enough to store the decoded samples of the largest frame in the stream + // 3) If the container is Ogg, a drflac_oggbs object + // + // The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration + // the different SIMD instruction sets. + drflac_uint32 allocationSize = sizeof(drflac); + // The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector + // we are supporting. + drflac_uint32 wholeSIMDVectorCountPerChannel; + if ((init.maxBlockSize % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); + } else { + wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; + } + + drflac_uint32 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + + allocationSize += decodedSamplesAllocationSize; + allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; // Allocate extra bytes to ensure we have enough for alignment. #ifndef DR_FLAC_NO_OGG // There's additional data required for Ogg streams. @@ -3586,13 +4583,13 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p } #endif - drflac* pFlac = (drflac*)malloc(allocationSize); + drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize); drflac__init_from_info(pFlac, &init); - pFlac->pDecodedSamples = (int32_t*)pFlac->pExtraData; + pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + init.maxBlockSize*init.channels); + drflac_oggbs* oggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); oggbs->onRead = onRead; oggbs->onSeek = onSeek; oggbs->pUserData = pUserData; @@ -3606,17 +4603,42 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; pFlac->bs.pUserData = (void*)oggbs; + pFlac->_oggbs = (void*)oggbs; } #endif // Decode metadata before returning. if (init.hasMetadataBlocks) { if (!drflac__read_and_decode_metadata(pFlac)) { - free(pFlac); + DRFLAC_FREE(pFlac); return NULL; } } + // If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode + // the first frame. + if (!init.hasStreamInfoBlock) { + pFlac->currentFrame.header = init.firstFrameHeader; + do + { + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + break; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + DRFLAC_FREE(pFlac); + return NULL; + } + continue; + } else { + DRFLAC_FREE(pFlac); + return NULL; + } + } + } while (1); + } + return pFlac; } @@ -3633,9 +4655,9 @@ static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t byt return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); } -static dr_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) { - assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } @@ -3664,9 +4686,14 @@ static void drflac__close_file_handle(drflac_file file) #else #include +// This doesn't seem to be defined for VC6. +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { - assert(bytesToRead < 0xFFFFFFFF); // dr_flac will never request huge amounts of data at a time. This is a safe assertion. + drflac_assert(bytesToRead < 0xFFFFFFFF); // dr_flac will never request huge amounts of data at a time. This is a safe assertion. DWORD bytesRead; ReadFile((HANDLE)pUserData, bufferOut, (DWORD)bytesToRead, &bytesRead, NULL); @@ -3674,9 +4701,9 @@ static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t byt return (size_t)bytesRead; } -static dr_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) { - assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); return SetFilePointer((HANDLE)pUserData, offset, NULL, (origin == drflac_seek_origin_current) ? FILE_CURRENT : FILE_BEGIN) != INVALID_SET_FILE_POINTER; } @@ -3721,7 +4748,7 @@ drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc on return NULL; } - drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, (void*)file, pUserData); + drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)file, pUserData); if (pFlac == NULL) { drflac__close_file_handle(file); return pFlac; @@ -3734,8 +4761,8 @@ drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc on static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; - assert(memoryStream != NULL); - assert(memoryStream->dataSize >= memoryStream->currentReadPos); + drflac_assert(memoryStream != NULL); + drflac_assert(memoryStream->dataSize >= memoryStream->currentReadPos); size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; if (bytesToRead > bytesRemaining) { @@ -3743,18 +4770,18 @@ static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t by } if (bytesToRead > 0) { - memcpy(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + drflac_copy_memory(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); memoryStream->currentReadPos += bytesToRead; } return bytesToRead; } -static dr_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) +static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; - assert(memoryStream != NULL); - assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + drflac_assert(memoryStream != NULL); + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); if (origin == drflac_seek_origin_current) { if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { @@ -3763,14 +4790,14 @@ static dr_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. } } else { - if ((uint32_t)offset <= memoryStream->dataSize) { + if ((drflac_uint32)offset <= memoryStream->dataSize) { memoryStream->currentReadPos = offset; } else { memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. } } - return DR_TRUE; + return DRFLAC_TRUE; } drflac* drflac_open_memory(const void* data, size_t dataSize) @@ -3790,7 +4817,7 @@ drflac* drflac_open_memory(const void* data, size_t dataSize) #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels); + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else @@ -3808,7 +4835,7 @@ drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drfl memoryStream.data = (const unsigned char*)data; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, &memoryStream, pUserData); + drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData); if (pFlac == NULL) { return NULL; } @@ -3819,7 +4846,7 @@ drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drfl #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels); + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else @@ -3835,12 +4862,20 @@ drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drfl drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData) { - return drflac_open_with_metadata_private(onRead, onSeek, NULL, pUserData, pUserData); + return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData); +} +drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData); } drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData) { - return drflac_open_with_metadata_private(onRead, onSeek, onMeta, pUserData, pUserData); + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData); +} +drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData); } void drflac_close(drflac* pFlac) @@ -3859,8 +4894,8 @@ void drflac_close(drflac* pFlac) #ifndef DR_FLAC_NO_OGG // Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. if (pFlac->container == drflac_container_ogg) { - assert(pFlac->bs.onRead == drflac__on_read_ogg); - drflac_oggbs* oggbs = (drflac_oggbs*)((int32_t*)pFlac->pExtraData + pFlac->maxBlockSize*pFlac->channels); + drflac_assert(pFlac->bs.onRead == drflac__on_read_ogg); + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; if (oggbs->onRead == drflac__on_read_stdio) { drflac__close_file_handle((drflac_file)oggbs->pUserData); } @@ -3868,26 +4903,25 @@ void drflac_close(drflac* pFlac) #endif #endif - free(pFlac); + DRFLAC_FREE(pFlac); } -uint64_t drflac__read_s32__misaligned(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferOut) +drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) { unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); // We should never be calling this when the number of samples to read is >= the sample count. - assert(samplesToRead < channelCount); - assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining); + drflac_assert(samplesToRead < channelCount); + drflac_assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining); - uint64_t samplesRead = 0; - while (samplesToRead > 0) - { - uint64_t totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; - uint64_t samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; - unsigned int channelIndex = samplesReadFromFrameSoFar % channelCount; + drflac_uint64 samplesRead = 0; + while (samplesToRead > 0) { + drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + drflac_uint64 channelIndex = samplesReadFromFrameSoFar % channelCount; - uint64_t nextSampleInFrame = samplesReadFromFrameSoFar / channelCount; + drflac_uint64 nextSampleInFrame = samplesReadFromFrameSoFar / channelCount; int decodedSample = 0; switch (pFlac->currentFrame.header.channelAssignment) @@ -3901,7 +4935,6 @@ uint64_t drflac__read_s32__misaligned(drflac* pFlac, uint64_t samplesToRead, int int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; decodedSample = left - side; } - } break; case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: @@ -3913,7 +4946,6 @@ uint64_t drflac__read_s32__misaligned(drflac* pFlac, uint64_t samplesToRead, int } else { decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; } - } break; case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: @@ -3933,7 +4965,6 @@ uint64_t drflac__read_s32__misaligned(drflac* pFlac, uint64_t samplesToRead, int mid = (((unsigned int)mid) << 1) | (side & 0x01); decodedSample = (mid - side) >> 1; } - } break; case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: @@ -3958,19 +4989,15 @@ uint64_t drflac__read_s32__misaligned(drflac* pFlac, uint64_t samplesToRead, int return samplesRead; } -uint64_t drflac__seek_forward_by_samples(drflac* pFlac, uint64_t samplesToRead) +drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 samplesToRead) { - uint64_t samplesRead = 0; - while (samplesToRead > 0) - { - if (pFlac->currentFrame.samplesRemaining == 0) - { + drflac_uint64 samplesRead = 0; + while (samplesToRead > 0) { + if (pFlac->currentFrame.samplesRemaining == 0) { if (!drflac__read_and_decode_next_frame(pFlac)) { break; // Couldn't read the next frame, so just break from the loop and return. } - } - else - { + } else { samplesRead += 1; pFlac->currentFrame.samplesRemaining -= 1; samplesToRead -= 1; @@ -3980,7 +5007,7 @@ uint64_t drflac__seek_forward_by_samples(drflac* pFlac, uint64_t samplesToRead) return samplesRead; } -uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferOut) +drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) { // Note that is allowed to be null, in which case this will be treated as something like a seek. if (pFlac == NULL || samplesToRead == 0) { @@ -3992,27 +5019,23 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO } - uint64_t samplesRead = 0; - while (samplesToRead > 0) - { + drflac_uint64 samplesRead = 0; + while (samplesToRead > 0) { // If we've run out of samples in this frame, go to the next. - if (pFlac->currentFrame.samplesRemaining == 0) - { + if (pFlac->currentFrame.samplesRemaining == 0) { if (!drflac__read_and_decode_next_frame(pFlac)) { break; // Couldn't read the next frame, so just break from the loop and return. } - } - else - { + } else { // Here is where we grab the samples and interleave them. unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); - uint64_t totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; - uint64_t samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; - int misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; + drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; if (misalignedSampleCount > 0) { - uint64_t misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); + drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); samplesRead += misalignedSamplesRead; samplesReadFromFrameSoFar += misalignedSamplesRead; bufferOut += misalignedSamplesRead; @@ -4020,22 +5043,22 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO } - uint64_t alignedSampleCountPerChannel = samplesToRead / channelCount; + drflac_uint64 alignedSampleCountPerChannel = samplesToRead / channelCount; if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) { alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount; } - uint64_t firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount; + drflac_uint64 firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount; unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample; switch (pFlac->currentFrame.header.channelAssignment) { case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; - const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; - for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { int left = pDecodedSamples0[i]; int side = pDecodedSamples1[i]; int right = left - side; @@ -4047,10 +5070,10 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; - const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; - for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { int side = pDecodedSamples0[i]; int right = pDecodedSamples1[i]; int left = right + side; @@ -4062,12 +5085,12 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; - const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; - for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { int side = pDecodedSamples1[i]; - int mid = (((uint32_t)pDecodedSamples0[i]) << 1) | (side & 0x01); + int mid = (((drflac_uint32)pDecodedSamples0[i]) << 1) | (side & 0x01); bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); @@ -4080,10 +5103,10 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO if (pFlac->currentFrame.header.channelAssignment == 1) // 1 = Stereo { // Stereo optimized inner loop unroll. - const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; - const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; - for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); } @@ -4091,7 +5114,7 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO else { // Generic interleaving. - for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { for (unsigned int j = 0; j < channelCount; ++j) { bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample); } @@ -4100,7 +5123,7 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO } break; } - uint64_t alignedSamplesRead = alignedSampleCountPerChannel * channelCount; + drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount; samplesRead += alignedSamplesRead; samplesReadFromFrameSoFar += alignedSamplesRead; bufferOut += alignedSamplesRead; @@ -4110,9 +5133,8 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO // At this point we may still have some excess samples left to read. - if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) - { - uint64_t excessSamplesRead = 0; + if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) { + drflac_uint64 excessSamplesRead = 0; if (samplesToRead < pFlac->currentFrame.samplesRemaining) { excessSamplesRead = drflac__read_s32__misaligned(pFlac, samplesToRead, bufferOut); } else { @@ -4130,35 +5152,66 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO return samplesRead; } -uint64_t drflac_read_s16(drflac* pFlac, uint64_t samplesToRead, int16_t* pBufferOut) +drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut) { // This reads samples in 2 passes and can probably be optimized. - uint64_t samplesRead = 0; + drflac_uint64 totalSamplesRead = 0; while (samplesToRead > 0) { - int32_t samples32[4096]; - uint64_t samplesJustRead = drflac_read_s32(pFlac, samplesToRead > 4096 ? 4096 : samplesToRead, samples32); + drflac_int32 samples32[4096]; + drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32); if (samplesJustRead == 0) { break; // Reached the end. } // s32 -> s16 - for (uint64_t i = 0; i < samplesJustRead; ++i) { - pBufferOut[i] = (int16_t)(samples32[i] >> 16); + for (drflac_uint64 i = 0; i < samplesJustRead; ++i) { + pBufferOut[i] = (drflac_int16)(samples32[i] >> 16); } - samplesRead += samplesJustRead; + totalSamplesRead += samplesJustRead; samplesToRead -= samplesJustRead; pBufferOut += samplesJustRead; } - return samplesRead; + return totalSamplesRead; } -dr_bool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex) +drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut) +{ + // This reads samples in 2 passes and can probably be optimized. + drflac_uint64 totalSamplesRead = 0; + + while (samplesToRead > 0) { + drflac_int32 samples32[4096]; + drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32); + if (samplesJustRead == 0) { + break; // Reached the end. + } + + // s32 -> f32 + for (drflac_uint64 i = 0; i < samplesJustRead; ++i) { + pBufferOut[i] = (float)(samples32[i] / 2147483648.0); + } + + totalSamplesRead += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; + } + + return totalSamplesRead; +} + +drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) { if (pFlac == NULL) { - return DR_FALSE; + return DRFLAC_FALSE; + } + + // If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present + // when the decoder was opened. + if (pFlac->firstFramePos == 0) { + return DRFLAC_FALSE; } if (sampleIndex == 0) { @@ -4188,156 +5241,91 @@ dr_bool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex) } - return DR_TRUE; + return DRFLAC_TRUE; } //// High Level APIs //// -int32_t* drflac__full_decode_and_close_s32(drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, uint64_t* totalSampleCountOut) -{ - assert(pFlac != NULL); +// I couldn't figure out where SIZE_MAX was defined for VC6. If anybody knows, let me know. +#if defined(_MSC_VER) && _MSC_VER <= 1200 +#ifdef DRFLAC_64BIT +#define SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) +#else +#define SIZE_MAX 0xFFFFFFFF +#endif +#endif - int32_t* pSampleData = NULL; - uint64_t totalSampleCount = pFlac->totalSampleCount; - - if (totalSampleCount == 0) - { - int32_t buffer[4096]; - - size_t sampleDataBufferSize = sizeof(buffer); - pSampleData = (int32_t*)malloc(sampleDataBufferSize); - if (pSampleData == NULL) { - goto on_error; - } - - uint64_t samplesRead; - while ((samplesRead = (uint64_t)drflac_read_s32(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) - { - if (((totalSampleCount + samplesRead) * sizeof(int32_t)) > sampleDataBufferSize) { - sampleDataBufferSize *= 2; - int32_t* pNewSampleData = (int32_t*)realloc(pSampleData, sampleDataBufferSize); - if (pNewSampleData == NULL) { - free(pSampleData); - goto on_error; - } - - pSampleData = pNewSampleData; - } - - memcpy(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(int32_t))); - totalSampleCount += samplesRead; - } - - // At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to - // protect those ears from random noise! - memset(pSampleData + totalSampleCount, 0, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(int32_t))); - } - else - { - uint64_t dataSize = totalSampleCount * sizeof(int32_t); - if (dataSize > SIZE_MAX) { - goto on_error; // The decoded data is too big. - } - - pSampleData = (int32_t*)malloc((size_t)dataSize); // <-- Safe cast as per the check above. - if (pSampleData == NULL) { - goto on_error; - } - - uint64_t samplesDecoded = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSampleData); - if (samplesDecoded != pFlac->totalSampleCount) { - free(pSampleData); - goto on_error; // Something went wrong when decoding the FLAC stream. - } - } - - - if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; - if (channelsOut) *channelsOut = pFlac->channels; - if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount; - - drflac_close(pFlac); - return pSampleData; - -on_error: - drflac_close(pFlac); - return NULL; +// Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me. +#define DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(extension, type) \ +static type* drflac__full_decode_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)\ +{ \ + drflac_assert(pFlac != NULL); \ + \ + type* pSampleData = NULL; \ + drflac_uint64 totalSampleCount = pFlac->totalSampleCount; \ + \ + if (totalSampleCount == 0) { \ + type buffer[4096]; \ + \ + size_t sampleDataBufferSize = sizeof(buffer); \ + pSampleData = (type*)DRFLAC_MALLOC(sampleDataBufferSize); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + drflac_uint64 samplesRead; \ + while ((samplesRead = (drflac_uint64)drflac_read_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) { \ + if (((totalSampleCount + samplesRead) * sizeof(type)) > sampleDataBufferSize) { \ + sampleDataBufferSize *= 2; \ + type* pNewSampleData = (type*)DRFLAC_REALLOC(pSampleData, sampleDataBufferSize); \ + if (pNewSampleData == NULL) { \ + DRFLAC_FREE(pSampleData); \ + goto on_error; \ + } \ + \ + pSampleData = pNewSampleData; \ + } \ + \ + drflac_copy_memory(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(type))); \ + totalSampleCount += samplesRead; \ + } \ + \ + /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ + protect those ears from random noise! */ \ + drflac_zero_memory(pSampleData + totalSampleCount, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(type))); \ + } else { \ + drflac_uint64 dataSize = totalSampleCount * sizeof(type); \ + if (dataSize > SIZE_MAX) { \ + goto on_error; /* The decoded data is too big. */ \ + } \ + \ + pSampleData = (type*)DRFLAC_MALLOC((size_t)dataSize); /* <-- Safe cast as per the check above. */ \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + totalSampleCount = drflac_read_##extension(pFlac, pFlac->totalSampleCount, pSampleData); \ + } \ + \ + if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ + if (channelsOut) *channelsOut = pFlac->channels; \ + if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount; \ + \ + drflac_close(pFlac); \ + return pSampleData; \ + \ +on_error: \ + drflac_close(pFlac); \ + return NULL; \ } -int16_t* drflac__full_decode_and_close_s16(drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, uint64_t* totalSampleCountOut) -{ - assert(pFlac != NULL); +DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s32, drflac_int32) +DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s16, drflac_int16) +DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(f32, float) - int16_t* pSampleData = NULL; - uint64_t totalSampleCount = pFlac->totalSampleCount; - - if (totalSampleCount == 0) - { - int16_t buffer[4096]; - - size_t sampleDataBufferSize = sizeof(buffer); - pSampleData = (int16_t*)malloc(sampleDataBufferSize); - if (pSampleData == NULL) { - goto on_error; - } - - uint64_t samplesRead; - while ((samplesRead = (uint64_t)drflac_read_s16(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) - { - if (((totalSampleCount + samplesRead) * sizeof(int16_t)) > sampleDataBufferSize) { - sampleDataBufferSize *= 2; - int16_t* pNewSampleData = (int16_t*)realloc(pSampleData, sampleDataBufferSize); - if (pNewSampleData == NULL) { - free(pSampleData); - goto on_error; - } - - pSampleData = pNewSampleData; - } - - memcpy(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(int16_t))); - totalSampleCount += samplesRead; - } - - // At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to - // protect those ears from random noise! - memset(pSampleData + totalSampleCount, 0, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(int16_t))); - } - else - { - uint64_t dataSize = totalSampleCount * sizeof(int16_t); - if (dataSize > SIZE_MAX) { - goto on_error; // The decoded data is too big. - } - - pSampleData = (int16_t*)malloc((size_t)dataSize); // <-- Safe cast as per the check above. - if (pSampleData == NULL) { - goto on_error; - } - - uint64_t samplesDecoded = drflac_read_s16(pFlac, pFlac->totalSampleCount, pSampleData); - if (samplesDecoded != pFlac->totalSampleCount) { - free(pSampleData); - goto on_error; // Something went wrong when decoding the FLAC stream. - } - } - - - if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; - if (channelsOut) *channelsOut = pFlac->channels; - if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount; - - drflac_close(pFlac); - return pSampleData; - -on_error: - drflac_close(pFlac); - return NULL; -} - -int32_t* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) +drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { // Safety. if (sampleRate) *sampleRate = 0; @@ -4352,7 +5340,7 @@ int32_t* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc on return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); } -int16_t* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) +drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { // Safety. if (sampleRate) *sampleRate = 0; @@ -4367,8 +5355,23 @@ int16_t* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc on return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); } +float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + // Safety. + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); +} + #ifndef DR_FLAC_NO_STDIO -int32_t* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) +drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { if (sampleRate) *sampleRate = 0; if (channels) *channels = 0; @@ -4382,7 +5385,7 @@ int32_t* drflac_open_and_decode_file_s32(const char* filename, unsigned int* cha return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); } -int16_t* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) +drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { if (sampleRate) *sampleRate = 0; if (channels) *channels = 0; @@ -4395,9 +5398,23 @@ int16_t* drflac_open_and_decode_file_s16(const char* filename, unsigned int* cha return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); } + +float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_file(filename); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); +} #endif -int32_t* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) +drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { if (sampleRate) *sampleRate = 0; if (channels) *channels = 0; @@ -4411,7 +5428,7 @@ int32_t* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, un return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); } -int16_t* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) +drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) { if (sampleRate) *sampleRate = 0; if (channels) *channels = 0; @@ -4425,15 +5442,29 @@ int16_t* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, un return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); } +float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); +} + void drflac_free(void* pSampleDataReturnedByOpenAndDecode) { - free(pSampleDataReturnedByOpenAndDecode); + DRFLAC_FREE(pSampleDataReturnedByOpenAndDecode); } -void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, uint32_t commentCount, const char* pComments) +void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments) { if (pIter == NULL) { return; @@ -4443,7 +5474,7 @@ void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, pIter->pRunningData = pComments; } -const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, uint32_t* pCommentLengthOut) +const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) { // Safety. if (pCommentLengthOut) *pCommentLengthOut = 0; @@ -4452,7 +5483,7 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, ui return NULL; } - uint32_t length = drflac__le2host_32(*(uint32_t*)pIter->pRunningData); + drflac_uint32 length = drflac__le2host_32(*(drflac_uint32*)pIter->pRunningData); pIter->pRunningData += 4; const char* pComment = pIter->pRunningData; @@ -4467,20 +5498,63 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, ui // REVISION HISTORY // +// v0.8d - 2017-09-22 +// - Add support for decoding streams with ID3 tags. ID3 tags are just skipped. +// +// v0.8c - 2017-09-07 +// - Fix warning on non-x86/x64 architectures. +// +// v0.8b - 2017-08-19 +// - Fix build on non-x86/x64 architectures. +// +// v0.8a - 2017-08-13 +// - A small optimization for the Clang build. +// +// v0.8 - 2017-08-12 +// - API CHANGE: Rename dr_* types to drflac_*. +// - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation. +// - Add support for custom implementations of malloc(), realloc(), etc. +// - Add CRC checking to Ogg encapsulated streams. +// - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported. +// - Bug fixes. +// +// v0.7 - 2017-07-23 +// - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed(). +// +// v0.6 - 2017-07-22 +// - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they +// never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame. +// +// v0.5 - 2017-07-16 +// - Fix typos. +// - Change drflac_bool* types to unsigned. +// - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC. +// +// v0.4f - 2017-03-10 +// - Fix a couple of bugs with the bitstreaming code. +// +// v0.4e - 2017-02-17 +// - Fix some warnings. +// +// v0.4d - 2016-12-26 +// - Add support for 32-bit floating-point PCM decoding. +// - Use drflac_int*/drflac_uint* sized types to improve compiler support. +// - Minor improvements to documentation. +// // v0.4c - 2016-12-26 // - Add support for signed 16-bit integer PCM decoding. // // v0.4b - 2016-10-23 -// - A minor change to dr_bool8 and dr_bool32 types. +// - A minor change to drflac_bool8 and drflac_bool32 types. // // v0.4a - 2016-10-11 -// - Rename drBool32 to dr_bool32 for styling consistency. +// - Rename drBool32 to drflac_bool32 for styling consistency. // // v0.4 - 2016-09-29 // - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type. -// - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32() +// - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32(). // - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to -// keep it consistent with dr_audio. +// keep it consistent with drflac_audio. // // v0.3f - 2016-09-21 // - Fix a warning with GCC. @@ -4533,11 +5607,6 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, ui // - Initial versioned release. -// TODO -// - Add support for initializing the decoder without a header STREAMINFO block. -// - Test CUESHEET metadata blocks. - - /* This is free and unencumbered software released into the public domain. diff --git a/raylib/external/dr_wav.h b/raylib/external/dr_wav.h new file mode 100644 index 0000000..536df8f --- /dev/null +++ b/raylib/external/dr_wav.h @@ -0,0 +1,3455 @@ +// WAV audio loader and writer. Public domain. See "unlicense" statement at the end of this file. +// dr_wav - v0.7a - 2017-11-17 +// +// David Reid - mackron@gmail.com + +// USAGE +// +// This is a single-file library. To use it, do something like the following in one .c file. +// #define DR_WAV_IMPLEMENTATION +// #include "dr_wav.h" +// +// You can then #include this file in other parts of the program as you would with any other header file. Do something +// like the following to read audio data: +// +// drwav wav; +// if (!drwav_init_file(&wav, "my_song.wav")) { +// // Error opening WAV file. +// } +// +// drwav_int32* pDecodedInterleavedSamples = malloc(wav.totalSampleCount * sizeof(drwav_int32)); +// size_t numberOfSamplesActuallyDecoded = drwav_read_s32(&wav, wav.totalSampleCount, pDecodedInterleavedSamples); +// +// ... +// +// drwav_uninit(&wav); +// +// You can also use drwav_open() to allocate and initialize the loader for you: +// +// drwav* pWav = drwav_open_file("my_song.wav"); +// if (pWav == NULL) { +// // Error opening WAV file. +// } +// +// ... +// +// drwav_close(pWav); +// +// If you just want to quickly open and read the audio data in a single operation you can do something like this: +// +// unsigned int channels; +// unsigned int sampleRate; +// drwav_uint64 totalSampleCount; +// float* pSampleData = drwav_open_and_read_file_s32("my_song.wav", &channels, &sampleRate, &totalSampleCount); +// if (pSampleData == NULL) { +// // Error opening and reading WAV file. +// } +// +// ... +// +// drwav_free(pSampleData); +// +// The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in +// this case), but you can still output the audio data in it's internal format (see notes below for supported formats): +// +// size_t samplesRead = drwav_read(&wav, wav.totalSampleCount, pDecodedInterleavedSamples); +// +// You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for +// a particular data format: +// +// size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer); +// +// +// dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work +// without any manual intervention. +// +// +// dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at +// drwav_open_write(), drwav_open_file_write(), etc. Use drwav_write() to write samples, or drwav_write_raw() to write +// raw data in the "data" chunk. +// +// drwav_data_format format; +// format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64. +// format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes. +// format.channels = 2; +// format.sampleRate = 44100; +// format.bitsPerSample = 16; +// drwav* pWav = drwav_open_file_write("data/recording.wav", &format); +// +// ... +// +// drwav_uint64 samplesWritten = drwav_write(pWav, sampleCount, pSamples); +// +// +// +// OPTIONS +// #define these options before including this file. +// +// #define DR_WAV_NO_CONVERSION_API +// Disables conversion APIs such as drwav_read_f32() and drwav_s16_to_f32(). +// +// #define DR_WAV_NO_STDIO +// Disables drwav_open_file(), drwav_open_file_write(), etc. +// +// +// +// QUICK NOTES +// - Samples are always interleaved. +// - The default read function does not do any data conversion. Use drwav_read_f32() to read and convert audio data +// to IEEE 32-bit floating point samples, drwav_read_s32() to read samples as signed 32-bit PCM and drwav_read_s16() +// to read samples as signed 16-bit PCM. Tested and supported internal formats include the following: +// - Unsigned 8-bit PCM +// - Signed 12-bit PCM +// - Signed 16-bit PCM +// - Signed 24-bit PCM +// - Signed 32-bit PCM +// - IEEE 32-bit floating point. +// - IEEE 64-bit floating point. +// - A-law and u-law +// - Microsoft ADPCM +// - IMA ADPCM (DVI, format code 0x11) +// - dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format. + + +#ifndef dr_wav_h +#define dr_wav_h + +#include + +#if defined(_MSC_VER) && _MSC_VER < 1600 +typedef signed char drwav_int8; +typedef unsigned char drwav_uint8; +typedef signed short drwav_int16; +typedef unsigned short drwav_uint16; +typedef signed int drwav_int32; +typedef unsigned int drwav_uint32; +typedef signed __int64 drwav_int64; +typedef unsigned __int64 drwav_uint64; +#else +#include +typedef int8_t drwav_int8; +typedef uint8_t drwav_uint8; +typedef int16_t drwav_int16; +typedef uint16_t drwav_uint16; +typedef int32_t drwav_int32; +typedef uint32_t drwav_uint32; +typedef int64_t drwav_int64; +typedef uint64_t drwav_uint64; +#endif +typedef drwav_uint8 drwav_bool8; +typedef drwav_uint32 drwav_bool32; +#define DRWAV_TRUE 1 +#define DRWAV_FALSE 0 + +#ifdef __cplusplus +extern "C" { +#endif + +// Common data formats. +#define DR_WAVE_FORMAT_PCM 0x1 +#define DR_WAVE_FORMAT_ADPCM 0x2 +#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 +#define DR_WAVE_FORMAT_ALAW 0x6 +#define DR_WAVE_FORMAT_MULAW 0x7 +#define DR_WAVE_FORMAT_DVI_ADPCM 0x11 +#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE + +typedef enum +{ + drwav_seek_origin_start, + drwav_seek_origin_current +} drwav_seek_origin; + +typedef enum +{ + drwav_container_riff, + drwav_container_w64 +} drwav_container; + +// Callback for when data is read. Return value is the number of bytes actually read. +// +// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family. +// pBufferOut [out] The output buffer. +// bytesToRead [in] The number of bytes to read. +// +// Returns the number of bytes actually read. +// +// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until +// either the entire bytesToRead is filled or you have reached the end of the stream. +typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +// Callback for when data is written. Returns value is the number of bytes actually written. +// +// pUserData [in] The user data that was passed to drwav_init_write(), drwav_open_write() and family. +// pData [out] A pointer to the data to write. +// bytesToWrite [in] The number of bytes to write. +// +// Returns the number of bytes actually written. +// +// If the return value differs from bytesToWrite, it indicates an error. +typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); + +// Callback for when data needs to be seeked. +// +// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family. +// offset [in] The number of bytes to move, relative to the origin. Will never be negative. +// origin [in] The origin of the seek - the current position or the start of the stream. +// +// Returns whether or not the seek was successful. +// +// Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which +// will be either drwav_seek_origin_start or drwav_seek_origin_current. +typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); + +// Structure for internal use. Only used for loaders opened with drwav_open_memory(). +typedef struct +{ + const drwav_uint8* data; + size_t dataSize; + size_t currentReadPos; +} drwav__memory_stream; + +// Structure for internal use. Only used for writers opened with drwav_open_memory_write(). +typedef struct +{ + void** ppData; + size_t* pDataSize; + size_t dataSize; + size_t dataCapacity; + size_t currentWritePos; +} drwav__memory_stream_write; + +typedef struct +{ + drwav_container container; // RIFF, W64. + drwav_uint32 format; // DR_WAVE_FORMAT_* + drwav_uint32 channels; + drwav_uint32 sampleRate; + drwav_uint32 bitsPerSample; +} drwav_data_format; + +typedef struct +{ + // The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications + // that require support for data formats not natively supported by dr_wav. + drwav_uint16 formatTag; + + // The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. + drwav_uint16 channels; + + // The sample rate. Usually set to something like 44100. + drwav_uint32 sampleRate; + + // Average bytes per second. You probably don't need this, but it's left here for informational purposes. + drwav_uint32 avgBytesPerSec; + + // Block align. This is equal to the number of channels * bytes per sample. + drwav_uint16 blockAlign; + + // Bit's per sample. + drwav_uint16 bitsPerSample; + + // The size of the extended data. Only used internally for validation, but left here for informational purposes. + drwav_uint16 extendedSize; + + // The number of valid bits per sample. When is equal to WAVE_FORMAT_EXTENSIBLE, + // is always rounded up to the nearest multiple of 8. This variable contains information about exactly how + // many bits a valid per sample. Mainly used for informational purposes. + drwav_uint16 validBitsPerSample; + + // The channel mask. Not used at the moment. + drwav_uint32 channelMask; + + // The sub-format, exactly as specified by the wave file. + drwav_uint8 subFormat[16]; +} drwav_fmt; + +typedef struct +{ + // A pointer to the function to call when more data is needed. + drwav_read_proc onRead; + + // A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. + drwav_write_proc onWrite; + + // A pointer to the function to call when the wav file needs to be seeked. + drwav_seek_proc onSeek; + + // The user data to pass to callbacks. + void* pUserData; + + + // Whether or not the WAV file is formatted as a standard RIFF file or W64. + drwav_container container; + + + // Structure containing format information exactly as specified by the wav file. + drwav_fmt fmt; + + // The sample rate. Will be set to something like 44100. + drwav_uint32 sampleRate; + + // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. + drwav_uint16 channels; + + // The bits per sample. Will be set to somthing like 16, 24, etc. + drwav_uint16 bitsPerSample; + + // The number of bytes per sample. + drwav_uint16 bytesPerSample; + + // Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). + drwav_uint16 translatedFormatTag; + + // The total number of samples making up the audio data. Use * to calculate + // the required size of a buffer to hold the entire audio data. + drwav_uint64 totalSampleCount; + + + // The size in bytes of the data chunk. + drwav_uint64 dataChunkDataSize; + + // The position in the stream of the first byte of the data chunk. This is used for seeking. + drwav_uint64 dataChunkDataPos; + + // The number of bytes remaining in the data chunk. + drwav_uint64 bytesRemaining; + + + // A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_open_memory(). + drwav__memory_stream memoryStream; + drwav__memory_stream_write memoryStreamWrite; + + // Generic data for compressed formats. This data is shared across all block-compressed formats. + struct + { + drwav_uint64 iCurrentSample; // The index of the next sample that will be read by drwav_read_*(). This is used with "totalSampleCount" to ensure we don't read excess samples at the end of the last block. + } compressed; + + // Microsoft ADPCM specific data. + struct + { + drwav_uint32 bytesRemainingInBlock; + drwav_uint16 predictor[2]; + drwav_int32 delta[2]; + drwav_int32 cachedSamples[4]; // Samples are stored in this cache during decoding. + drwav_uint32 cachedSampleCount; + drwav_int32 prevSamples[2][2]; // The previous 2 samples for each channel (2 channels at most). + } msadpcm; + + // IMA ADPCM specific data. + struct + { + drwav_uint32 bytesRemainingInBlock; + drwav_int32 predictor[2]; + drwav_int32 stepIndex[2]; + drwav_int32 cachedSamples[16]; // Samples are stored in this cache during decoding. + drwav_uint32 cachedSampleCount; + } ima; +} drwav; + + +// Initializes a pre-allocated drwav object. +// +// onRead [in] The function to call when data needs to be read from the client. +// onSeek [in] The function to call when the read position of the client data needs to move. +// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. +// +// Returns true if successful; false otherwise. +// +// Close the loader with drwav_uninit(). +// +// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory() +// to open the stream from a file or from a block of memory respectively. +// +// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate +// a drwav object on the heap and return a pointer to it. +// +// See also: drwav_init_file(), drwav_init_memory(), drwav_uninit() +drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData); + +// Initializes a pre-allocated drwav object for writing. +// +// onWrite [in] The function to call when data needs to be written. +// onSeek [in] The function to call when the write position needs to move. +// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek. +// +// Returns true if successful; false otherwise. +// +// Close the writer with drwav_uninit(). +// +// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory() +// to open the stream from a file or from a block of memory respectively. +// +// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate +// a drwav object on the heap and return a pointer to it. +// +// See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit() +drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData); + +// Uninitializes the given drwav object. +// +// Use this only for objects initialized with drwav_init(). +void drwav_uninit(drwav* pWav); + + +// Opens a wav file using the given callbacks. +// +// onRead [in] The function to call when data needs to be read from the client. +// onSeek [in] The function to call when the read position of the client data needs to move. +// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. +// +// Returns null on error. +// +// Close the loader with drwav_close(). +// +// This is the lowest level function for opening a WAV file. You can also use drwav_open_file() and drwav_open_memory() +// to open the stream from a file or from a block of memory respectively. +// +// This is different from drwav_init() in that it will allocate the drwav object for you via DRWAV_MALLOC() before +// initializing it. +// +// See also: drwav_open_file(), drwav_open_memory(), drwav_close() +drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData); + +// Opens a wav file for writing using the given callbacks. +// +// onWrite [in] The function to call when data needs to be written. +// onSeek [in] The function to call when the write position needs to move. +// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek. +// +// Returns null on error. +// +// Close the loader with drwav_close(). +// +// This is the lowest level function for opening a WAV file. You can also use drwav_open_file_write() and drwav_open_memory_write() +// to open the stream from a file or from a block of memory respectively. +// +// This is different from drwav_init_write() in that it will allocate the drwav object for you via DRWAV_MALLOC() before +// initializing it. +// +// See also: drwav_open_file_write(), drwav_open_memory_write(), drwav_close() +drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData); + +// Uninitializes and deletes the the given drwav object. +// +// Use this only for objects created with drwav_open(). +void drwav_close(drwav* pWav); + + +// Reads raw audio data. +// +// This is the lowest level function for reading audio data. It simply reads the given number of +// bytes of the raw internal sample data. +// +// Consider using drwav_read_s16(), drwav_read_s32() or drwav_read_f32() for reading sample data in +// a consistent format. +// +// Returns the number of bytes actually read. +size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); + +// Reads a chunk of audio data in the native internal format. +// +// This is typically the most efficient way to retrieve audio data, but it does not do any format +// conversions which means you'll need to convert the data manually if required. +// +// If the return value is less than it means the end of the file has been reached or +// you have requested more samples than can possibly fit in the output buffer. +// +// This function will only work when sample data is of a fixed size and uncompressed. If you are +// using a compressed format consider using drwav_read_raw() or drwav_read_s16/s32/f32/etc(). +drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut); + +// Seeks to the given sample. +// +// Returns true if successful; false otherwise. +drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample); + + +// Writes raw audio data. +// +// Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error. +size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); + +// Writes audio data based on sample counts. +// +// Returns the number of samples written. +drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData); + + + +//// Convertion Utilities //// +#ifndef DR_WAV_NO_CONVERSION_API + +// Reads a chunk of audio data and converts it to signed 16-bit PCM samples. +// +// Returns the number of samples actually read. +// +// If the return value is less than it means the end of the file has been reached. +drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); + +// Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. +void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + +// Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. +void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + +// Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. +void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); + +// Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. +void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); + +// Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. +void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); + +// Low-level function for converting A-law samples to signed 16-bit PCM samples. +void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + +// Low-level function for converting u-law samples to signed 16-bit PCM samples. +void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); + + +// Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples. +// +// Returns the number of samples actually read. +// +// If the return value is less than it means the end of the file has been reached. +drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut); + +// Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. +void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + +// Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. +void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); + +// Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. +void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + +// Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. +void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); + +// Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. +void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); + +// Low-level function for converting A-law samples to IEEE 32-bit floating point samples. +void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + +// Low-level function for converting u-law samples to IEEE 32-bit floating point samples. +void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); + + +// Reads a chunk of audio data and converts it to signed 32-bit PCM samples. +// +// Returns the number of samples actually read. +// +// If the return value is less than it means the end of the file has been reached. +drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut); + +// Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. +void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +// Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. +void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); + +// Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. +void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +// Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. +void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); + +// Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. +void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); + +// Low-level function for converting A-law samples to signed 32-bit PCM samples. +void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +// Low-level function for converting u-law samples to signed 32-bit PCM samples. +void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + +#endif //DR_WAV_NO_CONVERSION_API + + +//// High-Level Convenience Helpers //// + +#ifndef DR_WAV_NO_STDIO + +// Helper for initializing a wave file using stdio. +// +// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav +// objects because the operating system may restrict the number of file handles an application can have open at +// any given time. +drwav_bool32 drwav_init_file(drwav* pWav, const char* filename); + +// Helper for initializing a wave file for writing using stdio. +// +// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav +// objects because the operating system may restrict the number of file handles an application can have open at +// any given time. +drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat); + +// Helper for opening a wave file using stdio. +// +// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav +// objects because the operating system may restrict the number of file handles an application can have open at +// any given time. +drwav* drwav_open_file(const char* filename); + +// Helper for opening a wave file for writing using stdio. +// +// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav +// objects because the operating system may restrict the number of file handles an application can have open at +// any given time. +drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat); + +#endif //DR_WAV_NO_STDIO + +// Helper for initializing a loader from a pre-allocated memory buffer. +// +// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +// the lifetime of the drwav object. +// +// The buffer should contain the contents of the entire wave file, not just the sample data. +drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize); + +// Helper for initializing a writer which outputs data to a memory buffer. +// +// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free(). +// +// The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be +// considered valid until after drwav_uninit() has been called anyway. +drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat); + +// Helper for opening a loader from a pre-allocated memory buffer. +// +// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +// the lifetime of the drwav object. +// +// The buffer should contain the contents of the entire wave file, not just the sample data. +drwav* drwav_open_memory(const void* data, size_t dataSize); + +// Helper for opening a writer which outputs data to a memory buffer. +// +// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free(). +// +// The buffer will remain allocated even after drwav_close() is called. Indeed, the buffer should not be +// considered valid until after drwav_close() has been called anyway. +drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat); + + +#ifndef DR_WAV_NO_CONVERSION_API +// Opens and reads a wav file in a single operation. +drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +#ifndef DR_WAV_NO_STDIO +// Opens an decodes a wav file in a single operation. +drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +drwav_int32* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +#endif + +// Opens an decodes a wav file from a block of memory in a single operation. +drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +drwav_int32* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +#endif + +// Frees data that was allocated internally by dr_wav. +void drwav_free(void* pDataReturnedByOpenAndRead); + +#ifdef __cplusplus +} +#endif +#endif // dr_wav_h + + +///////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +///////////////////////////////////////////////////// + +#ifdef DR_WAV_IMPLEMENTATION +#include +#include // For memcpy(), memset() +#include // For INT_MAX + +#ifndef DR_WAV_NO_STDIO +#include +#endif + +// Standard library stuff. +#ifndef DRWAV_ASSERT +#include +#define DRWAV_ASSERT(expression) assert(expression) +#endif +#ifndef DRWAV_MALLOC +#define DRWAV_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRWAV_REALLOC +#define DRWAV_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRWAV_FREE +#define DRWAV_FREE(p) free((p)) +#endif +#ifndef DRWAV_COPY_MEMORY +#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRWAV_ZERO_MEMORY +#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif + +#define drwav_countof(x) (sizeof(x) / sizeof(x[0])) +#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) +#define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) +#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) + +#define drwav_assert DRWAV_ASSERT +#define drwav_copy_memory DRWAV_COPY_MEMORY +#define drwav_zero_memory DRWAV_ZERO_MEMORY + + +#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future. + +#ifdef _MSC_VER +#define DRWAV_INLINE __forceinline +#else +#ifdef __GNUC__ +#define DRWAV_INLINE inline __attribute__((always_inline)) +#else +#define DRWAV_INLINE inline +#endif +#endif + +// I couldn't figure out where SIZE_MAX was defined for VC6. If anybody knows, let me know. +#if defined(_MSC_VER) && _MSC_VER <= 1200 + #if defined(_WIN64) + #define SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) + #else + #define SIZE_MAX 0xFFFFFFFF + #endif +#endif + +static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; // 66666972-912E-11CF-A5D6-28DB04C10000 +static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 65766177-ACF3-11D3-8CD1-00C04F8EDB8A +static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A +static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A +static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 74636166-ACF3-11D3-8CD1-00C04F8EDB8A +static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 61746164-ACF3-11D3-8CD1-00C04F8EDB8A + +static DRWAV_INLINE drwav_bool32 drwav__guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) +{ + const drwav_uint32* a32 = (const drwav_uint32*)a; + const drwav_uint32* b32 = (const drwav_uint32*)b; + + return + a32[0] == b32[0] && + a32[1] == b32[1] && + a32[2] == b32[2] && + a32[3] == b32[3]; +} + +static DRWAV_INLINE drwav_bool32 drwav__fourcc_equal(const unsigned char* a, const char* b) +{ + return + a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] && + a[3] == b[3]; +} + + + +static DRWAV_INLINE int drwav__is_little_endian() +{ + int n = 1; + return (*(char*)&n) == 1; +} + +static DRWAV_INLINE unsigned short drwav__bytes_to_u16(const unsigned char* data) +{ + if (drwav__is_little_endian()) { + return (data[0] << 0) | (data[1] << 8); + } else { + return (data[1] << 0) | (data[0] << 8); + } +} + +static DRWAV_INLINE short drwav__bytes_to_s16(const unsigned char* data) +{ + return (short)drwav__bytes_to_u16(data); +} + +static DRWAV_INLINE unsigned int drwav__bytes_to_u32(const unsigned char* data) +{ + if (drwav__is_little_endian()) { + return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + } else { + return (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24); + } +} + +static DRWAV_INLINE drwav_uint64 drwav__bytes_to_u64(const unsigned char* data) +{ + if (drwav__is_little_endian()) { + return + ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | + ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); + } else { + return + ((drwav_uint64)data[7] << 0) | ((drwav_uint64)data[6] << 8) | ((drwav_uint64)data[5] << 16) | ((drwav_uint64)data[4] << 24) | + ((drwav_uint64)data[3] << 32) | ((drwav_uint64)data[2] << 40) | ((drwav_uint64)data[1] << 48) | ((drwav_uint64)data[0] << 56); + } +} + +static DRWAV_INLINE void drwav__bytes_to_guid(const unsigned char* data, drwav_uint8* guid) +{ + for (int i = 0; i < 16; ++i) { + guid[i] = data[i]; + } +} + + +static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) +{ + return + formatTag == DR_WAVE_FORMAT_ADPCM || + formatTag == DR_WAVE_FORMAT_DVI_ADPCM; +} + + +typedef struct +{ + union + { + drwav_uint8 fourcc[4]; + drwav_uint8 guid[16]; + } id; + + // The size in bytes of the chunk. + drwav_uint64 sizeInBytes; + + // RIFF = 2 byte alignment. + // W64 = 8 byte alignment. + unsigned int paddingSize; + +} drwav__chunk_header; + +static drwav_bool32 drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav__chunk_header* pHeaderOut) +{ + if (container == drwav_container_riff) { + if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { + return DRWAV_FALSE; + } + + unsigned char sizeInBytes[4]; + if (onRead(pUserData, sizeInBytes, 4) != 4) { + return DRWAV_FALSE; + } + + pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes); + pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 2); + *pRunningBytesReadOut += 8; + } else { + if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { + return DRWAV_FALSE; + } + + unsigned char sizeInBytes[8]; + if (onRead(pUserData, sizeInBytes, 8) != 8) { + return DRWAV_FALSE; + } + + pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; // <-- Subtract 24 because w64 includes the size of the header. + pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 8); + pRunningBytesReadOut += 24; + } + + return DRWAV_TRUE; +} + +static drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +{ + drwav_uint64 bytesRemainingToSeek = offset; + while (bytesRemainingToSeek > 0) { + if (bytesRemainingToSeek > 0x7FFFFFFF) { + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + bytesRemainingToSeek -= 0x7FFFFFFF; + } else { + if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + bytesRemainingToSeek = 0; + } + } + + return DRWAV_TRUE; +} + + +static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut) +{ + drwav__chunk_header header; + if (!drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header)) { + return DRWAV_FALSE; + } + + + // Skip junk chunks. + if ((container == drwav_container_riff && drwav__fourcc_equal(header.id.fourcc, "JUNK")) || (container == drwav_container_w64 && drwav__guid_equal(header.id.guid, drwavGUID_W64_JUNK))) { + if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize; + + return drwav__read_fmt(onRead, onSeek, pUserData, container, pRunningBytesReadOut, fmtOut); + } + + + // Validation. + if (container == drwav_container_riff) { + if (!drwav__fourcc_equal(header.id.fourcc, "fmt ")) { + return DRWAV_FALSE; + } + } else { + if (!drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT)) { + return DRWAV_FALSE; + } + } + + + unsigned char fmt[16]; + if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += sizeof(fmt); + + fmtOut->formatTag = drwav__bytes_to_u16(fmt + 0); + fmtOut->channels = drwav__bytes_to_u16(fmt + 2); + fmtOut->sampleRate = drwav__bytes_to_u32(fmt + 4); + fmtOut->avgBytesPerSec = drwav__bytes_to_u32(fmt + 8); + fmtOut->blockAlign = drwav__bytes_to_u16(fmt + 12); + fmtOut->bitsPerSample = drwav__bytes_to_u16(fmt + 14); + + fmtOut->extendedSize = 0; + fmtOut->validBitsPerSample = 0; + fmtOut->channelMask = 0; + memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat)); + + if (header.sizeInBytes > 16) { + unsigned char fmt_cbSize[2]; + if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { + return DRWAV_FALSE; // Expecting more data. + } + *pRunningBytesReadOut += sizeof(fmt_cbSize); + + int bytesReadSoFar = 18; + + fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize); + if (fmtOut->extendedSize > 0) { + // Simple validation. + if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + if (fmtOut->extendedSize != 22) { + return DRWAV_FALSE; + } + } + + if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + unsigned char fmtext[22]; + if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) { + return DRWAV_FALSE; // Expecting more data. + } + + fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0); + fmtOut->channelMask = drwav__bytes_to_u32(fmtext + 2); + drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat); + } else { + if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + } + *pRunningBytesReadOut += fmtOut->extendedSize; + + bytesReadSoFar += fmtOut->extendedSize; + } + + // Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. + if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar); + } + + if (header.paddingSize > 0) { + if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + *pRunningBytesReadOut += header.paddingSize; + } + + return DRWAV_TRUE; +} + + +#ifndef DR_WAV_NO_STDIO +static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) +{ + return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); +} + +static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +drwav_bool32 drwav_init_file(drwav* pWav, const char* filename) +{ + FILE* pFile; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (fopen_s(&pFile, filename, "rb") != 0) { + return DRWAV_FALSE; + } +#else + pFile = fopen(filename, "rb"); + if (pFile == NULL) { + return DRWAV_FALSE; + } +#endif + + return drwav_init(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile); +} + +drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat) +{ + FILE* pFile; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (fopen_s(&pFile, filename, "wb") != 0) { + return DRWAV_FALSE; + } +#else + pFile = fopen(filename, "wb"); + if (pFile == NULL) { + return DRWAV_FALSE; + } +#endif + + return drwav_init_write(pWav, pFormat, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile); +} + +drwav* drwav_open_file(const char* filename) +{ + FILE* pFile; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (fopen_s(&pFile, filename, "rb") != 0) { + return NULL; + } +#else + pFile = fopen(filename, "rb"); + if (pFile == NULL) { + return NULL; + } +#endif + + drwav* pWav = drwav_open(drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile); + if (pWav == NULL) { + fclose(pFile); + return NULL; + } + + return pWav; +} + +drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat) +{ + FILE* pFile; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (fopen_s(&pFile, filename, "wb") != 0) { + return NULL; + } +#else + pFile = fopen(filename, "wb"); + if (pFile == NULL) { + return NULL; + } +#endif + + drwav* pWav = drwav_open_write(pFormat, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile); + if (pWav == NULL) { + fclose(pFile); + return NULL; + } + + return pWav; +} +#endif //DR_WAV_NO_STDIO + + +static size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + drwav__memory_stream* memory = (drwav__memory_stream*)pUserData; + drwav_assert(memory != NULL); + drwav_assert(memory->dataSize >= memory->currentReadPos); + + size_t bytesRemaining = memory->dataSize - memory->currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + DRWAV_COPY_MEMORY(pBufferOut, memory->data + memory->currentReadPos, bytesToRead); + memory->currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) +{ + drwav__memory_stream* memory = (drwav__memory_stream*)pUserData; + drwav_assert(memory != NULL); + + if (origin == drwav_seek_origin_current) { + if (offset > 0) { + if (memory->currentReadPos + offset > memory->dataSize) { + offset = (int)(memory->dataSize - memory->currentReadPos); // Trying to seek too far forward. + } + } else { + if (memory->currentReadPos < (size_t)-offset) { + offset = -(int)memory->currentReadPos; // Trying to seek too far backwards. + } + } + + // This will never underflow thanks to the clamps above. + memory->currentReadPos += offset; + } else { + if ((drwav_uint32)offset <= memory->dataSize) { + memory->currentReadPos = offset; + } else { + memory->currentReadPos = memory->dataSize; // Trying to seek too far forward. + } + } + + return DRWAV_TRUE; +} + +static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) +{ + drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData; + drwav_assert(memory != NULL); + drwav_assert(memory->dataCapacity >= memory->currentWritePos); + + size_t bytesRemaining = memory->dataCapacity - memory->currentWritePos; + if (bytesRemaining < bytesToWrite) { + // Need to reallocate. + size_t newDataCapacity = (memory->dataCapacity == 0) ? 256 : memory->dataCapacity * 2; + + // If doubling wasn't enough, just make it the minimum required size to write the data. + if ((newDataCapacity - memory->currentWritePos) < bytesToWrite) { + newDataCapacity = memory->currentWritePos + bytesToWrite; + } + + void* pNewData = DRWAV_REALLOC(*memory->ppData, newDataCapacity); + if (pNewData == NULL) { + return 0; + } + + *memory->ppData = pNewData; + memory->dataCapacity = newDataCapacity; + } + + drwav_uint8* pDataOut = (drwav_uint8*)(*memory->ppData); + DRWAV_COPY_MEMORY(pDataOut + memory->currentWritePos, pDataIn, bytesToWrite); + + memory->currentWritePos += bytesToWrite; + if (memory->dataSize < memory->currentWritePos) { + memory->dataSize = memory->currentWritePos; + } + + *memory->pDataSize = memory->dataSize; + + return bytesToWrite; +} + +static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) +{ + drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData; + drwav_assert(memory != NULL); + + if (origin == drwav_seek_origin_current) { + if (offset > 0) { + if (memory->currentWritePos + offset > memory->dataSize) { + offset = (int)(memory->dataSize - memory->currentWritePos); // Trying to seek too far forward. + } + } else { + if (memory->currentWritePos < (size_t)-offset) { + offset = -(int)memory->currentWritePos; // Trying to seek too far backwards. + } + } + + // This will never underflow thanks to the clamps above. + memory->currentWritePos += offset; + } else { + if ((drwav_uint32)offset <= memory->dataSize) { + memory->currentWritePos = offset; + } else { + memory->currentWritePos = memory->dataSize; // Trying to seek too far forward. + } + } + + return DRWAV_TRUE; +} + +drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize) +{ + if (data == NULL || dataSize == 0) { + return DRWAV_FALSE; + } + + drwav__memory_stream memoryStream; + drwav_zero_memory(&memoryStream, sizeof(memoryStream)); + memoryStream.data = (const unsigned char*)data; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + + if (!drwav_init(pWav, drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream)) { + return DRWAV_FALSE; + } + + pWav->memoryStream = memoryStream; + pWav->pUserData = &pWav->memoryStream; + return DRWAV_TRUE; +} + +drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat) +{ + if (ppData == NULL) { + return DRWAV_FALSE; + } + + *ppData = NULL; // Important because we're using realloc()! + *pDataSize = 0; + + drwav__memory_stream_write memoryStreamWrite; + drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite)); + memoryStreamWrite.ppData = ppData; + memoryStreamWrite.pDataSize = pDataSize; + memoryStreamWrite.dataSize = 0; + memoryStreamWrite.dataCapacity = 0; + memoryStreamWrite.currentWritePos = 0; + + if (!drwav_init_write(pWav, pFormat, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite)) { + return DRWAV_FALSE; + } + + pWav->memoryStreamWrite = memoryStreamWrite; + pWav->pUserData = &pWav->memoryStreamWrite; + return DRWAV_TRUE; +} + +drwav* drwav_open_memory(const void* data, size_t dataSize) +{ + if (data == NULL || dataSize == 0) { + return NULL; + } + + drwav__memory_stream memoryStream; + drwav_zero_memory(&memoryStream, sizeof(memoryStream)); + memoryStream.data = (const unsigned char*)data; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + + drwav* pWav = drwav_open(drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream); + if (pWav == NULL) { + return NULL; + } + + pWav->memoryStream = memoryStream; + pWav->pUserData = &pWav->memoryStream; + return pWav; +} + +drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat) +{ + if (ppData == NULL) { + return NULL; + } + + *ppData = NULL; // Important because we're using realloc()! + *pDataSize = 0; + + drwav__memory_stream_write memoryStreamWrite; + drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite)); + memoryStreamWrite.ppData = ppData; + memoryStreamWrite.pDataSize = pDataSize; + memoryStreamWrite.dataSize = 0; + memoryStreamWrite.dataCapacity = 0; + memoryStreamWrite.currentWritePos = 0; + + drwav* pWav = drwav_open_write(pFormat, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite); + if (pWav == NULL) { + return NULL; + } + + pWav->memoryStreamWrite = memoryStreamWrite; + pWav->pUserData = &pWav->memoryStreamWrite; + return pWav; +} + + +drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData) +{ + if (onRead == NULL || onSeek == NULL) { + return DRWAV_FALSE; + } + + drwav_zero_memory(pWav, sizeof(*pWav)); + + + // The first 4 bytes should be the RIFF identifier. + unsigned char riff[4]; + if (onRead(pUserData, riff, sizeof(riff)) != sizeof(riff)) { + return DRWAV_FALSE; // Failed to read data. + } + + // The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for + // w64 it will start with "riff". + if (drwav__fourcc_equal(riff, "RIFF")) { + pWav->container = drwav_container_riff; + } else if (drwav__fourcc_equal(riff, "riff")) { + pWav->container = drwav_container_w64; + + // Check the rest of the GUID for validity. + drwav_uint8 riff2[12]; + if (onRead(pUserData, riff2, sizeof(riff2)) != sizeof(riff2)) { + return DRWAV_FALSE; + } + + for (int i = 0; i < 12; ++i) { + if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { + return DRWAV_FALSE; + } + } + } else { + return DRWAV_FALSE; // Unknown or unsupported container. + } + + + if (pWav->container == drwav_container_riff) { + // RIFF/WAVE + unsigned char chunkSizeBytes[4]; + if (onRead(pUserData, chunkSizeBytes, sizeof(chunkSizeBytes)) != sizeof(chunkSizeBytes)) { + return DRWAV_FALSE; + } + + unsigned int chunkSize = drwav__bytes_to_u32(chunkSizeBytes); + if (chunkSize < 36) { + return DRWAV_FALSE; // Chunk size should always be at least 36 bytes. + } + + unsigned char wave[4]; + if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) { + return DRWAV_FALSE; + } + + if (!drwav__fourcc_equal(wave, "WAVE")) { + return DRWAV_FALSE; // Expecting "WAVE". + } + + pWav->dataChunkDataPos = 4 + sizeof(chunkSizeBytes) + sizeof(wave); + } else { + // W64 + unsigned char chunkSize[8]; + if (onRead(pUserData, chunkSize, sizeof(chunkSize)) != sizeof(chunkSize)) { + return DRWAV_FALSE; + } + + if (drwav__bytes_to_u64(chunkSize) < 80) { + return DRWAV_FALSE; + } + + drwav_uint8 wave[16]; + if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) { + return DRWAV_FALSE; + } + + if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) { + return DRWAV_FALSE; + } + + pWav->dataChunkDataPos = 16 + sizeof(chunkSize) + sizeof(wave); + } + + + // The next 24 bytes should be the "fmt " chunk. + drwav_fmt fmt; + if (!drwav__read_fmt(onRead, onSeek, pUserData, pWav->container, &pWav->dataChunkDataPos, &fmt)) { + return DRWAV_FALSE; // Failed to read the "fmt " chunk. + } + + + // Translate the internal format. + unsigned short translatedFormatTag = fmt.formatTag; + if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { + translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0); + } + + + drwav_uint64 sampleCountFromFactChunk = 0; + + // The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop. + drwav_uint64 dataSize; + for (;;) + { + drwav__chunk_header header; + if (!drwav__read_chunk_header(onRead, pUserData, pWav->container, &pWav->dataChunkDataPos, &header)) { + return DRWAV_FALSE; + } + + dataSize = header.sizeInBytes; + if (pWav->container == drwav_container_riff) { + if (drwav__fourcc_equal(header.id.fourcc, "data")) { + break; + } + } else { + if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) { + break; + } + } + + // Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats. + if (pWav->container == drwav_container_riff) { + if (drwav__fourcc_equal(header.id.fourcc, "fact")) { + drwav_uint32 sampleCount; + if (onRead(pUserData, &sampleCount, 4) != 4) { + return DRWAV_FALSE; + } + pWav->dataChunkDataPos += 4; + dataSize -= 4; + + // The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this + // for Microsoft ADPCM formats. + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + sampleCountFromFactChunk = sampleCount; + } else { + sampleCountFromFactChunk = 0; + } + } + } else { + if (drwav__guid_equal(header.id.guid, drwavGUID_W64_FACT)) { + if (onRead(pUserData, &sampleCountFromFactChunk, 8) != 8) { + return DRWAV_FALSE; + } + pWav->dataChunkDataPos += 4; + dataSize -= 8; + } + } + + // If we get here it means we didn't find the "data" chunk. Seek past it. + + // Make sure we seek past the padding. + dataSize += header.paddingSize; + drwav__seek_forward(onSeek, dataSize, pUserData); + pWav->dataChunkDataPos += dataSize; + } + + // At this point we should be sitting on the first byte of the raw audio data. + + pWav->onRead = onRead; + pWav->onSeek = onSeek; + pWav->pUserData = pUserData; + pWav->fmt = fmt; + pWav->sampleRate = fmt.sampleRate; + pWav->channels = fmt.channels; + pWav->bitsPerSample = fmt.bitsPerSample; + pWav->bytesPerSample = (unsigned int)(fmt.blockAlign / fmt.channels); + pWav->bytesRemaining = dataSize; + pWav->translatedFormatTag = translatedFormatTag; + pWav->dataChunkDataSize = dataSize; + + if (sampleCountFromFactChunk != 0) { + pWav->totalSampleCount = sampleCountFromFactChunk * fmt.channels; + } else { + pWav->totalSampleCount = dataSize / pWav->bytesPerSample; + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + drwav_uint64 blockCount = dataSize / fmt.blockAlign; + pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2; // x2 because two samples per byte. + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 blockCount = dataSize / fmt.blockAlign; + pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels); + } + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + pWav->bytesPerSample = 0; + } + +#ifdef DR_WAV_LIBSNDFILE_COMPAT + // I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website), + // it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count + // from the number of blocks, however this results in the inclusion of the extra silent samples at the end of the last block. The correct + // way to know the total sample count is to inspect the "fact" chunk which should always be present for compressed formats, and should + // always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my + // correctness tests against libsndfile and is disabled by default. + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + drwav_uint64 blockCount = dataSize / fmt.blockAlign; + pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2; // x2 because two samples per byte. + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 blockCount = dataSize / fmt.blockAlign; + pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels); + } +#endif + + return DRWAV_TRUE; +} + +drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData) +{ + if (onWrite == NULL || onSeek == NULL) { + return DRWAV_FALSE; + } + + // Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. + if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { + return DRWAV_FALSE; + } + if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { + return DRWAV_FALSE; + } + + + drwav_zero_memory(pWav, sizeof(*pWav)); + pWav->onWrite = onWrite; + pWav->onSeek = onSeek; + pWav->pUserData = pUserData; + pWav->fmt.formatTag = (drwav_uint16)pFormat->format; + pWav->fmt.channels = (drwav_uint16)pFormat->channels; + pWav->fmt.sampleRate = pFormat->sampleRate; + pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) >> 3); + pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) >> 3); + pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; + pWav->fmt.extendedSize = 0; + + size_t runningPos = 0; + + // "RIFF" chunk. + drwav_uint64 chunkSizeRIFF = 0; + if (pFormat->container == drwav_container_riff) { + runningPos += pWav->onWrite(pUserData, "RIFF", 4); + runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 4); + runningPos += pWav->onWrite(pUserData, "WAVE", 4); + } else { + runningPos += pWav->onWrite(pUserData, drwavGUID_W64_RIFF, 16); + runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 8); + runningPos += pWav->onWrite(pUserData, drwavGUID_W64_WAVE, 16); + } + + // "fmt " chunk. + drwav_uint64 chunkSizeFMT; + if (pFormat->container == drwav_container_riff) { + chunkSizeFMT = 16; + runningPos += pWav->onWrite(pUserData, "fmt ", 4); + runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 4); + } else { + chunkSizeFMT = 40; + runningPos += pWav->onWrite(pUserData, drwavGUID_W64_FMT, 16); + runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 8); + } + + runningPos += pWav->onWrite(pUserData, &pWav->fmt.formatTag, 2); + runningPos += pWav->onWrite(pUserData, &pWav->fmt.channels, 2); + runningPos += pWav->onWrite(pUserData, &pWav->fmt.sampleRate, 4); + runningPos += pWav->onWrite(pUserData, &pWav->fmt.avgBytesPerSec, 4); + runningPos += pWav->onWrite(pUserData, &pWav->fmt.blockAlign, 2); + runningPos += pWav->onWrite(pUserData, &pWav->fmt.bitsPerSample, 2); + + pWav->dataChunkDataPos = runningPos; + pWav->dataChunkDataSize = 0; + + // "data" chunk. + drwav_uint64 chunkSizeDATA = 0; + if (pFormat->container == drwav_container_riff) { + runningPos += pWav->onWrite(pUserData, "data", 4); + runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 4); + } else { + runningPos += pWav->onWrite(pUserData, drwavGUID_W64_DATA, 16); + runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 8); + } + + + // Simple validation. + if (pFormat->container == drwav_container_riff) { + if (runningPos != 20 + chunkSizeFMT + 8) { + return DRWAV_FALSE; + } + } else { + if (runningPos != 40 + chunkSizeFMT + 24) { + return DRWAV_FALSE; + } + } + + + + // Set some properties for the client's convenience. + pWav->container = pFormat->container; + pWav->channels = (drwav_uint16)pFormat->channels; + pWav->sampleRate = pFormat->sampleRate; + pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; + pWav->bytesPerSample = (drwav_uint16)(pFormat->bitsPerSample >> 3); + pWav->translatedFormatTag = (drwav_uint16)pFormat->format; + + return DRWAV_TRUE; +} + +void drwav_uninit(drwav* pWav) +{ + if (pWav == NULL) { + return; + } + + // If the drwav object was opened in write mode we'll need to finialize a few things: + // - Make sure the "data" chunk is aligned to 16-bits + // - Set the size of the "data" chunk. + if (pWav->onWrite != NULL) { + // Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. + drwav_uint32 paddingSize = 0; + if (pWav->container == drwav_container_riff) { + paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 2); + } else { + paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 8); + } + + if (paddingSize > 0) { + drwav_uint64 paddingData = 0; + pWav->onWrite(pWav->pUserData, &paddingData, paddingSize); + } + + + // Chunk sizes. + if (pWav->onSeek) { + if (pWav->container == drwav_container_riff) { + // The "RIFF" chunk size. + if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { + drwav_uint32 riffChunkSize = 36; + if (pWav->dataChunkDataSize <= (0xFFFFFFFF - 36)) { + riffChunkSize = 36 + (drwav_uint32)pWav->dataChunkDataSize; + } else { + riffChunkSize = 0xFFFFFFFF; + } + + pWav->onWrite(pWav->pUserData, &riffChunkSize, 4); + } + + // the "data" chunk size. + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) { + drwav_uint32 dataChunkSize = 0; + if (pWav->dataChunkDataSize <= 0xFFFFFFFF) { + dataChunkSize = (drwav_uint32)pWav->dataChunkDataSize; + } else { + dataChunkSize = 0xFFFFFFFF; + } + + pWav->onWrite(pWav->pUserData, &dataChunkSize, 4); + } + } else { + // The "RIFF" chunk size. + if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { + drwav_uint64 riffChunkSize = 80 + 24 + pWav->dataChunkDataSize; + pWav->onWrite(pWav->pUserData, &riffChunkSize, 8); + } + + // The "data" chunk size. + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) { + drwav_uint64 dataChunkSize = 24 + pWav->dataChunkDataSize; // +24 because W64 includes the size of the GUID and size fields. + pWav->onWrite(pWav->pUserData, &dataChunkSize, 8); + } + } + } + } + +#ifndef DR_WAV_NO_STDIO + // If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file() + // was used by looking at the onRead and onSeek callbacks. + if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { + fclose((FILE*)pWav->pUserData); + } +#endif +} + + +drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData) +{ + drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav)); + if (pWav == NULL) { + return NULL; + } + + if (!drwav_init(pWav, onRead, onSeek, pUserData)) { + DRWAV_FREE(pWav); + return NULL; + } + + return pWav; +} + +drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData) +{ + drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav)); + if (pWav == NULL) { + return NULL; + } + + if (!drwav_init_write(pWav, pFormat, onWrite, onSeek, pUserData)) { + DRWAV_FREE(pWav); + return NULL; + } + + return pWav; +} + +void drwav_close(drwav* pWav) +{ + drwav_uninit(pWav); + DRWAV_FREE(pWav); +} + + +size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) +{ + if (pWav == NULL || bytesToRead == 0 || pBufferOut == NULL) { + return 0; + } + + if (bytesToRead > pWav->bytesRemaining) { + bytesToRead = (size_t)pWav->bytesRemaining; + } + + size_t bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); + + pWav->bytesRemaining -= bytesRead; + return bytesRead; +} + +drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut) +{ + if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) { + return 0; + } + + // Cannot use this function for compressed formats. + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + return 0; + } + + // Don't try to read more samples than can potentially fit in the output buffer. + if (samplesToRead * pWav->bytesPerSample > SIZE_MAX) { + samplesToRead = SIZE_MAX / pWav->bytesPerSample; + } + + size_t bytesRead = drwav_read_raw(pWav, (size_t)(samplesToRead * pWav->bytesPerSample), pBufferOut); + return bytesRead / pWav->bytesPerSample; +} + +drwav_bool32 drwav_seek_to_first_sample(drwav* pWav) +{ + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { + return DRWAV_FALSE; + } + + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + pWav->compressed.iCurrentSample = 0; + } + + pWav->bytesRemaining = pWav->dataChunkDataSize; + return DRWAV_TRUE; +} + +drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample) +{ + // Seeking should be compatible with wave files > 2GB. + + if (pWav == NULL || pWav->onSeek == NULL) { + return DRWAV_FALSE; + } + + // If there are no samples, just return DRWAV_TRUE without doing anything. + if (pWav->totalSampleCount == 0) { + return DRWAV_TRUE; + } + + // Make sure the sample is clamped. + if (sample >= pWav->totalSampleCount) { + sample = pWav->totalSampleCount - 1; + } + + + // For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need + // to seek back to the start. + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + // TODO: This can be optimized. + if (sample > pWav->compressed.iCurrentSample) { + // Seeking forward - just move from the current position. + drwav_uint64 offset = sample - pWav->compressed.iCurrentSample; + + drwav_int16 devnull[2048]; + while (offset > 0) { + drwav_uint64 samplesToRead = sample; + if (samplesToRead > 2048) { + samplesToRead = 2048; + } + + drwav_uint64 samplesRead = drwav_read_s16(pWav, samplesToRead, devnull); + if (samplesRead != samplesToRead) { + return DRWAV_FALSE; + } + + offset -= samplesRead; + } + } else { + // Seeking backwards. Just use the fallback. + goto fallback; + } + } else { + drwav_uint64 totalSizeInBytes = pWav->totalSampleCount * pWav->bytesPerSample; + drwav_assert(totalSizeInBytes >= pWav->bytesRemaining); + + drwav_uint64 currentBytePos = totalSizeInBytes - pWav->bytesRemaining; + drwav_uint64 targetBytePos = sample * pWav->bytesPerSample; + + drwav_uint64 offset; + if (currentBytePos < targetBytePos) { + // Offset forwards. + offset = (targetBytePos - currentBytePos); + } else { + // Offset backwards. + if (!drwav_seek_to_first_sample(pWav)) { + return DRWAV_FALSE; + } + offset = targetBytePos; + } + + while (offset > 0) { + int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); + if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + + pWav->bytesRemaining -= offset32; + offset -= offset32; + } + } + + return DRWAV_TRUE; + +fallback: + // This is a generic seek implementation that just continuously reads samples into a temporary buffer. This should work for all supported + // formats, but it is not efficient. This should be used as a fall back. + if (!drwav_seek_to_first_sample(pWav)) { + return DRWAV_FALSE; + } + + drwav_int16 devnull[2048]; + while (sample > 0) { + drwav_uint64 samplesToRead = sample; + if (samplesToRead > 2048) { + samplesToRead = 2048; + } + + drwav_uint64 samplesRead = drwav_read_s16(pWav, samplesToRead, devnull); + if (samplesRead != samplesToRead) { + return DRWAV_FALSE; + } + + sample -= samplesRead; + } + + return DRWAV_TRUE; +} + + +size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) +{ + if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { + return 0; + } + + size_t bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); + pWav->dataChunkDataSize += bytesWritten; + + return bytesWritten; +} + +drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData) +{ + if (pWav == NULL || samplesToWrite == 0 || pData == NULL) { + return 0; + } + + drwav_uint64 bytesToWrite = ((samplesToWrite * pWav->bitsPerSample) / 8); + if (bytesToWrite > SIZE_MAX) { + return 0; + } + + size_t bytesWritten = drwav_write_raw(pWav, (size_t)bytesToWrite, pData); + return ((drwav_uint64)bytesWritten * 8) / pWav->bitsPerSample; +} + + +#ifndef DR_WAV_NO_CONVERSION_API +static unsigned short g_drwavAlawTable[256] = { + 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, + 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, + 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, + 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, + 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, + 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, + 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, + 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, + 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, + 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, + 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, + 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, + 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, + 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, + 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, + 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 +}; + +static unsigned short g_drwavMulawTable[256] = { + 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, + 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, + 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, + 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, + 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, + 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, + 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, + 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, + 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, + 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, + 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, + 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, + 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, + 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, + 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 +}; + +static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) +{ + return (short)g_drwavAlawTable[sampleIn]; +} + +static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) +{ + return (short)g_drwavMulawTable[sampleIn]; +} + + + +static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) +{ + // Special case for 8-bit sample data because it's treated as unsigned. + if (bytesPerSample == 1) { + drwav_u8_to_s16(pOut, pIn, totalSampleCount); + return; + } + + + // Slightly more optimal implementation for common formats. + if (bytesPerSample == 2) { + for (unsigned int i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((drwav_int16*)pIn)[i]; + } + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_s16(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); + return; + } + + + // Generic, slow converter. + for (unsigned int i = 0; i < totalSampleCount; ++i) { + unsigned short sample = 0; + unsigned short shift = (8 - bytesPerSample) * 8; + for (unsigned short j = 0; j < bytesPerSample && j < 2; ++j) { + sample |= (unsigned short)(pIn[j]) << shift; + shift += 8; + } + + pIn += bytesPerSample; + *pOut++ = sample; + } +} + +static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) +{ + if (bytesPerSample == 4) { + drwav_f32_to_s16(pOut, (float*)pIn, totalSampleCount); + return; + } else { + drwav_f64_to_s16(pOut, (double*)pIn, totalSampleCount); + return; + } +} + +drwav_uint64 drwav_read_s16__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + // Fast path. + if (pWav->bytesPerSample == 2) { + return drwav_read(pWav, samplesToRead, pBufferOut); + } + + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + drwav_assert(pWav != NULL); + drwav_assert(samplesToRead > 0); + drwav_assert(pBufferOut != NULL); + + // TODO: Lots of room for optimization here. + + drwav_uint64 totalSamplesRead = 0; + + while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) { + // If there are no cached samples we need to load a new block. + if (pWav->msadpcm.cachedSampleCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + // Mono. + drwav_uint8 header[7]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalSamplesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 1); + pWav->msadpcm.prevSamples[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 3); + pWav->msadpcm.prevSamples[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 5); + pWav->msadpcm.cachedSamples[2] = pWav->msadpcm.prevSamples[0][0]; + pWav->msadpcm.cachedSamples[3] = pWav->msadpcm.prevSamples[0][1]; + pWav->msadpcm.cachedSampleCount = 2; + } else { + // Stereo. + drwav_uint8 header[14]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalSamplesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.predictor[1] = header[1]; + pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 2); + pWav->msadpcm.delta[1] = drwav__bytes_to_s16(header + 4); + pWav->msadpcm.prevSamples[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 6); + pWav->msadpcm.prevSamples[1][1] = (drwav_int32)drwav__bytes_to_s16(header + 8); + pWav->msadpcm.prevSamples[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 10); + pWav->msadpcm.prevSamples[1][0] = (drwav_int32)drwav__bytes_to_s16(header + 12); + + pWav->msadpcm.cachedSamples[0] = pWav->msadpcm.prevSamples[0][0]; + pWav->msadpcm.cachedSamples[1] = pWav->msadpcm.prevSamples[1][0]; + pWav->msadpcm.cachedSamples[2] = pWav->msadpcm.prevSamples[0][1]; + pWav->msadpcm.cachedSamples[3] = pWav->msadpcm.prevSamples[1][1]; + pWav->msadpcm.cachedSampleCount = 4; + } + } + + // Output anything that's cached. + while (samplesToRead > 0 && pWav->msadpcm.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) { + pBufferOut[0] = (drwav_int16)pWav->msadpcm.cachedSamples[drwav_countof(pWav->msadpcm.cachedSamples) - pWav->msadpcm.cachedSampleCount]; + pWav->msadpcm.cachedSampleCount -= 1; + + pBufferOut += 1; + samplesToRead -= 1; + totalSamplesRead += 1; + pWav->compressed.iCurrentSample += 1; + } + + if (samplesToRead == 0) { + return totalSamplesRead; + } + + + // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next + // loop iteration which will trigger the loading of a new block. + if (pWav->msadpcm.cachedSampleCount == 0) { + if (pWav->msadpcm.bytesRemainingInBlock == 0) { + continue; + } else { + drwav_uint8 nibbles; + if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { + return totalSamplesRead; + } + pWav->msadpcm.bytesRemainingInBlock -= 1; + + // TODO: Optimize away these if statements. + drwav_int32 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } + drwav_int32 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } + + static drwav_int32 adaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + + if (pWav->channels == 1) { + // Mono. + drwav_int32 newSample0; + newSample0 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = drwav_clamp(newSample0, -32768, 32767); + + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + + pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1]; + pWav->msadpcm.prevSamples[0][1] = newSample0; + + + drwav_int32 newSample1; + newSample1 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[0]; + newSample1 = drwav_clamp(newSample1, -32768, 32767); + + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + + pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1]; + pWav->msadpcm.prevSamples[0][1] = newSample1; + + + pWav->msadpcm.cachedSamples[2] = newSample0; + pWav->msadpcm.cachedSamples[3] = newSample1; + pWav->msadpcm.cachedSampleCount = 2; + } else { + // Stereo. + + // Left. + drwav_int32 newSample0; + newSample0 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = drwav_clamp(newSample0, -32768, 32767); + + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + + pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1]; + pWav->msadpcm.prevSamples[0][1] = newSample0; + + + // Right. + drwav_int32 newSample1; + newSample1 = ((pWav->msadpcm.prevSamples[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevSamples[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[1]; + newSample1 = drwav_clamp(newSample1, -32768, 32767); + + pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; + if (pWav->msadpcm.delta[1] < 16) { + pWav->msadpcm.delta[1] = 16; + } + + pWav->msadpcm.prevSamples[1][0] = pWav->msadpcm.prevSamples[1][1]; + pWav->msadpcm.prevSamples[1][1] = newSample1; + + pWav->msadpcm.cachedSamples[2] = newSample0; + pWav->msadpcm.cachedSamples[3] = newSample1; + pWav->msadpcm.cachedSampleCount = 2; + } + } + } + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + drwav_assert(pWav != NULL); + drwav_assert(samplesToRead > 0); + drwav_assert(pBufferOut != NULL); + + // TODO: Lots of room for optimization here. + + drwav_uint64 totalSamplesRead = 0; + + while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) { + // If there are no cached samples we need to load a new block. + if (pWav->ima.cachedSampleCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + // Mono. + drwav_uint8 header[4]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalSamplesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = header[2]; + pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 1] = pWav->ima.predictor[0]; + pWav->ima.cachedSampleCount = 1; + } else { + // Stereo. + drwav_uint8 header[8]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalSamplesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + + pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = header[2]; + pWav->ima.predictor[1] = drwav__bytes_to_s16(header + 4); + pWav->ima.stepIndex[1] = header[6]; + + pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 2] = pWav->ima.predictor[0]; + pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 1] = pWav->ima.predictor[1]; + pWav->ima.cachedSampleCount = 2; + } + } + + // Output anything that's cached. + while (samplesToRead > 0 && pWav->ima.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) { + pBufferOut[0] = (drwav_int16)pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount]; + pWav->ima.cachedSampleCount -= 1; + + pBufferOut += 1; + samplesToRead -= 1; + totalSamplesRead += 1; + pWav->compressed.iCurrentSample += 1; + } + + if (samplesToRead == 0) { + return totalSamplesRead; + } + + // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next + // loop iteration which will trigger the loading of a new block. + if (pWav->ima.cachedSampleCount == 0) { + if (pWav->ima.bytesRemainingInBlock == 0) { + continue; + } else { + static drwav_int32 indexTable[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + + static drwav_int32 stepTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + + // From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the + // left channel, 4 bytes for the right channel. + pWav->ima.cachedSampleCount = 8 * pWav->channels; + for (drwav_uint32 iChannel = 0; iChannel < pWav->channels; ++iChannel) { + drwav_uint8 nibbles[4]; + if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { + return totalSamplesRead; + } + pWav->ima.bytesRemainingInBlock -= 4; + + for (drwav_uint32 iByte = 0; iByte < 4; ++iByte) { + drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); + drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); + + drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; + drwav_int32 predictor = pWav->ima.predictor[iChannel]; + + drwav_int32 diff = step >> 3; + if (nibble0 & 1) diff += step >> 2; + if (nibble0 & 2) diff += step >> 1; + if (nibble0 & 4) diff += step; + if (nibble0 & 8) diff = -diff; + + predictor = drwav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedSamples[(drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount) + (iByte*2+0)*pWav->channels + iChannel] = predictor; + + + step = stepTable[pWav->ima.stepIndex[iChannel]]; + predictor = pWav->ima.predictor[iChannel]; + + diff = step >> 3; + if (nibble1 & 1) diff += step >> 2; + if (nibble1 & 2) diff += step >> 1; + if (nibble1 & 4) diff += step; + if (nibble1 & 8) diff = -diff; + + predictor = drwav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); + pWav->ima.cachedSamples[(drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount) + (iByte*2+1)*pWav->channels + iChannel] = predictor; + } + } + } + } + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s16__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s16__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s16__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) { + return 0; + } + + // Don't try to read more samples than can potentially fit in the output buffer. + if (samplesToRead * sizeof(drwav_int16) > SIZE_MAX) { + samplesToRead = SIZE_MAX / sizeof(drwav_int16); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_s16__pcm(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_s16__msadpcm(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_s16__ieee(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_s16__alaw(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_s16__mulaw(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_s16__ima(pWav, samplesToRead, pBufferOut); + } + + return 0; +} + +void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + int r; + for (size_t i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x - 128; + r = r << 8; + pOut[i] = (short)r; + } +} + +void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + int r; + for (size_t i = 0; i < sampleCount; ++i) { + int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8; + r = x >> 8; + pOut[i] = (short)r; + } +} + +void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount) +{ + int r; + for (size_t i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x >> 16; + pOut[i] = (short)r; + } +} + +void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount) +{ + int r; + for (size_t i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + float c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5f); + r = r - 32768; + pOut[i] = (short)r; + } +} + +void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount) +{ + int r; + for (size_t i = 0; i < sampleCount; ++i) { + double x = pIn[i]; + double c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5); + r = r - 32768; + pOut[i] = (short)r; + } +} + +void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + for (size_t i = 0; i < sampleCount; ++i) { + pOut[i] = drwav__alaw_to_s16(pIn[i]); + } +} + +void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + for (size_t i = 0; i < sampleCount; ++i) { + pOut[i] = drwav__mulaw_to_s16(pIn[i]); + } +} + + + +static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample) +{ + // Special case for 8-bit sample data because it's treated as unsigned. + if (bytesPerSample == 1) { + drwav_u8_to_f32(pOut, pIn, sampleCount); + return; + } + + // Slightly more optimal implementation for common formats. + if (bytesPerSample == 2) { + drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount); + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_f32(pOut, pIn, sampleCount); + return; + } + if (bytesPerSample == 4) { + drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount); + return; + } + + // Generic, slow converter. + for (unsigned int i = 0; i < sampleCount; ++i) { + unsigned int sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + for (unsigned short j = 0; j < bytesPerSample && j < 4; ++j) { + sample |= (unsigned int)(pIn[j]) << shift; + shift += 8; + } + + pIn += bytesPerSample; + *pOut++ = (float)((int)sample / 2147483648.0); + } +} + +static void drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample) +{ + if (bytesPerSample == 4) { + for (unsigned int i = 0; i < sampleCount; ++i) { + *pOut++ = ((float*)pIn)[i]; + } + return; + } else { + drwav_f64_to_f32(pOut, (double*)pIn, sampleCount); + return; + } +} + + +drwav_uint64 drwav_read_f32__pcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) +{ + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); + pBufferOut += samplesRead; + + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_f32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) +{ + // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't + // want to duplicate that code. + drwav_uint64 totalSamplesRead = 0; + drwav_int16 samples16[2048]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16); + if (samplesRead == 0) { + break; + } + + drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048. + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_f32__ima(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) +{ + // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't + // want to duplicate that code. + drwav_uint64 totalSamplesRead = 0; + drwav_int16 samples16[2048]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16); + if (samplesRead == 0) { + break; + } + + drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048. + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_f32__ieee(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) +{ + // Fast path. + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bytesPerSample == 4) { + return drwav_read(pWav, samplesToRead, pBufferOut); + } + + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_f32__alaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) +{ + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_f32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) +{ + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) +{ + if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) { + return 0; + } + + // Don't try to read more samples than can potentially fit in the output buffer. + if (samplesToRead * sizeof(float) > SIZE_MAX) { + samplesToRead = SIZE_MAX / sizeof(float); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_f32__pcm(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_f32__msadpcm(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_f32__ieee(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_f32__alaw(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_f32__mulaw(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_f32__ima(pWav, samplesToRead, pBufferOut); + } + + return 0; +} + +void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + +#ifdef DR_WAV_LIBSNDFILE_COMPAT + // It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears + // libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note + // the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated + // correctness testing. This is disabled by default. + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = (pIn[i] / 256.0f) * 2 - 1; + } +#else + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = (pIn[i] / 255.0f) * 2 - 1; + } +#endif +} + +void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] / 32768.0f; + } +} + +void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + unsigned int s0 = pIn[i*3 + 0]; + unsigned int s1 = pIn[i*3 + 1]; + unsigned int s2 = pIn[i*3 + 2]; + + int sample32 = (int)((s0 << 8) | (s1 << 16) | (s2 << 24)); + *pOut++ = (float)(sample32 / 2147483648.0); + } +} + +void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = (float)(pIn[i] / 2147483648.0); + } +} + +void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = (float)pIn[i]; + } +} + +void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f; + } +} + +void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f; + } +} + + + +static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) +{ + // Special case for 8-bit sample data because it's treated as unsigned. + if (bytesPerSample == 1) { + drwav_u8_to_s32(pOut, pIn, totalSampleCount); + return; + } + + // Slightly more optimal implementation for common formats. + if (bytesPerSample == 2) { + drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount); + return; + } + if (bytesPerSample == 3) { + drwav_s24_to_s32(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + for (unsigned int i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((drwav_int32*)pIn)[i]; + } + return; + } + + // Generic, slow converter. + for (unsigned int i = 0; i < totalSampleCount; ++i) { + unsigned int sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + for (unsigned short j = 0; j < bytesPerSample && j < 4; ++j) { + sample |= (unsigned int)(pIn[j]) << shift; + shift += 8; + } + + pIn += bytesPerSample; + *pOut++ = sample; + } +} + +static void drwav__ieee_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) +{ + if (bytesPerSample == 4) { + drwav_f32_to_s32(pOut, (float*)pIn, totalSampleCount); + return; + } else { + drwav_f64_to_s32(pOut, (double*)pIn, totalSampleCount); + return; + } +} + + +drwav_uint64 drwav_read_s32__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) +{ + // Fast path. + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bytesPerSample == 4) { + return drwav_read(pWav, samplesToRead, pBufferOut); + } + + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) +{ + // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't + // want to duplicate that code. + drwav_uint64 totalSamplesRead = 0; + drwav_int16 samples16[2048]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16); + if (samplesRead == 0) { + break; + } + + drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048. + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s32__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) +{ + // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't + // want to duplicate that code. + drwav_uint64 totalSamplesRead = 0; + drwav_int16 samples16[2048]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16); + if (samplesRead == 0) { + break; + } + + drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048. + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s32__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s32__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) +{ + drwav_uint64 totalSamplesRead = 0; + unsigned char sampleData[4096]; + while (samplesToRead > 0) { + drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) +{ + if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) { + return 0; + } + + // Don't try to read more samples than can potentially fit in the output buffer. + if (samplesToRead * sizeof(drwav_int32) > SIZE_MAX) { + samplesToRead = SIZE_MAX / sizeof(drwav_int32); + } + + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { + return drwav_read_s32__pcm(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + return drwav_read_s32__msadpcm(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { + return drwav_read_s32__ieee(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { + return drwav_read_s32__alaw(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + return drwav_read_s32__mulaw(pWav, samplesToRead, pBufferOut); + } + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + return drwav_read_s32__ima(pWav, samplesToRead, pBufferOut); + } + + return 0; +} + +void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = ((int)pIn[i] - 128) << 24; + } +} + +void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] << 16; + } +} + +void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + unsigned int s0 = pIn[i*3 + 0]; + unsigned int s1 = pIn[i*3 + 1]; + unsigned int s2 = pIn[i*3 + 2]; + + drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); + *pOut++ = sample32; + } +} + +void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + } +} + +void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + } +} + +void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i = 0; i < sampleCount; ++i) { + *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16; + } +} + +void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +{ + if (pOut == NULL || pIn == NULL) { + return; + } + + for (size_t i= 0; i < sampleCount; ++i) { + *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16; + } +} + + + +drwav_int16* drwav__read_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + drwav_assert(pWav != NULL); + + drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int16); + if (sampleDataSize > SIZE_MAX) { + drwav_uninit(pWav); + return NULL; // File's too big. + } + + drwav_int16* pSampleData = (drwav_int16*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above. + if (pSampleData == NULL) { + drwav_uninit(pWav); + return NULL; // Failed to allocate memory. + } + + drwav_uint64 samplesRead = drwav_read_s16(pWav, (size_t)pWav->totalSampleCount, pSampleData); + if (samplesRead != pWav->totalSampleCount) { + DRWAV_FREE(pSampleData); + drwav_uninit(pWav); + return NULL; // There was an error reading the samples. + } + + drwav_uninit(pWav); + + if (sampleRate) *sampleRate = pWav->sampleRate; + if (channels) *channels = pWav->channels; + if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount; + return pSampleData; +} + +float* drwav__read_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + drwav_assert(pWav != NULL); + + drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(float); + if (sampleDataSize > SIZE_MAX) { + drwav_uninit(pWav); + return NULL; // File's too big. + } + + float* pSampleData = (float*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above. + if (pSampleData == NULL) { + drwav_uninit(pWav); + return NULL; // Failed to allocate memory. + } + + drwav_uint64 samplesRead = drwav_read_f32(pWav, (size_t)pWav->totalSampleCount, pSampleData); + if (samplesRead != pWav->totalSampleCount) { + DRWAV_FREE(pSampleData); + drwav_uninit(pWav); + return NULL; // There was an error reading the samples. + } + + drwav_uninit(pWav); + + if (sampleRate) *sampleRate = pWav->sampleRate; + if (channels) *channels = pWav->channels; + if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount; + return pSampleData; +} + +drwav_int32* drwav__read_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + drwav_assert(pWav != NULL); + + drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int32); + if (sampleDataSize > SIZE_MAX) { + drwav_uninit(pWav); + return NULL; // File's too big. + } + + drwav_int32* pSampleData = (drwav_int32*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above. + if (pSampleData == NULL) { + drwav_uninit(pWav); + return NULL; // Failed to allocate memory. + } + + drwav_uint64 samplesRead = drwav_read_s32(pWav, (size_t)pWav->totalSampleCount, pSampleData); + if (samplesRead != pWav->totalSampleCount) { + DRWAV_FREE(pSampleData); + drwav_uninit(pWav); + return NULL; // There was an error reading the samples. + } + + drwav_uninit(pWav); + + if (sampleRate) *sampleRate = pWav->sampleRate; + if (channels) *channels = pWav->channels; + if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount; + return pSampleData; +} + + +drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drwav wav; + if (!drwav_init(&wav, onRead, onSeek, pUserData)) { + return NULL; + } + + return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount); +} + +float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drwav wav; + if (!drwav_init(&wav, onRead, onSeek, pUserData)) { + return NULL; + } + + return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount); +} + +drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drwav wav; + if (!drwav_init(&wav, onRead, onSeek, pUserData)) { + return NULL; + } + + return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount); +} + +#ifndef DR_WAV_NO_STDIO +drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drwav wav; + if (!drwav_init_file(&wav, filename)) { + return NULL; + } + + return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount); +} + +float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drwav wav; + if (!drwav_init_file(&wav, filename)) { + return NULL; + } + + return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount); +} + +drwav_int32* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drwav wav; + if (!drwav_init_file(&wav, filename)) { + return NULL; + } + + return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount); +} +#endif + +drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drwav wav; + if (!drwav_init_memory(&wav, data, dataSize)) { + return NULL; + } + + return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount); +} + +float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drwav wav; + if (!drwav_init_memory(&wav, data, dataSize)) { + return NULL; + } + + return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount); +} + +drwav_int32* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drwav wav; + if (!drwav_init_memory(&wav, data, dataSize)) { + return NULL; + } + + return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount); +} +#endif //DR_WAV_NO_CONVERSION_API + + +void drwav_free(void* pDataReturnedByOpenAndRead) +{ + DRWAV_FREE(pDataReturnedByOpenAndRead); +} + +#endif //DR_WAV_IMPLEMENTATION + + +// REVISION HISTORY +// +// v0.7a - 2017-11-17 +// - Fix some GCC warnings. +// +// v0.7 - 2017-11-04 +// - Add writing APIs. +// +// v0.6 - 2017-08-16 +// - API CHANGE: Rename dr_* types to drwav_*. +// - Add support for custom implementations of malloc(), realloc(), etc. +// - Add support for Microsoft ADPCM. +// - Add support for IMA ADPCM (DVI, format code 0x11). +// - Optimizations to drwav_read_s16(). +// - Bug fixes. +// +// v0.5g - 2017-07-16 +// - Change underlying type for booleans to unsigned. +// +// v0.5f - 2017-04-04 +// - Fix a minor bug with drwav_open_and_read_s16() and family. +// +// v0.5e - 2016-12-29 +// - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this. +// - Minor fixes to documentation. +// +// v0.5d - 2016-12-28 +// - Use drwav_int*/drwav_uint* sized types to improve compiler support. +// +// v0.5c - 2016-11-11 +// - Properly handle JUNK chunks that come before the FMT chunk. +// +// v0.5b - 2016-10-23 +// - A minor change to drwav_bool8 and drwav_bool32 types. +// +// v0.5a - 2016-10-11 +// - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering. +// - Improve A-law and mu-law efficiency. +// +// v0.5 - 2016-09-29 +// - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to +// keep it consistent with dr_audio and drwav_flac. +// +// v0.4b - 2016-09-18 +// - Fixed a typo in documentation. +// +// v0.4a - 2016-09-18 +// - Fixed a typo. +// - Change date format to ISO 8601 (YYYY-MM-DD) +// +// v0.4 - 2016-07-13 +// - API CHANGE. Make onSeek consistent with drwav_flac. +// - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with drwav_flac. +// - Added support for Sony Wave64. +// +// v0.3a - 2016-05-28 +// - API CHANGE. Return drwav_bool32 instead of int in onSeek callback. +// - Fixed a memory leak. +// +// v0.3 - 2016-05-22 +// - Lots of API changes for consistency. +// +// v0.2a - 2016-05-16 +// - Fixed Linux/GCC build. +// +// v0.2 - 2016-05-11 +// - Added support for reading data as signed 32-bit PCM for consistency with drwav_flac. +// +// v0.1a - 2016-05-07 +// - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize. +// +// v0.1 - 2016-05-04 +// - Initial versioned release. + + +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ diff --git a/raylib/external/mini_al.h b/raylib/external/mini_al.h new file mode 100644 index 0000000..7d83b54 --- /dev/null +++ b/raylib/external/mini_al.h @@ -0,0 +1,11601 @@ +// Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. +// mini_al - v0.x - 2017-xx-xx +// +// David Reid - davidreidsoftware@gmail.com + +// ABOUT +// ===== +// mini_al is a small library for making it easy to connect to a playback or capture device and send +// or receive data from that device. +// +// mini_al uses an asynchronous API. Every device is created with it's own thread, with audio data +// being delivered to or from the device via a callback. Synchronous APIs are not supported in the +// interest of keeping the library as simple and light-weight as possible. +// +// Supported Backends: +// - WASAPI +// - DirectSound +// - WinMM +// - ALSA +// - OSS +// - OpenSL|ES / Android +// - OpenAL +// - SDL +// - Null (Silence) +// - ... and more in the future. +// - Core Audio (OSX, iOS) +// +// Supported Formats: +// - Unsigned 8-bit PCM +// - Signed 16-bit PCM +// - Signed 24-bit PCM (tightly packed) +// - Signed 32-bit PCM +// - IEEE 32-bit floating point PCM +// +// +// USAGE +// ===== +// mini_al is a single-file library. To use it, do something like the following in one .c file. +// #define MAL_IMPLEMENTATION +// #include "mini_al.h" +// +// You can then #include this file in other parts of the program as you would with any other header file. +// +// The implementation of this library will try #include-ing necessary headers for each backend. If you do not have +// the development packages for any particular backend you can disable it by #define-ing the appropriate MAL_NO_* +// option before the implementation. +// +// +// Building (Windows) +// ------------------ +// The Windows build should compile clean on all modern versions of MSVC without the need to configure any include +// paths nor link to any libraries. The same applies to MinGW/GCC and Clang. +// +// Building (Linux) +// ---------------- +// The Linux build uses ALSA for it's backend so you will need to install the relevant ALSA development packages +// for your preferred distro. It also uses pthreads. Dependencies are dynamically linked at runtime so you do not +// need to link to -lasound nor -lpthread. You will need to link to -ldl. +// +// Building (BSD) +// -------------- +// The BSD build uses OSS and should Just Work without any linking nor include path configuration. +// +// Building (Emscripten) +// --------------------- +// The Emscripten build currently uses SDL 1.2 for it's backend which means specifying "-s USE_SDL=2" is unecessary +// as of this version. However, if in the future there is legitimate benefit or enough demand for SDL 2 to be used +// instead, you will need to specify this when compiling. +// +// +// Playback Example +// ---------------- +// mal_uint32 on_send_samples(mal_device* pDevice, mal_uint32 frameCount, void* pSamples) +// { +// // This callback is set at initialization time and will be called when a playback device needs more +// // data. You need to write as many frames as you can to pSamples (but no more than frameCount) and +// // then return the number of frames you wrote. +// // +// // The user data (pDevice->pUserData) is set by mal_device_init(). +// return (mal_uint32)drwav_read_f32((drwav*)pDevice->pUserData, frameCount * pDevice->channels, (float*)pSamples) / pDevice->channels; +// } +// +// ... +// +// mal_context context; +// if (mal_context_init(NULL, 0, NULL, &context) != MAL_SUCCESS) { +// printf("Failed to initialize context."); +// return -3; +// } +// +// mal_device_config config = mal_device_config_init_playback(mal_format_s16, wav.channels, wav.sampleRate, on_send_frames_to_device); +// +// mal_device device; +// mal_result result = mal_device_init(&context, mal_device_type_playback, NULL, &config, pMyData, &device); +// if (result != MAL_SUCCESS) { +// return -1; +// } +// +// mal_device_start(&device); // The device is sleeping by default so you'll need to start it manually. +// +// ... +// +// mal_device_uninit(&device); // This will stop the device so no need to do that manually. +// +// +// +// NOTES +// ===== +// - This library uses an asynchronous API for delivering and requesting audio data. Each device will have +// it's own worker thread which is managed by the library. +// - If mal_device_init() is called with a device that's not aligned to the platform's natural alignment +// boundary (4 bytes on 32-bit, 8 bytes on 64-bit), it will _not_ be thread-safe. The reason for this +// is that it depends on members of mal_device being correctly aligned for atomic assignments. +// - Sample data is always little-endian and interleaved. For example, mal_format_s16 means signed 16-bit +// integer samples, interleaved. Let me know if you need non-interleaved and I'll look into it. +// +// +// +// BACKEND NUANCES +// =============== +// - The absolute best latency I am able to get on DirectSound is about 10 milliseconds. This seems very +// consistent so I'm suspecting there's some kind of hard coded limit there or something. +// - DirectSound currently supports a maximum of 4 periods. +// - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: +// +// - UWP is only supported when compiling as C++. +// - UWP only supports default playback and capture devices. +// - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): +// +// ... +// +// +// +// +// +// +// OPTIONS +// ======= +// #define these options before including this file. +// +// #define MAL_NO_WASAPI +// Disables the WASAPI backend. +// +// #define MAL_NO_DSOUND +// Disables the DirectSound backend. +// +// #define MAL_NO_WINMM +// Disables the WinMM backend. +// +// #define MAL_NO_ALSA +// Disables the ALSA backend. +// +// #define MAL_NO_OSS +// Disables the OSS backend. +// +// #define MAL_NO_OPENSL +// Disables the OpenSL|ES backend. +// +// #define MAL_NO_OPENAL +// Disables the OpenAL backend. +// +// #define MAL_NO_SDL +// Disables the SDL backend. +// +// #define MAL_NO_NULL +// Disables the null backend. +// +// #define MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS +// When a buffer size of 0 is specified when a device is initialized, it will default to a size with +// this number of milliseconds worth of data. Note that some backends may adjust this setting if that +// particular backend has unusual latency characteristics. +// +// #define MAL_DEFAULT_PERIODS +// When a period count of 0 is specified when a device is initialized, it will default to this. + +#ifndef mini_al_h +#define mini_al_h + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4201) // nonstandard extension used: nameless struct/union +#endif + +// Platform/backend detection. +#ifdef _WIN32 + #define MAL_WIN32 + #if (!defined(WINAPI_FAMILY) || WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) + #define MAL_WIN32_DESKTOP + #endif +#else + #define MAL_POSIX + #include // Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. + + #define MAL_UNIX + #ifdef __linux__ + #define MAL_LINUX + #endif + #ifdef __APPLE__ + #define MAL_APPLE + #endif + #ifdef __ANDROID__ + #define MAL_ANDROID + #endif + #ifdef __EMSCRIPTEN__ + #define MAL_EMSCRIPTEN + #endif +#endif + +// Some backends are only supported on certain platforms. +#if defined(MAL_WIN32) + #define MAL_SUPPORT_WASAPI + #if defined(MAL_WIN32_DESKTOP) // DirectSound and WinMM backends are only supported on desktop's. + #define MAL_SUPPORT_DSOUND + #define MAL_SUPPORT_WINMM + #endif + + // Don't support WASAPI on older versions of MSVC for now. + #if defined(_MSC_VER) + #if _MSC_VER < 1600 + #if !defined(__audioclient_h__) + #undef MAL_SUPPORT_WASAPI + #endif + #endif + #endif +#endif +#if defined(MAL_UNIX) + #if defined(MAL_LINUX) + #if !defined(MAL_ANDROID) // ALSA is not supported on Android. + #define MAL_SUPPORT_ALSA + #endif + #endif + #if defined(MAL_APPLE) + #define MAL_SUPPORT_COREAUDIO + #endif + #if defined(MAL_ANDROID) + #define MAL_SUPPORT_OPENSL + #endif + #if !defined(MAL_LINUX) && !defined(MAL_APPLE) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN) + #define MAL_SUPPORT_OSS + #endif +#endif + +#define MAL_SUPPORT_SDL // All platforms support SDL. + +// Explicitly disable OpenAL and Null backends for Emscripten because they both use a background thread which is not properly supported right now. +#if !defined(MAL_EMSCRIPTEN) +#define MAL_SUPPORT_OPENAL +#define MAL_SUPPORT_NULL // All platforms support the null backend. +#endif + + +#if !defined(MAL_NO_WASAPI) && defined(MAL_SUPPORT_WASAPI) + #define MAL_ENABLE_WASAPI +#endif +#if !defined(MAL_NO_DSOUND) && defined(MAL_SUPPORT_DSOUND) + #define MAL_ENABLE_DSOUND +#endif +#if !defined(MAL_NO_WINMM) && defined(MAL_SUPPORT_WINMM) + #define MAL_ENABLE_WINMM +#endif +#if !defined(MAL_NO_ALSA) && defined(MAL_SUPPORT_ALSA) + #define MAL_ENABLE_ALSA +#endif +#if !defined(MAL_NO_COREAUDIO) && defined(MAL_SUPPORT_COREAUDIO) + #define MAL_ENABLE_COREAUDIO +#endif +#if !defined(MAL_NO_OSS) && defined(MAL_SUPPORT_OSS) + #define MAL_ENABLE_OSS +#endif +#if !defined(MAL_NO_OPENSL) && defined(MAL_SUPPORT_OPENSL) + #define MAL_ENABLE_OPENSL +#endif +#if !defined(MAL_NO_OPENAL) && defined(MAL_SUPPORT_OPENAL) + #define MAL_ENABLE_OPENAL +#endif +#if !defined(MAL_NO_SDL) && defined(MAL_SUPPORT_SDL) + #define MAL_ENABLE_SDL +#endif +#if !defined(MAL_NO_NULL) && defined(MAL_SUPPORT_NULL) + #define MAL_ENABLE_NULL +#endif + + +#if defined(_MSC_VER) && _MSC_VER < 1600 +typedef signed char mal_int8; +typedef unsigned char mal_uint8; +typedef signed short mal_int16; +typedef unsigned short mal_uint16; +typedef signed int mal_int32; +typedef unsigned int mal_uint32; +typedef signed __int64 mal_int64; +typedef unsigned __int64 mal_uint64; +#else +#include +typedef int8_t mal_int8; +typedef uint8_t mal_uint8; +typedef int16_t mal_int16; +typedef uint16_t mal_uint16; +typedef int32_t mal_int32; +typedef uint32_t mal_uint32; +typedef int64_t mal_int64; +typedef uint64_t mal_uint64; +#endif +typedef mal_uint8 mal_bool8; +typedef mal_uint32 mal_bool32; +#define MAL_TRUE 1 +#define MAL_FALSE 0 + +typedef void* mal_handle; +typedef void* mal_ptr; +typedef void (* mal_proc)(); + +typedef struct mal_context mal_context; +typedef struct mal_device mal_device; + +typedef struct +{ + mal_context* pContext; + + union + { +#ifdef MAL_WIN32 + struct + { + /*HANDLE*/ mal_handle hThread; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + pthread_t thread; + } posix; +#endif + + int _unused; + }; +} mal_thread; + +typedef struct +{ + mal_context* pContext; + + union + { +#ifdef MAL_WIN32 + struct + { + /*HANDLE*/ mal_handle hMutex; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + pthread_mutex_t mutex; + } posix; +#endif + + int _unused; + }; +} mal_mutex; + +typedef struct +{ + mal_context* pContext; + + union + { +#ifdef MAL_WIN32 + struct + { + /*HANDLE*/ mal_handle hEvent; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + pthread_mutex_t mutex; + pthread_cond_t condition; + mal_uint32 value; + } posix; +#endif + + int _unused; + }; +} mal_event; + +#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) +typedef mal_uint16 wchar_t; +#endif + +// Define NULL for some compilers. +#ifndef NULL +#define NULL 0 +#endif + +#define MAL_MAX_PERIODS_DSOUND 4 +#define MAL_MAX_PERIODS_OPENAL 4 + +typedef mal_uint8 mal_channel; +#define MAL_CHANNEL_NONE 0 +#define MAL_CHANNEL_FRONT_LEFT 1 +#define MAL_CHANNEL_FRONT_RIGHT 2 +#define MAL_CHANNEL_FRONT_CENTER 3 +#define MAL_CHANNEL_LFE 4 +#define MAL_CHANNEL_BACK_LEFT 5 +#define MAL_CHANNEL_BACK_RIGHT 6 +#define MAL_CHANNEL_FRONT_LEFT_CENTER 7 +#define MAL_CHANNEL_FRONT_RIGHT_CENTER 8 +#define MAL_CHANNEL_BACK_CENTER 9 +#define MAL_CHANNEL_SIDE_LEFT 10 +#define MAL_CHANNEL_SIDE_RIGHT 11 +#define MAL_CHANNEL_TOP_CENTER 12 +#define MAL_CHANNEL_TOP_FRONT_LEFT 13 +#define MAL_CHANNEL_TOP_FRONT_CENTER 14 +#define MAL_CHANNEL_TOP_FRONT_RIGHT 15 +#define MAL_CHANNEL_TOP_BACK_LEFT 16 +#define MAL_CHANNEL_TOP_BACK_CENTER 17 +#define MAL_CHANNEL_TOP_BACK_RIGHT 18 +#define MAL_CHANNEL_MONO MAL_CHANNEL_FRONT_CENTER +#define MAL_MAX_CHANNELS 18 + +#define MAL_MAX_SAMPLE_SIZE_IN_BYTES 8 + +typedef int mal_result; +#define MAL_SUCCESS 0 +#define MAL_ERROR -1 // A generic error. +#define MAL_INVALID_ARGS -2 +#define MAL_OUT_OF_MEMORY -3 +#define MAL_FORMAT_NOT_SUPPORTED -4 +#define MAL_NO_BACKEND -5 +#define MAL_NO_DEVICE -6 +#define MAL_API_NOT_FOUND -7 +#define MAL_DEVICE_BUSY -8 +#define MAL_DEVICE_NOT_INITIALIZED -9 +#define MAL_DEVICE_ALREADY_STARTED -10 +#define MAL_DEVICE_ALREADY_STARTING -11 +#define MAL_DEVICE_ALREADY_STOPPED -12 +#define MAL_DEVICE_ALREADY_STOPPING -13 +#define MAL_FAILED_TO_MAP_DEVICE_BUFFER -14 +#define MAL_FAILED_TO_INIT_BACKEND -15 +#define MAL_FAILED_TO_READ_DATA_FROM_CLIENT -16 +#define MAL_FAILED_TO_READ_DATA_FROM_DEVICE -17 +#define MAL_FAILED_TO_SEND_DATA_TO_CLIENT -18 +#define MAL_FAILED_TO_SEND_DATA_TO_DEVICE -19 +#define MAL_FAILED_TO_OPEN_BACKEND_DEVICE -20 +#define MAL_FAILED_TO_START_BACKEND_DEVICE -21 +#define MAL_FAILED_TO_STOP_BACKEND_DEVICE -22 +#define MAL_FAILED_TO_CREATE_MUTEX -23 +#define MAL_FAILED_TO_CREATE_EVENT -24 +#define MAL_FAILED_TO_CREATE_THREAD -25 +#define MAL_INVALID_DEVICE_CONFIG -26 +#define MAL_ACCESS_DENIED -27 +#define MAL_DSOUND_FAILED_TO_CREATE_DEVICE -1024 +#define MAL_DSOUND_FAILED_TO_SET_COOP_LEVEL -1025 +#define MAL_DSOUND_FAILED_TO_CREATE_BUFFER -1026 +#define MAL_DSOUND_FAILED_TO_QUERY_INTERFACE -1027 +#define MAL_DSOUND_FAILED_TO_SET_NOTIFICATIONS -1028 +#define MAL_ALSA_FAILED_TO_OPEN_DEVICE -2048 +#define MAL_ALSA_FAILED_TO_SET_HW_PARAMS -2049 +#define MAL_ALSA_FAILED_TO_SET_SW_PARAMS -2050 +#define MAL_ALSA_FAILED_TO_PREPARE_DEVICE -2051 +#define MAL_ALSA_FAILED_TO_RECOVER_DEVICE -2052 +#define MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR -3072 +#define MAL_WASAPI_FAILED_TO_CREATE_DEVICE -3073 +#define MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE -3074 +#define MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE -3075 +#define MAL_WASAPI_FAILED_TO_FIND_BEST_FORMAT -3076 +#define MAL_WASAPI_FAILED_TO_GET_INTERNAL_BUFFER -3077 +#define MAL_WASAPI_FAILED_TO_RELEASE_INTERNAL_BUFFER -3078 +#define MAL_WINMM_FAILED_TO_GET_DEVICE_CAPS -4096 +#define MAL_WINMM_FAILED_TO_GET_SUPPORTED_FORMATS -4097 + +typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message); +typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples); +typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples); +typedef void (* mal_stop_proc)(mal_device* pDevice); + +typedef enum +{ + mal_backend_null, + mal_backend_wasapi, + mal_backend_dsound, + mal_backend_winmm, + mal_backend_alsa, + mal_backend_oss, + mal_backend_opensl, + mal_backend_openal, + mal_backend_sdl +} mal_backend; + +typedef enum +{ + mal_device_type_playback, + mal_device_type_capture +} mal_device_type; + +typedef enum +{ + // I like to keep these explicitly defined because they're used as a key into a lookup table. When items are + // added to this, make sure there are no gaps and that they're added to the lookup table in mal_get_sample_size_in_bytes(). + mal_format_unknown = 0, // Mainly used for indicating an error. + mal_format_u8 = 1, + mal_format_s16 = 2, // Seems to be the most widely supported format. + mal_format_s24 = 3, // Tightly packed. 3 bytes per sample. + mal_format_s32 = 4, + mal_format_f32 = 5, +} mal_format; + +typedef enum +{ + mal_channel_mix_mode_basic, // Drop excess channels; zeroed out extra channels. + mal_channel_mix_mode_blend, // Blend channels based on locality. +} mal_channel_mix_mode; + +typedef union +{ +#ifdef MAL_SUPPORT_WASAPI + wchar_t wasapi[64]; // WASAPI uses a wchar_t string for identification. +#endif +#ifdef MAL_SUPPORT_DSOUND + mal_uint8 dsound[16]; // DirectSound uses a GUID for identification. +#endif +#ifdef MAL_SUPPORT_WINMM + /*UINT_PTR*/ mal_uint32 winmm; // When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. +#endif +#ifdef MAL_SUPPORT_ALSA + char alsa[256]; // ALSA uses a name string for identification. +#endif +#ifdef MAL_SUPPORT_COREAUDIO + // TODO: Implement me. +#endif +#ifdef MAL_SUPPORT_OSS + char oss[64]; // "dev/dsp0", etc. "dev/dsp" for the default device. +#endif +#ifdef MAL_SUPPORT_OPENSL + mal_uint32 opensl; // OpenSL|ES uses a 32-bit unsigned integer for identification. +#endif +#ifdef MAL_SUPPORT_OPENAL + char openal[256]; // OpenAL seems to use human-readable device names as the ID. +#endif +#ifdef MAL_SUPPORT_SDL + int sdl; // SDL devices are identified with an index. +#endif +#ifdef MAL_SUPPORT_NULL + int nullbackend; // Always 0. +#endif +} mal_device_id; + +typedef struct +{ + mal_device_id id; + char name[256]; +} mal_device_info; + +typedef struct +{ + mal_int64 counter; +} mal_timer; + + +typedef struct mal_src mal_src; +typedef mal_uint32 (* mal_src_read_proc)(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read. + +typedef enum +{ + mal_src_algorithm_none, + mal_src_algorithm_linear +} mal_src_algorithm; + +#define MAL_SRC_CACHE_SIZE_IN_FRAMES 512 +typedef struct +{ + mal_src* pSRC; + float pCachedFrames[MAL_MAX_CHANNELS * MAL_SRC_CACHE_SIZE_IN_FRAMES]; + mal_uint32 cachedFrameCount; + mal_uint32 iNextFrame; +} mal_src_cache; + +typedef struct +{ + mal_uint32 sampleRateIn; + mal_uint32 sampleRateOut; + mal_format formatIn; + mal_format formatOut; + mal_uint32 channels; + mal_src_algorithm algorithm; + mal_uint32 cacheSizeInFrames; // The number of frames to read from the client at a time. +} mal_src_config; + +struct mal_src +{ + mal_src_config config; + mal_src_read_proc onRead; + void* pUserData; + float bin[256]; + mal_src_cache cache; // <-- For simplifying and optimizing client -> memory reading. + + union + { + struct + { + float alpha; + mal_bool32 isPrevFramesLoaded : 1; + mal_bool32 isNextFramesLoaded : 1; + } linear; + }; +}; + +typedef struct mal_dsp mal_dsp; +typedef mal_uint32 (* mal_dsp_read_proc)(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData); + +typedef struct +{ + mal_format formatIn; + mal_uint32 channelsIn; + mal_uint32 sampleRateIn; + mal_channel channelMapIn[MAL_MAX_CHANNELS]; + mal_format formatOut; + mal_uint32 channelsOut; + mal_uint32 sampleRateOut; + mal_channel channelMapOut[MAL_MAX_CHANNELS]; + mal_uint32 cacheSizeInFrames; // Applications should set this to 0 for now. +} mal_dsp_config; + +struct mal_dsp +{ + mal_dsp_config config; + mal_dsp_read_proc onRead; + void* pUserDataForOnRead; + mal_src src; // For sample rate conversion. + mal_channel channelMapInPostMix[MAL_MAX_CHANNELS]; // <-- When mixing, new channels may need to be created. This represents the channel map after mixing. + mal_channel channelShuffleTable[MAL_MAX_CHANNELS]; + mal_bool32 isChannelMappingRequired : 1; + mal_bool32 isSRCRequired : 1; + mal_bool32 isPassthrough : 1; // <-- Will be set to true when the DSP pipeline is an optimized passthrough. +}; + + +typedef struct +{ + mal_format format; + mal_uint32 channels; + mal_uint32 sampleRate; + mal_channel channelMap[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFrames; + mal_uint32 periods; + mal_bool32 preferExclusiveMode; + mal_recv_proc onRecvCallback; + mal_send_proc onSendCallback; + mal_stop_proc onStopCallback; + + struct + { + mal_bool32 noMMap; // Disables MMap mode. + } alsa; +} mal_device_config; + +typedef struct +{ + mal_log_proc onLog; + + struct + { + mal_bool32 useVerboseDeviceEnumeration; + mal_bool32 excludeNullDevice; + } alsa; +} mal_context_config; + +struct mal_context +{ + mal_backend backend; // DirectSound, ALSA, etc. + mal_context_config config; + + union + { +#ifdef MAL_SUPPORT_WASAPI + struct + { + int _unused; + } wasapi; +#endif +#ifdef MAL_SUPPORT_DSOUND + struct + { + /*HMODULE*/ mal_handle hDSoundDLL; + } dsound; +#endif +#ifdef MAL_SUPPORT_WINMM + struct + { + /*HMODULE*/ mal_handle hWinMM; + mal_proc waveOutGetNumDevs; + mal_proc waveOutGetDevCapsA; + mal_proc waveOutOpen; + mal_proc waveOutClose; + mal_proc waveOutPrepareHeader; + mal_proc waveOutUnprepareHeader; + mal_proc waveOutWrite; + mal_proc waveOutReset; + mal_proc waveInGetNumDevs; + mal_proc waveInGetDevCapsA; + mal_proc waveInOpen; + mal_proc waveInClose; + mal_proc waveInPrepareHeader; + mal_proc waveInUnprepareHeader; + mal_proc waveInAddBuffer; + mal_proc waveInStart; + mal_proc waveInReset; + } winmm; +#endif +#ifdef MAL_SUPPORT_ALSA + struct + { + mal_handle asoundSO; + mal_proc snd_pcm_open; + mal_proc snd_pcm_close; + mal_proc snd_pcm_hw_params_sizeof; + mal_proc snd_pcm_hw_params_any; + mal_proc snd_pcm_hw_params_set_format; + mal_proc snd_pcm_hw_params_set_format_first; + mal_proc snd_pcm_hw_params_get_format_mask; + mal_proc snd_pcm_hw_params_set_channels_near; + mal_proc snd_pcm_hw_params_set_rate_resample; + mal_proc snd_pcm_hw_params_set_rate_near; + mal_proc snd_pcm_hw_params_set_buffer_size_near; + mal_proc snd_pcm_hw_params_set_periods_near; + mal_proc snd_pcm_hw_params_set_access; + mal_proc snd_pcm_hw_params_get_format; + mal_proc snd_pcm_hw_params_get_channels; + mal_proc snd_pcm_hw_params_get_rate; + mal_proc snd_pcm_hw_params_get_buffer_size; + mal_proc snd_pcm_hw_params_get_periods; + mal_proc snd_pcm_hw_params_get_access; + mal_proc snd_pcm_hw_params; + mal_proc snd_pcm_sw_params_sizeof; + mal_proc snd_pcm_sw_params_current; + mal_proc snd_pcm_sw_params_set_avail_min; + mal_proc snd_pcm_sw_params_set_start_threshold; + mal_proc snd_pcm_sw_params; + mal_proc snd_pcm_format_mask_sizeof; + mal_proc snd_pcm_format_mask_test; + mal_proc snd_pcm_get_chmap; + mal_proc snd_pcm_prepare; + mal_proc snd_pcm_start; + mal_proc snd_pcm_drop; + mal_proc snd_device_name_hint; + mal_proc snd_device_name_get_hint; + mal_proc snd_card_get_index; + mal_proc snd_device_name_free_hint; + mal_proc snd_pcm_mmap_begin; + mal_proc snd_pcm_mmap_commit; + mal_proc snd_pcm_recover; + mal_proc snd_pcm_readi; + mal_proc snd_pcm_writei; + mal_proc snd_pcm_avail; + mal_proc snd_pcm_avail_update; + mal_proc snd_pcm_wait; + mal_proc snd_pcm_info; + mal_proc snd_pcm_info_sizeof; + mal_proc snd_pcm_info_get_name; + } alsa; +#endif +#ifdef MAL_SUPPORT_COREAUDIO + struct + { + int _unused; + } coreaudio; +#endif +#ifdef MAL_SUPPORT_OSS + struct + { + int versionMajor; + int versionMinor; + } oss; +#endif +#ifdef MAL_SUPPORT_OPENSL + struct + { + int _unused; + } opensl; +#endif +#ifdef MAL_SUPPORT_OPENAL + struct + { + /*HMODULE*/ mal_handle hOpenAL; // OpenAL32.dll, etc. + mal_proc alcCreateContext; + mal_proc alcMakeContextCurrent; + mal_proc alcProcessContext; + mal_proc alcSuspendContext; + mal_proc alcDestroyContext; + mal_proc alcGetCurrentContext; + mal_proc alcGetContextsDevice; + mal_proc alcOpenDevice; + mal_proc alcCloseDevice; + mal_proc alcGetError; + mal_proc alcIsExtensionPresent; + mal_proc alcGetProcAddress; + mal_proc alcGetEnumValue; + mal_proc alcGetString; + mal_proc alcGetIntegerv; + mal_proc alcCaptureOpenDevice; + mal_proc alcCaptureCloseDevice; + mal_proc alcCaptureStart; + mal_proc alcCaptureStop; + mal_proc alcCaptureSamples; + + mal_proc alEnable; + mal_proc alDisable; + mal_proc alIsEnabled; + mal_proc alGetString; + mal_proc alGetBooleanv; + mal_proc alGetIntegerv; + mal_proc alGetFloatv; + mal_proc alGetDoublev; + mal_proc alGetBoolean; + mal_proc alGetInteger; + mal_proc alGetFloat; + mal_proc alGetDouble; + mal_proc alGetError; + mal_proc alIsExtensionPresent; + mal_proc alGetProcAddress; + mal_proc alGetEnumValue; + mal_proc alGenSources; + mal_proc alDeleteSources; + mal_proc alIsSource; + mal_proc alSourcef; + mal_proc alSource3f; + mal_proc alSourcefv; + mal_proc alSourcei; + mal_proc alSource3i; + mal_proc alSourceiv; + mal_proc alGetSourcef; + mal_proc alGetSource3f; + mal_proc alGetSourcefv; + mal_proc alGetSourcei; + mal_proc alGetSource3i; + mal_proc alGetSourceiv; + mal_proc alSourcePlayv; + mal_proc alSourceStopv; + mal_proc alSourceRewindv; + mal_proc alSourcePausev; + mal_proc alSourcePlay; + mal_proc alSourceStop; + mal_proc alSourceRewind; + mal_proc alSourcePause; + mal_proc alSourceQueueBuffers; + mal_proc alSourceUnqueueBuffers; + mal_proc alGenBuffers; + mal_proc alDeleteBuffers; + mal_proc alIsBuffer; + mal_proc alBufferData; + mal_proc alBufferf; + mal_proc alBuffer3f; + mal_proc alBufferfv; + mal_proc alBufferi; + mal_proc alBuffer3i; + mal_proc alBufferiv; + mal_proc alGetBufferf; + mal_proc alGetBuffer3f; + mal_proc alGetBufferfv; + mal_proc alGetBufferi; + mal_proc alGetBuffer3i; + mal_proc alGetBufferiv; + + mal_bool32 isEnumerationSupported : 1; + mal_bool32 isFloat32Supported : 1; + mal_bool32 isMCFormatsSupported : 1; + } openal; +#endif +#ifdef MAL_SUPPORT_SDL + struct + { + mal_handle hSDL; // SDL + mal_proc SDL_InitSubSystem; + mal_proc SDL_QuitSubSystem; + mal_proc SDL_CloseAudio; + mal_proc SDL_OpenAudio; + mal_proc SDL_PauseAudio; + mal_proc SDL_GetNumAudioDevices; + mal_proc SDL_GetAudioDeviceName; + mal_proc SDL_CloseAudioDevice; + mal_proc SDL_OpenAudioDevice; + mal_proc SDL_PauseAudioDevice; + + mal_bool32 usingSDL1; + } sdl; +#endif +#ifdef MAL_SUPPORT_NULL + struct + { + int _unused; + } null_backend; +#endif + }; + + union + { +#ifdef MAL_WIN32 + struct + { + /*HMODULE*/ mal_handle hOle32DLL; + mal_proc CoInitializeEx; + mal_proc CoUninitialize; + mal_proc CoCreateInstance; + mal_proc CoTaskMemFree; + mal_proc PropVariantClear; + + /*HMODULE*/ mal_handle hUser32DLL; + mal_proc GetForegroundWindow; + mal_proc GetDesktopWindow; + } win32; +#endif +#ifdef MAL_POSIX + struct + { + mal_handle pthreadSO; + mal_proc pthread_create; + mal_proc pthread_join; + mal_proc pthread_mutex_init; + mal_proc pthread_mutex_destroy; + mal_proc pthread_mutex_lock; + mal_proc pthread_mutex_unlock; + mal_proc pthread_cond_init; + mal_proc pthread_cond_destroy; + mal_proc pthread_cond_wait; + mal_proc pthread_cond_signal; + } posix; +#endif + int _unused; + }; +}; + +struct mal_device +{ + mal_context* pContext; + mal_device_type type; + mal_format format; + mal_uint32 channels; + mal_uint32 sampleRate; + mal_uint8 channelMap[MAL_MAX_CHANNELS]; + mal_uint32 bufferSizeInFrames; + mal_uint32 periods; + mal_uint32 state; + mal_recv_proc onRecv; + mal_send_proc onSend; + mal_stop_proc onStop; + void* pUserData; // Application defined data. + char name[256]; + mal_mutex lock; + mal_event wakeupEvent; + mal_event startEvent; + mal_event stopEvent; + mal_thread thread; + mal_result workResult; // This is set by the worker thread after it's finished doing a job. + mal_bool32 usingDefaultBufferSize : 1; + mal_bool32 usingDefaultPeriods : 1; + mal_bool32 exclusiveMode : 1; + mal_format internalFormat; + mal_uint32 internalChannels; + mal_uint32 internalSampleRate; + mal_uint8 internalChannelMap[MAL_MAX_CHANNELS]; + mal_dsp dsp; // Samples run through this to convert samples to a format suitable for use by the backend. + mal_uint32 _dspFrameCount; // Internal use only. Used when running the device -> DSP -> client pipeline. See mal_device__on_read_from_device(). + const mal_uint8* _dspFrames; // ^^^ AS ABOVE ^^^ + + union + { +#ifdef MAL_SUPPORT_WASAPI + struct + { + /*IAudioClient**/ mal_ptr pAudioClient; + /*IAudioRenderClient**/ mal_ptr pRenderClient; + /*IAudioCaptureClient**/ mal_ptr pCaptureClient; + /*HANDLE*/ mal_handle hEvent; + /*HANDLE*/ mal_handle hStopEvent; + mal_bool32 breakFromMainLoop; + } wasapi; +#endif +#ifdef MAL_SUPPORT_DSOUND + struct + { + /*HMODULE*/ mal_handle hDSoundDLL; + /*LPDIRECTSOUND*/ mal_ptr pPlayback; + /*LPDIRECTSOUNDBUFFER*/ mal_ptr pPlaybackPrimaryBuffer; + /*LPDIRECTSOUNDBUFFER*/ mal_ptr pPlaybackBuffer; + /*LPDIRECTSOUNDCAPTURE*/ mal_ptr pCapture; + /*LPDIRECTSOUNDCAPTUREBUFFER*/ mal_ptr pCaptureBuffer; + /*LPDIRECTSOUNDNOTIFY*/ mal_ptr pNotify; + /*HANDLE*/ mal_handle pNotifyEvents[MAL_MAX_PERIODS_DSOUND]; // One event handle for each period. + /*HANDLE*/ mal_handle hStopEvent; + mal_uint32 lastProcessedFrame; // This is circular. + mal_bool32 breakFromMainLoop; + } dsound; +#endif +#ifdef MAL_SUPPORT_WINMM + struct + { + /*HWAVEOUT, HWAVEIN*/ mal_handle hDevice; + /*HANDLE*/ mal_handle hEvent; + mal_uint32 fragmentSizeInFrames; + mal_uint32 fragmentSizeInBytes; + mal_uint32 iNextHeader; // [0,periods). Used as an index into pWAVEHDR. + /*WAVEHDR**/ mal_uint8* pWAVEHDR; // One instantiation for each period. + mal_uint8* pIntermediaryBuffer; + mal_uint8* _pHeapData; // Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. + mal_bool32 breakFromMainLoop; + } winmm; +#endif +#ifdef MAL_SUPPORT_ALSA + struct + { + /*snd_pcm_t**/ mal_ptr pPCM; + mal_bool32 isUsingMMap : 1; + mal_bool32 breakFromMainLoop : 1; + void* pIntermediaryBuffer; + } alsa; +#endif +#ifdef MAL_SUPPORT_COREAUDIO + struct + { + int _unused; + } coreaudio; +#endif +#ifdef MAL_SUPPORT_OSS + struct + { + int fd; + mal_uint32 fragmentSizeInFrames; + mal_bool32 breakFromMainLoop; + void* pIntermediaryBuffer; + } oss; +#endif +#ifdef MAL_SUPPORT_OPENSL + struct + { + /*SLObjectItf*/ mal_ptr pOutputMixObj; + /*SLOutputMixItf*/ mal_ptr pOutputMix; + /*SLObjectItf*/ mal_ptr pAudioPlayerObj; + /*SLPlayItf*/ mal_ptr pAudioPlayer; + /*SLObjectItf*/ mal_ptr pAudioRecorderObj; + /*SLRecordItf*/ mal_ptr pAudioRecorder; + /*SLAndroidSimpleBufferQueueItf*/ mal_ptr pBufferQueue; + mal_uint32 periodSizeInFrames; + mal_uint32 currentBufferIndex; + mal_uint8* pBuffer; // This is malloc()'d and is used for storing audio data. Typed as mal_uint8 for easy offsetting. + } opensl; +#endif +#ifdef MAL_SUPPORT_OPENAL + struct + { + /*ALCcontext**/ mal_ptr pContextALC; + /*ALCdevice**/ mal_ptr pDeviceALC; + /*ALuint*/ mal_uint32 sourceAL; + /*ALuint*/ mal_uint32 buffersAL[MAL_MAX_PERIODS_OPENAL]; + /*ALenum*/ mal_uint32 formatAL; + mal_uint32 subBufferSizeInFrames; // This is the size of each of the OpenAL buffers (buffersAL). + mal_uint8* pIntermediaryBuffer; // This is malloc()'d and is used as the destination for reading from the client. Typed as mal_uint8 for easy offsetting. + mal_uint32 iNextBuffer; // The next buffer to unenqueue and then re-enqueue as new data is read. + mal_bool32 breakFromMainLoop; + } openal; +#endif +#ifdef MAL_SUPPORT_SDL + struct + { + mal_uint32 deviceID; + } sdl; +#endif +#ifdef MAL_SUPPORT_NULL + struct + { + mal_timer timer; + mal_uint32 lastProcessedFrame; // This is circular. + mal_bool32 breakFromMainLoop; + mal_uint8* pBuffer; // This is malloc()'d and is used as the destination for reading from the client. Typed as mal_uint8 for easy offsetting. + } null_device; +#endif + }; +}; +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +// Initializes a context. +// +// The context is used for selecting and initializing the relevant backends. +// +// Note that the location of the device cannot change throughout it's lifetime. Consider allocating +// the mal_context object with malloc() if this is an issue. The reason for this is that a pointer +// to the context is stored in the mal_device structure. +// +// is used to allow the application to prioritize backends depending on it's specific +// requirements. This can be null in which case it uses the default priority, which is as follows: +// - WASAPI +// - DirectSound +// - WinMM +// - ALSA +// - OSS +// - OpenSL|ES +// - OpenAL +// - SDL +// - Null +// +// The onLog callback is used for posting log messages back to the client for diagnostics, debugging, +// etc. You can pass NULL for this if you do not need it. +// +// Return Value: +// MAL_SUCCESS if successful; any other error code otherwise. +// +// Thread Safety: UNSAFE +// +// Effeciency: LOW +// This will dynamically load backends DLLs/SOs (such as dsound.dll). +mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pConfig, mal_context* pContext); + +// Uninitializes a context. +// +// Results are undefined if you call this while any device created by this context is still active. +// +// Return Value: +// MAL_SUCCESS if successful; any other error code otherwise. +// +// Thread Safety: UNSAFE +// +// Efficiency: LOW +// This will unload the backend DLLs/SOs. +mal_result mal_context_uninit(mal_context* pContext); + +// Enumerates over each device of the given type (playback or capture). +// +// It is _not_ safe to assume the first enumerated device is the default device. +// +// Some backends and platforms may only support default playback and capture devices. +// +// Return Value: +// MAL_SUCCESS if successful; any other error code otherwise. +// +// Thread Safety: SAFE, SEE NOTES. +// This API uses an application-defined buffer for output. This is thread-safe so long as the +// application ensures mutal exclusion to the output buffer at their level. +// +// Efficiency: LOW +mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo); + +// Initializes a device. +// +// The device ID (pDeviceID) can be null, in which case the default device is used. Otherwise, you +// can retrieve the ID by calling mal_enumerate_devices() and using the ID from the returned data. +// Set pDeviceID to NULL to use the default device. Do _not_ rely on the first device ID returned +// by mal_enumerate_devices() to be the default device. +// +// This will try it's hardest to create a valid device, even if it means adjusting input arguments. +// Look at pDevice->internalChannels, pDevice->internalSampleRate, etc. to determine the actual +// properties after initialization. +// +// If is 0, it will default to MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS. If +// is set to 0 it will default to MAL_DEFAULT_PERIODS. +// +// The property controls how frequently the background thread is woken to check for more +// data. It's tied to the buffer size, so as an example, if your buffer size is equivalent to 10 +// milliseconds and you have 2 periods, the CPU will wake up approximately every 5 milliseconds. +// +// Use mal_device_config_init(), mal_device_config_init_playback(), etc. to initialize a +// mal_device_config object. +// +// When compiling for UWP you must ensure you call this function on the main UI thread because the +// operating system may need to present the user with a message asking for permissions. Please refer +// to the official documentation for ActivateAudioInterfaceAsync() for more information. +// +// Return Value: +// MAL_SUCCESS if successful; any other error code otherwise. +// +// Thread Safety: UNSAFE +// It is not safe to call this function simultaneously for different devices because some backends +// depend on and mutate global state (such as OpenSL|ES). The same applies to calling this as the +// same time as mal_device_uninit(). +// +// Results are undefined if you try using a device before this function has returned. +// +// Efficiency: LOW +// This is just slow due to the nature of it being an initialization API. +mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice); + +// Uninitializes a device. +// +// This will explicitly stop the device. You do not need to call mal_device_stop() beforehand, but it's +// harmless if you do. +// +// Return Value: +// MAL_SUCCESS if successful; any other error code otherwise. +// +// Thread Safety: UNSAFE +// As soon as this API is called the device should be considered undefined. All bets are off if you +// try using the device at the same time as uninitializing it. +// +// Efficiency: LOW +// This will stop the device with mal_device_stop() which is a slow, synchronized call. It also needs +// to destroy internal objects like the backend-specific objects and the background thread. +void mal_device_uninit(mal_device* pDevice); + +// Sets the callback to use when the application has received data from the device. +// +// Thread Safety: SAFE +// This API is implemented as a simple atomic assignment. +// +// Efficiency: HIGH +// This is just an atomic assignment. +void mal_device_set_recv_callback(mal_device* pDevice, mal_recv_proc proc); + +// Sets the callback to use when the application needs to send data to the device for playback. +// +// Note that the implementation of this callback must copy over as many samples as is available. The +// return value specifies how many samples were written to the output buffer. The backend will fill +// any leftover samples with silence. +// +// Thread Safety: SAFE +// This API is implemented as a simple atomic assignment. +// +// Efficiency: HIGH +// This is just an atomic assignment. +void mal_device_set_send_callback(mal_device* pDevice, mal_send_proc proc); + +// Sets the callback to use when the device has stopped, either explicitly or as a result of an error. +// +// Thread Safety: SAFE +// This API is implemented as a simple atomic assignment. +// +// Efficiency: HIGH +// This is just an atomic assignment. +void mal_device_set_stop_callback(mal_device* pDevice, mal_stop_proc proc); + +// Activates the device. For playback devices this begins playback. For capture devices it begins +// recording. +// +// For a playback device, this will retrieve an initial chunk of audio data from the client before +// returning. The reason for this is to ensure there is valid audio data in the buffer, which needs +// to be done _before_ the device begins playback. +// +// Return Value: +// - MAL_SUCCESS if successful; any other error code otherwise. +// - MAL_INVALID_ARGS +// One or more of the input arguments is invalid. +// - MAL_DEVICE_NOT_INITIALIZED +// The device is not currently or was never initialized. +// - MAL_DEVICE_BUSY +// The device is in the process of stopping. This will only happen if mal_device_start() and +// mal_device_stop() is called simultaneous on separate threads. This will never be returned in +// single-threaded applications. +// - MAL_DEVICE_ALREADY_STARTING +// The device is already in the process of starting. This will never be returned in single-threaded +// applications. +// - MAL_DEVICE_ALREADY_STARTED +// The device is already started. +// - MAL_FAILED_TO_READ_DATA_FROM_CLIENT +// Failed to read the initial chunk of audio data from the client. This initial chunk of data is +// required so that the device has valid audio data as soon as it starts playing. This will never +// be returned for capture devices. +// - MAL_FAILED_TO_START_BACKEND_DEVICE +// There was a backend-specific error starting the device. +// +// Thread Safety: SAFE +// +// Efficiency: LOW +// This API waits until the backend device has been started for real by the worker thread. It also +// waits on a mutex for thread-safety. +mal_result mal_device_start(mal_device* pDevice); + +// Puts the device to sleep, but does not uninitialize it. Use mal_device_start() to start it up again. +// +// Return Value: +// - MAL_SUCCESS if successful; any other error code otherwise. +// - MAL_INVALID_ARGS +// One or more of the input arguments is invalid. +// - MAL_DEVICE_NOT_INITIALIZED +// The device is not currently or was never initialized. +// - MAL_DEVICE_BUSY +// The device is in the process of starting. This will only happen if mal_device_start() and +// mal_device_stop() is called simultaneous on separate threads. This will never be returned in +// single-threaded applications. +// - MAL_DEVICE_ALREADY_STOPPING +// The device is already in the process of stopping. This will never be returned in single-threaded +// applications. +// - MAL_DEVICE_ALREADY_STOPPED +// The device is already stopped. +// - MAL_FAILED_TO_STOP_BACKEND_DEVICE +// There was a backend-specific error stopping the device. +// +// Thread Safety: SAFE +// +// Efficiency: LOW +// This API needs to wait on the worker thread to stop the backend device properly before returning. It +// also waits on a mutex for thread-safety. +// +// In addition, some backends need to wait for the device to finish playback/recording of the current +// fragment which can take some time (usually proportionate to the buffer size used when initializing +// the device). +mal_result mal_device_stop(mal_device* pDevice); + +// Determines whether or not the device is started. +// +// Return Value: +// True if the device is started, false otherwise. +// +// Thread Safety: SAFE +// If another thread calls mal_device_start() or mal_device_stop() at this same time as this function +// is called, there's a very small chance the return value will be out of sync. +// +// Efficiency: HIGH +// This is implemented with a simple accessor. +mal_bool32 mal_device_is_started(mal_device* pDevice); + +// Retrieves the size of the buffer in bytes for the given device. +// +// Thread Safety: SAFE +// This is calculated from constant values which are set at initialization time and never change. +// +// Efficiency: HIGH +// This is implemented with just a few 32-bit integer multiplications. +mal_uint32 mal_device_get_buffer_size_in_bytes(mal_device* pDevice); + +// Retrieves the size of a sample in bytes for the given format. +// +// Thread Safety: SAFE +// This is API is pure. +// +// Efficiency: HIGH +// This is implemented with a lookup table. +mal_uint32 mal_get_sample_size_in_bytes(mal_format format); + +// Helper function for initializing a mal_context_config object. +mal_context_config mal_context_config_init(mal_log_proc onLog); + +// Helper function for initializing a mal_device_config object. +// +// This is just a helper API, and as such the returned object can be safely modified as needed. +// +// The default channel mapping is based on the channel count, as per the table below. Note that these +// can be freely changed after this function returns if you are needing something in particular. +// +// |---------------|------------------------------| +// | Channel Count | Mapping | +// |---------------|------------------------------| +// | 1 (Mono) | 0: MAL_CHANNEL_FRONT_CENTER | +// |---------------|------------------------------| +// | 2 (Stereo) | 0: MAL_CHANNEL_FRONT_LEFT | +// | | 1: MAL_CHANNEL_FRONT_RIGHT | +// |---------------|------------------------------| +// | 3 (2.1) | 0: MAL_CHANNEL_FRONT_LEFT | +// | | 1: MAL_CHANNEL_FRONT_RIGHT | +// | | 2: MAL_CHANNEL_LFE | +// |---------------|------------------------------| +// | 4 (Quad) | 0: MAL_CHANNEL_FRONT_LEFT | +// | | 1: MAL_CHANNEL_FRONT_RIGHT | +// | | 2: MAL_CHANNEL_BACK_LEFT | +// | | 3: MAL_CHANNEL_BACK_RIGHT | +// |---------------|------------------------------| +// | 5 (4.1) | 0: MAL_CHANNEL_FRONT_LEFT | +// | | 1: MAL_CHANNEL_FRONT_RIGHT | +// | | 2: MAL_CHANNEL_BACK_LEFT | +// | | 3: MAL_CHANNEL_BACK_RIGHT | +// | | 4: MAL_CHANNEL_LFE | +// |---------------|------------------------------| +// | 6 (5.1) | 0: MAL_CHANNEL_FRONT_LEFT | +// | | 1: MAL_CHANNEL_FRONT_RIGHT | +// | | 2: MAL_CHANNEL_FRONT_CENTER | +// | | 3: MAL_CHANNEL_LFE | +// | | 4: MAL_CHANNEL_BACK_LEFT | +// | | 5: MAL_CHANNEL_BACK_RIGHT | +// |---------------|------------------------------| +// | 8 (7.1) | 0: MAL_CHANNEL_FRONT_LEFT | +// | | 1: MAL_CHANNEL_FRONT_RIGHT | +// | | 2: MAL_CHANNEL_FRONT_CENTER | +// | | 3: MAL_CHANNEL_LFE | +// | | 4: MAL_CHANNEL_BACK_LEFT | +// | | 5: MAL_CHANNEL_BACK_RIGHT | +// | | 6: MAL_CHANNEL_SIDE_LEFT | +// | | 7: MAL_CHANNEL_SIDE_RIGHT | +// |---------------|------------------------------| +// | Other | All channels set to 0. This | +// | | is equivalent to the same | +// | | mapping as the device. | +// |---------------|------------------------------| +// +// Thread Safety: SAFE +// +// Efficiency: HIGH +// This just returns a stack allocated object and consists of just a few assignments. +mal_device_config mal_device_config_init(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback, mal_send_proc onSendCallback); + +// A simplified version of mal_device_config_init() for capture devices. +static inline mal_device_config mal_device_config_init_capture(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback) { return mal_device_config_init(format, channels, sampleRate, onRecvCallback, NULL); } + +// A simplified version of mal_device_config_init() for playback devices. +static inline mal_device_config mal_device_config_init_playback(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_send_proc onSendCallback) { return mal_device_config_init(format, channels, sampleRate, NULL, onSendCallback); } + + + + +/////////////////////////////////////////////////////////////////////////////// +// +// SRC +// +/////////////////////////////////////////////////////////////////////////////// + +// Initializes a sample rate conversion object. +mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC); + +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut); + +// Reads a number of frames. +// +// Returns the number of frames actually read. +mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut); + +// The same mal_src_read_frames() with extra control over whether or not the internal buffers should be flushed at the end. +// +// Internally there exists a buffer that keeps track of the previous and next samples for sample rate conversion. The simple +// version of this function does _not_ flush this buffer because otherwise it causes clitches for streaming based conversion +// pipelines. The problem, however, is that sometimes you need those last few samples (such as if you're doing a bulk conversion +// of a static file). Enabling flushing will fix this for you. +mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); + + +/////////////////////////////////////////////////////////////////////////////// +// +// DSP +// +/////////////////////////////////////////////////////////////////////////////// + +// Initializes a DSP object. +mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp_read_proc onRead, void* pUserData, mal_dsp* pDSP); + +// Dynamically adjusts the output sample rate. +// +// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this +// is not acceptable you will need to use your own algorithm. +mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut); + +// Reads a number of frames and runs them through the DSP processor. +// +// This this _not_ flush the internal buffers which means you may end up with a few less frames than you may expect. Look at +// mal_dsp_read_frames_ex() if you want to flush the buffers at the end of the read. +mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut); + +// The same mal_dsp_read_frames() with extra control over whether or not the internal buffers should be flushed at the end. +// +// See documentation for mal_src_read_frames_ex() for an explanation on flushing. +mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); + +// High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to +// determine the required size of the output buffer. +// +// A return value of 0 indicates an error. +// +// This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead. +mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint32 frameCountIn); + +// Helper for initializing a mal_dsp_config object. +mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut); + +/////////////////////////////////////////////////////////////////////////////// +// +// Utiltities +// +/////////////////////////////////////////////////////////////////////////////// + +// Creates a mutex. +// +// A mutex must be created from a valid context. A mutex is initially unlocked. +mal_result mal_mutex_init(mal_context* pContext, mal_mutex* pMutex); + +// Deletes a mutex. +void mal_mutex_uninit(mal_mutex* pMutex); + +// Locks a mutex with an infinite timeout. +void mal_mutex_lock(mal_mutex* pMutex); + +// Unlocks a mutex. +void mal_mutex_unlock(mal_mutex* pMutex); + + + +/////////////////////////////////////////////////////////////////////////////// +// +// Miscellaneous Helpers +// +/////////////////////////////////////////////////////////////////////////////// + +// Retrieves a friendly name for a backend. +const char* mal_get_backend_name(mal_backend backend); + +// Retrieves a friendly name for a format. +const char* mal_get_format_name(mal_format format); + +// Blends two frames in floating point format. +void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels); + + + +/////////////////////////////////////////////////////////////////////////////// +// +// Format Conversion +// +/////////////////////////////////////////////////////////////////////////////// +void mal_pcm_u8_to_s16(short* pOut, const unsigned char* pIn, unsigned int count); +void mal_pcm_u8_to_s24(void* pOut, const unsigned char* pIn, unsigned int count); +void mal_pcm_u8_to_s32(int* pOut, const unsigned char* pIn, unsigned int count); +void mal_pcm_u8_to_f32(float* pOut, const unsigned char* pIn, unsigned int count); +void mal_pcm_s16_to_u8(unsigned char* pOut, const short* pIn, unsigned int count); +void mal_pcm_s16_to_s24(void* pOut, const short* pIn, unsigned int count); +void mal_pcm_s16_to_s32(int* pOut, const short* pIn, unsigned int count); +void mal_pcm_s16_to_f32(float* pOut, const short* pIn, unsigned int count); +void mal_pcm_s24_to_u8(unsigned char* pOut, const void* pIn, unsigned int count); +void mal_pcm_s24_to_s16(short* pOut, const void* pIn, unsigned int count); +void mal_pcm_s24_to_s32(int* pOut, const void* pIn, unsigned int count); +void mal_pcm_s24_to_f32(float* pOut, const void* pIn, unsigned int count); +void mal_pcm_s32_to_u8(unsigned char* pOut, const int* pIn, unsigned int count); +void mal_pcm_s32_to_s16(short* pOut, const int* pIn, unsigned int count); +void mal_pcm_s32_to_s24(void* pOut, const int* pIn, unsigned int count); +void mal_pcm_s32_to_f32(float* pOut, const int* pIn, unsigned int count); +void mal_pcm_f32_to_u8(unsigned char* pOut, const float* pIn, unsigned int count); +void mal_pcm_f32_to_s16(short* pOut, const float* pIn, unsigned int count); +void mal_pcm_f32_to_s24(void* pOut, const float* pIn, unsigned int count); +void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count); +void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, unsigned int sampleCount); + +#ifdef __cplusplus +} +#endif +#endif //mini_al_h + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_IMPLEMENTATION +#include + +#ifdef MAL_WIN32 +#include +#else +#include // For malloc()/free() +#include // For memset() +#endif + +#ifdef MAL_POSIX +#include +#include +#endif + +#if !defined(MAL_64BIT) && !defined(MAL_32BIT) +#ifdef _WIN32 +#ifdef _WIN64 +#define MAL_64BIT +#else +#define MAL_32BIT +#endif +#endif +#endif + +#if !defined(MAL_64BIT) && !defined(MAL_32BIT) +#ifdef __GNUC__ +#ifdef __LP64__ +#define MAL_64BIT +#else +#define MAL_32BIT +#endif +#endif +#endif + +#if !defined(MAL_64BIT) && !defined(MAL_32BIT) +#include +#if INTPTR_MAX == INT64_MAX +#define MAL_64BIT +#else +#define MAL_32BIT +#endif +#endif + + +// Disable run-time linking on certain backends. +#ifndef MAL_NO_RUNTIME_LINKING + #if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTEN) + #define MAL_NO_RUNTIME_LINKING + #endif +#endif + +// Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not +// certain unused functions and variables can be excluded from the build to avoid warnings. +#ifdef MAL_ENABLE_WASAPI + #define MAL_HAS_WASAPI + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_WASAPI + #endif + #endif +#endif +#ifdef MAL_ENABLE_DSOUND + #define MAL_HAS_DSOUND + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_DSOUND + #endif + #endif +#endif +#ifdef MAL_ENABLE_WINMM + #define MAL_HAS_WINMM // Every compiler I'm aware of supports WinMM. +#endif +#ifdef MAL_ENABLE_ALSA + #define MAL_HAS_ALSA + #ifdef __has_include + #if !__has_include() + #undef MAL_HAS_ALSA + #endif + #endif +#endif +#ifdef MAL_ENABLE_COREAUDIO + #define MAL_HAS_COREAUDIO +#endif +#ifdef MAL_ENABLE_OSS + #define MAL_HAS_OSS // OSS is the only supported backend for Unix and BSD, so it must be present else this library is useless. +#endif +#ifdef MAL_ENABLE_OPENSL + #define MAL_HAS_OPENSL // Like OSS, OpenSL is the only supported backend for Android. It must be present. +#endif +#ifdef MAL_ENABLE_OPENAL + #define MAL_HAS_OPENAL // mini_al inlines the necessary OpenAL stuff. +#endif +#ifdef MAL_ENABLE_SDL + #define MAL_HAS_SDL + + // SDL headers are necessary if using compile-time linking. + #ifdef MAL_NO_RUNTIME_LINKING + #ifdef __has_include + #ifdef MAL_EMSCRIPTEN + #if !__has_include() + #undef MAL_HAS_SDL + #endif + #else + #if !__has_include() + #undef MAL_HAS_SDL + #endif + #endif + #endif + #endif +#endif +#ifdef MAL_ENABLE_NULL + #define MAL_HAS_NULL // Everything supports the null backend. +#endif + + +#ifdef MAL_WIN32 + #define MAL_THREADCALL WINAPI + typedef unsigned long mal_thread_result; +#else + #define MAL_THREADCALL + typedef void* mal_thread_result; +#endif +typedef mal_thread_result (MAL_THREADCALL * mal_thread_entry_proc)(void* pData); + +#ifdef MAL_WIN32 +typedef HRESULT (WINAPI * MAL_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); +typedef void (WINAPI * MAL_PFN_CoUninitialize)(); +typedef HRESULT (WINAPI * MAL_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); +typedef void (WINAPI * MAL_PFN_CoTaskMemFree)(LPVOID pv); +typedef HRESULT (WINAPI * MAL_PFN_PropVariantClear)(PROPVARIANT *pvar); + +typedef HWND (WINAPI * MAL_PFN_GetForegroundWindow)(); +typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)(); +#endif + + +#define MAL_STATE_UNINITIALIZED 0 +#define MAL_STATE_STOPPED 1 // The device's default state after initialization. +#define MAL_STATE_STARTED 2 // The worker thread is in it's main loop waiting for the driver to request or deliver audio data. +#define MAL_STATE_STARTING 3 // Transitioning from a stopped state to started. +#define MAL_STATE_STOPPING 4 // Transitioning from a started state to stopped. + + +// The default size of the device's buffer in milliseconds. +// +// If this is too small you may get underruns and overruns in which case you'll need to either increase +// this value or use an explicit buffer size. +#ifndef MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS +#define MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS 25 +#endif + +// Default periods when none is specified in mal_device_init(). More periods means more work on the CPU. +#ifndef MAL_DEFAULT_PERIODS +#define MAL_DEFAULT_PERIODS 2 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// +// Standard Library Stuff +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef mal_zero_memory +#ifdef MAL_WIN32 +#define mal_zero_memory(p, sz) ZeroMemory((p), (sz)) +#else +#define mal_zero_memory(p, sz) memset((p), 0, (sz)) +#endif +#endif + +#define mal_zero_object(p) mal_zero_memory((p), sizeof(*(p))) + +#ifndef mal_copy_memory +#ifdef MAL_WIN32 +#define mal_copy_memory(dst, src, sz) CopyMemory((dst), (src), (sz)) +#else +#define mal_copy_memory(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#endif + +#ifndef mal_malloc +#ifdef MAL_WIN32 +#define mal_malloc(sz) HeapAlloc(GetProcessHeap(), 0, (sz)) +#else +#define mal_malloc(sz) malloc((sz)) +#endif +#endif + +#ifndef mal_realloc +#ifdef MAL_WIN32 +#define mal_realloc(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(SIZE_T)(HeapFree(GetProcessHeap(), 0, (p)) & 0))) +#else +#define mal_realloc(p, sz) realloc((p), (sz)) +#endif +#endif + +#ifndef mal_free +#ifdef MAL_WIN32 +#define mal_free(p) HeapFree(GetProcessHeap(), 0, (p)) +#else +#define mal_free(p) free((p)) +#endif +#endif + +#ifndef mal_assert +#ifdef MAL_WIN32 +#define mal_assert(condition) assert(condition) +#else +#define mal_assert(condition) assert(condition) +#endif +#endif + +#define mal_countof(x) (sizeof(x) / sizeof(x[0])) +#define mal_max(x, y) (((x) > (y)) ? (x) : (y)) +#define mal_min(x, y) (((x) < (y)) ? (x) : (y)) + +#define mal_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / mal_get_sample_size_in_bytes(format) / (channels)) + +// Some of these string utility functions are unused on some platforms. +#if defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" +#endif +// Return Values: +// 0: Success +// 22: EINVAL +// 34: ERANGE +// +// Not using symbolic constants for errors because I want to avoid #including errno.h +static int mal_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) +{ + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + size_t i; + for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (i < dstSizeInBytes) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} + +static int mal_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +{ + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + size_t maxcount = count; + if (count == ((size_t)-1) || count >= dstSizeInBytes) { // -1 = _TRUNCATE + maxcount = dstSizeInBytes - 1; + } + + size_t i; + for (i = 0; i < maxcount && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (src[i] == '\0' || i == count || count == ((size_t)-1)) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} + +static int mal_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) +{ + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + char* dstorig = dst; + + while (dstSizeInBytes > 0 && dst[0] != '\0') { + dst += 1; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes == 0) { + return 22; // Unterminated. + } + + + while (dstSizeInBytes > 0 && src[0] != '\0') { + *dst++ = *src++; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes > 0) { + dst[0] = '\0'; + } else { + dstorig[0] = '\0'; + return 34; + } + + return 0; +} + +static int mal_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) +{ + if (dst == NULL || dstSizeInBytes == 0) { + return 22; + } + if (radix < 2 || radix > 36) { + dst[0] = '\0'; + return 22; + } + + int sign = (value < 0 && radix == 10) ? -1 : 1; // The negative sign is only used when the base is 10. + + unsigned int valueU; + if (value < 0) { + valueU = -value; + } else { + valueU = value; + } + + char* dstEnd = dst; + do + { + int remainder = valueU % radix; + if (remainder > 9) { + *dstEnd = (char)((remainder - 10) + 'a'); + } else { + *dstEnd = (char)(remainder + '0'); + } + + dstEnd += 1; + dstSizeInBytes -= 1; + valueU /= radix; + } while (dstSizeInBytes > 0 && valueU > 0); + + if (dstSizeInBytes == 0) { + dst[0] = '\0'; + return 22; // Ran out of room in the output buffer. + } + + if (sign < 0) { + *dstEnd++ = '-'; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes == 0) { + dst[0] = '\0'; + return 22; // Ran out of room in the output buffer. + } + + *dstEnd = '\0'; + + + // At this point the string will be reversed. + dstEnd -= 1; + while (dst < dstEnd) { + char temp = *dst; + *dst = *dstEnd; + *dstEnd = temp; + + dst += 1; + dstEnd -= 1; + } + + return 0; +} + +static int mal_strcmp(const char* str1, const char* str2) +{ + if (str1 == str2) return 0; + + // These checks differ from the standard implementation. It's not important, but I prefer + // it just for sanity. + if (str1 == NULL) return -1; + if (str2 == NULL) return 1; + + for (;;) { + if (str1[0] == '\0') { + break; + } + if (str1[0] != str2[0]) { + break; + } + + str1 += 1; + str2 += 1; + } + + return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; +} +#if defined(__GNUC__) + #pragma GCC diagnostic pop +#endif + + +// Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +static inline unsigned int mal_next_power_of_2(unsigned int x) +{ + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + + return x; +} + +static inline unsigned int mal_prev_power_of_2(unsigned int x) +{ + return mal_next_power_of_2(x) >> 1; +} + +static inline unsigned int mal_round_to_power_of_2(unsigned int x) +{ + unsigned int prev = mal_prev_power_of_2(x); + unsigned int next = mal_next_power_of_2(x); + if ((next - x) > (x - prev)) { + return prev; + } else { + return next; + } +} + + + +// Clamps an f32 sample to -1..1 +static inline float mal_clip_f32(float x) +{ + if (x < -1) return -1; + if (x > +1) return +1; + return x; +} + +static inline float mal_mix_f32(float x, float y, float a) +{ + return x*(1-a) + y*a; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Atomics +// +/////////////////////////////////////////////////////////////////////////////// +#if defined(_WIN32) && defined(_MSC_VER) +#define mal_memory_barrier() MemoryBarrier() +#define mal_atomic_exchange_32(a, b) InterlockedExchange((LONG*)a, (LONG)b) +#define mal_atomic_exchange_64(a, b) InterlockedExchange64((LONGLONG*)a, (LONGLONG)b) +#define mal_atomic_increment_32(a) InterlockedIncrement((LONG*)a) +#define mal_atomic_decrement_32(a) InterlockedDecrement((LONG*)a) +#else +#define mal_memory_barrier() __sync_synchronize() +#define mal_atomic_exchange_32(a, b) (void)__sync_lock_test_and_set(a, b); __sync_synchronize() +#define mal_atomic_exchange_64(a, b) (void)__sync_lock_test_and_set(a, b); __sync_synchronize() +#define mal_atomic_increment_32(a) __sync_add_and_fetch(a, 1) +#define mal_atomic_decrement_32(a) __sync_sub_and_fetch(a, 1) +#endif + +#ifdef MAL_64BIT +#define mal_atomic_exchange_ptr mal_atomic_exchange_64 +#endif +#ifdef MAL_32BIT +#define mal_atomic_exchange_ptr mal_atomic_exchange_32 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// +// Timing +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_WIN32 +static LARGE_INTEGER g_mal_TimerFrequency = {{0}}; +void mal_timer_init(mal_timer* pTimer) +{ + if (g_mal_TimerFrequency.QuadPart == 0) { + QueryPerformanceFrequency(&g_mal_TimerFrequency); + } + + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + pTimer->counter = (mal_uint64)counter.QuadPart; +} + +double mal_timer_get_time_in_seconds(mal_timer* pTimer) +{ + LARGE_INTEGER counter; + if (!QueryPerformanceCounter(&counter)) { + return 0; + } + + return (counter.QuadPart - pTimer->counter) / (double)g_mal_TimerFrequency.QuadPart; +} +#else +void mal_timer_init(mal_timer* pTimer) +{ + struct timespec newTime; + clock_gettime(CLOCK_MONOTONIC, &newTime); + + pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; +} + +double mal_timer_get_time_in_seconds(mal_timer* pTimer) +{ + struct timespec newTime; + clock_gettime(CLOCK_MONOTONIC, &newTime); + + uint64_t newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; + uint64_t oldTimeCounter = pTimer->counter; + + return (newTimeCounter - oldTimeCounter) / 1000000000.0; +} +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// +// Dynamic Linking +// +/////////////////////////////////////////////////////////////////////////////// +mal_handle mal_dlopen(const char* filename) +{ +#ifdef _WIN32 +#ifdef MAL_WIN32_DESKTOP + return (mal_handle)LoadLibraryA(filename); +#else + // *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + return NULL; + } + + return (mal_handle)LoadPackagedLibrary(filenameW, 0); +#endif +#else + return (mal_handle)dlopen(filename, RTLD_NOW); +#endif +} + +void mal_dlclose(mal_handle handle) +{ +#ifdef _WIN32 + FreeLibrary((HMODULE)handle); +#else + dlclose((void*)handle); +#endif +} + +mal_proc mal_dlsym(mal_handle handle, const char* symbol) +{ +#ifdef _WIN32 + return (mal_proc)GetProcAddress((HMODULE)handle, symbol); +#else + return (mal_proc)dlsym((void*)handle, symbol); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Threading +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_WIN32 +mal_result mal_thread_create__win32(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) +{ + (void)pContext; + + pThread->win32.hThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL); + if (pThread->win32.hThread == NULL) { + return MAL_FAILED_TO_CREATE_THREAD; + } + + return MAL_SUCCESS; +} + +void mal_thread_wait__win32(mal_thread* pThread) +{ + WaitForSingleObject(pThread->win32.hThread, INFINITE); +} + +void mal_sleep__win32(mal_uint32 milliseconds) +{ + Sleep((DWORD)milliseconds); +} + + +mal_result mal_mutex_init__win32(mal_context* pContext, mal_mutex* pMutex) +{ + (void)pContext; + + pMutex->win32.hMutex = CreateEventA(NULL, FALSE, TRUE, NULL); + if (pMutex->win32.hMutex == NULL) { + return MAL_FAILED_TO_CREATE_MUTEX; + } + + return MAL_SUCCESS; +} + +void mal_mutex_uninit__win32(mal_mutex* pMutex) +{ + CloseHandle(pMutex->win32.hMutex); +} + +void mal_mutex_lock__win32(mal_mutex* pMutex) +{ + WaitForSingleObject(pMutex->win32.hMutex, INFINITE); +} + +void mal_mutex_unlock__win32(mal_mutex* pMutex) +{ + SetEvent(pMutex->win32.hMutex); +} + + +mal_result mal_event_init__win32(mal_context* pContext, mal_event* pEvent) +{ + (void)pContext; + + pEvent->win32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + if (pEvent->win32.hEvent == NULL) { + return MAL_FAILED_TO_CREATE_EVENT; + } + + return MAL_SUCCESS; +} + +void mal_event_uninit__win32(mal_event* pEvent) +{ + CloseHandle(pEvent->win32.hEvent); +} + +mal_bool32 mal_event_wait__win32(mal_event* pEvent) +{ + return WaitForSingleObject(pEvent->win32.hEvent, INFINITE) == WAIT_OBJECT_0; +} + +mal_bool32 mal_event_signal__win32(mal_event* pEvent) +{ + return SetEvent(pEvent->win32.hEvent); +} +#endif + + +#ifdef MAL_POSIX +typedef int (* mal_pthread_create_proc)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); +typedef int (* mal_pthread_join_proc)(pthread_t thread, void **retval); +typedef int (* mal_pthread_mutex_init_proc)(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr); +typedef int (* mal_pthread_mutex_destroy_proc)(pthread_mutex_t *__mutex); +typedef int (* mal_pthread_mutex_lock_proc)(pthread_mutex_t *__mutex); +typedef int (* mal_pthread_mutex_unlock_proc)(pthread_mutex_t *__mutex); +typedef int (* mal_pthread_cond_init_proc)(pthread_cond_t *__restrict __cond, const pthread_condattr_t *__restrict __cond_attr); +typedef int (* mal_pthread_cond_destroy_proc)(pthread_cond_t *__cond); +typedef int (* mal_pthread_cond_signal_proc)(pthread_cond_t *__cond); +typedef int (* mal_pthread_cond_wait_proc)(pthread_cond_t *__restrict __cond, pthread_mutex_t *__restrict __mutex); + +mal_bool32 mal_thread_create__posix(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) +{ + int result = ((mal_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, NULL, entryProc, pData); + if (result != 0) { + return MAL_FAILED_TO_CREATE_THREAD; + } + + return MAL_SUCCESS; +} + +void mal_thread_wait__posix(mal_thread* pThread) +{ + ((mal_pthread_join_proc)pThread->pContext->posix.pthread_join)(pThread->posix.thread, NULL); +} + +void mal_sleep__posix(mal_uint32 milliseconds) +{ + usleep(milliseconds * 1000); // <-- usleep is in microseconds. +} + + +mal_result mal_mutex_init__posix(mal_context* pContext, mal_mutex* pMutex) +{ + int result = ((mal_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pMutex->posix.mutex, NULL); + if (result != 0) { + return MAL_FAILED_TO_CREATE_MUTEX; + } + + return MAL_SUCCESS; +} + +void mal_mutex_uninit__posix(mal_mutex* pMutex) +{ + ((mal_pthread_mutex_destroy_proc)pMutex->pContext->posix.pthread_mutex_destroy)(&pMutex->posix.mutex); +} + +void mal_mutex_lock__posix(mal_mutex* pMutex) +{ + ((mal_pthread_mutex_lock_proc)pMutex->pContext->posix.pthread_mutex_lock)(&pMutex->posix.mutex); +} + +void mal_mutex_unlock__posix(mal_mutex* pMutex) +{ + ((mal_pthread_mutex_unlock_proc)pMutex->pContext->posix.pthread_mutex_unlock)(&pMutex->posix.mutex); +} + + +mal_result mal_event_init__posix(mal_context* pContext, mal_event* pEvent) +{ + if (((mal_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pEvent->posix.mutex, NULL) != 0) { + return MAL_FAILED_TO_CREATE_MUTEX; + } + + if (((mal_pthread_cond_init_proc)pContext->posix.pthread_cond_init)(&pEvent->posix.condition, NULL) != 0) { + return MAL_FAILED_TO_CREATE_EVENT; + } + + pEvent->posix.value = 0; + return MAL_SUCCESS; +} + +void mal_event_uninit__posix(mal_event* pEvent) +{ + ((mal_pthread_cond_destroy_proc)pEvent->pContext->posix.pthread_cond_destroy)(&pEvent->posix.condition); + ((mal_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex); +} + +mal_bool32 mal_event_wait__posix(mal_event* pEvent) +{ + ((mal_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex); + { + while (pEvent->posix.value == 0) { + ((mal_pthread_cond_wait_proc)pEvent->pContext->posix.pthread_cond_wait)(&pEvent->posix.condition, &pEvent->posix.mutex); + } + + pEvent->posix.value = 0; // Auto-reset. + } + ((mal_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex); + + return MAL_TRUE; +} + +mal_bool32 mal_event_signal__posix(mal_event* pEvent) +{ + ((mal_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex); + { + pEvent->posix.value = 1; + ((mal_pthread_cond_signal_proc)pEvent->pContext->posix.pthread_cond_signal)(&pEvent->posix.condition); + } + ((mal_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex); + + return MAL_TRUE; +} +#endif + +mal_result mal_thread_create(mal_context* pContext, mal_thread* pThread, mal_thread_entry_proc entryProc, void* pData) +{ + if (pContext == NULL || pThread == NULL || entryProc == NULL) return MAL_FALSE; + + pThread->pContext = pContext; + +#ifdef MAL_WIN32 + return mal_thread_create__win32(pContext, pThread, entryProc, pData); +#endif +#ifdef MAL_POSIX + return mal_thread_create__posix(pContext, pThread, entryProc, pData); +#endif +} + +void mal_thread_wait(mal_thread* pThread) +{ + if (pThread == NULL) return; + +#ifdef MAL_WIN32 + mal_thread_wait__win32(pThread); +#endif +#ifdef MAL_POSIX + mal_thread_wait__posix(pThread); +#endif +} + +void mal_sleep(mal_uint32 milliseconds) +{ +#ifdef MAL_WIN32 + mal_sleep__win32(milliseconds); +#endif +#ifdef MAL_POSIX + mal_sleep__posix(milliseconds); +#endif +} + + +mal_result mal_mutex_init(mal_context* pContext, mal_mutex* pMutex) +{ + if (pContext == NULL || pMutex == NULL) return MAL_INVALID_ARGS; + + pMutex->pContext = pContext; + +#ifdef MAL_WIN32 + return mal_mutex_init__win32(pContext, pMutex); +#endif +#ifdef MAL_POSIX + return mal_mutex_init__posix(pContext, pMutex); +#endif +} + +void mal_mutex_uninit(mal_mutex* pMutex) +{ + if (pMutex == NULL || pMutex->pContext == NULL) return; + +#ifdef MAL_WIN32 + mal_mutex_uninit__win32(pMutex); +#endif +#ifdef MAL_POSIX + mal_mutex_uninit__posix(pMutex); +#endif +} + +void mal_mutex_lock(mal_mutex* pMutex) +{ + if (pMutex == NULL || pMutex->pContext == NULL) return; + +#ifdef MAL_WIN32 + mal_mutex_lock__win32(pMutex); +#endif +#ifdef MAL_POSIX + mal_mutex_lock__posix(pMutex); +#endif +} + +void mal_mutex_unlock(mal_mutex* pMutex) +{ + if (pMutex == NULL || pMutex->pContext == NULL) return; + +#ifdef MAL_WIN32 + mal_mutex_unlock__win32(pMutex); +#endif +#ifdef MAL_POSIX + mal_mutex_unlock__posix(pMutex); +#endif +} + + +mal_result mal_event_init(mal_context* pContext, mal_event* pEvent) +{ + if (pContext == NULL || pEvent == NULL) return MAL_FALSE; + + pEvent->pContext = pContext; + +#ifdef MAL_WIN32 + return mal_event_init__win32(pContext, pEvent); +#endif +#ifdef MAL_POSIX + return mal_event_init__posix(pContext, pEvent); +#endif +} + +void mal_event_uninit(mal_event* pEvent) +{ + if (pEvent == NULL || pEvent->pContext == NULL) return; + +#ifdef MAL_WIN32 + mal_event_uninit__win32(pEvent); +#endif +#ifdef MAL_POSIX + mal_event_uninit__posix(pEvent); +#endif +} + +mal_bool32 mal_event_wait(mal_event* pEvent) +{ + if (pEvent == NULL || pEvent->pContext == NULL) return MAL_FALSE; + +#ifdef MAL_WIN32 + return mal_event_wait__win32(pEvent); +#endif +#ifdef MAL_POSIX + return mal_event_wait__posix(pEvent); +#endif +} + +mal_bool32 mal_event_signal(mal_event* pEvent) +{ + if (pEvent == NULL || pEvent->pContext == NULL) return MAL_FALSE; + +#ifdef MAL_WIN32 + return mal_event_signal__win32(pEvent); +#endif +#ifdef MAL_POSIX + return mal_event_signal__posix(pEvent); +#endif +} + + +// Posts a log message. +static void mal_log(mal_context* pContext, mal_device* pDevice, const char* message) +{ + if (pContext == NULL) return; + + mal_log_proc onLog = pContext->config.onLog; + if (onLog) { + onLog(pContext, pDevice, message); + } +} + +// Posts an error. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". +static mal_result mal_context_post_error(mal_context* pContext, mal_device* pDevice, const char* message, mal_result resultCode) +{ + // Derive the context from the device if necessary. + if (pContext == NULL) { + if (pDevice != NULL) { + pContext = pDevice->pContext; + } + } + + mal_log(pContext, pDevice, message); + return resultCode; +} + +static mal_result mal_post_error(mal_device* pDevice, const char* message, mal_result resultCode) +{ + return mal_context_post_error(NULL, pDevice, message, resultCode); +} + + +#if !defined(MAL_ANDROID) +static void mal_get_default_channel_mapping(mal_backend backend, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + if (channels == 1) { // Mono + channelMap[0] = MAL_CHANNEL_FRONT_CENTER; + } else if (channels == 2) { // Stereo + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + } else if (channels == 3) { // 2.1 + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_LFE; + } else if (channels == 4) { // 4.0 + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_SIDE_LEFT; + channelMap[3] = MAL_CHANNEL_SIDE_RIGHT; + } else if (channels == 5) { // Not sure about this one. 4.1? + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_SIDE_LEFT; + channelMap[3] = MAL_CHANNEL_SIDE_RIGHT; + channelMap[4] = MAL_CHANNEL_LFE; + } else if (channels >= 6) { // 5.1 + // Some backends use different default layouts. + if (backend == mal_backend_wasapi || backend == mal_backend_dsound || backend == mal_backend_winmm || backend == mal_backend_oss) { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + channelMap[3] = MAL_CHANNEL_LFE; + channelMap[4] = MAL_CHANNEL_SIDE_LEFT; + channelMap[5] = MAL_CHANNEL_SIDE_RIGHT; + } else { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + channelMap[2] = MAL_CHANNEL_SIDE_LEFT; + channelMap[3] = MAL_CHANNEL_SIDE_RIGHT; + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + channelMap[5] = MAL_CHANNEL_LFE; + } + + if (channels == 7) { // Not sure about this one. + channelMap[6] = MAL_CHANNEL_BACK_CENTER; + } else { + // I don't know what mapping to use in this case, but I'm making it upwards compatible with 7.1. Good luck! + mal_assert(channels >= 8); + channelMap[6] = MAL_CHANNEL_BACK_LEFT; + channelMap[7] = MAL_CHANNEL_BACK_RIGHT; + + // Beyond 7.1 I'm just guessing... + if (channels == 9) { + channelMap[8] = MAL_CHANNEL_BACK_CENTER; + } else if (channels == 10) { + channelMap[8] = MAL_CHANNEL_FRONT_LEFT_CENTER; + channelMap[9] = MAL_CHANNEL_FRONT_RIGHT_CENTER; + } else if (channels == 11) { + channelMap[ 8] = MAL_CHANNEL_FRONT_LEFT_CENTER; + channelMap[ 9] = MAL_CHANNEL_FRONT_RIGHT_CENTER; + channelMap[10] = MAL_CHANNEL_BACK_CENTER; + } else { + mal_assert(channels >= 12); + for (mal_uint8 iChannel = 11; iChannel < channels && iChannel < MAL_MAX_CHANNELS; ++iChannel) { + channelMap[iChannel] = iChannel + 1; + } + } + } + } +} +#endif + + +// The callback for reading from the client -> DSP -> device. +static inline mal_uint32 mal_device__on_read_from_client(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + (void)pDSP; + + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_send_proc onSend = pDevice->onSend; + if (onSend) { + return onSend(pDevice, frameCount, pFramesOut); + } + + return 0; +} + +// The callback for reading from the device -> DSP -> client. +static inline mal_uint32 mal_device__on_read_from_device(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + (void)pDSP; + + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + if (pDevice->_dspFrameCount == 0) { + return 0; // Nothing left. + } + + mal_uint32 framesToRead = frameCount; + if (framesToRead > pDevice->_dspFrameCount) { + framesToRead = pDevice->_dspFrameCount; + } + + mal_uint32 bytesToRead = framesToRead * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + mal_copy_memory(pFramesOut, pDevice->_dspFrames, bytesToRead); + pDevice->_dspFrameCount -= framesToRead; + pDevice->_dspFrames += bytesToRead; + + return framesToRead; +} + +// A helper function for reading sample data from the client. Returns the number of samples read from the client. Remaining samples +// are filled with silence. +static inline mal_uint32 mal_device__read_frames_from_client(mal_device* pDevice, mal_uint32 frameCount, void* pSamples) +{ + mal_assert(pDevice != NULL); + mal_assert(frameCount > 0); + mal_assert(pSamples != NULL); + + mal_uint32 framesRead = mal_dsp_read_frames(&pDevice->dsp, frameCount, pSamples); + mal_uint32 samplesRead = framesRead * pDevice->internalChannels; + mal_uint32 sampleSize = mal_get_sample_size_in_bytes(pDevice->internalFormat); + mal_uint32 consumedBytes = samplesRead*sampleSize; + mal_uint32 remainingBytes = ((frameCount * pDevice->internalChannels) - samplesRead)*sampleSize; + mal_zero_memory((mal_uint8*)pSamples + consumedBytes, remainingBytes); + + return samplesRead; +} + +// A helper for sending sample data to the client. +static inline void mal_device__send_frames_to_client(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples) +{ + mal_assert(pDevice != NULL); + mal_assert(frameCount > 0); + mal_assert(pSamples != NULL); + + mal_recv_proc onRecv = pDevice->onRecv; + if (onRecv) { + pDevice->_dspFrameCount = frameCount; + pDevice->_dspFrames = (const mal_uint8*)pSamples; + + mal_uint8 chunkBuffer[4096]; + mal_uint32 chunkFrameCount = sizeof(chunkBuffer) / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + + for (;;) { + mal_uint32 framesJustRead = mal_dsp_read_frames(&pDevice->dsp, chunkFrameCount, chunkBuffer); + if (framesJustRead == 0) { + break; + } + + onRecv(pDevice, framesJustRead, chunkBuffer); + + if (framesJustRead < chunkFrameCount) { + break; + } + } + } +} + +// A helper for changing the state of the device. +static inline void mal_device__set_state(mal_device* pDevice, mal_uint32 newState) +{ + mal_atomic_exchange_32(&pDevice->state, newState); +} + +// A helper for getting the state of the device. +static inline mal_uint32 mal_device__get_state(mal_device* pDevice) +{ + return pDevice->state; +} + + +#ifdef MAL_WIN32 + #if defined(MAL_HAS_WASAPI) || defined(MAL_HAS_DSOUND) + static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + //static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + //static GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + #endif +#endif + + +// Generic function for retrieving the name of a device by it's ID. +// +// This function simply enumerates every device and then retrieves the name of the first device that has the same ID. +static mal_result mal_context__try_get_device_name_by_id(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, char* pName, size_t nameBufferSize) +{ + mal_assert(pContext != NULL); + mal_assert(pName != NULL); + + if (pDeviceID == NULL) { + return MAL_NO_DEVICE; + } + + mal_uint32 deviceCount; + mal_result result = mal_enumerate_devices(pContext, type, &deviceCount, NULL); + if (result != MAL_SUCCESS) { + return result; + } + + mal_device_info* pInfos = (mal_device_info*)mal_malloc(sizeof(*pInfos) * deviceCount); + if (pInfos == NULL) { + return MAL_OUT_OF_MEMORY; + } + + result = mal_enumerate_devices(pContext, type, &deviceCount, pInfos); + if (result != MAL_SUCCESS) { + mal_free(pInfos); + return result; + } + + mal_bool32 found = MAL_FALSE; + for (mal_uint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + // Prefer backend specific comparisons for efficiency and accuracy, but fall back to a generic method if a backend-specific comparison + // is not implemented. + switch (pContext->backend) + { + #ifdef MAL_HAS_WASAPI + case mal_backend_wasapi: + { + if (memcmp(pDeviceID->wasapi, &pInfos[iDevice].id.wasapi, sizeof(pDeviceID->wasapi)) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_DSOUND + case mal_backend_dsound: + { + if (memcmp(pDeviceID->dsound, &pInfos[iDevice].id.dsound, sizeof(pDeviceID->dsound)) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_WINMM + case mal_backend_winmm: + { + if (pInfos[iDevice].id.winmm == pDeviceID->winmm) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_ALSA + case mal_backend_alsa: + { + if (mal_strcmp(pInfos[iDevice].id.alsa, pDeviceID->alsa) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_COREAUDIO + case mal_backend_coreaudio + { + // TODO: Implement me. + } break; + #endif + #ifdef MAL_HAS_OSS + case mal_backend_oss: + { + if (mal_strcmp(pInfos[iDevice].id.oss, pDeviceID->oss) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_OPENSL + case mal_backend_opensl: + { + if (pInfos[iDevice].id.opensl == pDeviceID->opensl) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_OPENAL + case mal_backend_openal: + { + if (mal_strcmp(pInfos[iDevice].id.openal, pDeviceID->openal) == 0) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_SDL + case mal_backend_sdl: + { + if (pInfos[iDevice].id.sdl == pDeviceID->sdl) { + found = MAL_TRUE; + } + } break; + #endif + #ifdef MAL_HAS_NULL + case mal_backend_null: + { + if (pInfos[iDevice].id.nullbackend == pDeviceID->nullbackend) { + found = MAL_TRUE; + } + } break; + #endif + + // Fall back to a generic memory comparison. + default: + { + if (memcmp(pDeviceID, &pInfos[iDevice].id, sizeof(*pDeviceID)) == 0) { + found = MAL_TRUE; + } + } break; + } + + if (found) { + mal_strncpy_s(pName, nameBufferSize, pInfos[iDevice].name, (size_t)-1); + result = MAL_SUCCESS; + break; + } + } + + mal_free(pInfos); + return result; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Null Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_NULL +mal_result mal_context_init__null(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + // The null backend always works. + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__null(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_null); + + (void)pContext; + return MAL_SUCCESS; +} + +static mal_result mal_enumerate_devices__null(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + (void)pContext; + + mal_uint32 infoSize = *pCount; + *pCount = 1; // There's only one "device" each for playback and recording for the null backend. + + if (pInfo != NULL && infoSize > 0) { + mal_zero_object(pInfo); + + if (type == mal_device_type_playback) { + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "NULL Playback Device", (size_t)-1); + } else { + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "NULL Capture Device", (size_t)-1); + } + } + + return MAL_SUCCESS; +} + +static void mal_device_uninit__null(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + mal_free(pDevice->null_device.pBuffer); +} + +static mal_result mal_device_init__null(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + (void)type; + (void)pDeviceID; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->null_device); + + pDevice->bufferSizeInFrames = pConfig->bufferSizeInFrames; + pDevice->periods = pConfig->periods; + + pDevice->null_device.pBuffer = (mal_uint8*)mal_malloc(pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format)); + if (pDevice->null_device.pBuffer == NULL) { + return MAL_OUT_OF_MEMORY; + } + + mal_zero_memory(pDevice->null_device.pBuffer, mal_device_get_buffer_size_in_bytes(pDevice)); + + return MAL_SUCCESS; +} + +static mal_result mal_device__start_backend__null(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_timer_init(&pDevice->null_device.timer); + pDevice->null_device.lastProcessedFrame = 0; + + return MAL_SUCCESS; +} + +static mal_result mal_device__stop_backend__null(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + (void)pDevice; + + return MAL_SUCCESS; +} + +static mal_result mal_device__break_main_loop__null(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->null_device.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +static mal_bool32 mal_device__get_current_frame__null(mal_device* pDevice, mal_uint32* pCurrentPos) +{ + mal_assert(pDevice != NULL); + mal_assert(pCurrentPos != NULL); + *pCurrentPos = 0; + + mal_uint64 currentFrameAbs = (mal_uint64)(mal_timer_get_time_in_seconds(&pDevice->null_device.timer) * pDevice->sampleRate) / pDevice->channels; + + *pCurrentPos = (mal_uint32)(currentFrameAbs % pDevice->bufferSizeInFrames); + return MAL_TRUE; +} + +static mal_uint32 mal_device__get_available_frames__null(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_uint32 currentFrame; + if (!mal_device__get_current_frame__null(pDevice, ¤tFrame)) { + return 0; + } + + // In a playback device the last processed frame should always be ahead of the current frame. The space between + // the last processed and current frame (moving forward, starting from the last processed frame) is the amount + // of space available to write. + // + // For a recording device it's the other way around - the last processed frame is always _behind_ the current + // frame and the space between is the available space. + mal_uint32 totalFrameCount = pDevice->bufferSizeInFrames; + if (pDevice->type == mal_device_type_playback) { + mal_uint32 committedBeg = currentFrame; + mal_uint32 committedEnd = pDevice->null_device.lastProcessedFrame; + if (committedEnd <= committedBeg) { + committedEnd += totalFrameCount; // Wrap around. + } + + mal_uint32 committedSize = (committedEnd - committedBeg); + mal_assert(committedSize <= totalFrameCount); + + return totalFrameCount - committedSize; + } else { + mal_uint32 validBeg = pDevice->null_device.lastProcessedFrame; + mal_uint32 validEnd = currentFrame; + if (validEnd < validBeg) { + validEnd += totalFrameCount; // Wrap around. + } + + mal_uint32 validSize = (validEnd - validBeg); + mal_assert(validSize <= totalFrameCount); + + return validSize; + } +} + +static mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + while (!pDevice->null_device.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__get_available_frames__null(pDevice); + if (framesAvailable > 0) { + return framesAvailable; + } + + mal_sleep(16); + } + + // We'll get here if the loop was terminated. Just return whatever's available. + return mal_device__get_available_frames__null(pDevice); +} + +static mal_result mal_device__main_loop__null(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->null_device.breakFromMainLoop = MAL_FALSE; + while (!pDevice->null_device.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__wait_for_frames__null(pDevice); + if (framesAvailable == 0) { + continue; + } + + // If it's a playback device, don't bother grabbing more data if the device is being stopped. + if (pDevice->null_device.breakFromMainLoop && pDevice->type == mal_device_type_playback) { + return MAL_FALSE; + } + + if (framesAvailable + pDevice->null_device.lastProcessedFrame > pDevice->bufferSizeInFrames) { + framesAvailable = pDevice->bufferSizeInFrames - pDevice->null_device.lastProcessedFrame; + } + + mal_uint32 sampleCount = framesAvailable * pDevice->channels; + mal_uint32 lockOffset = pDevice->null_device.lastProcessedFrame * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); + mal_uint32 lockSize = sampleCount * mal_get_sample_size_in_bytes(pDevice->format); + + if (pDevice->type == mal_device_type_playback) { + if (pDevice->null_device.breakFromMainLoop) { + return MAL_FALSE; + } + + mal_device__read_frames_from_client(pDevice, framesAvailable, pDevice->null_device.pBuffer + lockOffset); + } else { + mal_zero_memory(pDevice->null_device.pBuffer + lockOffset, lockSize); + mal_device__send_frames_to_client(pDevice, framesAvailable, pDevice->null_device.pBuffer + lockOffset); + } + + pDevice->null_device.lastProcessedFrame = (pDevice->null_device.lastProcessedFrame + framesAvailable) % pDevice->bufferSizeInFrames; + } + + return MAL_SUCCESS; +} +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// +// WIN32 COMMON +// +/////////////////////////////////////////////////////////////////////////////// +#if defined(MAL_WIN32) +#include "objbase.h" +#if defined(MAL_WIN32_DESKTOP) + #define mal_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MAL_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) + #define mal_CoUninitialize(pContext) ((MAL_PFN_CoUninitialize)pContext->win32.CoUninitialize)() + #define mal_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MAL_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) + #define mal_CoTaskMemFree(pContext, pv) ((MAL_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) + #define mal_PropVariantClear(pContext, pvar) ((MAL_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) +#else + #define mal_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) + #define mal_CoUninitialize(pContext) CoUninitialize() + #define mal_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) + #define mal_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) + #define mal_PropVariantClear(pContext, pvar) PropVariantClear(pvar) +#endif +#endif + +#if defined(MAL_HAS_WASAPI) || defined(MAL_HAS_DSOUND) +#include + +#ifndef SPEAKER_FRONT_LEFT +#define SPEAKER_FRONT_LEFT 0x1 +#define SPEAKER_FRONT_RIGHT 0x2 +#define SPEAKER_FRONT_CENTER 0x4 +#define SPEAKER_LOW_FREQUENCY 0x8 +#define SPEAKER_BACK_LEFT 0x10 +#define SPEAKER_BACK_RIGHT 0x20 +#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 +#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 +#define SPEAKER_BACK_CENTER 0x100 +#define SPEAKER_SIDE_LEFT 0x200 +#define SPEAKER_SIDE_RIGHT 0x400 +#define SPEAKER_TOP_CENTER 0x800 +#define SPEAKER_TOP_FRONT_LEFT 0x1000 +#define SPEAKER_TOP_FRONT_CENTER 0x2000 +#define SPEAKER_TOP_FRONT_RIGHT 0x4000 +#define SPEAKER_TOP_BACK_LEFT 0x8000 +#define SPEAKER_TOP_BACK_CENTER 0x10000 +#define SPEAKER_TOP_BACK_RIGHT 0x20000 +#endif + +// The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We +// define our own implementation in this case. +#if defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_) +typedef struct +{ + WAVEFORMATEX Format; + union + { + WORD wValidBitsPerSample; + WORD wSamplesPerBlock; + WORD wReserved; + } Samples; + DWORD dwChannelMask; + GUID SubFormat; +} WAVEFORMATEXTENSIBLE; +#endif + +#ifndef WAVE_FORMAT_EXTENSIBLE +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#endif + +// Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to mini_al. +static mal_uint8 mal_channel_id_to_mal__win32(DWORD id) +{ + switch (id) + { + case SPEAKER_FRONT_LEFT: return MAL_CHANNEL_FRONT_LEFT; + case SPEAKER_FRONT_RIGHT: return MAL_CHANNEL_FRONT_RIGHT; + case SPEAKER_FRONT_CENTER: return MAL_CHANNEL_FRONT_CENTER; + case SPEAKER_LOW_FREQUENCY: return MAL_CHANNEL_LFE; + case SPEAKER_BACK_LEFT: return MAL_CHANNEL_BACK_LEFT; + case SPEAKER_BACK_RIGHT: return MAL_CHANNEL_BACK_RIGHT; + case SPEAKER_FRONT_LEFT_OF_CENTER: return MAL_CHANNEL_FRONT_LEFT_CENTER; + case SPEAKER_FRONT_RIGHT_OF_CENTER: return MAL_CHANNEL_FRONT_RIGHT_CENTER; + case SPEAKER_BACK_CENTER: return MAL_CHANNEL_BACK_CENTER; + case SPEAKER_SIDE_LEFT: return MAL_CHANNEL_SIDE_LEFT; + case SPEAKER_SIDE_RIGHT: return MAL_CHANNEL_SIDE_RIGHT; + case SPEAKER_TOP_CENTER: return MAL_CHANNEL_TOP_CENTER; + case SPEAKER_TOP_FRONT_LEFT: return MAL_CHANNEL_TOP_FRONT_LEFT; + case SPEAKER_TOP_FRONT_CENTER: return MAL_CHANNEL_TOP_FRONT_CENTER; + case SPEAKER_TOP_FRONT_RIGHT: return MAL_CHANNEL_TOP_FRONT_RIGHT; + case SPEAKER_TOP_BACK_LEFT: return MAL_CHANNEL_TOP_BACK_LEFT; + case SPEAKER_TOP_BACK_CENTER: return MAL_CHANNEL_TOP_BACK_CENTER; + case SPEAKER_TOP_BACK_RIGHT: return MAL_CHANNEL_TOP_BACK_RIGHT; + default: return 0; + } +} + +// Converts an individual mini_al channel identifier (MAL_CHANNEL_FRONT_LEFT, etc.) to Win32-style. +static DWORD mal_channel_id_to_win32(DWORD id) +{ + switch (id) + { + case MAL_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; + case MAL_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; + case MAL_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; + case MAL_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; + case MAL_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; + case MAL_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; + case MAL_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; + case MAL_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; + case MAL_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; + case MAL_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; + case MAL_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; + case MAL_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; + case MAL_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; + case MAL_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; + case MAL_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; + case MAL_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; + case MAL_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; + case MAL_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; + default: return 0; + } +} + +// Converts a channel mapping to a Win32-style channel mask. +static DWORD mal_channel_map_to_channel_mask__win32(const mal_uint8 channelMap[MAL_MAX_CHANNELS], mal_uint32 channels) +{ + DWORD dwChannelMask = 0; + for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { + dwChannelMask |= mal_channel_id_to_win32(channelMap[iChannel]); + } + + return dwChannelMask; +} + +// Converts a Win32-style channel mask to a mini_al channel map. +static void mal_channel_mask_to_channel_map__win32(DWORD dwChannelMask, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +{ + if (channels == 1 && dwChannelMask == 0) { + channelMap[0] = MAL_CHANNEL_FRONT_CENTER; + } else if (channels == 2 && dwChannelMask == 0) { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + } else { + // Just iterate over each bit. + mal_uint32 iChannel = 0; + for (mal_uint32 iBit = 0; iBit < 32; ++iBit) { + DWORD bitValue = (dwChannelMask & (1 << iBit)); + if (bitValue != 0) { + // The bit is set. + channelMap[iChannel] = mal_channel_id_to_mal__win32(bitValue); + iChannel += 1; + } + } + } +} +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// +// WASAPI Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_WASAPI +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4091) // 'typedef ': ignored on left of '' when no variable is declared +#endif +#include +#include +#include +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +const PROPERTYKEY g_malPKEY_Device_FriendlyName = {{0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0}}, 14}; +const PROPERTYKEY g_malPKEY_AudioEngine_DeviceFormat = {{0xf19f064d, 0x82c, 0x4e27, {0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c}}, 0}; + +const IID g_malCLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; // BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) +const IID g_malIID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; // A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) +const IID g_malIID_IAudioClient_Instance = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; // 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) +const IID g_malIID_IAudioRenderClient_Instance = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; // F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) +const IID g_malIID_IAudioCaptureClient_Instance = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; // C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) + +#ifndef MAL_WIN32_DESKTOP +const IID g_malIID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; // E6327CAD-DCEC-4949-AE8A-991E976A79D2 +const IID g_malIID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; // 2EEF81BE-33FA-4800-9670-1CD474972C3F +#endif + +#ifdef __cplusplus +#define g_malCLSID_MMDeviceEnumerator g_malCLSID_MMDeviceEnumerator_Instance +#define g_malIID_IMMDeviceEnumerator g_malIID_IMMDeviceEnumerator_Instance +#define g_malIID_IAudioClient g_malIID_IAudioClient_Instance +#define g_malIID_IAudioRenderClient g_malIID_IAudioRenderClient_Instance +#define g_malIID_IAudioCaptureClient g_malIID_IAudioCaptureClient_Instance +#else +#define g_malCLSID_MMDeviceEnumerator &g_malCLSID_MMDeviceEnumerator_Instance +#define g_malIID_IMMDeviceEnumerator &g_malIID_IMMDeviceEnumerator_Instance +#define g_malIID_IAudioClient &g_malIID_IAudioClient_Instance +#define g_malIID_IAudioRenderClient &g_malIID_IAudioRenderClient_Instance +#define g_malIID_IAudioCaptureClient &g_malIID_IAudioCaptureClient_Instance +#endif + +#ifdef __cplusplus +#define mal_is_guid_equal(a, b) IsEqualGUID(a, b) +#else +#define mal_is_guid_equal(a, b) IsEqualGUID(&a, &b) +#endif + +#ifdef MAL_WIN32_DESKTOP + // IMMDeviceEnumerator + #ifdef __cplusplus + #define IMMDeviceEnumerator_Release(p) ((IMMDeviceEnumerator*)p)->Release() + #else + #define IMMDeviceEnumerator_Release(p) ((IMMDeviceEnumerator*)p)->lpVtbl->Release((IMMDeviceEnumerator*)p) + #endif + #ifdef __cplusplus + #define IMMDeviceEnumerator_EnumAudioEndpoints(p, a, b, c) ((IMMDeviceEnumerator*)p)->EnumAudioEndpoints(a, b, c) + #else + #define IMMDeviceEnumerator_EnumAudioEndpoints(p, a, b, c) ((IMMDeviceEnumerator*)p)->lpVtbl->EnumAudioEndpoints(p, a, b, c) + #endif + #ifdef __cplusplus + #define IMMDeviceEnumerator_GetDefaultAudioEndpoint(p, a, b, c) ((IMMDeviceEnumerator*)p)->GetDefaultAudioEndpoint(a, b, c) + #else + #define IMMDeviceEnumerator_GetDefaultAudioEndpoint(p, a, b, c) ((IMMDeviceEnumerator*)p)->lpVtbl->GetDefaultAudioEndpoint(p, a, b, c) + #endif + #ifdef __cplusplus + #define IMMDeviceEnumerator_GetDevice(p, a, b) ((IMMDeviceEnumerator*)p)->GetDevice(a, b) + #else + #define IMMDeviceEnumerator_GetDevice(p, a, b) ((IMMDeviceEnumerator*)p)->lpVtbl->GetDevice(p, a, b) + #endif + + // IMMDeviceCollection + #ifdef __cplusplus + #define IMMDeviceCollection_Release(p) ((IMMDeviceCollection*)p)->Release() + #else + #define IMMDeviceCollection_Release(p) ((IMMDeviceCollection*)p)->lpVtbl->Release((IMMDeviceCollection*)p) + #endif + #ifdef __cplusplus + #define IMMDeviceCollection_GetCount(p, a) ((IMMDeviceCollection*)p)->GetCount(a) + #else + #define IMMDeviceCollection_GetCount(p, a) ((IMMDeviceCollection*)p)->lpVtbl->GetCount((IMMDeviceCollection*)p, a) + #endif + #ifdef __cplusplus + #define IMMDeviceCollection_Item(p, a, b) ((IMMDeviceCollection*)p)->Item(a, b) + #else + #define IMMDeviceCollection_Item(p, a, b) ((IMMDeviceCollection*)p)->lpVtbl->Item((IMMDeviceCollection*)p, a, b) + #endif + + // IMMDevice + #ifdef __cplusplus + #define IMMDevice_Release(p) ((IMMDevice*)p)->Release() + #else + #define IMMDevice_Release(p) ((IMMDevice*)p)->lpVtbl->Release((IMMDevice*)p) + #endif + #ifdef __cplusplus + #define IMMDevice_GetId(p, a) ((IMMDevice*)p)->GetId(a) + #else + #define IMMDevice_GetId(p, a) ((IMMDevice*)p)->lpVtbl->GetId((IMMDevice*)p, a) + #endif + #ifdef __cplusplus + #define IMMDevice_OpenPropertyStore(p, a, b) ((IMMDevice*)p)->OpenPropertyStore(a, b) + #else + #define IMMDevice_OpenPropertyStore(p, a, b) ((IMMDevice*)p)->lpVtbl->OpenPropertyStore((IMMDevice*)p, a, b) + #endif + #ifdef __cplusplus + #define IMMDevice_Activate(p, a, b, c, d) ((IMMDevice*)p)->Activate(a, b, c, d) + #else + #define IMMDevice_Activate(p, a, b, c, d) ((IMMDevice*)p)->lpVtbl->Activate((IMMDevice*)p, a, b, c, d) + #endif +#else + // IActivateAudioInterfaceAsyncOperation + #ifdef __cplusplus + #define IActivateAudioInterfaceAsyncOperation_Release(p) ((IActivateAudioInterfaceAsyncOperation*)p)->Release() + #else + #define IActivateAudioInterfaceAsyncOperation_Release(p) ((IActivateAudioInterfaceAsyncOperation*)p)->lpVtbl->Release((IActivateAudioInterfaceAsyncOperation*)p) + #endif + #ifdef __cplusplus + #define IActivateAudioInterfaceAsyncOperation_GetActivateResult(p, a, b) ((IActivateAudioInterfaceAsyncOperation*)p)->GetActivateResult(a, b) + #else + #define IActivateAudioInterfaceAsyncOperation_GetActivateResult(p, a, b) ((IActivateAudioInterfaceAsyncOperation*)p)->lpVtbl->GetActivateResult((IActivateAudioInterfaceAsyncOperation*)p, a, b) + #endif +#endif + +// IPropertyStore +#ifdef __cplusplus + #define IPropertyStore_Release(p) ((IPropertyStore*)p)->Release() +#else + #define IPropertyStore_Release(p) ((IPropertyStore*)p)->lpVtbl->Release((IPropertyStore*)p) +#endif +#ifdef __cplusplus + #define IPropertyStore_GetValue(p, a, b) ((IPropertyStore*)p)->GetValue(a, b) +#else + #define IPropertyStore_GetValue(p, a, b) ((IPropertyStore*)p)->lpVtbl->GetValue((IPropertyStore*)p, &a, b) +#endif + +// IAudioClient +#ifdef __cplusplus + #define IAudioClient_Release(p) ((IAudioClient*)p)->Release() +#else + #define IAudioClient_Release(p) ((IAudioClient*)p)->lpVtbl->Release((IAudioClient*)p) +#endif +#ifdef __cplusplus + #define IAudioClient_IsFormatSupported(p, a, b, c) ((IAudioClient*)p)->IsFormatSupported(a, b, c) +#else + #define IAudioClient_IsFormatSupported(p, a, b, c) ((IAudioClient*)p)->lpVtbl->IsFormatSupported((IAudioClient*)p, a, b, c) +#endif +#ifdef __cplusplus + #define IAudioClient_GetMixFormat(p, a) ((IAudioClient*)p)->GetMixFormat(a) +#else + #define IAudioClient_GetMixFormat(p, a) ((IAudioClient*)p)->lpVtbl->GetMixFormat((IAudioClient*)p, a) +#endif +#ifdef __cplusplus + #define IAudioClient_Initialize(p, a, b, c, d, e, f) ((IAudioClient*)p)->Initialize(a, b, c, d, e, f) +#else + #define IAudioClient_Initialize(p, a, b, c, d, e, f) ((IAudioClient*)p)->lpVtbl->Initialize((IAudioClient*)p, a, b, c, d, e, f) +#endif +#ifdef __cplusplus + #define IAudioClient_GetBufferSize(p, a) ((IAudioClient*)p)->GetBufferSize(a) +#else + #define IAudioClient_GetBufferSize(p, a) ((IAudioClient*)p)->lpVtbl->GetBufferSize((IAudioClient*)p, a) +#endif +#ifdef __cplusplus + #define IAudioClient_GetService(p, a, b) ((IAudioClient*)p)->GetService(a, b) +#else + #define IAudioClient_GetService(p, a, b) ((IAudioClient*)p)->lpVtbl->GetService((IAudioClient*)p, a, b) +#endif +#ifdef __cplusplus + #define IAudioClient_Start(p) ((IAudioClient*)p)->Start() +#else + #define IAudioClient_Start(p) ((IAudioClient*)p)->lpVtbl->Start((IAudioClient*)p) +#endif +#ifdef __cplusplus + #define IAudioClient_Stop(p) ((IAudioClient*)p)->Stop() +#else + #define IAudioClient_Stop(p) ((IAudioClient*)p)->lpVtbl->Stop((IAudioClient*)p) +#endif +#ifdef __cplusplus + #define IAudioClient_GetCurrentPadding(p, a) ((IAudioClient*)p)->GetCurrentPadding(a) +#else + #define IAudioClient_GetCurrentPadding(p, a) ((IAudioClient*)p)->lpVtbl->GetCurrentPadding((IAudioClient*)p, a) +#endif +#ifdef __cplusplus + #define IAudioClient_SetEventHandle(p, a) ((IAudioClient*)p)->SetEventHandle(a) +#else + #define IAudioClient_SetEventHandle(p, a) ((IAudioClient*)p)->lpVtbl->SetEventHandle((IAudioClient*)p, a) +#endif + +// IAudioRenderClient +#ifdef __cplusplus + #define IAudioRenderClient_Release(p) ((IAudioRenderClient*)p)->Release() +#else + #define IAudioRenderClient_Release(p) ((IAudioRenderClient*)p)->lpVtbl->Release((IAudioRenderClient*)p) +#endif +#ifdef __cplusplus + #define IAudioRenderClient_GetBuffer(p, a, b) ((IAudioRenderClient*)p)->GetBuffer(a, b) +#else + #define IAudioRenderClient_GetBuffer(p, a, b) ((IAudioRenderClient*)p)->lpVtbl->GetBuffer((IAudioRenderClient*)p, a, b) +#endif +#ifdef __cplusplus + #define IAudioRenderClient_ReleaseBuffer(p, a, b) ((IAudioRenderClient*)p)->ReleaseBuffer(a, b) +#else + #define IAudioRenderClient_ReleaseBuffer(p, a, b) ((IAudioRenderClient*)p)->lpVtbl->ReleaseBuffer((IAudioRenderClient*)p, a, b) +#endif + +// IAudioCaptureClient +#ifdef __cplusplus + #define IAudioCaptureClient_Release(p) ((IAudioCaptureClient*)p)->Release() +#else + #define IAudioCaptureClient_Release(p) ((IAudioCaptureClient*)p)->lpVtbl->Release((IAudioCaptureClient*)p) +#endif +#ifdef __cplusplus + #define IAudioCaptureClient_GetNextPacketSize(p, a) ((IAudioCaptureClient*)p)->GetNextPacketSize(a) +#else + #define IAudioCaptureClient_GetNextPacketSize(p, a) ((IAudioCaptureClient*)p)->lpVtbl->GetNextPacketSize((IAudioCaptureClient*)p, a) +#endif +#ifdef __cplusplus + #define IAudioCaptureClient_GetBuffer(p, a, b, c, d, e) ((IAudioCaptureClient*)p)->GetBuffer(a, b, c, d, e) +#else + #define IAudioCaptureClient_GetBuffer(p, a, b, c, d, e) ((IAudioCaptureClient*)p)->lpVtbl->GetBuffer((IAudioCaptureClient*)p, a, b, c, d, e) +#endif +#ifdef __cplusplus + #define IAudioCaptureClient_ReleaseBuffer(p, a) ((IAudioCaptureClient*)p)->ReleaseBuffer(a) +#else + #define IAudioCaptureClient_ReleaseBuffer(p, a) ((IAudioCaptureClient*)p)->lpVtbl->ReleaseBuffer((IAudioCaptureClient*)p, a) +#endif + +mal_result mal_context_init__wasapi(mal_context* pContext) +{ + mal_assert(pContext != NULL); + (void)pContext; + +#ifdef MAL_WIN32_DESKTOP + // WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven + // exclusive mode does not work until SP1. + OSVERSIONINFOEXW osvi; + mal_zero_object(&osvi); + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_VISTA); + osvi.dwMinorVersion = LOBYTE(_WIN32_WINNT_VISTA); + osvi.wServicePackMajor = 1; + if (VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL))) { + return MAL_SUCCESS; + } else { + return MAL_NO_BACKEND; + } +#else + return MAL_SUCCESS; +#endif +} + +mal_result mal_context_uninit__wasapi(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_wasapi); + (void)pContext; + + return MAL_SUCCESS; +} + +static mal_result mal_enumerate_devices__wasapi(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + mal_uint32 infoSize = *pCount; + *pCount = 0; + +#ifdef MAL_WIN32_DESKTOP + IMMDeviceEnumerator* pDeviceEnumerator; + HRESULT hr = mal_CoCreateInstance(pContext, g_malCLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, g_malIID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to create device enumerator.", MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR); + } + + IMMDeviceCollection* pDeviceCollection; + hr = IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, (type == mal_device_type_playback) ? eRender : eCapture, DEVICE_STATE_ACTIVE, &pDeviceCollection); + if (FAILED(hr)) { + IMMDeviceEnumerator_Release(pDeviceEnumerator); + return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to enumerate audio endpoints.", MAL_NO_DEVICE); + } + + IMMDeviceEnumerator_Release(pDeviceEnumerator); + + UINT count; + hr = IMMDeviceCollection_GetCount(pDeviceCollection, &count); + if (FAILED(hr)) { + IMMDeviceCollection_Release(pDeviceCollection); + return mal_context_post_error(pContext, NULL, "[WASAPI] Failed to get device count.", MAL_NO_DEVICE); + } + + for (mal_uint32 iDevice = 0; iDevice < count; ++iDevice) { + if (pInfo != NULL) { + if (infoSize > 0) { + mal_zero_object(pInfo); + + IMMDevice* pDevice; + hr = IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pDevice); + if (SUCCEEDED(hr)) { + // ID. + LPWSTR id; + hr = IMMDevice_GetId(pDevice, &id); + if (SUCCEEDED(hr)) { + size_t idlen = wcslen(id); + if (idlen+sizeof(wchar_t) > sizeof(pInfo->id.wasapi)) { + mal_CoTaskMemFree(pContext, id); + mal_assert(MAL_FALSE); // NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. + continue; + } + + memcpy(pInfo->id.wasapi, id, idlen * sizeof(wchar_t)); + pInfo->id.wasapi[idlen] = '\0'; + + mal_CoTaskMemFree(pContext, id); + } + + // Description / Friendly Name. + IPropertyStore *pProperties; + hr = IMMDevice_OpenPropertyStore(pDevice, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + PROPVARIANT varName; + PropVariantInit(&varName); + hr = IPropertyStore_GetValue(pProperties, g_malPKEY_Device_FriendlyName, &varName); + if (SUCCEEDED(hr)) { + WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); + mal_PropVariantClear(pContext, &varName); + } + + IPropertyStore_Release(pProperties); + } + } + + pInfo += 1; + infoSize -= 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + } + + IMMDeviceCollection_Release(pDeviceCollection); +#else + // The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate + // over devices without using MMDevice, I'm restricting devices to defaults. + if (pInfo != NULL) { + if (infoSize > 0) { + if (type == mal_device_type_playback) { + mal_copy_memory(pInfo->id.wasapi, &g_malIID_DEVINTERFACE_AUDIO_RENDER, sizeof(g_malIID_DEVINTERFACE_AUDIO_RENDER)); + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1); + } else { + mal_copy_memory(pInfo->id.wasapi, &g_malIID_DEVINTERFACE_AUDIO_CAPTURE, sizeof(g_malIID_DEVINTERFACE_AUDIO_CAPTURE)); + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1); + } + + pInfo += 1; + *pCount += 1; + } + } else { + *pCount += 1; + } +#endif + + return MAL_SUCCESS; +} + +static void mal_device_uninit__wasapi(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->wasapi.pRenderClient) { + IAudioRenderClient_Release(pDevice->wasapi.pRenderClient); + } + if (pDevice->wasapi.pCaptureClient) { + IAudioCaptureClient_Release(pDevice->wasapi.pCaptureClient); + } + if (pDevice->wasapi.pAudioClient) { + IAudioClient_Release(pDevice->wasapi.pAudioClient); + } + + if (pDevice->wasapi.hEvent) { + CloseHandle(pDevice->wasapi.hEvent); + } + if (pDevice->wasapi.hStopEvent) { + CloseHandle(pDevice->wasapi.hStopEvent); + } +} + +#ifndef MAL_WIN32_DESKTOP + #ifdef __cplusplus + #include + class malCompletionHandler : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::ClassicCom >, Microsoft::WRL::FtmBase, IActivateAudioInterfaceCompletionHandler > + { + public: + + malCompletionHandler() + : m_hEvent(NULL) + { + } + + mal_result Init() + { + m_hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (m_hEvent == NULL) { + return MAL_ERROR; + } + + return MAL_SUCCESS; + } + + void Uninit() + { + if (m_hEvent != NULL) { + CloseHandle(m_hEvent); + } + } + + void Wait() + { + WaitForSingleObject(m_hEvent, INFINITE); + } + + HRESULT STDMETHODCALLTYPE ActivateCompleted(IActivateAudioInterfaceAsyncOperation *activateOperation) + { + (void)activateOperation; + SetEvent(m_hEvent); + return S_OK; + } + + private: + HANDLE m_hEvent; // This is created in Init(), deleted in Uninit(), waited on in Wait() and signaled in ActivateCompleted(). + }; + #else + #error "The UWP build is currently only supported in C++." + #endif +#endif // !MAL_WIN32_DESKTOP + +static mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->wasapi); + + HRESULT hr; + mal_result result = MAL_SUCCESS; + const char* errorMsg = ""; + AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED; + + WAVEFORMATEXTENSIBLE wf; + mal_zero_object(&wf); + wf.Format.cbSize = sizeof(wf); + wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf.Format.nChannels = (WORD)pDevice->channels; + wf.Format.nSamplesPerSec = (DWORD)pDevice->sampleRate; + wf.Format.wBitsPerSample = (WORD)mal_get_sample_size_in_bytes(pDevice->format)*8; + wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8; + wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; + wf.dwChannelMask = mal_channel_map_to_channel_mask__win32(pDevice->channelMap, pDevice->channels); + if (pDevice->format == mal_format_f32) { + wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } else { + wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM; + } + +#ifdef MAL_WIN32_DESKTOP + IMMDevice* pMMDevice = NULL; + + IMMDeviceEnumerator* pDeviceEnumerator; + hr = mal_CoCreateInstance(pContext, g_malCLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, g_malIID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to create IMMDeviceEnumerator.", result = MAL_WASAPI_FAILED_TO_CREATE_DEVICE_ENUMERATOR; + goto done; + } + + if (pDeviceID == NULL) { + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (type == mal_device_type_playback) ? eRender : eCapture, eConsole, &pMMDevice); + if (FAILED(hr)) { + IMMDeviceEnumerator_Release(pDeviceEnumerator); + errorMsg = "[WASAPI] Failed to create default backend device.", result = MAL_WASAPI_FAILED_TO_CREATE_DEVICE; + goto done; + } + } else { + hr = IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, &pMMDevice); + if (FAILED(hr)) { + IMMDeviceEnumerator_Release(pDeviceEnumerator); + errorMsg = "[WASAPI] Failed to create backend device.", result = MAL_WASAPI_FAILED_TO_CREATE_DEVICE; + goto done; + } + } + + IMMDeviceEnumerator_Release(pDeviceEnumerator); + + hr = IMMDevice_Activate(pMMDevice, g_malIID_IAudioClient, CLSCTX_ALL, NULL, &pDevice->wasapi.pAudioClient); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to activate device.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; + goto done; + } +#else + IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; + malCompletionHandler completionHandler; + + IID iid; + if (pDeviceID != NULL) { + mal_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid)); + } else { + if (type == mal_device_type_playback) { + iid = g_malIID_DEVINTERFACE_AUDIO_RENDER; + } else { + iid = g_malIID_DEVINTERFACE_AUDIO_CAPTURE; + } + } + + LPOLESTR iidStr; + hr = StringFromIID(iid, &iidStr); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", result = MAL_OUT_OF_MEMORY; + goto done; + } + + result = completionHandler.Init(); + if (result != MAL_SUCCESS) { + mal_CoTaskMemFree(pContext, iidStr); + + errorMsg = "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; + goto done; + } + + hr = ActivateAudioInterfaceAsync(iidStr, g_malIID_IAudioClient, NULL, &completionHandler, &pAsyncOp); + if (FAILED(hr)) { + completionHandler.Uninit(); + mal_CoTaskMemFree(pContext, iidStr); + + errorMsg = "[WASAPI] ActivateAudioInterfaceAsync() failed.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; + goto done; + } + + mal_CoTaskMemFree(pContext, iidStr); + + // Wait for the async operation for finish. + completionHandler.Wait(); + completionHandler.Uninit(); + + HRESULT activateResult; + IUnknown* pActivatedInterface; + hr = IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); + if (FAILED(hr) || FAILED(activateResult)) { + errorMsg = "[WASAPI] Failed to activate device.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; + goto done; + } + + // Here is where we grab the IAudioClient interface. + hr = pActivatedInterface->QueryInterface(g_malIID_IAudioClient, &pDevice->wasapi.pAudioClient); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to query IAudioClient interface.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; + goto done; + } +#endif + + // Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. + WAVEFORMATEXTENSIBLE* pBestFormatTemp = NULL; + result = MAL_FORMAT_NOT_SUPPORTED; + if (pConfig->preferExclusiveMode) { + hr = IAudioClient_IsFormatSupported(pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL); + #ifdef MAL_WIN32_DESKTOP + if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) { + // The format isn't supported, so retrieve the actual format from the property store and try that. + IPropertyStore* pStore = NULL; + hr = IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pStore); + if (SUCCEEDED(hr)) { + PROPVARIANT prop; + PropVariantInit(&prop); + hr = IPropertyStore_GetValue(pStore, g_malPKEY_AudioEngine_DeviceFormat, &prop); + if (SUCCEEDED(hr)) { + WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData; + hr = IAudioClient_IsFormatSupported(pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); + if (SUCCEEDED(hr)) { + mal_copy_memory(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE)); + } + + mal_PropVariantClear(pDevice->pContext, &prop); + } + + IPropertyStore_Release(pStore); + } + } + #endif + + if (hr == S_OK) { + shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; + result = MAL_SUCCESS; + } + } + + // Fall back to shared mode if necessary. + if (result != MAL_SUCCESS) { + hr = IAudioClient_IsFormatSupported(pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&wf, (WAVEFORMATEX**)&pBestFormatTemp); + if (hr != S_OK && hr != S_FALSE) { + hr = IAudioClient_GetMixFormat(pDevice->wasapi.pAudioClient, (WAVEFORMATEX**)&pBestFormatTemp); + if (hr != S_OK) { + result = MAL_WASAPI_FAILED_TO_FIND_BEST_FORMAT; + } else { + result = MAL_SUCCESS; + } + } else { + result = MAL_SUCCESS; + } + + shareMode = AUDCLNT_SHAREMODE_SHARED; + } + + // Return an error if we still haven't found a format. + if (result != MAL_SUCCESS) { + errorMsg = "[WASAPI] Failed to find best device mix format.", result = MAL_WASAPI_FAILED_TO_ACTIVATE_DEVICE; + goto done; + } + + if (pBestFormatTemp != NULL) { + mal_copy_memory(&wf, pBestFormatTemp, sizeof(wf)); + mal_CoTaskMemFree(pDevice->pContext, pBestFormatTemp); + } + + + REFERENCE_TIME bufferDurationInMicroseconds = ((mal_uint64)pDevice->bufferSizeInFrames * 1000 * 1000) / pConfig->sampleRate; + + if (mal_is_guid_equal(wf.SubFormat, MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { + pDevice->internalFormat = mal_format_f32; + } else { + if (wf.Format.wBitsPerSample == 32) { + pDevice->internalFormat = mal_format_s32; + } else if (wf.Format.wBitsPerSample == 24) { + pDevice->internalFormat = mal_format_s24; + } else if (wf.Format.wBitsPerSample == 16) { + pDevice->internalFormat = mal_format_s16; + } else if (wf.Format.wBitsPerSample == 8) { + pDevice->internalFormat = mal_format_u8; + } else { + errorMsg = "[WASAPI] Device's native format is not supported.", result = MAL_FORMAT_NOT_SUPPORTED; + goto done; + } + } + + pDevice->internalChannels = wf.Format.nChannels; + pDevice->internalSampleRate = wf.Format.nSamplesPerSec; + + // Get the internal channel map based on the channel mask. + mal_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); + + // Slightly different initialization for shared and exclusive modes. + if (shareMode == AUDCLNT_SHAREMODE_SHARED) { + // Shared. + REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10; + hr = IAudioClient_Initialize(pDevice->wasapi.pAudioClient, shareMode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL); + if (FAILED(hr)) { + if (hr == E_ACCESSDENIED) { + errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MAL_ACCESS_DENIED; + } else { + errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE; + } + + goto done; + } + } else { + // Exclusive. + REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10; + hr = IAudioClient_Initialize(pDevice->wasapi.pAudioClient, shareMode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { + UINT bufferSizeInFrames; + hr = IAudioClient_GetBufferSize(pDevice->wasapi.pAudioClient, &bufferSizeInFrames); + if (SUCCEEDED(hr)) { + bufferDuration = (REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5); + + // Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! + IAudioClient_Release(pDevice->wasapi.pAudioClient); + + #ifdef MAL_WIN32_DESKTOP + hr = IMMDevice_Activate(pMMDevice, g_malIID_IAudioClient, CLSCTX_ALL, NULL, &pDevice->wasapi.pAudioClient); + #else + hr = pActivatedInterface->QueryInterface(g_malIID_IAudioClient, &pDevice->wasapi.pAudioClient); + #endif + + if (SUCCEEDED(hr)) { + hr = IAudioClient_Initialize(pDevice->wasapi.pAudioClient, shareMode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + } + } + } + + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to initialize device.", result = MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE; + goto done; + } + } + + hr = IAudioClient_GetBufferSize(pDevice->wasapi.pAudioClient, &pDevice->bufferSizeInFrames); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE; + goto done; + } + + if (type == mal_device_type_playback) { + hr = IAudioClient_GetService((IAudioClient*)pDevice->wasapi.pAudioClient, g_malIID_IAudioRenderClient, &pDevice->wasapi.pRenderClient); + } else { + hr = IAudioClient_GetService((IAudioClient*)pDevice->wasapi.pAudioClient, g_malIID_IAudioCaptureClient, &pDevice->wasapi.pCaptureClient); + } + + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to get audio client service.", result = MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE; + goto done; + } + + + if (shareMode == AUDCLNT_SHAREMODE_SHARED) { + pDevice->exclusiveMode = MAL_FALSE; + } else /*if (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)*/ { + pDevice->exclusiveMode = MAL_TRUE; + } + + + // We need to create and set the event for event-driven mode. This event is signalled whenever a new chunk of audio + // data needs to be written or read from the device. + pDevice->wasapi.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pDevice->wasapi.hEvent == NULL) { + errorMsg = "[WASAPI] Failed to create main event for main loop.", result = MAL_FAILED_TO_CREATE_EVENT; + goto done; + } + + IAudioClient_SetEventHandle(pDevice->wasapi.pAudioClient, pDevice->wasapi.hEvent); + + + // When the device is playing the worker thread will be waiting on a bunch of notification events. To return from + // this wait state we need to signal a special event. + pDevice->wasapi.hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pDevice->wasapi.hStopEvent == NULL) { + errorMsg = "[WASAPI] Failed to create stop event for main loop break notification.", result = MAL_FAILED_TO_CREATE_EVENT; + goto done; + } + + result = MAL_SUCCESS; + +done: + // Clean up. +#ifdef MAL_WIN32_DESKTOP + if (pMMDevice != NULL) { + IMMDevice_Release(pMMDevice); + } +#else + if (pAsyncOp != NULL) { + IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); + } +#endif + + if (result != MAL_SUCCESS) { + mal_device_uninit__wasapi(pDevice); + return mal_post_error(pDevice, errorMsg, result); + } else { + return MAL_SUCCESS; + } +} + +static mal_result mal_device__start_backend__wasapi(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // Playback devices need to have an initial chunk of data loaded. + if (pDevice->type == mal_device_type_playback) { + BYTE* pData; + HRESULT hr = IAudioRenderClient_GetBuffer(pDevice->wasapi.pRenderClient, pDevice->bufferSizeInFrames, &pData); + if (FAILED(hr)) { + return mal_post_error(pDevice, "[WASAPI] Failed to retrieve buffer from internal playback device.", MAL_WASAPI_FAILED_TO_GET_INTERNAL_BUFFER); + } + + mal_device__read_frames_from_client(pDevice, pDevice->bufferSizeInFrames, pData); + + hr = IAudioRenderClient_ReleaseBuffer(pDevice->wasapi.pRenderClient, pDevice->bufferSizeInFrames, 0); + if (FAILED(hr)) { + return mal_post_error(pDevice, "[WASAPI] Failed to release internal buffer for playback device.", MAL_WASAPI_FAILED_TO_RELEASE_INTERNAL_BUFFER); + } + } + + HRESULT hr = IAudioClient_Start(pDevice->wasapi.pAudioClient); + if (FAILED(hr)) { + return mal_post_error(pDevice, "[WASAPI] Failed to start internal device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__stop_backend__wasapi(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + HRESULT hr = IAudioClient_Stop(pDevice->wasapi.pAudioClient); + if (FAILED(hr)) { + return mal_post_error(pDevice, "[WASAPI] Failed to stop internal device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__break_main_loop__wasapi(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // The main loop will be waiting on a bunch of events via the WaitForMultipleObjects() API. One of those events + // is a special event we use for forcing that function to return. + pDevice->wasapi.breakFromMainLoop = MAL_TRUE; + SetEvent(pDevice->wasapi.hStopEvent); + return MAL_SUCCESS; +} + +static mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + +#if 1 + if (pDevice->type == mal_device_type_playback) { + UINT32 paddingFramesCount; + HRESULT hr = IAudioClient_GetCurrentPadding(pDevice->wasapi.pAudioClient, &paddingFramesCount); + if (FAILED(hr)) { + return 0; + } + + if (pDevice->exclusiveMode) { + return paddingFramesCount; + } else { + return pDevice->bufferSizeInFrames - paddingFramesCount; + } + } else { + UINT32 framesAvailable; + HRESULT hr = IAudioCaptureClient_GetNextPacketSize(pDevice->wasapi.pCaptureClient, &framesAvailable); + if (FAILED(hr)) { + return 0; + } + + return framesAvailable; + } +#else + UINT32 paddingFramesCount; + HRESULT hr = IAudioClient_GetCurrentPadding(pDevice->wasapi.pAudioClient, &paddingFramesCount); + if (FAILED(hr)) { + return 0; + } + + if (pDevice->exclusiveMode) { + return paddingFramesCount; + } else { + return pDevice->bufferSizeInFrames - paddingFramesCount; + } +#endif +} + +static mal_uint32 mal_device__wait_for_frames__wasapi(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + while (!pDevice->wasapi.breakFromMainLoop) { + // Wait for a buffer to become available or for the stop event to be signalled. + HANDLE hEvents[2]; + hEvents[0] = (HANDLE)pDevice->wasapi.hEvent; + hEvents[1] = (HANDLE)pDevice->wasapi.hStopEvent; + if (WaitForMultipleObjects(mal_countof(hEvents), hEvents, FALSE, INFINITE) == WAIT_FAILED) { + break; + } + + // Break from the main loop if the device isn't started anymore. Likely what's happened is the application + // has requested that the device be stopped. + if (!mal_device_is_started(pDevice)) { + break; + } + + mal_uint32 framesAvailable = mal_device__get_available_frames__wasapi(pDevice); + if (framesAvailable > 0) { + return framesAvailable; + } + } + + // We'll get here if the loop was terminated. Just return whatever's available. + return mal_device__get_available_frames__wasapi(pDevice); +} + +static mal_result mal_device__main_loop__wasapi(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // Make sure the stop event is not signaled to ensure we don't end up immediately returning from WaitForMultipleObjects(). + ResetEvent(pDevice->wasapi.hStopEvent); + + pDevice->wasapi.breakFromMainLoop = MAL_FALSE; + while (!pDevice->wasapi.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__wait_for_frames__wasapi(pDevice); + if (framesAvailable == 0) { + continue; + } + + // If it's a playback device, don't bother grabbing more data if the device is being stopped. + if (pDevice->wasapi.breakFromMainLoop && pDevice->type == mal_device_type_playback) { + return MAL_FALSE; + } + + if (pDevice->type == mal_device_type_playback) { + BYTE* pData; + HRESULT hr = IAudioRenderClient_GetBuffer(pDevice->wasapi.pRenderClient, framesAvailable, &pData); + if (FAILED(hr)) { + return mal_post_error(pDevice, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for sending new data to the device.", MAL_WASAPI_FAILED_TO_GET_INTERNAL_BUFFER); + } + + mal_device__read_frames_from_client(pDevice, framesAvailable, pData); + + hr = IAudioRenderClient_ReleaseBuffer(pDevice->wasapi.pRenderClient, framesAvailable, 0); + if (FAILED(hr)) { + return mal_post_error(pDevice, "[WASAPI] Failed to release internal buffer from playback device in preparation for sending new data to the device.", MAL_WASAPI_FAILED_TO_RELEASE_INTERNAL_BUFFER); + } + } else { + UINT32 framesRemaining = framesAvailable; + while (framesRemaining > 0) { + BYTE* pData; + UINT32 framesToSend; + DWORD flags; + HRESULT hr = IAudioCaptureClient_GetBuffer(pDevice->wasapi.pCaptureClient, &pData, &framesToSend, &flags, NULL, NULL); + if (FAILED(hr)) { + mal_post_error(pDevice, "[WASAPI] WARNING: Failed to retrieve internal buffer from capture device in preparation for sending new data to the client.", MAL_WASAPI_FAILED_TO_GET_INTERNAL_BUFFER); + break; + } + + if (hr != AUDCLNT_S_BUFFER_EMPTY) { + mal_device__send_frames_to_client(pDevice, framesToSend, pData); + + hr = IAudioCaptureClient_ReleaseBuffer(pDevice->wasapi.pCaptureClient, framesToSend); + if (FAILED(hr)) { + mal_post_error(pDevice, "[WASAPI] WARNING: Failed to release internal buffer from capture device in preparation for sending new data to the client.", MAL_WASAPI_FAILED_TO_RELEASE_INTERNAL_BUFFER); + break; + } + + if (framesRemaining >= framesToSend) { + framesRemaining -= framesToSend; + } else { + framesRemaining = 0; + } + } + } + } + } + + return MAL_SUCCESS; +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +// DirectSound Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_DSOUND +#include + +#if 0 // MAL_GUID_NULL is not currently used, but leaving it here in case I need to add it back again. +static GUID MAL_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +#endif +static GUID MAL_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}}; +static GUID MAL_GUID_IID_IDirectSoundCaptureBuffer = {0xb0210782, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}}; + +typedef HRESULT (WINAPI * mal_DirectSoundCreateProc)(const GUID* pcGuidDevice, LPDIRECTSOUND *ppDS8, LPUNKNOWN pUnkOuter); +typedef HRESULT (WINAPI * mal_DirectSoundEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext); +typedef HRESULT (WINAPI * mal_DirectSoundCaptureCreateProc)(const GUID* pcGuidDevice, LPDIRECTSOUNDCAPTURE *ppDSC8, LPUNKNOWN pUnkOuter); +typedef HRESULT (WINAPI * mal_DirectSoundCaptureEnumerateAProc)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext); + +static HMODULE mal_open_dsound_dll() +{ + return LoadLibraryW(L"dsound.dll"); +} + +static void mal_close_dsound_dll(HMODULE hModule) +{ + FreeLibrary(hModule); +} + + +mal_result mal_context_init__dsound(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__dsound(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_dsound); + + (void)pContext; + return MAL_SUCCESS; +} + + +typedef struct +{ + mal_uint32 deviceCount; + mal_uint32 infoCount; + mal_device_info* pInfo; +} mal_device_enum_data__dsound; + +static BOOL CALLBACK mal_enum_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) +{ + (void)lpcstrModule; + + mal_device_enum_data__dsound* pData = (mal_device_enum_data__dsound*)lpContext; + mal_assert(pData != NULL); + + if (pData->pInfo != NULL) { + if (pData->infoCount > 0) { + mal_zero_object(pData->pInfo); + mal_strncpy_s(pData->pInfo->name, sizeof(pData->pInfo->name), lpcstrDescription, (size_t)-1); + + if (lpGuid != NULL) { + mal_copy_memory(pData->pInfo->id.dsound, lpGuid, 16); + } else { + mal_zero_memory(pData->pInfo->id.dsound, 16); + } + + pData->pInfo += 1; + pData->infoCount -= 1; + pData->deviceCount += 1; + } + } else { + pData->deviceCount += 1; + } + + return TRUE; +} + +static mal_result mal_enumerate_devices__dsound(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + (void)pContext; + + mal_uint32 infoSize = *pCount; + *pCount = 0; + + mal_device_enum_data__dsound enumData; + enumData.deviceCount = 0; + enumData.infoCount = infoSize; + enumData.pInfo = pInfo; + + HMODULE dsoundDLL = mal_open_dsound_dll(); + if (dsoundDLL == NULL) { + return MAL_NO_BACKEND; + } + + if (type == mal_device_type_playback) { + mal_DirectSoundEnumerateAProc pDirectSoundEnumerateA = (mal_DirectSoundEnumerateAProc)GetProcAddress(dsoundDLL, "DirectSoundEnumerateA"); + if (pDirectSoundEnumerateA) { + pDirectSoundEnumerateA(mal_enum_devices_callback__dsound, &enumData); + } + } else { + mal_DirectSoundCaptureEnumerateAProc pDirectSoundCaptureEnumerateA = (mal_DirectSoundCaptureEnumerateAProc)GetProcAddress(dsoundDLL, "DirectSoundCaptureEnumerateA"); + if (pDirectSoundCaptureEnumerateA) { + pDirectSoundCaptureEnumerateA(mal_enum_devices_callback__dsound, &enumData); + } + } + + + mal_close_dsound_dll(dsoundDLL); + + *pCount = enumData.deviceCount; + return MAL_SUCCESS; +} + +static void mal_device_uninit__dsound(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->dsound.hDSoundDLL != NULL) { + if (pDevice->dsound.pNotify) { + IDirectSoundNotify_Release((LPDIRECTSOUNDNOTIFY)pDevice->dsound.pNotify); + } + + if (pDevice->dsound.hStopEvent) { + CloseHandle(pDevice->dsound.hStopEvent); + } + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + if (pDevice->dsound.pNotifyEvents[i]) { + CloseHandle(pDevice->dsound.pNotifyEvents[i]); + } + } + + if (pDevice->dsound.pCaptureBuffer) { + IDirectSoundCaptureBuffer_Release((LPDIRECTSOUNDBUFFER)pDevice->dsound.pCaptureBuffer); + } + if (pDevice->dsound.pCapture) { + IDirectSoundCapture_Release((LPDIRECTSOUNDCAPTURE)pDevice->dsound.pCapture); + } + + if (pDevice->dsound.pPlaybackBuffer) { + IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer); + } + if (pDevice->dsound.pPlaybackPrimaryBuffer) { + IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer); + } + if (pDevice->dsound.pPlayback != NULL) { + IDirectSound_Release((LPDIRECTSOUND)pDevice->dsound.pPlayback); + } + + mal_close_dsound_dll((HMODULE)pDevice->dsound.hDSoundDLL); + } +} + +static mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + +#ifdef __cplusplus + GUID _MAL_GUID_IID_DirectSoundNotify = MAL_GUID_IID_DirectSoundNotify; + GUID _MAL_GUID_IID_IDirectSoundCaptureBuffer = MAL_GUID_IID_IDirectSoundCaptureBuffer; +#else + GUID* _MAL_GUID_IID_DirectSoundNotify = &MAL_GUID_IID_DirectSoundNotify; + GUID* _MAL_GUID_IID_IDirectSoundCaptureBuffer = &MAL_GUID_IID_IDirectSoundCaptureBuffer; +#endif + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->dsound); + + pDevice->dsound.hDSoundDLL = (mal_handle)mal_open_dsound_dll(); + if (pDevice->dsound.hDSoundDLL == NULL) { + return MAL_NO_BACKEND; + } + + // Check that we have a valid format. + GUID subformat; + switch (pConfig->format) + { + case mal_format_u8: + case mal_format_s16: + case mal_format_s24: + case mal_format_s32: + { + subformat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM; + } break; + + case mal_format_f32: + { + subformat = MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } break; + + default: + return MAL_FORMAT_NOT_SUPPORTED; + } + + + WAVEFORMATEXTENSIBLE wf; + mal_zero_object(&wf); + wf.Format.cbSize = sizeof(wf); + wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf.Format.nChannels = (WORD)pConfig->channels; + wf.Format.nSamplesPerSec = (DWORD)pConfig->sampleRate; + wf.Format.wBitsPerSample = (WORD)mal_get_sample_size_in_bytes(pConfig->format)*8; + wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8; + wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; + wf.dwChannelMask = mal_channel_map_to_channel_mask__win32(pConfig->channelMap, pConfig->channels); + wf.SubFormat = subformat; + + DWORD bufferSizeInBytes = 0; + + // Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices :( + if (type == mal_device_type_playback) { + mal_DirectSoundCreateProc pDirectSoundCreate = (mal_DirectSoundCreateProc)GetProcAddress((HMODULE)pDevice->dsound.hDSoundDLL, "DirectSoundCreate"); + if (pDirectSoundCreate == NULL) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] Could not find DirectSoundCreate().", MAL_API_NOT_FOUND); + } + + if (FAILED(pDirectSoundCreate((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, (LPDIRECTSOUND*)&pDevice->dsound.pPlayback, NULL))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] DirectSoundCreate() failed for playback device.", MAL_DSOUND_FAILED_TO_CREATE_DEVICE); + } + + // The cooperative level must be set before doing anything else. + HWND hWnd = ((MAL_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); + if (hWnd == NULL) { + hWnd = ((MAL_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); + } + if (FAILED(IDirectSound_SetCooperativeLevel((LPDIRECTSOUND)pDevice->dsound.pPlayback, hWnd, (pConfig->preferExclusiveMode) ? DSSCL_EXCLUSIVE : DSSCL_PRIORITY))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", MAL_DSOUND_FAILED_TO_SET_COOP_LEVEL); + } + + DSBUFFERDESC descDSPrimary; + mal_zero_object(&descDSPrimary); + descDSPrimary.dwSize = sizeof(DSBUFFERDESC); + descDSPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME; + if (FAILED(IDirectSound_CreateSoundBuffer((LPDIRECTSOUND)pDevice->dsound.pPlayback, &descDSPrimary, (LPDIRECTSOUNDBUFFER*)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", MAL_DSOUND_FAILED_TO_CREATE_BUFFER); + } + + // From MSDN: + // + // The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest + // supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer + // and compare the result with the format that was requested with the SetFormat method. + if (FAILED(IDirectSoundBuffer_SetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] Failed to set format of playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED); + } + + // Get the _actual_ properties of the buffer. This is silly API design... + DWORD requiredSize; + if (FAILED(IDirectSoundBuffer_GetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, NULL, 0, &requiredSize))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED); + } + + char rawdata[1024]; + WAVEFORMATEXTENSIBLE* pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; + if (FAILED(IDirectSoundBuffer_GetFormat((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, requiredSize, NULL))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalChannels = pActualFormat->Format.nChannels; + pDevice->internalSampleRate = pActualFormat->Format.nSamplesPerSec; + bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->format); + + // Get the internal channel map based on the channel mask. + mal_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->internalChannels, pDevice->internalChannelMap); + + + // Meaning of dwFlags (from MSDN): + // + // DSBCAPS_CTRLPOSITIONNOTIFY + // The buffer has position notification capability. + // + // DSBCAPS_GLOBALFOCUS + // With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to + // another application, even if the new application uses DirectSound. + // + // DSBCAPS_GETCURRENTPOSITION2 + // In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated + // sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the + // application can get a more accurate play cursor. + DSBUFFERDESC descDS; + mal_zero_object(&descDS); + descDS.dwSize = sizeof(DSBUFFERDESC); + descDS.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; + descDS.dwBufferBytes = bufferSizeInBytes; + descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; + if (FAILED(IDirectSound_CreateSoundBuffer((LPDIRECTSOUND)pDevice->dsound.pPlayback, &descDS, (LPDIRECTSOUNDBUFFER*)&pDevice->dsound.pPlaybackBuffer, NULL))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", MAL_DSOUND_FAILED_TO_CREATE_BUFFER); + } + + // Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer. + if (FAILED(IDirectSoundBuffer_QueryInterface((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, _MAL_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_QueryInterface() failed for playback device's IDirectSoundNotify object.", MAL_DSOUND_FAILED_TO_QUERY_INTERFACE); + } + } else { + // The default buffer size is treated slightly differently for DirectSound which, for some reason, seems to + // have worse latency with capture than playback (sometimes _much_ worse). + if (pDevice->usingDefaultBufferSize) { + pDevice->bufferSizeInFrames *= 2; // <-- Might need to fiddle with this to find a more ideal value. May even be able to just add a fixed amount rather than scaling. + } + + mal_DirectSoundCaptureCreateProc pDirectSoundCaptureCreate = (mal_DirectSoundCaptureCreateProc)GetProcAddress((HMODULE)pDevice->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); + if (pDirectSoundCaptureCreate == NULL) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] Could not find DirectSoundCreate().", MAL_API_NOT_FOUND); + } + + if (FAILED(pDirectSoundCaptureCreate((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, (LPDIRECTSOUNDCAPTURE*)&pDevice->dsound.pCapture, NULL))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", MAL_DSOUND_FAILED_TO_CREATE_DEVICE); + } + + bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); + + DSCBUFFERDESC descDS; + mal_zero_object(&descDS); + descDS.dwSize = sizeof(descDS); + descDS.dwFlags = 0; + descDS.dwBufferBytes = bufferSizeInBytes; + descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; + LPDIRECTSOUNDCAPTUREBUFFER pDSCB_Temp; + if (FAILED(IDirectSoundCapture_CreateCaptureBuffer((LPDIRECTSOUNDCAPTURE)pDevice->dsound.pCapture, &descDS, &pDSCB_Temp, NULL))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MAL_DSOUND_FAILED_TO_CREATE_BUFFER); + } + + HRESULT hr = IDirectSoundCapture_QueryInterface(pDSCB_Temp, _MAL_GUID_IID_IDirectSoundCaptureBuffer, (LPVOID*)&pDevice->dsound.pCaptureBuffer); + IDirectSoundCaptureBuffer_Release(pDSCB_Temp); + if (FAILED(hr)) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] IDirectSoundCapture_QueryInterface() failed for capture device's IDirectSoundCaptureBuffer8 object.", MAL_DSOUND_FAILED_TO_QUERY_INTERFACE); + } + + // Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer. + if (FAILED(IDirectSoundCaptureBuffer_QueryInterface((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, _MAL_GUID_IID_DirectSoundNotify, (void**)&pDevice->dsound.pNotify))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_QueryInterface() failed for capture device's IDirectSoundNotify object.", MAL_DSOUND_FAILED_TO_QUERY_INTERFACE); + } + } + + // We need a notification for each period. The notification offset is slightly different depending on whether or not the + // device is a playback or capture device. For a playback device we want to be notified when a period just starts playing, + // whereas for a capture device we want to be notified when a period has just _finished_ capturing. + mal_uint32 periodSizeInBytes = pDevice->bufferSizeInFrames / pDevice->periods; + DSBPOSITIONNOTIFY notifyPoints[MAL_MAX_PERIODS_DSOUND]; // One notification event for each period. + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + pDevice->dsound.pNotifyEvents[i] = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pDevice->dsound.pNotifyEvents[i] == NULL) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] Failed to create event for buffer notifications.", MAL_FAILED_TO_CREATE_EVENT); + } + + // The notification offset is in bytes. + notifyPoints[i].dwOffset = i * periodSizeInBytes; + notifyPoints[i].hEventNotify = pDevice->dsound.pNotifyEvents[i]; + } + + if (FAILED(IDirectSoundNotify_SetNotificationPositions((LPDIRECTSOUNDNOTIFY)pDevice->dsound.pNotify, pDevice->periods, notifyPoints))) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] IDirectSoundNotify_SetNotificationPositions() failed.", MAL_DSOUND_FAILED_TO_SET_NOTIFICATIONS); + } + + // When the device is playing the worker thread will be waiting on a bunch of notification events. To return from + // this wait state we need to signal a special event. + pDevice->dsound.hStopEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pDevice->dsound.hStopEvent == NULL) { + mal_device_uninit__dsound(pDevice); + return mal_post_error(pDevice, "[DirectSound] Failed to create event for main loop break notification.", MAL_FAILED_TO_CREATE_EVENT); + } + + return MAL_SUCCESS; +} + + +static mal_result mal_device__start_backend__dsound(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + // Before playing anything we need to grab an initial group of samples from the client. + mal_uint32 framesToRead = pDevice->bufferSizeInFrames / pDevice->periods; + mal_uint32 desiredLockSize = framesToRead * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); + + void* pLockPtr; + DWORD actualLockSize; + void* pLockPtr2; + DWORD actualLockSize2; + if (SUCCEEDED(IDirectSoundBuffer_Lock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0, desiredLockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) { + framesToRead = actualLockSize / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + mal_device__read_frames_from_client(pDevice, framesToRead, pLockPtr); + IDirectSoundBuffer_Unlock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2); + + pDevice->dsound.lastProcessedFrame = framesToRead; + if (FAILED(IDirectSoundBuffer_Play((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0, 0, DSBPLAY_LOOPING))) { + return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Play() failed.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } else { + return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); + } + } else { + if (FAILED(IDirectSoundCaptureBuffer_Start((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, DSCBSTART_LOOPING))) { + return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__stop_backend__dsound(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + if (FAILED(IDirectSoundBuffer_Stop((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer))) { + return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } + + IDirectSoundBuffer_SetCurrentPosition((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, 0); + } else { + if (FAILED(IDirectSoundCaptureBuffer_Stop((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer))) { + return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__break_main_loop__dsound(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // The main loop will be waiting on a bunch of events via the WaitForMultipleObjects() API. One of those events + // is a special event we use for forcing that function to return. + pDevice->dsound.breakFromMainLoop = MAL_TRUE; + SetEvent(pDevice->dsound.hStopEvent); + return MAL_SUCCESS; +} + +static mal_bool32 mal_device__get_current_frame__dsound(mal_device* pDevice, mal_uint32* pCurrentPos) +{ + mal_assert(pDevice != NULL); + mal_assert(pCurrentPos != NULL); + *pCurrentPos = 0; + + DWORD dwCurrentPosition; + if (pDevice->type == mal_device_type_playback) { + if (FAILED(IDirectSoundBuffer_GetCurrentPosition((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, NULL, &dwCurrentPosition))) { + return MAL_FALSE; + } + } else { + if (FAILED(IDirectSoundCaptureBuffer_GetCurrentPosition((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, &dwCurrentPosition, NULL))) { + return MAL_FALSE; + } + } + + *pCurrentPos = (mal_uint32)dwCurrentPosition / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + return MAL_TRUE; +} + +static mal_uint32 mal_device__get_available_frames__dsound(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_uint32 currentFrame; + if (!mal_device__get_current_frame__dsound(pDevice, ¤tFrame)) { + return 0; + } + + // In a playback device the last processed frame should always be ahead of the current frame. The space between + // the last processed and current frame (moving forward, starting from the last processed frame) is the amount + // of space available to write. + // + // For a recording device it's the other way around - the last processed frame is always _behind_ the current + // frame and the space between is the available space. + mal_uint32 totalFrameCount = pDevice->bufferSizeInFrames; + if (pDevice->type == mal_device_type_playback) { + mal_uint32 committedBeg = currentFrame; + mal_uint32 committedEnd; + committedEnd = pDevice->dsound.lastProcessedFrame; + if (committedEnd <= committedBeg) { + committedEnd += totalFrameCount; + } + + mal_uint32 committedSize = (committedEnd - committedBeg); + mal_assert(committedSize <= totalFrameCount); + + return totalFrameCount - committedSize; + } else { + mal_uint32 validBeg = pDevice->dsound.lastProcessedFrame; + mal_uint32 validEnd = currentFrame; + if (validEnd < validBeg) { + validEnd += totalFrameCount; // Wrap around. + } + + mal_uint32 validSize = (validEnd - validBeg); + mal_assert(validSize <= totalFrameCount); + + return validSize; + } +} + +static mal_uint32 mal_device__wait_for_frames__dsound(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // The timeout to use for putting the thread to sleep is based on the size of the buffer and the period count. + DWORD timeoutInMilliseconds = (pDevice->bufferSizeInFrames / (pDevice->sampleRate/1000)) / pDevice->periods; + if (timeoutInMilliseconds < 1) { + timeoutInMilliseconds = 1; + } + + unsigned int eventCount = pDevice->periods + 1; + HANDLE pEvents[MAL_MAX_PERIODS_DSOUND + 1]; // +1 for the stop event. + mal_copy_memory(pEvents, pDevice->dsound.pNotifyEvents, sizeof(HANDLE) * pDevice->periods); + pEvents[eventCount-1] = pDevice->dsound.hStopEvent; + + while (!pDevice->dsound.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__get_available_frames__dsound(pDevice); + if (framesAvailable > 0) { + return framesAvailable; + } + + // If we get here it means we weren't able to find any frames. We'll just wait here for a bit. + WaitForMultipleObjects(eventCount, pEvents, FALSE, timeoutInMilliseconds); + } + + // We'll get here if the loop was terminated. Just return whatever's available. + return mal_device__get_available_frames__dsound(pDevice); +} + +static mal_result mal_device__main_loop__dsound(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // Make sure the stop event is not signaled to ensure we don't end up immediately returning from WaitForMultipleObjects(). + ResetEvent(pDevice->dsound.hStopEvent); + + pDevice->dsound.breakFromMainLoop = MAL_FALSE; + while (!pDevice->dsound.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__wait_for_frames__dsound(pDevice); + if (framesAvailable == 0) { + continue; + } + + // If it's a playback device, don't bother grabbing more data if the device is being stopped. + if (pDevice->dsound.breakFromMainLoop && pDevice->type == mal_device_type_playback) { + return MAL_FALSE; + } + + DWORD lockOffset = pDevice->dsound.lastProcessedFrame * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); + DWORD lockSize = framesAvailable * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); + + if (pDevice->type == mal_device_type_playback) { + void* pLockPtr; + DWORD actualLockSize; + void* pLockPtr2; + DWORD actualLockSize2; + if (FAILED(IDirectSoundBuffer_Lock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, lockOffset, lockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) { + return mal_post_error(pDevice, "[DirectSound] IDirectSoundBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); + } + + mal_uint32 frameCount = actualLockSize / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + mal_device__read_frames_from_client(pDevice, frameCount, pLockPtr); + pDevice->dsound.lastProcessedFrame = (pDevice->dsound.lastProcessedFrame + frameCount) % pDevice->bufferSizeInFrames; + + IDirectSoundBuffer_Unlock((LPDIRECTSOUNDBUFFER)pDevice->dsound.pPlaybackBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2); + } else { + void* pLockPtr; + DWORD actualLockSize; + void* pLockPtr2; + DWORD actualLockSize2; + if (FAILED(IDirectSoundCaptureBuffer_Lock((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, lockOffset, lockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) { + return mal_post_error(pDevice, "[DirectSound] IDirectSoundCaptureBuffer_Lock() failed.", MAL_FAILED_TO_MAP_DEVICE_BUFFER); + } + + mal_uint32 frameCount = actualLockSize / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; + mal_device__send_frames_to_client(pDevice, frameCount, pLockPtr); + pDevice->dsound.lastProcessedFrame = (pDevice->dsound.lastProcessedFrame + frameCount) % pDevice->bufferSizeInFrames; + + IDirectSoundCaptureBuffer_Unlock((LPDIRECTSOUNDCAPTUREBUFFER)pDevice->dsound.pCaptureBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2); + } + } + + return MAL_SUCCESS; +} +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// +// WinMM Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_WINMM +#include + +#if !defined(MAXULONG_PTR) +typedef size_t DWORD_PTR; +#endif + +#if !defined(WAVE_FORMAT_44M08) +#define WAVE_FORMAT_44M08 0x00000100 +#define WAVE_FORMAT_44S08 0x00000200 +#define WAVE_FORMAT_44M16 0x00000400 +#define WAVE_FORMAT_44S16 0x00000800 +#define WAVE_FORMAT_48M08 0x00001000 +#define WAVE_FORMAT_48S08 0x00002000 +#define WAVE_FORMAT_48M16 0x00004000 +#define WAVE_FORMAT_48S16 0x00008000 +#define WAVE_FORMAT_96M08 0x00010000 +#define WAVE_FORMAT_96S08 0x00020000 +#define WAVE_FORMAT_96M16 0x00040000 +#define WAVE_FORMAT_96S16 0x00080000 +#endif + +typedef UINT (WINAPI * MAL_PFN_waveOutGetNumDevs)(void); +typedef MMRESULT (WINAPI * MAL_PFN_waveOutGetDevCapsA)(UINT_PTR uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc); +typedef MMRESULT (WINAPI * MAL_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MMRESULT (WINAPI * MAL_PFN_waveOutClose)(HWAVEOUT hwo); +typedef MMRESULT (WINAPI * MAL_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); +typedef MMRESULT (WINAPI * MAL_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); +typedef MMRESULT (WINAPI * MAL_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); +typedef MMRESULT (WINAPI * MAL_PFN_waveOutReset)(HWAVEOUT hwo); +typedef UINT (WINAPI * MAL_PFN_waveInGetNumDevs)(void); +typedef MMRESULT (WINAPI * MAL_PFN_waveInGetDevCapsA)(UINT_PTR uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic); +typedef MMRESULT (WINAPI * MAL_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MMRESULT (WINAPI * MAL_PFN_waveInClose)(HWAVEIN hwi); +typedef MMRESULT (WINAPI * MAL_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); +typedef MMRESULT (WINAPI * MAL_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); +typedef MMRESULT (WINAPI * MAL_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); +typedef MMRESULT (WINAPI * MAL_PFN_waveInStart)(HWAVEIN hwi); +typedef MMRESULT (WINAPI * MAL_PFN_waveInReset)(HWAVEIN hwi); + +mal_result mal_result_from_MMRESULT(MMRESULT resultMM) +{ + switch (resultMM) { + case MMSYSERR_NOERROR: return MAL_SUCCESS; + case MMSYSERR_BADDEVICEID: return MAL_INVALID_ARGS; + case MMSYSERR_INVALHANDLE: return MAL_INVALID_ARGS; + case MMSYSERR_NOMEM: return MAL_OUT_OF_MEMORY; + case MMSYSERR_INVALFLAG: return MAL_INVALID_ARGS; + case MMSYSERR_INVALPARAM: return MAL_INVALID_ARGS; + case MMSYSERR_HANDLEBUSY: return MAL_DEVICE_BUSY; + case MMSYSERR_ERROR: return MAL_ERROR; + default: return MAL_ERROR; + } +} + +mal_result mal_context_init__winmm(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + pContext->winmm.hWinMM = mal_dlopen("winmm.dll"); + if (pContext->winmm.hWinMM == NULL) { + return MAL_NO_BACKEND; + } + + pContext->winmm.waveOutGetNumDevs = mal_dlsym(pContext->winmm.hWinMM, "waveOutGetNumDevs"); + pContext->winmm.waveOutGetDevCapsA = mal_dlsym(pContext->winmm.hWinMM, "waveOutGetDevCapsA"); + pContext->winmm.waveOutOpen = mal_dlsym(pContext->winmm.hWinMM, "waveOutOpen"); + pContext->winmm.waveOutClose = mal_dlsym(pContext->winmm.hWinMM, "waveOutClose"); + pContext->winmm.waveOutPrepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveOutPrepareHeader"); + pContext->winmm.waveOutUnprepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveOutUnprepareHeader"); + pContext->winmm.waveOutWrite = mal_dlsym(pContext->winmm.hWinMM, "waveOutWrite"); + pContext->winmm.waveOutReset = mal_dlsym(pContext->winmm.hWinMM, "waveOutReset"); + pContext->winmm.waveInGetNumDevs = mal_dlsym(pContext->winmm.hWinMM, "waveInGetNumDevs"); + pContext->winmm.waveInGetDevCapsA = mal_dlsym(pContext->winmm.hWinMM, "waveInGetDevCapsA"); + pContext->winmm.waveInOpen = mal_dlsym(pContext->winmm.hWinMM, "waveInOpen"); + pContext->winmm.waveInClose = mal_dlsym(pContext->winmm.hWinMM, "waveInClose"); + pContext->winmm.waveInPrepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveInPrepareHeader"); + pContext->winmm.waveInUnprepareHeader = mal_dlsym(pContext->winmm.hWinMM, "waveInUnprepareHeader"); + pContext->winmm.waveInAddBuffer = mal_dlsym(pContext->winmm.hWinMM, "waveInAddBuffer"); + pContext->winmm.waveInStart = mal_dlsym(pContext->winmm.hWinMM, "waveInStart"); + pContext->winmm.waveInReset = mal_dlsym(pContext->winmm.hWinMM, "waveInReset"); + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__winmm(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_winmm); + + mal_dlclose(pContext->winmm.hWinMM); + return MAL_SUCCESS; +} + +static mal_result mal_enumerate_devices__winmm(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + (void)pContext; + + mal_uint32 infoSize = *pCount; + *pCount = 0; + + if (type == mal_device_type_playback) { + UINT deviceCount = ((MAL_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); + for (UINT iDevice = 0; iDevice < deviceCount; ++iDevice) { + if (pInfo != NULL) { + if (infoSize > 0) { + WAVEOUTCAPSA caps; + MMRESULT result = ((MAL_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iDevice, &caps, sizeof(caps)); + if (result == MMSYSERR_NOERROR) { + pInfo->id.winmm = iDevice; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), caps.szPname, (size_t)-1); + } + + pInfo += 1; + infoSize -= 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + } + } else { + UINT deviceCount = ((MAL_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); + for (UINT iDevice = 0; iDevice < deviceCount; ++iDevice) { + if (pInfo != NULL) { + if (infoSize > 0) { + WAVEINCAPSA caps; + MMRESULT result = ((MAL_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iDevice, &caps, sizeof(caps)); + if (result == MMSYSERR_NOERROR) { + pInfo->id.winmm = iDevice; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), caps.szPname, (size_t)-1); + } + + pInfo += 1; + infoSize -= 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + } + } + + return MAL_SUCCESS; +} + +static void mal_device_uninit__winmm(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + ((MAL_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevice); + } else { + ((MAL_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDevice); + } + + mal_free(pDevice->winmm._pHeapData); + CloseHandle((HANDLE)pDevice->winmm.hEvent); +} + +static mal_result mal_device_init__winmm(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_uint32 heapSize; + mal_uint32 iBit; + + WORD closestBitsPerSample = 0; + WORD closestChannels = 0; + DWORD closestSampleRate = 0; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->winmm); + + UINT winMMDeviceID = 0; + if (pDeviceID != NULL) { + winMMDeviceID = (UINT)pDeviceID->winmm; + } + + const char* errorMsg = ""; + mal_result errorCode = MAL_ERROR; + + + // WinMM doesn't seem to have a good way to query the format of the device. Therefore, we'll restrict the formats to the + // standard formats documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd743855(v=vs.85).aspx. If + // that link goes stale, just look up the documentation for WAVEOUTCAPS or WAVEINCAPS. + WAVEFORMATEX wf; + mal_zero_object(&wf); + wf.cbSize = sizeof(wf); + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = (WORD)pConfig->channels; + wf.nSamplesPerSec = (DWORD)pConfig->sampleRate; + wf.wBitsPerSample = (WORD)mal_get_sample_size_in_bytes(pConfig->format)*8; + + if (wf.nChannels > 2) { + wf.nChannels = 2; + } + + if (wf.wBitsPerSample != 8 && wf.wBitsPerSample != 16) { + if (wf.wBitsPerSample <= 8) { + wf.wBitsPerSample = 8; + } else { + wf.wBitsPerSample = 16; + } + } + + if (wf.nSamplesPerSec <= 11025) { + wf.nSamplesPerSec = 11025; + } else if (wf.nSamplesPerSec <= 22050) { + wf.nSamplesPerSec = 22050; + } else if (wf.nSamplesPerSec <= 44100) { + wf.nSamplesPerSec = 44100; + } else if (wf.nSamplesPerSec <= 48000) { + wf.nSamplesPerSec = 48000; + } else { + wf.nSamplesPerSec = 96000; + } + + + // Change the format based on the closest match of the supported standard formats. + DWORD dwFormats = 0; + if (type == mal_device_type_playback) { + WAVEOUTCAPSA caps; + if (((MAL_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { + dwFormats = caps.dwFormats; + } else { + errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MAL_WINMM_FAILED_TO_GET_DEVICE_CAPS; + goto on_error; + } + } else { + WAVEINCAPSA caps; + if (((MAL_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { + dwFormats = caps.dwFormats; + } else { + errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MAL_WINMM_FAILED_TO_GET_DEVICE_CAPS; + goto on_error; + } + } + + if (dwFormats == 0) { + errorMsg = "[WinMM] Failed to retrieve the supported formats for the internal device.", errorCode = MAL_WINMM_FAILED_TO_GET_SUPPORTED_FORMATS; + goto on_error; + } + + for (iBit = 0; iBit < 32; ++iBit) { + WORD formatBitsPerSample = 0; + WORD formatChannels = 0; + DWORD formatSampleRate = 0; + + DWORD format = (dwFormats & (1 << iBit)); + if (format != 0) { + switch (format) + { + case WAVE_FORMAT_1M08: + { + formatBitsPerSample = 8; + formatChannels = 1; + formatSampleRate = 110025; + } break; + case WAVE_FORMAT_1M16: + { + formatBitsPerSample = 16; + formatChannels = 1; + formatSampleRate = 110025; + } break; + case WAVE_FORMAT_1S08: + { + formatBitsPerSample = 8; + formatChannels = 2; + formatSampleRate = 110025; + } break; + case WAVE_FORMAT_1S16: + { + formatBitsPerSample = 16; + formatChannels = 2; + formatSampleRate = 110025; + } break; + case WAVE_FORMAT_2M08: + { + formatBitsPerSample = 8; + formatChannels = 1; + formatSampleRate = 22050; + } break; + case WAVE_FORMAT_2M16: + { + formatBitsPerSample = 16; + formatChannels = 1; + formatSampleRate = 22050; + } break; + case WAVE_FORMAT_2S08: + { + formatBitsPerSample = 8; + formatChannels = 2; + formatSampleRate = 22050; + } break; + case WAVE_FORMAT_2S16: + { + formatBitsPerSample = 16; + formatChannels = 2; + formatSampleRate = 22050; + } break; + case WAVE_FORMAT_44M08: + { + formatBitsPerSample = 8; + formatChannels = 1; + formatSampleRate = 44100; + } break; + case WAVE_FORMAT_44M16: + { + formatBitsPerSample = 16; + formatChannels = 1; + formatSampleRate = 44100; + } break; + case WAVE_FORMAT_44S08: + { + formatBitsPerSample = 8; + formatChannels = 2; + formatSampleRate = 44100; + } break; + case WAVE_FORMAT_44S16: + { + formatBitsPerSample = 16; + formatChannels = 2; + formatSampleRate = 44100; + } break; + case WAVE_FORMAT_48M08: + { + formatBitsPerSample = 8; + formatChannels = 1; + formatSampleRate = 48000; + } break; + case WAVE_FORMAT_48M16: + { + formatBitsPerSample = 16; + formatChannels = 1; + formatSampleRate = 48000; + } break; + case WAVE_FORMAT_48S08: + { + formatBitsPerSample = 8; + formatChannels = 2; + formatSampleRate = 48000; + } break; + case WAVE_FORMAT_48S16: + { + formatBitsPerSample = 16; + formatChannels = 2; + formatSampleRate = 48000; + } break; + case WAVE_FORMAT_96M08: + { + formatBitsPerSample = 8; + formatChannels = 1; + formatSampleRate = 96000; + } break; + case WAVE_FORMAT_96M16: + { + formatBitsPerSample = 16; + formatChannels = 1; + formatSampleRate = 96000; + } break; + case WAVE_FORMAT_96S08: + { + formatBitsPerSample = 8; + formatChannels = 2; + formatSampleRate = 96000; + } break; + case WAVE_FORMAT_96S16: + { + formatBitsPerSample = 16; + formatChannels = 2; + formatSampleRate = 96000; + } break; + default: + { + errorMsg = "[WinMM] The internal device does not support any of the standard formats.", errorCode = MAL_ERROR; // <-- Should never hit this. + goto on_error; + } break; + } + + if (formatBitsPerSample == wf.wBitsPerSample && formatChannels == wf.nChannels && formatSampleRate == wf.nSamplesPerSec) { + break; // It's an exact match. + } else { + // It's not an exact match. Compare it with the closest match. + if (closestBitsPerSample == 0) { + // This is the first format, so nothing to compare against. + closestBitsPerSample = formatBitsPerSample; + closestChannels = formatChannels; + closestSampleRate = formatSampleRate; + } else { + // Prefer the channel count be the same over the others. + if (formatChannels != closestChannels) { + // Channels aren't equal. Favour the one equal to our desired channel count. + if (formatChannels == wf.nChannels) { + closestBitsPerSample = formatBitsPerSample; + closestChannels = formatChannels; + closestSampleRate = formatSampleRate; + } + } else { + // The channels are equal. Look at the format now. + if (formatBitsPerSample != closestBitsPerSample) { + if (formatBitsPerSample == wf.wBitsPerSample) { + closestBitsPerSample = formatBitsPerSample; + closestChannels = formatChannels; + closestSampleRate = formatSampleRate; + } + } else { + // Both the channels and formats are the same, so now just favour whichever's sample rate is closest to the requested rate. + mal_uint32 closestRateDiff = (closestSampleRate > wf.nSamplesPerSec) ? (closestSampleRate - wf.nSamplesPerSec) : (wf.nSamplesPerSec - closestSampleRate); + mal_uint32 formatRateDiff = (formatSampleRate > wf.nSamplesPerSec) ? (formatSampleRate - wf.nSamplesPerSec) : (wf.nSamplesPerSec - formatSampleRate); + if (formatRateDiff < closestRateDiff) { + closestBitsPerSample = formatBitsPerSample; + closestChannels = formatChannels; + closestSampleRate = formatSampleRate; + } + } + } + } + } + } + } + + wf.wBitsPerSample = closestBitsPerSample; + wf.nChannels = closestChannels; + wf.nSamplesPerSec = closestSampleRate; + wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8; + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + + + // We use an event to know when a new fragment needs to be enqueued. + pDevice->winmm.hEvent = (mal_handle)CreateEvent(NULL, TRUE, TRUE, NULL); + if (pDevice->winmm.hEvent == NULL) { + errorMsg = "[WinMM] Failed to create event for fragment enqueing.", errorCode = MAL_FAILED_TO_CREATE_EVENT; + goto on_error; + } + + + if (type == mal_device_type_playback) { + MMRESULT result = ((MAL_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); + if (result != MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to open playback device.", errorCode = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + goto on_error; + } + } else { + MMRESULT result = ((MAL_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDevice, winMMDeviceID, &wf, (DWORD_PTR)pDevice->winmm.hEvent, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); + if (result != MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to open capture device.", errorCode = MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + goto on_error; + } + } + + + // The internal formats need to be set based on the wf object. + if (wf.wFormatTag == WAVE_FORMAT_PCM) { + switch (wf.wBitsPerSample) { + case 8: pDevice->internalFormat = mal_format_u8; break; + case 16: pDevice->internalFormat = mal_format_s16; break; + case 24: pDevice->internalFormat = mal_format_s24; break; + case 32: pDevice->internalFormat = mal_format_s32; break; + default: mal_post_error(pDevice, "[WinMM] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + } + } else { + errorMsg = "[WinMM] The device's internal format is not supported by mini_al.", errorCode = MAL_FORMAT_NOT_SUPPORTED; + goto on_error; + } + + pDevice->internalChannels = wf.nChannels; + pDevice->internalSampleRate = wf.nSamplesPerSec; + + + // Just use the default channel mapping. WinMM only supports mono or stereo anyway so it'll reliably be left/right order for stereo. + mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); + + + // Latency with WinMM seems pretty bad from my testing... Need to increase the default buffer size. + if (pDevice->usingDefaultBufferSize) { + if (pDevice->type == mal_device_type_playback) { + pDevice->bufferSizeInFrames *= 4; // <-- Might need to fiddle with this to find a more ideal value. May even be able to just add a fixed amount rather than scaling. + } else { + pDevice->bufferSizeInFrames *= 2; + } + } + + // The size of the intermediary buffer needs to be able to fit every fragment. + pDevice->winmm.fragmentSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; + pDevice->winmm.fragmentSizeInBytes = pDevice->winmm.fragmentSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + + heapSize = (sizeof(WAVEHDR) * pDevice->periods) + (pDevice->winmm.fragmentSizeInBytes * pDevice->periods); + pDevice->winmm._pHeapData = (mal_uint8*)mal_malloc(heapSize); + if (pDevice->winmm._pHeapData == NULL) { + errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MAL_OUT_OF_MEMORY; + goto on_error; + } + + mal_zero_memory(pDevice->winmm._pHeapData, pDevice->winmm.fragmentSizeInBytes * pDevice->periods); + + pDevice->winmm.pWAVEHDR = pDevice->winmm._pHeapData; + pDevice->winmm.pIntermediaryBuffer = pDevice->winmm._pHeapData + (sizeof(WAVEHDR) * pDevice->periods); + + + return MAL_SUCCESS; + +on_error: + if (pDevice->type == mal_device_type_playback) { + ((MAL_PFN_waveOutClose)pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevice); + } else { + ((MAL_PFN_waveInClose)pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDevice); + } + + mal_free(pDevice->winmm._pHeapData); + return mal_post_error(pDevice, errorMsg, errorCode); +} + + +static mal_result mal_device__start_backend__winmm(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + // Playback. The device is started when we call waveOutWrite() with a block of data. From MSDN: + // + // Unless the device is paused by calling the waveOutPause function, playback begins when the first data block is sent to the device. + // + // When starting the device we commit every fragment. We signal the event before calling waveOutWrite(). + mal_uint32 i; + for (i = 0; i < pDevice->periods; ++i) { + mal_zero_object(&((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i]); + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBuffer + (pDevice->winmm.fragmentSizeInBytes * i)); + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwBufferLength = pDevice->winmm.fragmentSizeInBytes; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwFlags = 0L; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwLoops = 0L; + mal_device__read_frames_from_client(pDevice, pDevice->winmm.fragmentSizeInFrames, ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].lpData); + + if (((MAL_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { + return mal_post_error(pDevice, "[WinMM] Failed to start backend device. Failed to prepare header.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } + + ResetEvent(pDevice->winmm.hEvent); + + for (i = 0; i < pDevice->periods; ++i) { + if (((MAL_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { + return mal_post_error(pDevice, "[WinMM] Failed to start backend device. Failed to send data to the backend device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } + } else { + // Capture. + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + mal_zero_object(&((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i]); + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBuffer + (pDevice->winmm.fragmentSizeInBytes * i)); + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwBufferLength = pDevice->winmm.fragmentSizeInBytes; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwFlags = 0L; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwLoops = 0L; + + MMRESULT resultMM = ((MAL_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] Failed to prepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); + break; + } + + resultMM = ((MAL_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] Failed to add new capture buffer to the internal capture device.", mal_result_from_MMRESULT(resultMM)); + break; + } + } + + ResetEvent(pDevice->winmm.hEvent); + + if (((MAL_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDevice) != MMSYSERR_NOERROR) { + return mal_post_error(pDevice, "[WinMM] Failed to start backend device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } + + pDevice->winmm.iNextHeader = 0; + return MAL_SUCCESS; +} + +static mal_result mal_device__stop_backend__winmm(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + MMRESULT resultMM = ((MAL_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevice); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] WARNING: Failed to reset playback device.", mal_result_from_MMRESULT(resultMM)); + } + + // Unprepare all WAVEHDR structures. + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + resultMM = ((MAL_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] WARNING: Failed to unprepare header for playback device.", mal_result_from_MMRESULT(resultMM)); + } + } + } else { + MMRESULT resultMM = ((MAL_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDevice); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] WARNING: Failed to reset capture device.", mal_result_from_MMRESULT(resultMM)); + } + + // Unprepare all WAVEHDR structures. + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + resultMM = ((MAL_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] WARNING: Failed to unprepare header for playback device.", mal_result_from_MMRESULT(resultMM)); + } + } + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__break_main_loop__winmm(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->winmm.breakFromMainLoop = MAL_TRUE; + SetEvent((HANDLE)pDevice->winmm.hEvent); + + return MAL_SUCCESS; +} + +static mal_result mal_device__main_loop__winmm(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_uint32 counter; + + pDevice->winmm.breakFromMainLoop = MAL_FALSE; + while (!pDevice->winmm.breakFromMainLoop) { + // Wait for a block of data to finish processing... + if (WaitForSingleObject((HANDLE)pDevice->winmm.hEvent, INFINITE) != WAIT_OBJECT_0) { + break; + } + + // Break from the main loop if the device isn't started anymore. Likely what's happened is the application + // has requested that the device be stopped. + if (!mal_device_is_started(pDevice)) { + break; + } + + // Any headers that are marked as done need to be handled. We start by processing the completed blocks. Then we reset the event + // and then write or add replacement buffers to the device. + mal_uint32 iFirstHeader = pDevice->winmm.iNextHeader; + for (counter = 0; counter < pDevice->periods; ++counter) { + mal_uint32 i = pDevice->winmm.iNextHeader; + if ((((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwFlags & WHDR_DONE) == 0) { + break; + } + + if (pDevice->type == mal_device_type_playback) { + // Playback. + MMRESULT resultMM = ((MAL_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] Failed to unprepare header for playback device in preparation for sending a new block of data to the device for playback.", mal_result_from_MMRESULT(resultMM)); + break; + } + + mal_zero_object(&((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i]); + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBuffer + (pDevice->winmm.fragmentSizeInBytes * i)); + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwBufferLength = pDevice->winmm.fragmentSizeInBytes; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwFlags = 0L; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwLoops = 0L; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwUser = 1; // <-- Used in the next section to identify the buffers that needs to be re-written to the device. + mal_device__read_frames_from_client(pDevice, pDevice->winmm.fragmentSizeInFrames, ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].lpData); + + resultMM = ((MAL_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] Failed to prepare header for playback device in preparation for sending a new block of data to the device for playback.", mal_result_from_MMRESULT(resultMM)); + break; + } + } else { + // Capture. + mal_uint32 framesCaptured = (mal_uint32)(((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwBytesRecorded) / pDevice->internalChannels / mal_get_sample_size_in_bytes(pDevice->internalFormat); + if (framesCaptured > 0) { + mal_device__send_frames_to_client(pDevice, framesCaptured, ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].lpData); + } + + MMRESULT resultMM = ((MAL_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] Failed to unprepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); + break; + } + + mal_zero_object(&((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i]); + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBuffer + (pDevice->winmm.fragmentSizeInBytes * i)); + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwBufferLength = pDevice->winmm.fragmentSizeInBytes; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwFlags = 0L; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwLoops = 0L; + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwUser = 1; // <-- Used in the next section to identify the buffers that needs to be re-added to the device. + + resultMM = ((MAL_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] Failed to prepare header for capture device in preparation for adding a new capture buffer for the device.", mal_result_from_MMRESULT(resultMM)); + break; + } + } + + pDevice->winmm.iNextHeader = (pDevice->winmm.iNextHeader + 1) % pDevice->periods; + } + + ResetEvent((HANDLE)pDevice->winmm.hEvent); + + for (counter = 0; counter < pDevice->periods; ++counter) { + mal_uint32 i = (iFirstHeader + counter) % pDevice->periods; + + if (((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwUser == 1) { + ((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i].dwUser = 0; + + if (pDevice->type == mal_device_type_playback) { + // Playback. + MMRESULT resultMM = ((MAL_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] Failed to write data to the internal playback device.", mal_result_from_MMRESULT(resultMM)); + break; + } + } else { + // Capture. + MMRESULT resultMM = ((MAL_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDevice, &((LPWAVEHDR)pDevice->winmm.pWAVEHDR)[i], sizeof(WAVEHDR)); + if (resultMM != MMSYSERR_NOERROR) { + mal_post_error(pDevice, "[WinMM] Failed to add new capture buffer to the internal capture device.", mal_result_from_MMRESULT(resultMM)); + break; + } + } + } + } + } + + return MAL_SUCCESS; +} +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// +// ALSA Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_ALSA +#include + +// This array allows mini_al to control device-specific default buffer sizes. This uses a scaling factor. Order is important. If +// any part of the string is present in the device's name, the associated scale will be used. +struct +{ + const char* name; + float scale; +} g_malDefaultBufferSizeScalesALSA[] = { + {"bcm2835 IEC958/HDMI", 32}, + {"bcm2835 ALSA", 32} +}; + +static float mal_find_default_buffer_size_scale__alsa(const char* deviceName) +{ + if (deviceName == NULL) { + return 1; + } + + for (size_t i = 0; i < mal_countof(g_malDefaultBufferSizeScalesALSA); ++i) { + if (strstr(g_malDefaultBufferSizeScalesALSA[i].name, deviceName) != NULL) { + return g_malDefaultBufferSizeScalesALSA[i].scale; + } + } + + return 1; +} + + +typedef int (* mal_snd_pcm_open_proc) (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode); +typedef int (* mal_snd_pcm_close_proc) (snd_pcm_t *pcm); +typedef size_t (* mal_snd_pcm_hw_params_sizeof_proc) (void); +typedef int (* mal_snd_pcm_hw_params_any_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +typedef int (* mal_snd_pcm_hw_params_set_format_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); +typedef int (* mal_snd_pcm_hw_params_set_format_first_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t *format); +typedef void (* mal_snd_pcm_hw_params_get_format_mask_proc) (snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); +typedef int (* mal_snd_pcm_hw_params_set_channels_near_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* mal_snd_pcm_hw_params_set_rate_resample_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); +typedef int (* mal_snd_pcm_hw_params_set_rate_near_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* mal_snd_pcm_hw_params_set_buffer_size_near_proc)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); +typedef int (* mal_snd_pcm_hw_params_set_periods_near_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* mal_snd_pcm_hw_params_set_access_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access); +typedef int (* mal_snd_pcm_hw_params_get_format_proc) (snd_pcm_hw_params_t *params, snd_pcm_format_t *format); +typedef int (* mal_snd_pcm_hw_params_get_channels_proc) (snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* mal_snd_pcm_hw_params_get_rate_proc) (snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* mal_snd_pcm_hw_params_get_buffer_size_proc) (snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); +typedef int (* mal_snd_pcm_hw_params_get_periods_proc) (snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* mal_snd_pcm_hw_params_get_access_proc) (snd_pcm_hw_params_t *params, snd_pcm_access_t *_access); +typedef int (* mal_snd_pcm_hw_params_proc) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +typedef size_t (* mal_snd_pcm_sw_params_sizeof_proc) (void); +typedef int (* mal_snd_pcm_sw_params_current_proc) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params); +typedef int (* mal_snd_pcm_sw_params_set_avail_min_proc) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); +typedef int (* mal_snd_pcm_sw_params_set_start_threshold_proc) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); +typedef int (* mal_snd_pcm_sw_params_proc) (snd_pcm_t *pcm, snd_pcm_sw_params_t *params); +typedef size_t (* mal_snd_pcm_format_mask_sizeof_proc) (void); +typedef int (* mal_snd_pcm_format_mask_test_proc) (const snd_pcm_format_mask_t *mask, snd_pcm_format_t val); +typedef snd_pcm_chmap_t * (* mal_snd_pcm_get_chmap_proc) (snd_pcm_t *pcm); +typedef int (* mal_snd_pcm_prepare_proc) (snd_pcm_t *pcm); +typedef int (* mal_snd_pcm_start_proc) (snd_pcm_t *pcm); +typedef int (* mal_snd_pcm_drop_proc) (snd_pcm_t *pcm); +typedef int (* mal_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); +typedef char * (* mal_snd_device_name_get_hint_proc) (const void *hint, const char *id); +typedef int (* mal_snd_card_get_index_proc) (const char *name); +typedef int (* mal_snd_device_name_free_hint_proc) (void **hints); +typedef int (* mal_snd_pcm_mmap_begin_proc) (snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames); +typedef snd_pcm_sframes_t (* mal_snd_pcm_mmap_commit_proc) (snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames); +typedef int (* mal_snd_pcm_recover_proc) (snd_pcm_t *pcm, int err, int silent); +typedef snd_pcm_sframes_t (* mal_snd_pcm_readi_proc) (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size); +typedef snd_pcm_sframes_t (* mal_snd_pcm_writei_proc) (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); +typedef snd_pcm_sframes_t (* mal_snd_pcm_avail_proc) (snd_pcm_t *pcm); +typedef snd_pcm_sframes_t (* mal_snd_pcm_avail_update_proc) (snd_pcm_t *pcm); +typedef int (* mal_snd_pcm_wait_proc) (snd_pcm_t *pcm, int timeout); +typedef int (* mal_snd_pcm_info) (snd_pcm_t *pcm, snd_pcm_info_t* info); +typedef size_t (* mal_snd_pcm_info_sizeof) (); +typedef const char* (* mal_snd_pcm_info_get_name) (const snd_pcm_info_t* info); + +static snd_pcm_format_t g_mal_ALSAFormats[] = { + SND_PCM_FORMAT_UNKNOWN, // mal_format_unknown + SND_PCM_FORMAT_U8, // mal_format_u8 + SND_PCM_FORMAT_S16_LE, // mal_format_s16 + SND_PCM_FORMAT_S24_3LE, // mal_format_s24 + SND_PCM_FORMAT_S32_LE, // mal_format_s32 + SND_PCM_FORMAT_FLOAT_LE // mal_format_f32 +}; + +snd_pcm_format_t mal_convert_mal_format_to_alsa_format(mal_format format) +{ + return g_mal_ALSAFormats[format]; +} + +mal_format mal_convert_alsa_format_to_mal_format(snd_pcm_format_t formatALSA) +{ + switch (formatALSA) + { + case SND_PCM_FORMAT_U8: return mal_format_u8; + case SND_PCM_FORMAT_S16_LE: return mal_format_s16; + case SND_PCM_FORMAT_S24_3LE: return mal_format_s24; + case SND_PCM_FORMAT_S32_LE: return mal_format_s32; + case SND_PCM_FORMAT_FLOAT_LE: return mal_format_f32; + default: return mal_format_unknown; + } +} + +mal_channel mal_convert_alsa_channel_position_to_mal_channel(unsigned int alsaChannelPos) +{ + switch (alsaChannelPos) + { + case SND_CHMAP_FL: return MAL_CHANNEL_FRONT_LEFT; + case SND_CHMAP_FR: return MAL_CHANNEL_FRONT_RIGHT; + case SND_CHMAP_RL: return MAL_CHANNEL_BACK_LEFT; + case SND_CHMAP_RR: return MAL_CHANNEL_BACK_RIGHT; + case SND_CHMAP_FC: return MAL_CHANNEL_FRONT_CENTER; + case SND_CHMAP_LFE: return MAL_CHANNEL_LFE; + case SND_CHMAP_SL: return MAL_CHANNEL_SIDE_LEFT; + case SND_CHMAP_SR: return MAL_CHANNEL_SIDE_RIGHT; + case SND_CHMAP_RC: return MAL_CHANNEL_BACK_CENTER; + case SND_CHMAP_FLC: return MAL_CHANNEL_FRONT_LEFT_CENTER; + case SND_CHMAP_FRC: return MAL_CHANNEL_FRONT_RIGHT_CENTER; + case SND_CHMAP_RLC: return 0; + case SND_CHMAP_RRC: return 0; + case SND_CHMAP_FLW: return 0; + case SND_CHMAP_FRW: return 0; + case SND_CHMAP_FLH: return 0; + case SND_CHMAP_FCH: return 0; + case SND_CHMAP_FRH: return 0; + case SND_CHMAP_TC: return MAL_CHANNEL_TOP_CENTER; + case SND_CHMAP_TFL: return MAL_CHANNEL_TOP_FRONT_LEFT; + case SND_CHMAP_TFR: return MAL_CHANNEL_TOP_FRONT_RIGHT; + case SND_CHMAP_TFC: return MAL_CHANNEL_TOP_FRONT_CENTER; + case SND_CHMAP_TRL: return MAL_CHANNEL_TOP_BACK_LEFT; + case SND_CHMAP_TRR: return MAL_CHANNEL_TOP_BACK_RIGHT; + case SND_CHMAP_TRC: return MAL_CHANNEL_TOP_BACK_CENTER; + default: break; + } + + return 0; +} + +mal_result mal_context_init__alsa(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + pContext->alsa.asoundSO = mal_dlopen("libasound.so"); + if (pContext->alsa.asoundSO == NULL) { + return MAL_NO_BACKEND; + } + + pContext->alsa.snd_pcm_open = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_open"); + pContext->alsa.snd_pcm_close = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_close"); + pContext->alsa.snd_pcm_hw_params_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); + pContext->alsa.snd_pcm_hw_params_any = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); + pContext->alsa.snd_pcm_hw_params_set_format = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); + pContext->alsa.snd_pcm_hw_params_set_format_first = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); + pContext->alsa.snd_pcm_hw_params_get_format_mask = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); + pContext->alsa.snd_pcm_hw_params_set_channels_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); + pContext->alsa.snd_pcm_hw_params_set_rate_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); + pContext->alsa.snd_pcm_hw_params_set_periods_near = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); + pContext->alsa.snd_pcm_hw_params_set_access = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); + pContext->alsa.snd_pcm_hw_params_get_format = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); + pContext->alsa.snd_pcm_hw_params_get_channels = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); + pContext->alsa.snd_pcm_hw_params_get_rate = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); + pContext->alsa.snd_pcm_hw_params_get_periods = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); + pContext->alsa.snd_pcm_hw_params_get_access = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); + pContext->alsa.snd_pcm_hw_params = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_hw_params"); + pContext->alsa.snd_pcm_sw_params_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); + pContext->alsa.snd_pcm_sw_params_current = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); + pContext->alsa.snd_pcm_sw_params_set_avail_min = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); + pContext->alsa.snd_pcm_sw_params = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_sw_params"); + pContext->alsa.snd_pcm_format_mask_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); + pContext->alsa.snd_pcm_format_mask_test = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); + pContext->alsa.snd_pcm_get_chmap = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_get_chmap"); + pContext->alsa.snd_pcm_prepare = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_prepare"); + pContext->alsa.snd_pcm_start = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_start"); + pContext->alsa.snd_pcm_drop = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_drop"); + pContext->alsa.snd_device_name_hint = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_device_name_hint"); + pContext->alsa.snd_device_name_get_hint = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_device_name_get_hint"); + pContext->alsa.snd_card_get_index = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_card_get_index"); + pContext->alsa.snd_device_name_free_hint = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_device_name_free_hint"); + pContext->alsa.snd_pcm_mmap_begin = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); + pContext->alsa.snd_pcm_mmap_commit = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); + pContext->alsa.snd_pcm_recover = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_recover"); + pContext->alsa.snd_pcm_readi = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_readi"); + pContext->alsa.snd_pcm_writei = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_writei"); + pContext->alsa.snd_pcm_avail = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail"); + pContext->alsa.snd_pcm_avail_update = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_avail_update"); + pContext->alsa.snd_pcm_wait = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_wait"); + pContext->alsa.snd_pcm_info = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info"); + pContext->alsa.snd_pcm_info_sizeof = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); + pContext->alsa.snd_pcm_info_get_name = (mal_proc)mal_dlsym(pContext->alsa.asoundSO, "snd_pcm_info_get_name"); + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__alsa(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_alsa); + + (void)pContext; + return MAL_SUCCESS; +} + +static const char* mal_find_char(const char* str, char c, int* index) +{ + int i = 0; + for (;;) { + if (str[i] == '\0') { + if (index) *index = -1; + return NULL; + } + + if (str[i] == c) { + if (index) *index = i; + return str + i; + } + + i += 1; + } + + // Should never get here, but treat it as though the character was not found to make me feel + // better inside. + if (index) *index = -1; + return NULL; +} + +// Waits for a number of frames to become available for either capture or playback. The return +// value is the number of frames available. +// +// This will return early if the main loop is broken with mal_device__break_main_loop(). +static mal_uint32 mal_device__wait_for_frames__alsa(mal_device* pDevice, mal_bool32* pRequiresRestart) +{ + mal_assert(pDevice != NULL); + + if (pRequiresRestart) *pRequiresRestart = MAL_FALSE; + + mal_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; + + while (!pDevice->alsa.breakFromMainLoop) { + // Wait for something to become available. The timeout should not affect latency - it's only used to break from the wait + // so we can check whether or not the device has been stopped. + const int timeoutInMilliseconds = 10; + int waitResult = ((mal_snd_pcm_wait_proc)pDevice->pContext->alsa.snd_pcm_wait)((snd_pcm_t*)pDevice->alsa.pPCM, timeoutInMilliseconds); + if (waitResult < 0) { + if (waitResult == -EPIPE) { + if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, waitResult, MAL_TRUE) < 0) { + return 0; + } + + if (pRequiresRestart) *pRequiresRestart = MAL_TRUE; // A device recovery means a restart for mmap mode. + } + } + + if (pDevice->alsa.breakFromMainLoop) { + return 0; + } + + snd_pcm_sframes_t framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((snd_pcm_t*)pDevice->alsa.pPCM); + if (framesAvailable < 0) { + if (framesAvailable == -EPIPE) { + if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, framesAvailable, MAL_TRUE) < 0) { + return 0; + } + + if (pRequiresRestart) *pRequiresRestart = MAL_TRUE; // A device recovery means a restart for mmap mode. + + // Try again, but if it fails this time just return an error. + framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((snd_pcm_t*)pDevice->alsa.pPCM); + if (framesAvailable < 0) { + return 0; + } + } + } + + // Keep the returned number of samples consistent and based on the period size. + if (framesAvailable >= periodSizeInFrames) { + return periodSizeInFrames; + } + } + + // We'll get here if the loop was terminated. Just return whatever's available. + snd_pcm_sframes_t framesAvailable = ((mal_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((snd_pcm_t*)pDevice->alsa.pPCM); + if (framesAvailable < 0) { + return 0; + } + + return framesAvailable; +} + +static mal_bool32 mal_device_write__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + if (!mal_device_is_started(pDevice) && mal_device__get_state(pDevice) != MAL_STATE_STARTING) { + return MAL_FALSE; + } + if (pDevice->alsa.breakFromMainLoop) { + return MAL_FALSE; + } + + + if (pDevice->alsa.isUsingMMap) { + // mmap. + mal_bool32 requiresRestart; + mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, &requiresRestart); + if (framesAvailable == 0) { + return MAL_FALSE; + } + + // Don't bother asking the client for more audio data if we're just stopping the device anyway. + if (pDevice->alsa.breakFromMainLoop) { + return MAL_FALSE; + } + + const snd_pcm_channel_area_t* pAreas; + snd_pcm_uframes_t mappedOffset; + snd_pcm_uframes_t mappedFrames = framesAvailable; + while (framesAvailable > 0) { + int result = ((mal_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames); + if (result < 0) { + return MAL_FALSE; + } + + if (mappedFrames > 0) { + void* pBuffer = (mal_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8); + mal_device__read_frames_from_client(pDevice, mappedFrames, pBuffer); + } + + result = ((mal_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames); + if (result < 0 || (snd_pcm_uframes_t)result != mappedFrames) { + ((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, result, MAL_TRUE); + return MAL_FALSE; + } + + if (requiresRestart) { + if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return MAL_FALSE; + } + } + + framesAvailable -= mappedFrames; + } + } else { + // readi/writei. + while (!pDevice->alsa.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, NULL); + if (framesAvailable == 0) { + continue; + } + + // Don't bother asking the client for more audio data if we're just stopping the device anyway. + if (pDevice->alsa.breakFromMainLoop) { + return MAL_FALSE; + } + + mal_device__read_frames_from_client(pDevice, framesAvailable, pDevice->alsa.pIntermediaryBuffer); + + snd_pcm_sframes_t framesWritten = ((mal_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); + if (framesWritten < 0) { + if (framesWritten == -EAGAIN) { + continue; // Just keep trying... + } else if (framesWritten == -EPIPE) { + // Underrun. Just recover and try writing again. + if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, framesWritten, MAL_TRUE) < 0) { + mal_post_error(pDevice, "[ALSA] Failed to recover device after underrun.", MAL_ALSA_FAILED_TO_RECOVER_DEVICE); + return MAL_FALSE; + } + + framesWritten = ((mal_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); + if (framesWritten < 0) { + mal_post_error(pDevice, "[ALSA] Failed to write data to the internal device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + return MAL_FALSE; + } + + break; // Success. + } else { + mal_post_error(pDevice, "[ALSA] snd_pcm_writei() failed when writing initial data.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + return MAL_FALSE; + } + } else { + break; // Success. + } + } + } + + return MAL_TRUE; +} + +static mal_bool32 mal_device_read__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + if (!mal_device_is_started(pDevice)) { + return MAL_FALSE; + } + if (pDevice->alsa.breakFromMainLoop) { + return MAL_FALSE; + } + + mal_uint32 framesToSend = 0; + void* pBuffer = NULL; + if (pDevice->alsa.pIntermediaryBuffer == NULL) { + // mmap. + mal_bool32 requiresRestart; + mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, &requiresRestart); + if (framesAvailable == 0) { + return MAL_FALSE; + } + + const snd_pcm_channel_area_t* pAreas; + snd_pcm_uframes_t mappedOffset; + snd_pcm_uframes_t mappedFrames = framesAvailable; + while (framesAvailable > 0) { + int result = ((mal_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames); + if (result < 0) { + return MAL_FALSE; + } + + if (mappedFrames > 0) { + void* pBuffer = (mal_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8); + mal_device__send_frames_to_client(pDevice, mappedFrames, pBuffer); + } + + result = ((mal_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames); + if (result < 0 || (snd_pcm_uframes_t)result != mappedFrames) { + ((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, result, MAL_TRUE); + return MAL_FALSE; + } + + if (requiresRestart) { + if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return MAL_FALSE; + } + } + + framesAvailable -= mappedFrames; + } + } else { + // readi/writei. + snd_pcm_sframes_t framesRead = 0; + while (!pDevice->alsa.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__wait_for_frames__alsa(pDevice, NULL); + if (framesAvailable == 0) { + continue; + } + + framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); + if (framesRead < 0) { + if (framesRead == -EAGAIN) { + continue; // Just keep trying... + } else if (framesRead == -EPIPE) { + // Overrun. Just recover and try reading again. + if (((mal_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((snd_pcm_t*)pDevice->alsa.pPCM, framesRead, MAL_TRUE) < 0) { + mal_post_error(pDevice, "[ALSA] Failed to recover device after overrun.", MAL_ALSA_FAILED_TO_RECOVER_DEVICE); + return MAL_FALSE; + } + + framesRead = ((mal_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable); + if (framesRead < 0) { + mal_post_error(pDevice, "[ALSA] Failed to read data from the internal device.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + return MAL_FALSE; + } + + break; // Success. + } else { + return MAL_FALSE; + } + } else { + break; // Success. + } + } + + framesToSend = framesRead; + pBuffer = pDevice->alsa.pIntermediaryBuffer; + } + + if (framesToSend > 0) { + mal_device__send_frames_to_client(pDevice, framesToSend, pBuffer); + } + + return MAL_TRUE; +} + + + +static mal_bool32 mal_is_device_name_in_hw_format__alsa(const char* hwid) +{ + // This function is just checking whether or not hwid is in "hw:%d,%d" format. + + if (hwid == NULL) { + return MAL_FALSE; + } + + if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { + return MAL_FALSE; + } + + hwid += 3; + + int commaPos; + const char* dev = mal_find_char(hwid, ',', &commaPos); + if (dev == NULL) { + return MAL_FALSE; + } else { + dev += 1; // Skip past the ",". + } + + // Check if the part between the ":" and the "," contains only numbers. If not, return false. + for (int i = 0; i < commaPos; ++i) { + if (hwid[i] < '0' || hwid[i] > '9') { + return MAL_FALSE; + } + } + + // Check if everything after the "," is numeric. If not, return false. + int i = 0; + while (dev[i] != '\0') { + if (dev[i] < '0' || dev[i] > '9') { + return MAL_FALSE; + } + i += 1; + } + + return MAL_TRUE; +} + +static int mal_convert_device_name_to_hw_format__alsa(mal_context* pContext, char* dst, size_t dstSize, const char* src) // Returns 0 on success, non-0 on error. +{ + // src should look something like this: "hw:CARD=I82801AAICH,DEV=0" + + if (dst == NULL) return -1; + if (dstSize < 7) return -1; // Absolute minimum size of the output buffer is 7 bytes. + + *dst = '\0'; // Safety. + if (src == NULL) return -1; + + // If the input name is already in "hw:%d,%d" format, just return that verbatim. + if (mal_is_device_name_in_hw_format__alsa(src)) { + return mal_strcpy_s(dst, dstSize, src); + } + + + int colonPos; + src = mal_find_char(src, ':', &colonPos); + if (src == NULL) { + return -1; // Couldn't find a colon + } + + char card[256]; + + int commaPos; + const char* dev = mal_find_char(src, ',', &commaPos); + if (dev == NULL) { + dev = "0"; + mal_strncpy_s(card, sizeof(card), src+6, (size_t)-1); // +6 = ":CARD=" + } else { + dev = dev + 5; // +5 = ",DEV=" + mal_strncpy_s(card, sizeof(card), src+6, commaPos-6); // +6 = ":CARD=" + } + + int cardIndex = ((mal_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); + if (cardIndex < 0) { + return -2; // Failed to retrieve the card index. + } + + //printf("TESTING: CARD=%s,DEV=%s\n", card, dev); + + + // Construction. + dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; + if (mal_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { + return -3; + } + if (mal_strcat_s(dst, dstSize, ",") != 0) { + return -3; + } + if (mal_strcat_s(dst, dstSize, dev) != 0) { + return -3; + } + + return 0; +} + +static mal_bool32 mal_does_id_exist_in_list__alsa(mal_device_id* pUniqueIDs, mal_uint32 count, const char* pHWID) +{ + mal_assert(pHWID != NULL); + + for (mal_uint32 i = 0; i < count; ++i) { + if (mal_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { + return MAL_TRUE; + } + } + + return MAL_FALSE; +} + +static mal_result mal_enumerate_devices__alsa(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + (void)pContext; + + mal_uint32 infoSize = *pCount; + *pCount = 0; + + char** ppDeviceHints; + if (((mal_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) { + return MAL_NO_BACKEND; + } + + mal_device_id* pUniqueIDs = NULL; + mal_uint32 uniqueIDCount = 0; + + char** ppNextDeviceHint = ppDeviceHints; + while (*ppNextDeviceHint != NULL) { + char* NAME = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); + char* DESC = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); + char* IOID = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); + + // Only include devices if they are of the correct type. Special cases for "default", "null" and "pulse" - these are included for both playback and capture + // regardless of the IOID setting. + mal_bool32 includeThisDevice = MAL_FALSE; + if (strcmp(NAME, "default") == 0 || strcmp(NAME, "pulse") == 0 || strcmp(NAME, "null") == 0) { + includeThisDevice = MAL_TRUE; + + // Exclude the "null" device if requested. + if (strcmp(NAME, "null") == 0 && pContext->config.alsa.excludeNullDevice) { + includeThisDevice = MAL_FALSE; + } + } else { + if ((type == mal_device_type_playback && (IOID == NULL || strcmp(IOID, "Output") == 0)) || + (type == mal_device_type_capture && (IOID != NULL && strcmp(IOID, "Input" ) == 0))) { + includeThisDevice = MAL_TRUE; + } + } + + + + if (includeThisDevice) { +#if 0 + printf("NAME: %s\n", NAME); + printf("DESC: %s\n", DESC); + printf("IOID: %s\n", IOID); + + char hwid2[256]; + mal_convert_device_name_to_hw_format__alsa(pContext, hwid2, sizeof(hwid2), NAME); + printf("DEVICE ID: %s (%d)\n\n", hwid2, *pCount); +#endif + + char hwid[sizeof(pUniqueIDs->alsa)]; + if (NAME != NULL) { + if (pContext->config.alsa.useVerboseDeviceEnumeration) { + // Verbose mode. Use the name exactly as-is. + mal_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); + } else { + // Simplified mode. Use ":%d,%d" format. + if (mal_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { + // At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the + // plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device + // initialization time and is used as an indicator to try and use the most appropriate plugin depending on the + // device type and sharing mode. + char* dst = hwid; + char* src = hwid+2; + while ((*dst++ = *src++)); + } else { + // Conversion to "hw:%d,%d" failed. Just use the name as-is. + mal_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); + } + + if (mal_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { + goto next_device; // The device has already been enumerated. Move on to the next one. + } else { + // The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. + mal_device_id* pNewUniqueIDs = mal_realloc(pUniqueIDs, sizeof(*pUniqueIDs) * (uniqueIDCount + 1)); + if (pNewUniqueIDs == NULL) { + goto next_device; // Failed to allocate memory. + } + + pUniqueIDs = pNewUniqueIDs; + mal_copy_memory(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); + uniqueIDCount += 1; + } + } + } else { + mal_zero_memory(hwid, sizeof(hwid)); + } + + if (pInfo != NULL) { + if (infoSize > 0) { + mal_zero_object(pInfo); + mal_strncpy_s(pInfo->id.alsa, sizeof(pInfo->id.alsa), hwid, (size_t)-1); + + // DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose + // device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish + // between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the + // description. + // + // The value in DESC seems to be split into two lines, with the first line being the name of the device and the + // second line being a description of the device. I don't like having the description be across two lines because + // it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line + // being put into parentheses. In simplified mode I'm just stripping the second line entirely. + if (DESC != NULL) { + int lfPos; + const char* line2 = mal_find_char(DESC, '\n', &lfPos); + if (line2 != NULL) { + line2 += 1; // Skip past the new-line character. + + if (pContext->config.alsa.useVerboseDeviceEnumeration) { + // Verbose mode. Put the second line in brackets. + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), DESC, lfPos); + mal_strcat_s (pInfo->name, sizeof(pInfo->name), " ("); + mal_strcat_s (pInfo->name, sizeof(pInfo->name), line2); + mal_strcat_s (pInfo->name, sizeof(pInfo->name), ")"); + } else { + // Simplified mode. Strip the second line entirely. + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), DESC, lfPos); + } + } else { + // There's no second line. Just copy the whole description. + mal_strcpy_s(pInfo->name, sizeof(pInfo->name), DESC); + } + } + + pInfo += 1; + infoSize -= 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + } + + next_device: + free(NAME); + free(DESC); + free(IOID); + ppNextDeviceHint += 1; + } + + mal_free(pUniqueIDs); + + ((mal_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); + return MAL_SUCCESS; +} + +static void mal_device_uninit__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if ((snd_pcm_t*)pDevice->alsa.pPCM) { + ((mal_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((snd_pcm_t*)pDevice->alsa.pPCM); + + if (pDevice->alsa.pIntermediaryBuffer != NULL) { + mal_free(pDevice->alsa.pIntermediaryBuffer); + } + } +} + +static mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->alsa); + + snd_pcm_format_t formatALSA = mal_convert_mal_format_to_alsa_format(pConfig->format); + snd_pcm_stream_t stream = (type == mal_device_type_playback) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE; + + if (pDeviceID == NULL) { + // We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes + // me feel better to try as hard as we can get to get _something_ working. + const char* defaultDeviceNames[] = { + "default", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + if (pConfig->preferExclusiveMode) { + defaultDeviceNames[1] = "hw"; + defaultDeviceNames[2] = "hw:0"; + defaultDeviceNames[3] = "hw:0,0"; + } else { + if (type == mal_device_type_playback) { + defaultDeviceNames[1] = "dmix"; + defaultDeviceNames[2] = "dmix:0"; + defaultDeviceNames[3] = "dmix:0,0"; + } else { + defaultDeviceNames[1] = "dsnoop"; + defaultDeviceNames[2] = "dsnoop:0"; + defaultDeviceNames[3] = "dsnoop:0,0"; + } + defaultDeviceNames[4] = "hw"; + defaultDeviceNames[5] = "hw:0"; + defaultDeviceNames[6] = "hw:0,0"; + } + + mal_bool32 isDeviceOpen = MAL_FALSE; + for (size_t i = 0; i < mal_countof(defaultDeviceNames); ++i) { + if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { + if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((snd_pcm_t**)&pDevice->alsa.pPCM, defaultDeviceNames[i], stream, 0) == 0) { + isDeviceOpen = MAL_TRUE; + break; + } + } + } + + if (!isDeviceOpen) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.", MAL_ALSA_FAILED_TO_OPEN_DEVICE); + } + } else { + // We're trying to open a specific device. There's a few things to consider here: + // + // mini_al recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When + // an ID of this format is specified, it indicates to mini_al that it can try different combinations of plugins ("hw", "dmix", etc.) until it + // finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). + mal_bool32 isDeviceOpen = MAL_FALSE; + if (pDeviceID->alsa[0] != ':') { + // The ID is not in ":0,0" format. Use the ID exactly as-is. + if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((snd_pcm_t**)&pDevice->alsa.pPCM, pDeviceID->alsa, stream, 0) == 0) { + isDeviceOpen = MAL_TRUE; + } + } else { + // The ID is in ":0,0" format. Try different plugins depending on the shared mode. + if (pDeviceID->alsa[1] == '\0') { + pDeviceID->alsa[0] = '\0'; // An ID of ":" should be converted to "". + } + + char hwid[256]; + if (!pConfig->preferExclusiveMode) { + if (type == mal_device_type_playback) { + mal_strcpy_s(hwid, sizeof(hwid), "dmix"); + } else { + mal_strcpy_s(hwid, sizeof(hwid), "dsnoop"); + } + + if (mal_strcat_s(hwid, sizeof(hwid), pDeviceID->alsa) == 0) { + if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((snd_pcm_t**)&pDevice->alsa.pPCM, hwid, stream, 0) == 0) { + isDeviceOpen = MAL_TRUE; + } + } + } + + // If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. + if (!isDeviceOpen) { + mal_strcpy_s(hwid, sizeof(hwid), "hw"); + if (mal_strcat_s(hwid, sizeof(hwid), pDeviceID->alsa) == 0) { + if (((mal_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)((snd_pcm_t**)&pDevice->alsa.pPCM, hwid, stream, 0) == 0) { + isDeviceOpen = MAL_TRUE; + } + } + } + } + + if (!isDeviceOpen) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] snd_pcm_open() failed.", MAL_ALSA_FAILED_TO_OPEN_DEVICE); + } + } + + // We may need to scale the size of the buffer depending on the device. + if (pDevice->usingDefaultBufferSize) { + float bufferSizeScale = 1; + + snd_pcm_info_t* pInfo = (snd_pcm_info_t*)alloca(((mal_snd_pcm_info_sizeof)pContext->alsa.snd_pcm_info_sizeof)()); + mal_zero_memory(pInfo, ((mal_snd_pcm_info_sizeof)pContext->alsa.snd_pcm_info_sizeof)()); + + if (((mal_snd_pcm_info)pContext->alsa.snd_pcm_info)((snd_pcm_t*)pDevice->alsa.pPCM, pInfo) == 0) { + const char* deviceName = ((mal_snd_pcm_info_get_name)pContext->alsa.snd_pcm_info_get_name)(pInfo); + if (deviceName != NULL) { + if (strcmp(deviceName, "default") == 0) { + // It's the default device. We need to use DESC from snd_device_name_hint(). + char** ppDeviceHints; + if (((mal_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) { + return MAL_NO_BACKEND; + } + + char** ppNextDeviceHint = ppDeviceHints; + while (*ppNextDeviceHint != NULL) { + char* NAME = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); + char* DESC = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); + char* IOID = ((mal_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); + + mal_bool32 foundDevice = MAL_FALSE; + if ((type == mal_device_type_playback && (IOID == NULL || strcmp(IOID, "Output") == 0)) || + (type == mal_device_type_capture && (IOID != NULL && strcmp(IOID, "Input" ) == 0))) { + if (strcmp(NAME, deviceName) == 0) { + bufferSizeScale = mal_find_default_buffer_size_scale__alsa(DESC); + foundDevice = MAL_TRUE; + } + } + + free(NAME); + free(DESC); + free(IOID); + + if (foundDevice) { + break; + } + } + + ((mal_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); + } else { + bufferSizeScale = mal_find_default_buffer_size_scale__alsa(deviceName); + } + } + + pDevice->bufferSizeInFrames = (mal_uint32)(pDevice->bufferSizeInFrames * bufferSizeScale); + } + } + + + // Hardware parameters. + snd_pcm_hw_params_t* pHWParams = (snd_pcm_hw_params_t*)alloca(((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); + mal_zero_memory(pHWParams, ((mal_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)()); + + if (((mal_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MAL_ALSA_FAILED_TO_SET_HW_PARAMS); + } + + + // MMAP Mode + // + // Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. + pDevice->alsa.isUsingMMap = MAL_FALSE; + if (!pConfig->alsa.noMMap && pDevice->type != mal_device_type_capture) { // <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it so I can test it... Contributions welcome. + if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { + pDevice->alsa.isUsingMMap = MAL_TRUE; + } + } + + if (!pDevice->alsa.isUsingMMap) { + if (((mal_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {; + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", MAL_FORMAT_NOT_SUPPORTED); + } + } + + + // Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't + // find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. + + // Format. + // Try getting every supported format. + snd_pcm_format_mask_t* pFormatMask = (snd_pcm_format_mask_t*)alloca(((mal_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)()); + mal_zero_memory(pFormatMask, ((mal_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)()); + + ((mal_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask); + + // At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is + // supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. + if (!((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, formatALSA)) { + // The requested format is not supported so now try running through the list of formats and return the best one. + snd_pcm_format_t preferredFormatsALSA[] = { + SND_PCM_FORMAT_FLOAT_LE, // mal_format_f32 + SND_PCM_FORMAT_S32_LE, // mal_format_s32 + SND_PCM_FORMAT_S24_3LE, // mal_format_s24 + SND_PCM_FORMAT_S16_LE, // mal_format_s16 + SND_PCM_FORMAT_U8 // mal_format_u8 + }; + + formatALSA = SND_PCM_FORMAT_UNKNOWN; + for (size_t i = 0; i < (sizeof(preferredFormatsALSA) / sizeof(preferredFormatsALSA[0])); ++i) { + if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, preferredFormatsALSA[i])) { + formatALSA = preferredFormatsALSA[i]; + break; + } + } + + if (formatALSA == SND_PCM_FORMAT_UNKNOWN) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Format not supported. The device does not support any mini_al formats.", MAL_FORMAT_NOT_SUPPORTED); + } + } + + if (((mal_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, formatALSA) < 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalFormat = mal_convert_alsa_format_to_mal_format(formatALSA); + if (pDevice->internalFormat == mal_format_unknown) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] The chosen format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + } + + // Channels. + mal_uint32 channels = pConfig->channels; + if (((mal_snd_pcm_hw_params_set_channels_near_proc)pContext->alsa.snd_pcm_hw_params_set_channels_near)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &channels) < 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", MAL_FORMAT_NOT_SUPPORTED); + } + pDevice->internalChannels = channels; + + + // Sample Rate. It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling + // enabled causes problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we + // need to disable resampling. + // + // To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a + // sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling + // doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly + // faster rate. + // + // mini_al has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine + // for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very + // good quality until I get a chance to improve the quality of mini_al's software sample rate conversion. + // + // I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce + // this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. + ((mal_snd_pcm_hw_params_set_rate_resample_proc)pContext->alsa.snd_pcm_hw_params_set_rate_resample)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, 0); + + mal_uint32 sampleRate = pConfig->sampleRate; + if (((mal_snd_pcm_hw_params_set_rate_near_proc)pContext->alsa.snd_pcm_hw_params_set_rate_near)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &sampleRate, 0) < 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", MAL_FORMAT_NOT_SUPPORTED); + } + pDevice->internalSampleRate = sampleRate; + + + // Periods. + mal_uint32 periods = pConfig->periods; + int dir = 0; + if (((mal_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &periods, &dir) < 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", MAL_FORMAT_NOT_SUPPORTED); + } + pDevice->periods = periods; + + // Buffer Size + snd_pcm_uframes_t actualBufferSize = pDevice->bufferSizeInFrames; + if (((mal_snd_pcm_hw_params_set_buffer_size_near_proc)pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams, &actualBufferSize) < 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", MAL_FORMAT_NOT_SUPPORTED); + } + pDevice->bufferSizeInFrames = actualBufferSize; + + + // Apply hardware parameters. + if (((mal_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)((snd_pcm_t*)pDevice->alsa.pPCM, pHWParams) < 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", MAL_ALSA_FAILED_TO_SET_HW_PARAMS); + } + + + + + // Software parameters. + snd_pcm_sw_params_t* pSWParams = (snd_pcm_sw_params_t*)alloca(((mal_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)()); + mal_zero_memory(pSWParams, ((mal_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)()); + + if (((mal_snd_pcm_sw_params_current_proc)pContext->alsa.snd_pcm_sw_params_current)((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS); + } + + if (((mal_snd_pcm_sw_params_set_avail_min_proc)pContext->alsa.snd_pcm_sw_params_set_avail_min)((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, (pDevice->sampleRate/1000) * 1) != 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", MAL_FORMAT_NOT_SUPPORTED); + } + + if (type == mal_device_type_playback && !pDevice->alsa.isUsingMMap) { // Only playback devices in writei/readi mode need a start threshold. + if (((mal_snd_pcm_sw_params_set_start_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_start_threshold)((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams, (pDevice->sampleRate/1000) * 1) != 0) { //mal_prev_power_of_2(pDevice->bufferSizeInFrames/pDevice->periods) + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS); + } + } + + if (((mal_snd_pcm_sw_params_proc)pContext->alsa.snd_pcm_sw_params)((snd_pcm_t*)pDevice->alsa.pPCM, pSWParams) != 0) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", MAL_ALSA_FAILED_TO_SET_SW_PARAMS); + } + + + + // If we're _not_ using mmap we need to use an intermediary buffer. + if (!pDevice->alsa.isUsingMMap) { + pDevice->alsa.pIntermediaryBuffer = mal_malloc(pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format)); + if (pDevice->alsa.pIntermediaryBuffer == NULL) { + mal_device_uninit__alsa(pDevice); + return mal_post_error(pDevice, "[ALSA] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + } + } + + + + // Grab the internal channel map. For now we're not going to bother trying to change the channel map and + // instead just do it ourselves. + snd_pcm_chmap_t* pChmap = ((mal_snd_pcm_get_chmap_proc)pContext->alsa.snd_pcm_get_chmap)((snd_pcm_t*)pDevice->alsa.pPCM); + if (pChmap != NULL) { + // There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). + if (pChmap->channels >= pDevice->internalChannels) { + // Drop excess channels. + for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) { + pDevice->internalChannelMap[iChannel] = mal_convert_alsa_channel_position_to_mal_channel(pChmap->pos[iChannel]); + } + } else { + // Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate + // channels. If validation fails, fall back to defaults. + + // Fill with defaults. + mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); + + // Overwrite first pChmap->channels channels. + for (mal_uint32 iChannel = 0; iChannel < pChmap->channels; ++iChannel) { + pDevice->internalChannelMap[iChannel] = mal_convert_alsa_channel_position_to_mal_channel(pChmap->pos[iChannel]); + } + + // Validate. + mal_bool32 isValid = MAL_TRUE; + for (mal_uint32 i = 0; i < pDevice->internalChannels && isValid; ++i) { + for (mal_uint32 j = i+1; j < pDevice->internalChannels; ++j) { + if (pDevice->internalChannelMap[i] == pDevice->internalChannelMap[j]) { + isValid = MAL_FALSE; + break; + } + } + } + + // If our channel map is invalid, fall back to defaults. + if (!isValid) { + mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); + } + } + + free(pChmap); + pChmap = NULL; + } else { + // Could not retrieve the channel map. Fall back to a hard-coded assumption. + mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); + } + + return MAL_SUCCESS; +} + + +static mal_result mal_device__start_backend__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // Prepare the device first... + if (((mal_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return mal_post_error(pDevice, "[ALSA] Failed to prepare device.", MAL_ALSA_FAILED_TO_PREPARE_DEVICE); + } + + // ... and then grab an initial chunk from the client. After this is done, the device should + // automatically start playing, since that's how we configured the software parameters. + if (pDevice->type == mal_device_type_playback) { + if (!mal_device_write__alsa(pDevice)) { + return mal_post_error(pDevice, "[ALSA] Failed to write initial chunk of data to the playback device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + + // mmap mode requires an explicit start. + if (pDevice->alsa.isUsingMMap) { + if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return mal_post_error(pDevice, "[ALSA] Failed to start capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } + } else { + if (((mal_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((snd_pcm_t*)pDevice->alsa.pPCM) < 0) { + return mal_post_error(pDevice, "[ALSA] Failed to start capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__stop_backend__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + ((mal_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((snd_pcm_t*)pDevice->alsa.pPCM); + return MAL_SUCCESS; +} + +static mal_result mal_device__break_main_loop__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // Fallback. We just set a variable to tell the worker thread to terminate after handling the + // next bunch of frames. This is a slow way of handling this. + pDevice->alsa.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +static mal_result mal_device__main_loop__alsa(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->alsa.breakFromMainLoop = MAL_FALSE; + if (pDevice->type == mal_device_type_playback) { + // Playback. Read from client, write to device. + while (!pDevice->alsa.breakFromMainLoop && mal_device_write__alsa(pDevice)) { + } + } else { + // Capture. Read from device, write to client. + while (!pDevice->alsa.breakFromMainLoop && mal_device_read__alsa(pDevice)) { + } + } + + return MAL_SUCCESS; +} +#endif // ALSA + + +/////////////////////////////////////////////////////////////////////////////// +// +// OSS Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_OSS +#include +#include +#include +#include + +int mal_open_temp_device__oss() +{ + // The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. + int fd = open("/dev/mixer", O_RDONLY, 0); + if (fd >= 0) { + return fd; + } + + return -1; +} + +mal_result mal_context_init__oss(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + // Try opening a temporary device first so we can get version information. This is closed at the end. + int fd = mal_open_temp_device__oss(); + if (fd == -1) { + return mal_context_post_error(pContext, NULL, "[OSS] Failed to open temporary device for retrieving system properties.", MAL_NO_BACKEND); // Looks liks OSS isn't installed, or there are no available devices. + } + + // Grab the OSS version. + int ossVersion = 0; + int result = ioctl(fd, OSS_GETVERSION, &ossVersion); + if (result == -1) { + close(fd); + return mal_context_post_error(pContext, NULL, "[OSS] Failed to retrieve OSS version.", MAL_NO_BACKEND); + } + + pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); + pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); + + close(fd); + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__oss(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_oss); + + (void)pContext; + return MAL_SUCCESS; +} + +static mal_result mal_enumerate_devices__oss(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + (void)pContext; + + mal_uint32 infoSize = *pCount; + *pCount = 0; + + // The object returned by SNDCTL_SYSINFO will have the information we're after. + int fd = mal_open_temp_device__oss(); + if (fd == -1) { + return mal_context_post_error(pContext, NULL, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MAL_NO_BACKEND); + } + + oss_sysinfo si; + int result = ioctl(fd, SNDCTL_SYSINFO, &si); + if (result != -1) { + for (int iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { + oss_audioinfo ai; + ai.dev = iAudioDevice; + result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); + if (result != -1) { + mal_bool32 includeThisDevice = MAL_FALSE; + if (type == mal_device_type_playback && (ai.caps & PCM_CAP_OUTPUT) != 0) { + includeThisDevice = MAL_TRUE; + } else if (type == mal_device_type_capture && (ai.caps & PCM_CAP_INPUT) != 0) { + includeThisDevice = MAL_TRUE; + } + + if (includeThisDevice) { + if (ai.devnode[0] != '\0') { // <-- Can be blank, according to documentation. + if (pInfo != NULL) { + if (infoSize > 0) { + mal_strncpy_s(pInfo->id.oss, sizeof(pInfo->id.oss), ai.devnode, (size_t)-1); + + // The human readable device name should be in the "ai.handle" variable, but it can + // sometimes be empty in which case we just fall back to "ai.name" which is less user + // friendly, but usually has a value. + if (ai.handle[0] != '\0') { + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), ai.handle, (size_t)-1); + } else { + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), ai.name, (size_t)-1); + } + + pInfo += 1; + infoSize -= 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + } + } + } + } + } else { + // Failed to retrieve the system information. Just return a default device for both playback and capture. + if (pInfo != NULL) { + if (infoSize > 0) { + mal_strncpy_s(pInfo[0].id.oss, sizeof(pInfo[0].id.oss), "/dev/dsp", (size_t)-1); + if (type == mal_device_type_playback) { + mal_strncpy_s(pInfo[0].name, sizeof(pInfo[0].name), "Default Playback Device", (size_t)-1); + } else { + mal_strncpy_s(pInfo[0].name, sizeof(pInfo[0].name), "Default Capture Device", (size_t)-1); + } + + *pCount = 1; + } + } else { + *pCount = 1; + } + } + + close(fd); + return MAL_SUCCESS; +} + +static void mal_device_uninit__oss(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + close(pDevice->oss.fd); + mal_free(pDevice->oss.pIntermediaryBuffer); +} + +static mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->oss); + + char deviceName[64]; + if (pDeviceID != NULL) { + mal_strncpy_s(deviceName, sizeof(deviceName), pDeviceID->oss, (size_t)-1); + } else { + mal_strncpy_s(deviceName, sizeof(deviceName), "/dev/dsp", (size_t)-1); + } + + pDevice->oss.fd = open(deviceName, (type == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0); + if (pDevice->oss.fd == -1) { + return mal_post_error(pDevice, "[OSS] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + // The OSS documantation is very clear about the order we should be initializing the device's properties: + // 1) Format + // 2) Channels + // 3) Sample rate. + + // Format. + int ossFormat = AFMT_U8; + switch (pDevice->format) { + case mal_format_s16: ossFormat = AFMT_S16_LE; break; + case mal_format_s24: ossFormat = AFMT_S32_LE; break; + case mal_format_s32: ossFormat = AFMT_S32_LE; break; + case mal_format_f32: ossFormat = AFMT_S32_LE; break; + case mal_format_u8: + default: ossFormat = AFMT_U8; break; + } + int result = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFMT, &ossFormat); + if (result == -1) { + close(pDevice->oss.fd); + return mal_post_error(pDevice, "[OSS] Failed to set format.", MAL_FORMAT_NOT_SUPPORTED); + } + + switch (ossFormat) { + case AFMT_U8: pDevice->internalFormat = mal_format_u8; break; + case AFMT_S16_LE: pDevice->internalFormat = mal_format_s16; break; + case AFMT_S32_LE: pDevice->internalFormat = mal_format_s32; break; + default: mal_post_error(pDevice, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED); + } + + + // Channels. + int ossChannels = (int)pConfig->channels; + result = ioctl(pDevice->oss.fd, SNDCTL_DSP_CHANNELS, &ossChannels); + if (result == -1) { + close(pDevice->oss.fd); + return mal_post_error(pDevice, "[OSS] Failed to set channel count.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->internalChannels = ossChannels; + + + // Sample rate. + int ossSampleRate = (int)pConfig->sampleRate; + result = ioctl(pDevice->oss.fd, SNDCTL_DSP_SPEED, &ossSampleRate); + if (result == -1) { + close(pDevice->oss.fd); + return mal_post_error(pDevice, "[OSS] Failed to set sample rate.", MAL_FORMAT_NOT_SUPPORTED); + } + + pDevice->sampleRate = ossSampleRate; + + + + // The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if + // it should be done before or after format/channels/rate. + // + // OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual + // value. + mal_uint32 fragmentSizeInBytes = mal_round_to_power_of_2(pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat)); + if (fragmentSizeInBytes < 16) { + fragmentSizeInBytes = 16; + } + + mal_uint32 ossFragmentSizePower = 4; + fragmentSizeInBytes >>= 4; + while (fragmentSizeInBytes >>= 1) { + ossFragmentSizePower += 1; + } + + int ossFragment = (int)((pDevice->periods << 16) | ossFragmentSizePower); + result = ioctl(pDevice->oss.fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); + if (result == -1) { + close(pDevice->oss.fd); + return mal_post_error(pDevice, "[OSS] Failed to set fragment size and period count.", MAL_FORMAT_NOT_SUPPORTED); + } + + int actualFragmentSizeInBytes = 1 << (ossFragment & 0xFFFF); + pDevice->oss.fragmentSizeInFrames = actualFragmentSizeInBytes / mal_get_sample_size_in_bytes(pDevice->internalFormat) / pDevice->internalChannels; + + pDevice->periods = (mal_uint32)(ossFragment >> 16); + pDevice->bufferSizeInFrames = (mal_uint32)(pDevice->oss.fragmentSizeInFrames * pDevice->periods); + + + // Set the internal channel map. Not sure if this can be queried. For now just using our default assumptions. + mal_get_default_channel_mapping(pDevice->pContext->backend, pDevice->internalChannels, pDevice->internalChannelMap); + + + // When not using MMAP mode, we need to use an intermediary buffer for the client <-> device transfer. We do + // everything by the size of a fragment. + pDevice->oss.pIntermediaryBuffer = mal_malloc(fragmentSizeInBytes); + if (pDevice->oss.pIntermediaryBuffer == NULL) { + close(pDevice->oss.fd); + return mal_post_error(pDevice, "[OSS] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + } + + return MAL_SUCCESS; +} + + +static mal_result mal_device__start_backend__oss(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // The device is started by the next calls to read() and write(). For playback it's simple - just read + // data from the client, then write it to the device with write() which will in turn start the device. + // For capture it's a bit less intuitive - we do nothing (it'll be started automatically by the first + // call to read(). + if (pDevice->type == mal_device_type_playback) { + // Playback. + mal_device__read_frames_from_client(pDevice, pDevice->oss.fragmentSizeInFrames, pDevice->oss.pIntermediaryBuffer); + + int bytesWritten = write(pDevice->oss.fd, pDevice->oss.pIntermediaryBuffer, pDevice->oss.fragmentSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat)); + if (bytesWritten == -1) { + return mal_post_error(pDevice, "[OSS] Failed to send initial chunk of data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + } else { + // Capture. Do nothing. + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__stop_backend__oss(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // We want to use SNDCTL_DSP_HALT. From the documentation: + // + // In multithreaded applications SNDCTL_DSP_HALT (SNDCTL_DSP_RESET) must only be called by the thread + // that actually reads/writes the audio device. It must not be called by some master thread to kill the + // audio thread. The audio thread will not stop or get any kind of notification that the device was + // stopped by the master thread. The device gets stopped but the next read or write call will silently + // restart the device. + // + // This is actually safe in our case, because this function is only ever called from within our worker + // thread anyway. Just keep this in mind, though... + + int result = ioctl(pDevice->oss.fd, SNDCTL_DSP_HALT, 0); + if (result == -1) { + return mal_post_error(pDevice, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__break_main_loop__oss(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->oss.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +static mal_result mal_device__main_loop__oss(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->oss.breakFromMainLoop = MAL_FALSE; + while (!pDevice->oss.breakFromMainLoop) { + // Break from the main loop if the device isn't started anymore. Likely what's happened is the application + // has requested that the device be stopped. + if (!mal_device_is_started(pDevice)) { + break; + } + + if (pDevice->type == mal_device_type_playback) { + // Playback. + mal_device__read_frames_from_client(pDevice, pDevice->oss.fragmentSizeInFrames, pDevice->oss.pIntermediaryBuffer); + + int bytesWritten = write(pDevice->oss.fd, pDevice->oss.pIntermediaryBuffer, pDevice->oss.fragmentSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat)); + if (bytesWritten < 0) { + return mal_post_error(pDevice, "[OSS] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE); + } + } else { + // Capture. + int bytesRead = read(pDevice->oss.fd, pDevice->oss.pIntermediaryBuffer, pDevice->oss.fragmentSizeInFrames * mal_get_sample_size_in_bytes(pDevice->internalFormat)); + if (bytesRead < 0) { + return mal_post_error(pDevice, "[OSS] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE); + } + + mal_uint32 framesRead = (mal_uint32)bytesRead / pDevice->internalChannels / mal_get_sample_size_in_bytes(pDevice->internalFormat); + mal_device__send_frames_to_client(pDevice, framesRead, pDevice->oss.pIntermediaryBuffer); + } + } + + return MAL_SUCCESS; +} +#endif // OSS + + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenSL|ES Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_OPENSL +#include +#ifdef MAL_ANDROID +#include +#endif + +// Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to mini_al. +static mal_uint8 mal_channel_id_to_mal__opensl(SLuint32 id) +{ + switch (id) + { + case SL_SPEAKER_FRONT_LEFT: return MAL_CHANNEL_FRONT_LEFT; + case SL_SPEAKER_FRONT_RIGHT: return MAL_CHANNEL_FRONT_RIGHT; + case SL_SPEAKER_FRONT_CENTER: return MAL_CHANNEL_FRONT_CENTER; + case SL_SPEAKER_LOW_FREQUENCY: return MAL_CHANNEL_LFE; + case SL_SPEAKER_BACK_LEFT: return MAL_CHANNEL_BACK_LEFT; + case SL_SPEAKER_BACK_RIGHT: return MAL_CHANNEL_BACK_RIGHT; + case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MAL_CHANNEL_FRONT_LEFT_CENTER; + case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MAL_CHANNEL_FRONT_RIGHT_CENTER; + case SL_SPEAKER_BACK_CENTER: return MAL_CHANNEL_BACK_CENTER; + case SL_SPEAKER_SIDE_LEFT: return MAL_CHANNEL_SIDE_LEFT; + case SL_SPEAKER_SIDE_RIGHT: return MAL_CHANNEL_SIDE_RIGHT; + case SL_SPEAKER_TOP_CENTER: return MAL_CHANNEL_TOP_CENTER; + case SL_SPEAKER_TOP_FRONT_LEFT: return MAL_CHANNEL_TOP_FRONT_LEFT; + case SL_SPEAKER_TOP_FRONT_CENTER: return MAL_CHANNEL_TOP_FRONT_CENTER; + case SL_SPEAKER_TOP_FRONT_RIGHT: return MAL_CHANNEL_TOP_FRONT_RIGHT; + case SL_SPEAKER_TOP_BACK_LEFT: return MAL_CHANNEL_TOP_BACK_LEFT; + case SL_SPEAKER_TOP_BACK_CENTER: return MAL_CHANNEL_TOP_BACK_CENTER; + case SL_SPEAKER_TOP_BACK_RIGHT: return MAL_CHANNEL_TOP_BACK_RIGHT; + default: return 0; + } +} + +// Converts an individual mini_al channel identifier (MAL_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. +static SLuint32 mal_channel_id_to_opensl(mal_uint8 id) +{ + switch (id) + { + case MAL_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; + case MAL_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; + case MAL_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; + case MAL_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; + case MAL_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; + case MAL_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; + case MAL_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; + case MAL_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; + case MAL_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; + case MAL_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; + case MAL_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; + case MAL_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; + case MAL_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; + case MAL_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; + case MAL_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; + case MAL_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; + case MAL_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; + case MAL_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; + default: return 0; + } +} + +// Converts a channel mapping to an OpenSL-style channel mask. +static SLuint32 mal_channel_map_to_channel_mask__opensl(const mal_uint8 channelMap[MAL_MAX_CHANNELS], mal_uint32 channels) +{ + SLuint32 channelMask = 0; + for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { + channelMask |= mal_channel_id_to_opensl(channelMap[iChannel]); + } + + return channelMask; +} + +// Converts an OpenSL-style channel mask to a mini_al channel map. +static void mal_channel_mask_to_channel_map__opensl(SLuint32 channelMask, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +{ + if (channels == 2 && channelMask == 0) { + channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + } else { + // Just iterate over each bit. + mal_uint32 iChannel = 0; + for (mal_uint32 iBit = 0; iBit < 32; ++iBit) { + SLuint32 bitValue = (channelMask & (1 << iBit)); + if (bitValue != 0) { + // The bit is set. + channelMap[iChannel] = mal_channel_id_to_mal__opensl(bitValue); + iChannel += 1; + } + } + } +} + +SLuint32 mal_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) +{ + if (samplesPerSec <= SL_SAMPLINGRATE_8) { + return SL_SAMPLINGRATE_8; + } + if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { + return SL_SAMPLINGRATE_11_025; + } + if (samplesPerSec <= SL_SAMPLINGRATE_12) { + return SL_SAMPLINGRATE_12; + } + if (samplesPerSec <= SL_SAMPLINGRATE_16) { + return SL_SAMPLINGRATE_16; + } + if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { + return SL_SAMPLINGRATE_22_05; + } + if (samplesPerSec <= SL_SAMPLINGRATE_24) { + return SL_SAMPLINGRATE_24; + } + if (samplesPerSec <= SL_SAMPLINGRATE_32) { + return SL_SAMPLINGRATE_32; + } + if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { + return SL_SAMPLINGRATE_44_1; + } + if (samplesPerSec <= SL_SAMPLINGRATE_48) { + return SL_SAMPLINGRATE_48; + } + + // Android doesn't support more than 48000. +#ifndef MAL_ANDROID + if (samplesPerSec <= SL_SAMPLINGRATE_64) { + return SL_SAMPLINGRATE_64; + } + if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { + return SL_SAMPLINGRATE_88_2; + } + if (samplesPerSec <= SL_SAMPLINGRATE_96) { + return SL_SAMPLINGRATE_96; + } + if (samplesPerSec <= SL_SAMPLINGRATE_192) { + return SL_SAMPLINGRATE_192; + } +#endif + + return SL_SAMPLINGRATE_16; +} + +mal_result mal_context_init__opensl(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__opensl(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_opensl); + + (void)pContext; + return MAL_SUCCESS; +} + +mal_result mal_enumerate_devices__opensl(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + (void)pContext; + + mal_uint32 infoSize = *pCount; + *pCount = 0; + + SLObjectItf engineObj; + SLresult resultSL = slCreateEngine(&engineObj, 0, NULL, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + return MAL_NO_BACKEND; + } + + (*engineObj)->Realize(engineObj, SL_BOOLEAN_FALSE); + + // TODO: Test Me. + // + // This is currently untested, so for now we are just returning default devices. +#if 0 + SLuint32 pDeviceIDs[128]; + SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); + + SLAudioIODeviceCapabilitiesItf deviceCaps; + resultSL = (*engineObj)->GetInterface(engineObj, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + if (resultSL != SL_RESULT_SUCCESS) { + // The interface may not be supported so just report a default device. + (*engineObj)->Destroy(engineObj); + goto return_default_device; + } + + if (type == mal_device_type_playback) { + resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); + if (resultSL != SL_RESULT_SUCCESS) { + (*engineObj)->Destroy(engineObj); + return MAL_NO_DEVICE; + } + } else { + resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); + if (resultSL != SL_RESULT_SUCCESS) { + (*engineObj)->Destroy(engineObj); + return MAL_NO_DEVICE; + } + } + + for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + if (pInfo != NULL) { + if (infoSize > 0) { + mal_zero_object(pInfo); + pInfo->id.opensl = pDeviceIDs[iDevice]; + + mal_bool32 isValidDevice = MAL_TRUE; + if (type == mal_device_type_playback) { + SLAudioOutputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pInfo->id.opensl, &desc); + if (resultSL != SL_RESULT_SUCCESS) { + isValidDevice = MAL_FALSE; + } + + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), (const char*)desc.pDeviceName, (size_t)-1); + } else { + SLAudioInputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pInfo->id.opensl, &desc); + if (resultSL != SL_RESULT_SUCCESS) { + isValidDevice = MAL_FALSE; + } + + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), (const char*)desc.deviceName, (size_t)-1); + } + + if (isValidDevice) { + pInfo += 1; + infoSize -= 1; + *pCount += 1; + } + } + } else { + *pCount += 1; + } + } + + (*engineObj)->Destroy(engineObj); + return MAL_SUCCESS; +#else + (*engineObj)->Destroy(engineObj); + goto return_default_device; +#endif + +return_default_device: + *pCount = 1; + if (pInfo != NULL) { + if (infoSize > 0) { + if (type == mal_device_type_playback) { + pInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1); + } else { + pInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1); + } + } + } + + return MAL_SUCCESS; +} + + +// OpenSL|ES has one-per-application objects :( +static SLObjectItf g_malEngineObjectSL = NULL; +static SLEngineItf g_malEngineSL = NULL; +static mal_uint32 g_malOpenSLInitCounter = 0; + +#define MAL_OPENSL_OBJ(p) (*((SLObjectItf)(p))) +#define MAL_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) +#define MAL_OPENSL_PLAY(p) (*((SLPlayItf)(p))) +#define MAL_OPENSL_RECORD(p) (*((SLRecordItf)(p))) + +#ifdef MAL_ANDROID +#define MAL_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) +#else +#define MAL_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) +#endif + +#ifdef MAL_ANDROID +//static void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext) +static void mal_buffer_queue_callback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) +{ + (void)pBufferQueue; + + // For now, only supporting Android implementations of OpenSL|ES since that's the only one I've + // been able to test with and I currently depend on Android-specific extensions (simple buffer + // queues). +#ifndef MAL_ANDROID + return MAL_NO_BACKEND; +#endif + + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + // For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like + // OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, + // but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( + if (pDevice->state != MAL_STATE_STARTED) { + return; + } + + size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + mal_uint8* pBuffer = pDevice->opensl.pBuffer + (pDevice->opensl.currentBufferIndex * periodSizeInBytes); + + if (pDevice->type == mal_device_type_playback) { + if (pDevice->state != MAL_STATE_STARTED) { + return; + } + + mal_device__read_frames_from_client(pDevice, pDevice->opensl.periodSizeInFrames, pBuffer); + + SLresult resultSL = MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, pBuffer, periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + return; + } + } else { + mal_device__send_frames_to_client(pDevice, pDevice->opensl.periodSizeInFrames, pBuffer); + + SLresult resultSL = MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, pBuffer, periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + return; + } + } + + pDevice->opensl.currentBufferIndex = (pDevice->opensl.currentBufferIndex + 1) % pDevice->periods; +} +#endif + +static void mal_device_uninit__opensl(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + // Uninit device. + if (pDevice->type == mal_device_type_playback) { + if (pDevice->opensl.pAudioPlayerObj) MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); + if (pDevice->opensl.pOutputMixObj) MAL_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); + } else { + if (pDevice->opensl.pAudioRecorderObj) MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); + } + + mal_free(pDevice->opensl.pBuffer); + + + // Uninit global data. + if (g_malOpenSLInitCounter > 0) { + if (mal_atomic_decrement_32(&g_malOpenSLInitCounter) == 0) { + (*g_malEngineObjectSL)->Destroy(g_malEngineObjectSL); + } + } +} + +static mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + (void)pContext; + + // For now, only supporting Android implementations of OpenSL|ES since that's the only one I've + // been able to test with and I currently depend on Android-specific extensions (simple buffer + // queues). +#ifndef MAL_ANDROID + return MAL_NO_BACKEND; +#endif + + // Use s32 as the internal format for when floating point is specified. + if (pConfig->format == mal_format_f32) { + pDevice->internalFormat = mal_format_s32; + } + + // Initialize global data first if applicable. + if (mal_atomic_increment_32(&g_malOpenSLInitCounter) == 1) { + SLresult resultSL = slCreateEngine(&g_malEngineObjectSL, 0, NULL, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + mal_atomic_decrement_32(&g_malOpenSLInitCounter); + return mal_post_error(pDevice, "[OpenSL] slCreateEngine() failed.", MAL_NO_BACKEND); + } + + (*g_malEngineObjectSL)->Realize(g_malEngineObjectSL, SL_BOOLEAN_FALSE); + + resultSL = (*g_malEngineObjectSL)->GetInterface(g_malEngineObjectSL, SL_IID_ENGINE, &g_malEngineSL); + if (resultSL != SL_RESULT_SUCCESS) { + (*g_malEngineObjectSL)->Destroy(g_malEngineObjectSL); + mal_atomic_decrement_32(&g_malOpenSLInitCounter); + return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_ENGINE interface.", MAL_NO_BACKEND); + } + } + + + // Now we can start initializing the device properly. + mal_assert(pDevice != NULL); + mal_zero_object(&pDevice->opensl); + + pDevice->opensl.currentBufferIndex = 0; + pDevice->opensl.periodSizeInFrames = pDevice->bufferSizeInFrames / pConfig->periods; + pDevice->bufferSizeInFrames = pDevice->opensl.periodSizeInFrames * pConfig->periods; + + SLDataLocator_AndroidSimpleBufferQueue queue; + queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + queue.numBuffers = pConfig->periods; + + SLDataFormat_PCM* pFormat = NULL; + +#if defined(MAL_ANDROID) && __ANDROID_API__ >= 21 + SLAndroidDataFormat_PCM_EX pcmEx; + if (pDevice->format == mal_format_f32 /*|| pDevice->format == mal_format_f64*/) { + pcmEx.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; + pcmEx.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; + } else { + pcmEx.formatType = SL_DATAFORMAT_PCM; + } + pFormat = (SLDataFormat_PCM*)&pcmEx; +#else + SLDataFormat_PCM pcm; + pcm.formatType = SL_DATAFORMAT_PCM; + pFormat = &pcm; +#endif + + pFormat->numChannels = pDevice->channels; + pFormat->samplesPerSec = mal_round_to_standard_sample_rate__opensl(pDevice->sampleRate * 1000); // In millihertz. + pFormat->bitsPerSample = mal_get_sample_size_in_bytes(pDevice->format)*8; + pFormat->containerSize = pFormat->bitsPerSample; // Always tightly packed for now. + pFormat->channelMask = mal_channel_map_to_channel_mask__opensl(pConfig->channelMap, pFormat->numChannels); + pFormat->endianness = SL_BYTEORDER_LITTLEENDIAN; + + // Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html + // - Only mono and stereo is supported. + // - Only u8 and s16 formats are supported. + // - Limited to a sample rate of 48000. +#ifdef MAL_ANDROID + if (pFormat->numChannels > 2) { + pFormat->numChannels = 2; + } +#if __ANDROID_API__ >= 21 + if (pFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { + // It's floating point. + mal_assert(pcmEx.representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); + if (pFormat->bitsPerSample > 32) { + pFormat->bitsPerSample = 32; + } + } else { + if (pFormat->bitsPerSample > 16) { + pFormat->bitsPerSample = 16; + } + } +#else + if (pFormat->bitsPerSample > 16) { + pFormat->bitsPerSample = 16; + } +#endif + pFormat->containerSize = pFormat->bitsPerSample; // Always tightly packed for now. + + if (pFormat->samplesPerSec > SL_SAMPLINGRATE_48) { + pFormat->samplesPerSec = SL_SAMPLINGRATE_48; + } +#endif + + if (type == mal_device_type_playback) { + SLresult resultSL = (*g_malEngineSL)->CreateOutputMix(g_malEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to create output mix.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (MAL_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE)) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to realize output mix object.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (MAL_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix) != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + // Set the output device. + if (pDeviceID != NULL) { + MAL_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &pDeviceID->opensl); + } + + SLDataSource source; + source.pLocator = &queue; + source.pFormat = pFormat; + + SLDataLocator_OutputMix outmixLocator; + outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; + outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; + + SLDataSink sink; + sink.pLocator = &outmixLocator; + sink.pFormat = NULL; + + const SLInterfaceID itfIDs1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; + const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE}; + resultSL = (*g_malEngineSL)->CreateAudioPlayer(g_malEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1); + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) { + // Unsupported format. Fall back to something safer and try again. If this fails, just abort. + pFormat->formatType = SL_DATAFORMAT_PCM; + pFormat->numChannels = 2; + pFormat->samplesPerSec = SL_SAMPLINGRATE_16; + pFormat->bitsPerSample = 16; + pFormat->containerSize = pFormat->bitsPerSample; // Always tightly packed for now. + pFormat->channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + resultSL = (*g_malEngineSL)->CreateAudioPlayer(g_malEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1); + } + + if (resultSL != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to create audio player.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + + if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to realize audio player.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_PLAY, &pDevice->opensl.pAudioPlayer) != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueue) != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, mal_buffer_queue_callback__opensl_android, pDevice) != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to register buffer queue callback.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } else { + SLDataLocator_IODevice locatorDevice; + locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; + locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; + locatorDevice.deviceID = (pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pDeviceID->opensl; + locatorDevice.device = NULL; + + SLDataSource source; + source.pLocator = &locatorDevice; + source.pFormat = NULL; + + SLDataSink sink; + sink.pLocator = &queue; + sink.pFormat = pFormat; + + const SLInterfaceID itfIDs1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; + const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE}; + SLresult resultSL = (*g_malEngineSL)->CreateAudioRecorder(g_malEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1); + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) { + // Unsupported format. Fall back to something safer and try again. If this fails, just abort. + pFormat->formatType = SL_DATAFORMAT_PCM; + pFormat->numChannels = 1; + pFormat->samplesPerSec = SL_SAMPLINGRATE_16; + pFormat->bitsPerSample = 16; + pFormat->containerSize = pFormat->bitsPerSample; // Always tightly packed for now. + pFormat->channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + resultSL = (*g_malEngineSL)->CreateAudioRecorder(g_malEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1); + } + + if (resultSL != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to create audio recorder.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to realize audio recorder.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_RECORD, &pDevice->opensl.pAudioRecorder) != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueue) != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + if (MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, mal_buffer_queue_callback__opensl_android, pDevice) != SL_RESULT_SUCCESS) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to register buffer queue callback.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } + + + // The internal format is determined by the pFormat object. + mal_bool32 isFloatingPoint = MAL_FALSE; +#if defined(MAL_ANDROID) && __ANDROID_API__ >= 21 + if (pFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { + mal_assert(pcmEx.representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); + isFloatingPoint = MAL_TRUE; + } +#endif + if (isFloatingPoint) { + if (pFormat->bitsPerSample == 32) { + pDevice->internalFormat = mal_format_f32; + } +#if 0 + if (pFormat->bitsPerSample == 64) { + pDevice->internalFormat = mal_format_f64; + } +#endif + } else { + if (pFormat->bitsPerSample == 8) { + pDevice->internalFormat = mal_format_u8; + } else if (pFormat->bitsPerSample == 16) { + pDevice->internalFormat = mal_format_s16; + } else if (pFormat->bitsPerSample == 24) { + pDevice->internalFormat = mal_format_s24; + } else if (pFormat->bitsPerSample == 32) { + pDevice->internalFormat = mal_format_s32; + } + } + + pDevice->internalChannels = pFormat->numChannels; + pDevice->internalSampleRate = pFormat->samplesPerSec / 1000; + mal_channel_mask_to_channel_map__opensl(pFormat->channelMask, pDevice->internalChannels, pDevice->internalChannelMap); + + + size_t bufferSizeInBytes = pDevice->bufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + pDevice->opensl.pBuffer = (mal_uint8*)mal_malloc(bufferSizeInBytes); + if (pDevice->opensl.pBuffer == NULL) { + mal_device_uninit__opensl(pDevice); + return mal_post_error(pDevice, "[OpenSL] Failed to allocate memory for data buffer.", MAL_OUT_OF_MEMORY); + } + + mal_zero_memory(pDevice->opensl.pBuffer, bufferSizeInBytes); + + return MAL_SUCCESS; +} + +static mal_result mal_device__start_backend__opensl(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); + if (resultSL != SL_RESULT_SUCCESS) { + return mal_post_error(pDevice, "[OpenSL] Failed to start internal playback device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + + // We need to enqueue a buffer for each period. + mal_device__read_frames_from_client(pDevice, pDevice->bufferSizeInFrames, pDevice->opensl.pBuffer); + + size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; ++iPeriod) { + resultSL = MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, pDevice->opensl.pBuffer + (periodSizeInBytes * iPeriod), periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); + return mal_post_error(pDevice, "[OpenSL] Failed to enqueue buffer for playback device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } + } else { + SLresult resultSL = MAL_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); + if (resultSL != SL_RESULT_SUCCESS) { + return mal_post_error(pDevice, "[OpenSL] Failed to start internal capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + + size_t periodSizeInBytes = pDevice->opensl.periodSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat); + for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; ++iPeriod) { + resultSL = MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue, pDevice->opensl.pBuffer + (periodSizeInBytes * iPeriod), periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + MAL_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); + return mal_post_error(pDevice, "[OpenSL] Failed to enqueue buffer for capture device.", MAL_FAILED_TO_START_BACKEND_DEVICE); + } + } + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__stop_backend__opensl(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); + if (resultSL != SL_RESULT_SUCCESS) { + return mal_post_error(pDevice, "[OpenSL] Failed to stop internal playback device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } + } else { + SLresult resultSL = MAL_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); + if (resultSL != SL_RESULT_SUCCESS) { + return mal_post_error(pDevice, "[OpenSL] Failed to stop internal capture device.", MAL_FAILED_TO_STOP_BACKEND_DEVICE); + } + } + + // Make sure any queued buffers are cleared. + MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue); + + // Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + if (pDevice->onStop) { + pDevice->onStop(pDevice); + } + + return MAL_SUCCESS; +} +#endif // OpenSL|ES + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenAL Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_OPENAL +#ifdef MAL_WIN32 +#define MAL_AL_APIENTRY __cdecl +#else +#define MAL_AL_APIENTRY +#endif + +#ifdef MAL_NO_RUNTIME_LINKING + #if defined(MAL_APPLE) + #include + #include + #else + #include + #include + #endif +#endif + +typedef struct mal_ALCdevice_struct mal_ALCdevice; +typedef struct mal_ALCcontext_struct mal_ALCcontext; +typedef char mal_ALCboolean; +typedef char mal_ALCchar; +typedef signed char mal_ALCbyte; +typedef unsigned char mal_ALCubyte; +typedef short mal_ALCshort; +typedef unsigned short mal_ALCushort; +typedef int mal_ALCint; +typedef unsigned int mal_ALCuint; +typedef int mal_ALCsizei; +typedef int mal_ALCenum; +typedef float mal_ALCfloat; +typedef double mal_ALCdouble; +typedef void mal_ALCvoid; + +typedef mal_ALCboolean mal_ALboolean; +typedef mal_ALCchar mal_ALchar; +typedef mal_ALCbyte mal_ALbyte; +typedef mal_ALCubyte mal_ALubyte; +typedef mal_ALCshort mal_ALshort; +typedef mal_ALCushort mal_ALushort; +typedef mal_ALCint mal_ALint; +typedef mal_ALCuint mal_ALuint; +typedef mal_ALCsizei mal_ALsizei; +typedef mal_ALCenum mal_ALenum; +typedef mal_ALCfloat mal_ALfloat; +typedef mal_ALCdouble mal_ALdouble; +typedef mal_ALCvoid mal_ALvoid; + +#define MAL_ALC_DEVICE_SPECIFIER 0x1005 +#define MAL_ALC_CAPTURE_DEVICE_SPECIFIER 0x310 +#define MAL_ALC_CAPTURE_SAMPLES 0x312 + +#define MAL_AL_SOURCE_STATE 0x1010 +#define MAL_AL_INITIAL 0x1011 +#define MAL_AL_PLAYING 0x1012 +#define MAL_AL_PAUSED 0x1013 +#define MAL_AL_STOPPED 0x1014 +#define MAL_AL_BUFFERS_PROCESSED 0x1016 + +#define MAL_AL_FORMAT_MONO8 0x1100 +#define MAL_AL_FORMAT_MONO16 0x1101 +#define MAL_AL_FORMAT_STEREO8 0x1102 +#define MAL_AL_FORMAT_STEREO16 0x1103 +#define MAL_AL_FORMAT_MONO_FLOAT32 0x10010 +#define MAL_AL_FORMAT_STEREO_FLOAT32 0x10011 +#define MAL_AL_FORMAT_51CHN16 0x120B +#define MAL_AL_FORMAT_51CHN32 0x120C +#define MAL_AL_FORMAT_51CHN8 0x120A +#define MAL_AL_FORMAT_61CHN16 0x120E +#define MAL_AL_FORMAT_61CHN32 0x120F +#define MAL_AL_FORMAT_61CHN8 0x120D +#define MAL_AL_FORMAT_71CHN16 0x1211 +#define MAL_AL_FORMAT_71CHN32 0x1212 +#define MAL_AL_FORMAT_71CHN8 0x1210 +#define MAL_AL_FORMAT_QUAD16 0x1205 +#define MAL_AL_FORMAT_QUAD32 0x1206 +#define MAL_AL_FORMAT_QUAD8 0x1204 +#define MAL_AL_FORMAT_REAR16 0x1208 +#define MAL_AL_FORMAT_REAR32 0x1209 +#define MAL_AL_FORMAT_REAR8 0x1207 + +typedef mal_ALCcontext* (MAL_AL_APIENTRY * MAL_LPALCCREATECONTEXT) (mal_ALCdevice *device, const mal_ALCint *attrlist); +typedef mal_ALCboolean (MAL_AL_APIENTRY * MAL_LPALCMAKECONTEXTCURRENT) (mal_ALCcontext *context); +typedef void (MAL_AL_APIENTRY * MAL_LPALCPROCESSCONTEXT) (mal_ALCcontext *context); +typedef void (MAL_AL_APIENTRY * MAL_LPALCSUSPENDCONTEXT) (mal_ALCcontext *context); +typedef void (MAL_AL_APIENTRY * MAL_LPALCDESTROYCONTEXT) (mal_ALCcontext *context); +typedef mal_ALCcontext* (MAL_AL_APIENTRY * MAL_LPALCGETCURRENTCONTEXT) (void); +typedef mal_ALCdevice* (MAL_AL_APIENTRY * MAL_LPALCGETCONTEXTSDEVICE) (mal_ALCcontext *context); +typedef mal_ALCdevice* (MAL_AL_APIENTRY * MAL_LPALCOPENDEVICE) (const mal_ALCchar *devicename); +typedef mal_ALCboolean (MAL_AL_APIENTRY * MAL_LPALCCLOSEDEVICE) (mal_ALCdevice *device); +typedef mal_ALCenum (MAL_AL_APIENTRY * MAL_LPALCGETERROR) (mal_ALCdevice *device); +typedef mal_ALCboolean (MAL_AL_APIENTRY * MAL_LPALCISEXTENSIONPRESENT) (mal_ALCdevice *device, const mal_ALCchar *extname); +typedef void* (MAL_AL_APIENTRY * MAL_LPALCGETPROCADDRESS) (mal_ALCdevice *device, const mal_ALCchar *funcname); +typedef mal_ALCenum (MAL_AL_APIENTRY * MAL_LPALCGETENUMVALUE) (mal_ALCdevice *device, const mal_ALCchar *enumname); +typedef const mal_ALCchar* (MAL_AL_APIENTRY * MAL_LPALCGETSTRING) (mal_ALCdevice *device, mal_ALCenum param); +typedef void (MAL_AL_APIENTRY * MAL_LPALCGETINTEGERV) (mal_ALCdevice *device, mal_ALCenum param, mal_ALCsizei size, mal_ALCint *values); +typedef mal_ALCdevice* (MAL_AL_APIENTRY * MAL_LPALCCAPTUREOPENDEVICE) (const mal_ALCchar *devicename, mal_ALCuint frequency, mal_ALCenum format, mal_ALCsizei buffersize); +typedef mal_ALCboolean (MAL_AL_APIENTRY * MAL_LPALCCAPTURECLOSEDEVICE) (mal_ALCdevice *device); +typedef void (MAL_AL_APIENTRY * MAL_LPALCCAPTURESTART) (mal_ALCdevice *device); +typedef void (MAL_AL_APIENTRY * MAL_LPALCCAPTURESTOP) (mal_ALCdevice *device); +typedef void (MAL_AL_APIENTRY * MAL_LPALCCAPTURESAMPLES) (mal_ALCdevice *device, mal_ALCvoid *buffer, mal_ALCsizei samples); + +typedef void (MAL_AL_APIENTRY * MAL_LPALENABLE) (mal_ALenum capability); +typedef void (MAL_AL_APIENTRY * MAL_LPALDISABLE) (mal_ALenum capability); +typedef mal_ALboolean (MAL_AL_APIENTRY * MAL_LPALISENABLED) (mal_ALenum capability); +typedef const mal_ALchar* (MAL_AL_APIENTRY * MAL_LPALGETSTRING) (mal_ALenum param); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETBOOLEANV) (mal_ALenum param, mal_ALboolean *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETINTEGERV) (mal_ALenum param, mal_ALint *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETFLOATV) (mal_ALenum param, mal_ALfloat *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETDOUBLEV) (mal_ALenum param, mal_ALdouble *values); +typedef mal_ALboolean (MAL_AL_APIENTRY * MAL_LPALGETBOOLEAN) (mal_ALenum param); +typedef mal_ALint (MAL_AL_APIENTRY * MAL_LPALGETINTEGER) (mal_ALenum param); +typedef mal_ALfloat (MAL_AL_APIENTRY * MAL_LPALGETFLOAT) (mal_ALenum param); +typedef mal_ALdouble (MAL_AL_APIENTRY * MAL_LPALGETDOUBLE) (mal_ALenum param); +typedef mal_ALenum (MAL_AL_APIENTRY * MAL_LPALGETERROR) (void); +typedef mal_ALboolean (MAL_AL_APIENTRY * MAL_LPALISEXTENSIONPRESENT) (const mal_ALchar *extname); +typedef void* (MAL_AL_APIENTRY * MAL_LPALGETPROCADDRESS) (const mal_ALchar *fname); +typedef mal_ALenum (MAL_AL_APIENTRY * MAL_LPALGETENUMVALUE) (const mal_ALchar *ename); +typedef void (MAL_AL_APIENTRY * MAL_LPALGENSOURCES) (mal_ALsizei n, mal_ALuint *sources); +typedef void (MAL_AL_APIENTRY * MAL_LPALDELETESOURCES) (mal_ALsizei n, const mal_ALuint *sources); +typedef mal_ALboolean (MAL_AL_APIENTRY * MAL_LPALISSOURCE) (mal_ALuint source); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEF) (mal_ALuint source, mal_ALenum param, mal_ALfloat value); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCE3F) (mal_ALuint source, mal_ALenum param, mal_ALfloat value1, mal_ALfloat value2, mal_ALfloat value3); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEFV) (mal_ALuint source, mal_ALenum param, const mal_ALfloat *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEI) (mal_ALuint source, mal_ALenum param, mal_ALint value); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCE3I) (mal_ALuint source, mal_ALenum param, mal_ALint value1, mal_ALint value2, mal_ALint value3); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEIV) (mal_ALuint source, mal_ALenum param, const mal_ALint *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETSOURCEF) (mal_ALuint source, mal_ALenum param, mal_ALfloat *value); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETSOURCE3F) (mal_ALuint source, mal_ALenum param, mal_ALfloat *value1, mal_ALfloat *value2, mal_ALfloat *value3); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETSOURCEFV) (mal_ALuint source, mal_ALenum param, mal_ALfloat *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETSOURCEI) (mal_ALuint source, mal_ALenum param, mal_ALint *value); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETSOURCE3I) (mal_ALuint source, mal_ALenum param, mal_ALint *value1, mal_ALint *value2, mal_ALint *value3); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETSOURCEIV) (mal_ALuint source, mal_ALenum param, mal_ALint *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEPLAYV) (mal_ALsizei n, const mal_ALuint *sources); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCESTOPV) (mal_ALsizei n, const mal_ALuint *sources); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEREWINDV) (mal_ALsizei n, const mal_ALuint *sources); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEPAUSEV) (mal_ALsizei n, const mal_ALuint *sources); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEPLAY) (mal_ALuint source); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCESTOP) (mal_ALuint source); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEREWIND) (mal_ALuint source); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEPAUSE) (mal_ALuint source); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEQUEUEBUFFERS) (mal_ALuint source, mal_ALsizei nb, const mal_ALuint *buffers); +typedef void (MAL_AL_APIENTRY * MAL_LPALSOURCEUNQUEUEBUFFERS)(mal_ALuint source, mal_ALsizei nb, mal_ALuint *buffers); +typedef void (MAL_AL_APIENTRY * MAL_LPALGENBUFFERS) (mal_ALsizei n, mal_ALuint *buffers); +typedef void (MAL_AL_APIENTRY * MAL_LPALDELETEBUFFERS) (mal_ALsizei n, const mal_ALuint *buffers); +typedef mal_ALboolean (MAL_AL_APIENTRY * MAL_LPALISBUFFER) (mal_ALuint buffer); +typedef void (MAL_AL_APIENTRY * MAL_LPALBUFFERDATA) (mal_ALuint buffer, mal_ALenum format, const mal_ALvoid *data, mal_ALsizei size, mal_ALsizei freq); +typedef void (MAL_AL_APIENTRY * MAL_LPALBUFFERF) (mal_ALuint buffer, mal_ALenum param, mal_ALfloat value); +typedef void (MAL_AL_APIENTRY * MAL_LPALBUFFER3F) (mal_ALuint buffer, mal_ALenum param, mal_ALfloat value1, mal_ALfloat value2, mal_ALfloat value3); +typedef void (MAL_AL_APIENTRY * MAL_LPALBUFFERFV) (mal_ALuint buffer, mal_ALenum param, const mal_ALfloat *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALBUFFERI) (mal_ALuint buffer, mal_ALenum param, mal_ALint value); +typedef void (MAL_AL_APIENTRY * MAL_LPALBUFFER3I) (mal_ALuint buffer, mal_ALenum param, mal_ALint value1, mal_ALint value2, mal_ALint value3); +typedef void (MAL_AL_APIENTRY * MAL_LPALBUFFERIV) (mal_ALuint buffer, mal_ALenum param, const mal_ALint *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETBUFFERF) (mal_ALuint buffer, mal_ALenum param, mal_ALfloat *value); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETBUFFER3F) (mal_ALuint buffer, mal_ALenum param, mal_ALfloat *value1, mal_ALfloat *value2, mal_ALfloat *value3); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETBUFFERFV) (mal_ALuint buffer, mal_ALenum param, mal_ALfloat *values); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETBUFFERI) (mal_ALuint buffer, mal_ALenum param, mal_ALint *value); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETBUFFER3I) (mal_ALuint buffer, mal_ALenum param, mal_ALint *value1, mal_ALint *value2, mal_ALint *value3); +typedef void (MAL_AL_APIENTRY * MAL_LPALGETBUFFERIV) (mal_ALuint buffer, mal_ALenum param, mal_ALint *values); + +mal_result mal_context_init__openal(mal_context* pContext) +{ + mal_assert(pContext != NULL); + +#ifndef MAL_NO_RUNTIME_LINKING + const char* libName = NULL; +#ifdef MAL_WIN32 + libName = "OpenAL32.dll"; +#endif +#if defined(MAL_UNIX) && !defined(MAL_APPLE) + libName = "libopenal.so"; +#endif +#ifdef MAL_APPLE + libName = "OpenAL.framework/OpenAL"; +#endif + if (libName == NULL) { + return MAL_NO_BACKEND; // Don't know what the library name is called. + } + + + pContext->openal.hOpenAL = mal_dlopen(libName); + +#ifdef MAL_WIN32 + // Special case for Win32 - try "soft_oal.dll" for OpenAL-Soft drop-ins. + if (pContext->openal.hOpenAL == NULL) { + pContext->openal.hOpenAL = mal_dlopen("soft_oal.dll"); + } +#endif + + if (pContext->openal.hOpenAL == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + pContext->openal.alcCreateContext = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcCreateContext"); + pContext->openal.alcMakeContextCurrent = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcMakeContextCurrent"); + pContext->openal.alcProcessContext = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcProcessContext"); + pContext->openal.alcSuspendContext = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcSuspendContext"); + pContext->openal.alcDestroyContext = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcDestroyContext"); + pContext->openal.alcGetCurrentContext = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcGetCurrentContext"); + pContext->openal.alcGetContextsDevice = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcGetContextsDevice"); + pContext->openal.alcOpenDevice = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcOpenDevice"); + pContext->openal.alcCloseDevice = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcCloseDevice"); + pContext->openal.alcGetError = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcGetError"); + pContext->openal.alcIsExtensionPresent = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcIsExtensionPresent"); + pContext->openal.alcGetProcAddress = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcGetProcAddress"); + pContext->openal.alcGetEnumValue = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcGetEnumValue"); + pContext->openal.alcGetString = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcGetString"); + pContext->openal.alcGetIntegerv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcGetIntegerv"); + pContext->openal.alcCaptureOpenDevice = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcCaptureOpenDevice"); + pContext->openal.alcCaptureCloseDevice = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcCaptureCloseDevice"); + pContext->openal.alcCaptureStart = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcCaptureStart"); + pContext->openal.alcCaptureStop = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcCaptureStop"); + pContext->openal.alcCaptureSamples = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alcCaptureSamples"); + + pContext->openal.alEnable = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alEnable"); + pContext->openal.alDisable = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alDisable"); + pContext->openal.alIsEnabled = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alIsEnabled"); + pContext->openal.alGetString = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetString"); + pContext->openal.alGetBooleanv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBooleanv"); + pContext->openal.alGetIntegerv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetIntegerv"); + pContext->openal.alGetFloatv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetFloatv"); + pContext->openal.alGetDoublev = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetDoublev"); + pContext->openal.alGetBoolean = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBoolean"); + pContext->openal.alGetInteger = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetInteger"); + pContext->openal.alGetFloat = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetFloat"); + pContext->openal.alGetDouble = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetDouble"); + pContext->openal.alGetError = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetError"); + pContext->openal.alIsExtensionPresent = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alIsExtensionPresent"); + pContext->openal.alGetProcAddress = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetProcAddress"); + pContext->openal.alGetEnumValue = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetEnumValue"); + pContext->openal.alGenSources = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGenSources"); + pContext->openal.alDeleteSources = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alDeleteSources"); + pContext->openal.alIsSource = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alIsSource"); + pContext->openal.alSourcef = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourcef"); + pContext->openal.alSource3f = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSource3f"); + pContext->openal.alSourcefv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourcefv"); + pContext->openal.alSourcei = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourcei"); + pContext->openal.alSource3i = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSource3i"); + pContext->openal.alSourceiv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourceiv"); + pContext->openal.alGetSourcef = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetSourcef"); + pContext->openal.alGetSource3f = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetSource3f"); + pContext->openal.alGetSourcefv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetSourcefv"); + pContext->openal.alGetSourcei = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetSourcei"); + pContext->openal.alGetSource3i = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetSource3i"); + pContext->openal.alGetSourceiv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetSourceiv"); + pContext->openal.alSourcePlayv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourcePlayv"); + pContext->openal.alSourceStopv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourceStopv"); + pContext->openal.alSourceRewindv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourceRewindv"); + pContext->openal.alSourcePausev = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourcePausev"); + pContext->openal.alSourcePlay = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourcePlay"); + pContext->openal.alSourceStop = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourceStop"); + pContext->openal.alSourceRewind = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourceRewind"); + pContext->openal.alSourcePause = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourcePause"); + pContext->openal.alSourceQueueBuffers = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourceQueueBuffers"); + pContext->openal.alSourceUnqueueBuffers = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alSourceUnqueueBuffers"); + pContext->openal.alGenBuffers = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGenBuffers"); + pContext->openal.alDeleteBuffers = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alDeleteBuffers"); + pContext->openal.alIsBuffer = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alIsBuffer"); + pContext->openal.alBufferData = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alBufferData"); + pContext->openal.alBufferf = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alBufferf"); + pContext->openal.alBuffer3f = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alBuffer3f"); + pContext->openal.alBufferfv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alBufferfv"); + pContext->openal.alBufferi = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alBufferi"); + pContext->openal.alBuffer3i = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alBuffer3i"); + pContext->openal.alBufferiv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alBufferiv"); + pContext->openal.alGetBufferf = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBufferf"); + pContext->openal.alGetBuffer3f = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBuffer3f"); + pContext->openal.alGetBufferfv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBufferfv"); + pContext->openal.alGetBufferi = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBufferi"); + pContext->openal.alGetBuffer3i = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBuffer3i"); + pContext->openal.alGetBufferiv = (mal_proc)mal_dlsym(pContext->openal.hOpenAL, "alGetBufferiv"); +#else + pContext->openal.alcCreateContext = (mal_proc)alcCreateContext; + pContext->openal.alcMakeContextCurrent = (mal_proc)alcMakeContextCurrent; + pContext->openal.alcProcessContext = (mal_proc)alcProcessContext; + pContext->openal.alcSuspendContext = (mal_proc)alcSuspendContext; + pContext->openal.alcDestroyContext = (mal_proc)alcDestroyContext; + pContext->openal.alcGetCurrentContext = (mal_proc)alcGetCurrentContext; + pContext->openal.alcGetContextsDevice = (mal_proc)alcGetContextsDevice; + pContext->openal.alcOpenDevice = (mal_proc)alcOpenDevice; + pContext->openal.alcCloseDevice = (mal_proc)alcCloseDevice; + pContext->openal.alcGetError = (mal_proc)alcGetError; + pContext->openal.alcIsExtensionPresent = (mal_proc)alcIsExtensionPresent; + pContext->openal.alcGetProcAddress = (mal_proc)alcGetProcAddress; + pContext->openal.alcGetEnumValue = (mal_proc)alcGetEnumValue; + pContext->openal.alcGetString = (mal_proc)alcGetString; + pContext->openal.alcGetIntegerv = (mal_proc)alcGetIntegerv; + pContext->openal.alcCaptureOpenDevice = (mal_proc)alcCaptureOpenDevice; + pContext->openal.alcCaptureCloseDevice = (mal_proc)alcCaptureCloseDevice; + pContext->openal.alcCaptureStart = (mal_proc)alcCaptureStart; + pContext->openal.alcCaptureStop = (mal_proc)alcCaptureStop; + pContext->openal.alcCaptureSamples = (mal_proc)alcCaptureSamples; + + pContext->openal.alEnable = (mal_proc)alEnable; + pContext->openal.alDisable = (mal_proc)alDisable; + pContext->openal.alIsEnabled = (mal_proc)alIsEnabled; + pContext->openal.alGetString = (mal_proc)alGetString; + pContext->openal.alGetBooleanv = (mal_proc)alGetBooleanv; + pContext->openal.alGetIntegerv = (mal_proc)alGetIntegerv; + pContext->openal.alGetFloatv = (mal_proc)alGetFloatv; + pContext->openal.alGetDoublev = (mal_proc)alGetDoublev; + pContext->openal.alGetBoolean = (mal_proc)alGetBoolean; + pContext->openal.alGetInteger = (mal_proc)alGetInteger; + pContext->openal.alGetFloat = (mal_proc)alGetFloat; + pContext->openal.alGetDouble = (mal_proc)alGetDouble; + pContext->openal.alGetError = (mal_proc)alGetError; + pContext->openal.alIsExtensionPresent = (mal_proc)alIsExtensionPresent; + pContext->openal.alGetProcAddress = (mal_proc)alGetProcAddress; + pContext->openal.alGetEnumValue = (mal_proc)alGetEnumValue; + pContext->openal.alGenSources = (mal_proc)alGenSources; + pContext->openal.alDeleteSources = (mal_proc)alDeleteSources; + pContext->openal.alIsSource = (mal_proc)alIsSource; + pContext->openal.alSourcef = (mal_proc)alSourcef; + pContext->openal.alSource3f = (mal_proc)alSource3f; + pContext->openal.alSourcefv = (mal_proc)alSourcefv; + pContext->openal.alSourcei = (mal_proc)alSourcei; + pContext->openal.alSource3i = (mal_proc)alSource3i; + pContext->openal.alSourceiv = (mal_proc)alSourceiv; + pContext->openal.alGetSourcef = (mal_proc)alGetSourcef; + pContext->openal.alGetSource3f = (mal_proc)alGetSource3f; + pContext->openal.alGetSourcefv = (mal_proc)alGetSourcefv; + pContext->openal.alGetSourcei = (mal_proc)alGetSourcei; + pContext->openal.alGetSource3i = (mal_proc)alGetSource3i; + pContext->openal.alGetSourceiv = (mal_proc)alGetSourceiv; + pContext->openal.alSourcePlayv = (mal_proc)alSourcePlayv; + pContext->openal.alSourceStopv = (mal_proc)alSourceStopv; + pContext->openal.alSourceRewindv = (mal_proc)alSourceRewindv; + pContext->openal.alSourcePausev = (mal_proc)alSourcePausev; + pContext->openal.alSourcePlay = (mal_proc)alSourcePlay; + pContext->openal.alSourceStop = (mal_proc)alSourceStop; + pContext->openal.alSourceRewind = (mal_proc)alSourceRewind; + pContext->openal.alSourcePause = (mal_proc)alSourcePause; + pContext->openal.alSourceQueueBuffers = (mal_proc)alSourceQueueBuffers; + pContext->openal.alSourceUnqueueBuffers = (mal_proc)alSourceUnqueueBuffers; + pContext->openal.alGenBuffers = (mal_proc)alGenBuffers; + pContext->openal.alDeleteBuffers = (mal_proc)alDeleteBuffers; + pContext->openal.alIsBuffer = (mal_proc)alIsBuffer; + pContext->openal.alBufferData = (mal_proc)alBufferData; + pContext->openal.alBufferf = (mal_proc)alBufferf; + pContext->openal.alBuffer3f = (mal_proc)alBuffer3f; + pContext->openal.alBufferfv = (mal_proc)alBufferfv; + pContext->openal.alBufferi = (mal_proc)alBufferi; + pContext->openal.alBuffer3i = (mal_proc)alBuffer3i; + pContext->openal.alBufferiv = (mal_proc)alBufferiv; + pContext->openal.alGetBufferf = (mal_proc)alGetBufferf; + pContext->openal.alGetBuffer3f = (mal_proc)alGetBuffer3f; + pContext->openal.alGetBufferfv = (mal_proc)alGetBufferfv; + pContext->openal.alGetBufferi = (mal_proc)alGetBufferi; + pContext->openal.alGetBuffer3i = (mal_proc)alGetBuffer3i; + pContext->openal.alGetBufferiv = (mal_proc)alGetBufferiv; +#endif + + // We depend on the ALC_ENUMERATION_EXT extension for enumeration. If this is not supported we fall back to default devices. + pContext->openal.isEnumerationSupported = ((MAL_LPALCISEXTENSIONPRESENT)pContext->openal.alcIsExtensionPresent)(NULL, "ALC_ENUMERATION_EXT"); + pContext->openal.isFloat32Supported = ((MAL_LPALISEXTENSIONPRESENT)pContext->openal.alIsExtensionPresent)("AL_EXT_float32"); + pContext->openal.isMCFormatsSupported = ((MAL_LPALISEXTENSIONPRESENT)pContext->openal.alIsExtensionPresent)("AL_EXT_MCFORMATS"); + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__openal(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_openal); + +#ifndef MAL_NO_RUNTIME_LINKING + mal_dlclose(pContext->openal.hOpenAL); +#endif + + return MAL_SUCCESS; +} + +mal_result mal_enumerate_devices__openal(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + mal_uint32 infoSize = *pCount; + *pCount = 0; + + if (pContext->openal.isEnumerationSupported) { + const mal_ALCchar* pDeviceNames = ((MAL_LPALCGETSTRING)pContext->openal.alcGetString)(NULL, (type == mal_device_type_playback) ? MAL_ALC_DEVICE_SPECIFIER : MAL_ALC_CAPTURE_DEVICE_SPECIFIER); + if (pDeviceNames == NULL) { + return MAL_NO_DEVICE; + } + + // Each device is stored in pDeviceNames, separated by a null-terminator. The string itself is double-null-terminated. + const mal_ALCchar* pNextDeviceName = pDeviceNames; + while (pNextDeviceName[0] != '\0') { + if (pInfo != NULL) { + if (infoSize > 0) { + mal_strncpy_s(pInfo->id.openal, sizeof(pInfo->id.openal), (const char*)pNextDeviceName, (size_t)-1); + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), (const char*)pNextDeviceName, (size_t)-1); + + pInfo += 1; + infoSize -= 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + + // Move to the next device name. + while (*pNextDeviceName != '\0') { + pNextDeviceName += 1; + } + + // Skip past the null terminator. + pNextDeviceName += 1; + }; + } else { + // Enumeration is not supported. Use default devices. + if (pInfo != NULL) { + if (infoSize > 0) { + if (type == mal_device_type_playback) { + pInfo->id.sdl = 0; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1); + } else { + pInfo->id.sdl = 0; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1); + } + + pInfo += 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + } + + return MAL_SUCCESS; +} + +void mal_device_uninit__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)(NULL); + ((MAL_LPALCDESTROYCONTEXT)pDevice->pContext->openal.alcDestroyContext)((mal_ALCcontext*)pDevice->openal.pContextALC); + + if (pDevice->type == mal_device_type_playback) { + ((MAL_LPALCCLOSEDEVICE)pDevice->pContext->openal.alcCloseDevice)((mal_ALCdevice*)pDevice->openal.pDeviceALC); + } else { + ((MAL_LPALCCAPTURECLOSEDEVICE)pDevice->pContext->openal.alcCaptureCloseDevice)((mal_ALCdevice*)pDevice->openal.pDeviceALC); + } + + mal_free(pDevice->openal.pIntermediaryBuffer); +} + +mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + if (pDevice->periods > MAL_MAX_PERIODS_OPENAL) { + pDevice->periods = MAL_MAX_PERIODS_OPENAL; + } + + // OpenAL has bad latency in my testing :( + if (pDevice->usingDefaultBufferSize) { + pDevice->bufferSizeInFrames *= 4; + } + + mal_ALCsizei bufferSizeInSamplesAL = pDevice->bufferSizeInFrames; + mal_ALCuint frequencyAL = pConfig->sampleRate; + + mal_uint32 channelsAL = 0; + + // OpenAL currently only supports only mono and stereo. TODO: Check for the AL_EXT_MCFORMATS extension and use one of those formats for quad, 5.1, etc. + mal_ALCenum formatAL = 0; + if (pConfig->channels == 1) { + // Mono. + channelsAL = 1; + if (pConfig->format == mal_format_f32) { + if (pContext->openal.isFloat32Supported) { + formatAL = MAL_AL_FORMAT_MONO_FLOAT32; + } else { + formatAL = MAL_AL_FORMAT_MONO16; + } + } else if (pConfig->format == mal_format_s32) { + formatAL = MAL_AL_FORMAT_MONO16; + } else if (pConfig->format == mal_format_s24) { + formatAL = MAL_AL_FORMAT_MONO16; + } else if (pConfig->format == mal_format_s16) { + formatAL = MAL_AL_FORMAT_MONO16; + } else if (pConfig->format == mal_format_u8) { + formatAL = MAL_AL_FORMAT_MONO8; + } + } else { + // Stereo. + channelsAL = 2; + if (pConfig->format == mal_format_f32) { + if (pContext->openal.isFloat32Supported) { + formatAL = MAL_AL_FORMAT_STEREO_FLOAT32; + } else { + formatAL = MAL_AL_FORMAT_STEREO16; + } + } else if (pConfig->format == mal_format_s32) { + formatAL = MAL_AL_FORMAT_STEREO16; + } else if (pConfig->format == mal_format_s24) { + formatAL = MAL_AL_FORMAT_STEREO16; + } else if (pConfig->format == mal_format_s16) { + formatAL = MAL_AL_FORMAT_STEREO16; + } else if (pConfig->format == mal_format_u8) { + formatAL = MAL_AL_FORMAT_STEREO8; + } + } + + if (formatAL == 0) { + return mal_context_post_error(pContext, NULL, "[OpenAL] Format not supported.", MAL_FORMAT_NOT_SUPPORTED); + } + + bufferSizeInSamplesAL *= channelsAL; + + + // OpenAL feels a bit unintuitive to me... The global object is a device, and it would appear that each device can have + // many context's... + mal_ALCdevice* pDeviceALC = NULL; + if (type == mal_device_type_playback) { + pDeviceALC = ((MAL_LPALCOPENDEVICE)pContext->openal.alcOpenDevice)((pDeviceID == NULL) ? NULL : pDeviceID->openal); + } else { + pDeviceALC = ((MAL_LPALCCAPTUREOPENDEVICE)pContext->openal.alcCaptureOpenDevice)((pDeviceID == NULL) ? NULL : pDeviceID->openal, frequencyAL, formatAL, bufferSizeInSamplesAL); + } + + if (pDeviceALC == NULL) { + return mal_context_post_error(pContext, NULL, "[OpenAL] Failed to open device.", MAL_FAILED_TO_INIT_BACKEND); + } + + // A context is only required for playback. + mal_ALCcontext* pContextALC = NULL; + if (pDevice->type == mal_device_type_playback) { + pContextALC = ((MAL_LPALCCREATECONTEXT)pContext->openal.alcCreateContext)(pDeviceALC, NULL); + if (pContextALC == NULL) { + ((MAL_LPALCCLOSEDEVICE)pDevice->pContext->openal.alcCloseDevice)(pDeviceALC); + return mal_context_post_error(pContext, NULL, "[OpenAL] Failed to open OpenAL context.", MAL_FAILED_TO_INIT_BACKEND); + } + + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)(pContextALC); + + mal_ALuint sourceAL; + ((MAL_LPALGENSOURCES)pDevice->pContext->openal.alGenSources)(1, &sourceAL); + pDevice->openal.sourceAL = sourceAL; + + // We create the buffers, but only fill and queue them when the device is started. + mal_ALuint buffersAL[MAL_MAX_PERIODS_OPENAL]; + ((MAL_LPALGENBUFFERS)pDevice->pContext->openal.alGenBuffers)(pDevice->periods, buffersAL); + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + pDevice->openal.buffersAL[i] = buffersAL[i]; + } + } + + pDevice->internalChannels = channelsAL; + pDevice->internalSampleRate = frequencyAL; + + // The internal format is a little bit straight with OpenAL. + switch (formatAL) + { + case MAL_AL_FORMAT_MONO8: + case MAL_AL_FORMAT_STEREO8: + case MAL_AL_FORMAT_REAR8: + case MAL_AL_FORMAT_QUAD8: + case MAL_AL_FORMAT_51CHN8: + case MAL_AL_FORMAT_61CHN8: + case MAL_AL_FORMAT_71CHN8: + { + pDevice->internalFormat = mal_format_u8; + } break; + + case MAL_AL_FORMAT_MONO16: + case MAL_AL_FORMAT_STEREO16: + case MAL_AL_FORMAT_REAR16: + case MAL_AL_FORMAT_QUAD16: + case MAL_AL_FORMAT_51CHN16: + case MAL_AL_FORMAT_61CHN16: + case MAL_AL_FORMAT_71CHN16: + { + pDevice->internalFormat = mal_format_s16; + } break; + + case MAL_AL_FORMAT_REAR32: + case MAL_AL_FORMAT_QUAD32: + case MAL_AL_FORMAT_51CHN32: + case MAL_AL_FORMAT_61CHN32: + case MAL_AL_FORMAT_71CHN32: + { + pDevice->internalFormat = mal_format_s32; + } break; + + case MAL_AL_FORMAT_MONO_FLOAT32: + case MAL_AL_FORMAT_STEREO_FLOAT32: + { + pDevice->internalFormat = mal_format_f32; + } break; + } + + // From what I can tell, the ordering of channels is fixed for OpenAL. + switch (formatAL) + { + case MAL_AL_FORMAT_MONO8: + case MAL_AL_FORMAT_MONO16: + case MAL_AL_FORMAT_MONO_FLOAT32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case MAL_AL_FORMAT_STEREO8: + case MAL_AL_FORMAT_STEREO16: + case MAL_AL_FORMAT_STEREO_FLOAT32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + } break; + + case MAL_AL_FORMAT_REAR8: + case MAL_AL_FORMAT_REAR16: + case MAL_AL_FORMAT_REAR32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_BACK_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case MAL_AL_FORMAT_QUAD8: + case MAL_AL_FORMAT_QUAD16: + case MAL_AL_FORMAT_QUAD32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + pDevice->internalChannelMap[2] = MAL_CHANNEL_BACK_LEFT; + pDevice->internalChannelMap[3] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case MAL_AL_FORMAT_51CHN8: + case MAL_AL_FORMAT_51CHN16: + case MAL_AL_FORMAT_51CHN32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + pDevice->internalChannelMap[2] = MAL_CHANNEL_FRONT_CENTER; + pDevice->internalChannelMap[3] = MAL_CHANNEL_LFE; + pDevice->internalChannelMap[4] = MAL_CHANNEL_BACK_LEFT; + pDevice->internalChannelMap[5] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case MAL_AL_FORMAT_61CHN8: + case MAL_AL_FORMAT_61CHN16: + case MAL_AL_FORMAT_61CHN32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + pDevice->internalChannelMap[2] = MAL_CHANNEL_FRONT_CENTER; + pDevice->internalChannelMap[3] = MAL_CHANNEL_LFE; + pDevice->internalChannelMap[4] = MAL_CHANNEL_BACK_CENTER; + pDevice->internalChannelMap[5] = MAL_CHANNEL_SIDE_LEFT; + pDevice->internalChannelMap[6] = MAL_CHANNEL_SIDE_RIGHT; + } break; + + case MAL_AL_FORMAT_71CHN8: + case MAL_AL_FORMAT_71CHN16: + case MAL_AL_FORMAT_71CHN32: + { + pDevice->internalChannelMap[0] = MAL_CHANNEL_FRONT_LEFT; + pDevice->internalChannelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + pDevice->internalChannelMap[2] = MAL_CHANNEL_FRONT_CENTER; + pDevice->internalChannelMap[3] = MAL_CHANNEL_LFE; + pDevice->internalChannelMap[4] = MAL_CHANNEL_BACK_LEFT; + pDevice->internalChannelMap[5] = MAL_CHANNEL_BACK_RIGHT; + pDevice->internalChannelMap[6] = MAL_CHANNEL_SIDE_LEFT; + pDevice->internalChannelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + } break; + + default: break; + } + + pDevice->openal.pDeviceALC = pDeviceALC; + pDevice->openal.pContextALC = pContextALC; + pDevice->openal.formatAL = formatAL; + pDevice->openal.subBufferSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods; + pDevice->openal.pIntermediaryBuffer = (mal_uint8*)mal_malloc(pDevice->openal.subBufferSizeInFrames * channelsAL * mal_get_sample_size_in_bytes(pDevice->internalFormat)); + if (pDevice->openal.pIntermediaryBuffer == NULL) { + mal_device_uninit__openal(pDevice); + return mal_context_post_error(pContext, NULL, "[OpenAL] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY); + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__start_backend__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + // Playback. + // + // When starting playback we want to ensure each buffer is filled and queued before playing the source. + pDevice->openal.iNextBuffer = 0; + + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); + + for (mal_uint32 i = 0; i < pDevice->periods; ++i) { + mal_device__read_frames_from_client(pDevice, pDevice->openal.subBufferSizeInFrames, pDevice->openal.pIntermediaryBuffer); + + mal_ALuint bufferAL = pDevice->openal.buffersAL[i]; + ((MAL_LPALBUFFERDATA)pDevice->pContext->openal.alBufferData)(bufferAL, pDevice->openal.formatAL, pDevice->openal.pIntermediaryBuffer, pDevice->openal.subBufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat), pDevice->internalSampleRate); + ((MAL_LPALSOURCEQUEUEBUFFERS)pDevice->pContext->openal.alSourceQueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL); + } + + // Start the source only after filling and queueing each buffer. + ((MAL_LPALSOURCEPLAY)pDevice->pContext->openal.alSourcePlay)(pDevice->openal.sourceAL); + } else { + // Capture. + ((MAL_LPALCCAPTURESTART)pDevice->pContext->openal.alcCaptureStart)((mal_ALCdevice*)pDevice->openal.pDeviceALC); + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__stop_backend__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); + ((MAL_LPALSOURCESTOP)pDevice->pContext->openal.alSourceStop)(pDevice->openal.sourceAL); + } else { + ((MAL_LPALCCAPTURESTOP)pDevice->pContext->openal.alcCaptureStop)((mal_ALCdevice*)pDevice->openal.pDeviceALC); + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__break_main_loop__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->openal.breakFromMainLoop = MAL_TRUE; + return MAL_SUCCESS; +} + +static mal_uint32 mal_device__get_available_frames__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + if (pDevice->type == mal_device_type_playback) { + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); + + mal_ALint processedBufferCount = 0; + ((MAL_LPALGETSOURCEI)pDevice->pContext->openal.alGetSourcei)(pDevice->openal.sourceAL, MAL_AL_BUFFERS_PROCESSED, &processedBufferCount); + + return processedBufferCount * pDevice->openal.subBufferSizeInFrames; + } else { + mal_ALint samplesAvailable = 0; + ((MAL_LPALCGETINTEGERV)pDevice->pContext->openal.alcGetIntegerv)((mal_ALCdevice*)pDevice->openal.pDeviceALC, MAL_ALC_CAPTURE_SAMPLES, 1, &samplesAvailable); + + return samplesAvailable / pDevice->channels; + } +} + +static mal_uint32 mal_device__wait_for_frames__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + while (!pDevice->openal.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__get_available_frames__openal(pDevice); + if (framesAvailable > 0) { + return framesAvailable; + } + + mal_sleep(1); + } + + // We'll get here if the loop was terminated. When capturing we want to return whatever is available. For playback we just drop it. + if (pDevice->type == mal_device_type_playback) { + return 0; + } else { + return mal_device__get_available_frames__openal(pDevice); + } +} + +static mal_result mal_device__main_loop__openal(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + pDevice->openal.breakFromMainLoop = MAL_FALSE; + while (!pDevice->openal.breakFromMainLoop) { + mal_uint32 framesAvailable = mal_device__wait_for_frames__openal(pDevice); + if (framesAvailable == 0) { + continue; + } + + // If it's a playback device, don't bother grabbing more data if the device is being stopped. + if (pDevice->openal.breakFromMainLoop && pDevice->type == mal_device_type_playback) { + return MAL_FALSE; + } + + if (pDevice->type == mal_device_type_playback) { + while (framesAvailable > 0) { + mal_uint32 framesToRead = (framesAvailable > pDevice->openal.subBufferSizeInFrames) ? pDevice->openal.subBufferSizeInFrames : framesAvailable; + + mal_ALuint bufferAL = pDevice->openal.buffersAL[pDevice->openal.iNextBuffer]; + pDevice->openal.iNextBuffer = (pDevice->openal.iNextBuffer + 1) % pDevice->periods; + + mal_device__read_frames_from_client(pDevice, framesToRead, pDevice->openal.pIntermediaryBuffer); + + ((MAL_LPALCMAKECONTEXTCURRENT)pDevice->pContext->openal.alcMakeContextCurrent)((mal_ALCcontext*)pDevice->openal.pContextALC); + ((MAL_LPALSOURCEUNQUEUEBUFFERS)pDevice->pContext->openal.alSourceUnqueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL); + ((MAL_LPALBUFFERDATA)pDevice->pContext->openal.alBufferData)(bufferAL, pDevice->openal.formatAL, pDevice->openal.pIntermediaryBuffer, pDevice->openal.subBufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->internalFormat), pDevice->internalSampleRate); + ((MAL_LPALSOURCEQUEUEBUFFERS)pDevice->pContext->openal.alSourceQueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL); + + framesAvailable -= framesToRead; + } + + + // There's a chance the source has stopped playing due to there not being any buffer's queue. Make sure it's restarted. + mal_ALenum state; + ((MAL_LPALGETSOURCEI)pDevice->pContext->openal.alGetSourcei)(pDevice->openal.sourceAL, MAL_AL_SOURCE_STATE, &state); + + if (state != MAL_AL_PLAYING) { + ((MAL_LPALSOURCEPLAY)pDevice->pContext->openal.alSourcePlay)(pDevice->openal.sourceAL); + } + } else { + while (framesAvailable > 0) { + mal_uint32 framesToSend = (framesAvailable > pDevice->openal.subBufferSizeInFrames) ? pDevice->openal.subBufferSizeInFrames : framesAvailable; + ((MAL_LPALCCAPTURESAMPLES)pDevice->pContext->openal.alcCaptureSamples)((mal_ALCdevice*)pDevice->openal.pDeviceALC, pDevice->openal.pIntermediaryBuffer, framesToSend); + + mal_device__send_frames_to_client(pDevice, framesToSend, pDevice->openal.pIntermediaryBuffer); + framesAvailable -= framesToSend; + } + } + } + + return MAL_SUCCESS; +} +#endif // OpenAL + + + +/////////////////////////////////////////////////////////////////////////////// +// +// SDL Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_SDL + +//#define MAL_USE_SDL_1 + +#define MAL_SDL_INIT_AUDIO 0x00000010 +#define MAL_AUDIO_U8 0x0008 +#define MAL_AUDIO_S16 0x8010 +#define MAL_AUDIO_S32 0x8020 +#define MAL_AUDIO_F32 0x8120 +#define MAL_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001 +#define MAL_SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002 +#define MAL_SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004 +#define MAL_SDL_AUDIO_ALLOW_ANY_CHANGE (MAL_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | MAL_SDL_AUDIO_ALLOW_FORMAT_CHANGE | MAL_SDL_AUDIO_ALLOW_CHANNELS_CHANGE) + +// If we are linking at compile time we'll just #include SDL.h. Otherwise we can just redeclare some stuff to avoid the +// need for development packages to be installed. +#ifdef MAL_NO_RUNTIME_LINKING + #define SDL_MAIN_HANDLED + #ifdef MAL_EMSCRIPTEN + #include + + // For now just use SDL 1.2 with Emscripten. This avoids the need for "-s USE_SDL=2" at compile time. + #ifndef MAL_USE_SDL_1 + #define MAL_USE_SDL_1 + #endif + #else + #include + #endif + + typedef SDL_AudioCallback MAL_SDL_AudioCallback; + typedef SDL_AudioSpec MAL_SDL_AudioSpec; + typedef SDL_AudioFormat MAL_SDL_AudioFormat; + typedef SDL_AudioDeviceID MAL_SDL_AudioDeviceID; +#else + typedef void (* MAL_SDL_AudioCallback)(void* userdata, mal_uint8* stream, int len); + typedef mal_uint16 MAL_SDL_AudioFormat; + typedef mal_uint32 MAL_SDL_AudioDeviceID; + + typedef struct MAL_SDL_AudioSpec + { + int freq; + MAL_SDL_AudioFormat format; + mal_uint8 channels; + mal_uint8 silence; + mal_uint16 samples; + mal_uint16 padding; + mal_uint32 size; + MAL_SDL_AudioCallback callback; + void* userdata; + } MAL_SDL_AudioSpec; +#endif + +typedef int (* MAL_PFN_SDL_InitSubSystem)(mal_uint32 flags); +typedef void (* MAL_PFN_SDL_QuitSubSystem)(mal_uint32 flags); +typedef int (* MAL_PFN_SDL_GetNumAudioDevices)(int iscapture); +typedef const char* (* MAL_PFN_SDL_GetAudioDeviceName)(int index, int iscapture); +typedef void (* MAL_PFN_SDL_CloseAudio)(void); +typedef void (* MAL_PFN_SDL_CloseAudioDevice)(MAL_SDL_AudioDeviceID dev); +typedef int (* MAL_PFN_SDL_OpenAudio)(MAL_SDL_AudioSpec* desired, MAL_SDL_AudioSpec* obtained); +typedef MAL_SDL_AudioDeviceID (* MAL_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const MAL_SDL_AudioSpec* desired, MAL_SDL_AudioSpec* obtained, int allowed_changes); +typedef void (* MAL_PFN_SDL_PauseAudio)(int pause_on); +typedef void (* MAL_PFN_SDL_PauseAudioDevice)(MAL_SDL_AudioDeviceID dev, int pause_on); + +MAL_SDL_AudioFormat mal_format_to_sdl(mal_format format) +{ + switch (format) + { + case mal_format_unknown: return 0; + case mal_format_u8: return MAL_AUDIO_U8; + case mal_format_s16: return MAL_AUDIO_S16; + case mal_format_s24: return MAL_AUDIO_S32; // Closest match. + case mal_format_s32: return MAL_AUDIO_S32; + default: return 0; + } +} + +mal_format mal_format_from_sdl(MAL_SDL_AudioFormat format) +{ + switch (format) + { + case MAL_AUDIO_U8: return mal_format_u8; + case MAL_AUDIO_S16: return mal_format_s16; + case MAL_AUDIO_S32: return mal_format_s32; + case MAL_AUDIO_F32: return mal_format_f32; + default: return mal_format_unknown; + } +} + + +mal_result mal_context_init__sdl(mal_context* pContext) +{ + mal_assert(pContext != NULL); + +#ifndef MAL_NO_RUNTIME_LINKING + // Run-time linking. + const char* libNames[] = { +#if defined(MAL_WIN32) + "SDL2.dll", + "SDL.dll" +#elif defined(MAL_APPLE) + "libSDL2-2.0.0.dylib", // Can any Mac users out there comfirm these library names? + "libSDL-1.2.0.dylib" +#else + "libSDL2-2.0.so.0", + "libSDL-1.2.so.0" +#endif + }; + + for (size_t i = 0; i < mal_countof(libNames); ++i) { + pContext->sdl.hSDL = mal_dlopen(libNames[i]); + if (pContext->sdl.hSDL != NULL) { + break; + } + } + + if (pContext->sdl.hSDL == NULL) { + return MAL_NO_BACKEND; // Couldn't find SDL2.dll, etc. Most likely it's not installed. + } + + pContext->sdl.SDL_InitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_InitSubSystem"); + pContext->sdl.SDL_QuitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_QuitSubSystem"); + pContext->sdl.SDL_CloseAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_CloseAudio"); + pContext->sdl.SDL_OpenAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_OpenAudio"); + pContext->sdl.SDL_PauseAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_PauseAudio"); +#ifndef MAL_USE_SDL_1 + pContext->sdl.SDL_GetNumAudioDevices = mal_dlsym(pContext->sdl.hSDL, "SDL_GetNumAudioDevices"); + pContext->sdl.SDL_GetAudioDeviceName = mal_dlsym(pContext->sdl.hSDL, "SDL_GetAudioDeviceName"); + pContext->sdl.SDL_CloseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_CloseAudioDevice"); + pContext->sdl.SDL_OpenAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_OpenAudioDevice"); + pContext->sdl.SDL_PauseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_PauseAudioDevice"); +#endif +#else + // Compile-time linking. + pContext->sdl.SDL_InitSubSystem = (mal_proc)SDL_InitSubSystem; + pContext->sdl.SDL_QuitSubSystem = (mal_proc)SDL_QuitSubSystem; + pContext->sdl.SDL_CloseAudio = (mal_proc)SDL_CloseAudio; + pContext->sdl.SDL_OpenAudio = (mal_proc)SDL_OpenAudio; + pContext->sdl.SDL_PauseAudio = (mal_proc)SDL_PauseAudio; +#ifndef MAL_USE_SDL_1 + pContext->sdl.SDL_GetNumAudioDevices = (mal_proc)SDL_GetNumAudioDevices; + pContext->sdl.SDL_GetAudioDeviceName = (mal_proc)SDL_GetAudioDeviceName; + pContext->sdl.SDL_CloseAudioDevice = (mal_proc)SDL_CloseAudioDevice; + pContext->sdl.SDL_OpenAudioDevice = (mal_proc)SDL_OpenAudioDevice; + pContext->sdl.SDL_PauseAudioDevice = (mal_proc)SDL_PauseAudioDevice; +#endif +#endif + + // We need to determine whether or not we are using SDL2 or SDL1. We can know this by looking at whether or not certain + // function pointers are NULL. + if (pContext->sdl.SDL_GetNumAudioDevices == NULL || + pContext->sdl.SDL_GetAudioDeviceName == NULL || + pContext->sdl.SDL_CloseAudioDevice == NULL || + pContext->sdl.SDL_OpenAudioDevice == NULL || + pContext->sdl.SDL_PauseAudioDevice == NULL) { + pContext->sdl.usingSDL1 = MAL_TRUE; + } + + int resultSDL = ((MAL_PFN_SDL_InitSubSystem)pContext->sdl.SDL_InitSubSystem)(MAL_SDL_INIT_AUDIO); + if (resultSDL != 0) { + return MAL_ERROR; + } + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__sdl(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_sdl); + + ((MAL_PFN_SDL_QuitSubSystem)pContext->sdl.SDL_QuitSubSystem)(MAL_SDL_INIT_AUDIO); + return MAL_SUCCESS; +} + +mal_result mal_enumerate_devices__sdl(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + (void)pContext; + + mal_uint32 infoSize = *pCount; + *pCount = 0; + +#ifndef MAL_USE_SDL_1 + if (!pContext->sdl.usingSDL1) { + int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)((type == mal_device_type_playback) ? 0 : 1); + for (int i = 0; i < deviceCount; ++i) { + if (pInfo != NULL) { + if (infoSize > 0) { + pInfo->id.sdl = i; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, (type == mal_device_type_playback) ? 0 : 1), (size_t)-1); + + pInfo += 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + } + } else +#endif + { + if (pInfo != NULL) { + if (infoSize > 0) { + // SDL1 uses default devices. + if (type == mal_device_type_playback) { + pInfo->id.sdl = 0; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Playback Device", (size_t)-1); + } else { + pInfo->id.sdl = 0; + mal_strncpy_s(pInfo->name, sizeof(pInfo->name), "Default Capture Device", (size_t)-1); + } + + pInfo += 1; + *pCount += 1; + } + } else { + *pCount += 1; + } + } + + return MAL_SUCCESS; +} + +void mal_device_uninit__sdl(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + +#ifndef MAL_USE_SDL_1 + if (!pDevice->pContext->sdl.usingSDL1) { + ((MAL_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID); + } else +#endif + { + ((MAL_PFN_SDL_CloseAudio)pDevice->pContext->sdl.SDL_CloseAudio)(); + } +} + + +static void mal_audio_callback__sdl(void* pUserData, mal_uint8* pBuffer, int bufferSizeInBytes) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + mal_uint32 bufferSizeInFrames = (mal_uint32)bufferSizeInBytes / mal_get_sample_size_in_bytes(pDevice->internalFormat) / pDevice->internalChannels; + + if (pDevice->type == mal_device_type_playback) { + mal_device__read_frames_from_client(pDevice, bufferSizeInFrames, pBuffer); + } else { + mal_device__send_frames_to_client(pDevice, bufferSizeInFrames, pBuffer); + } +} + +mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + mal_assert(pContext != NULL); + mal_assert(pConfig != NULL); + mal_assert(pDevice != NULL); + + // SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need + // to explicitly clamp this because it will be easy to overflow. + mal_uint32 bufferSize = pConfig->bufferSizeInFrames; + if (bufferSize > 32768) { + bufferSize = 32768; + } else { + bufferSize = mal_next_power_of_2(bufferSize); + } + + mal_assert(bufferSize <= 32768); + + + MAL_SDL_AudioSpec desiredSpec, obtainedSpec; + mal_zero_memory(&desiredSpec, sizeof(desiredSpec)); + desiredSpec.freq = (int)pConfig->sampleRate; + desiredSpec.format = mal_format_to_sdl(pConfig->format); + desiredSpec.channels = (mal_uint8)pConfig->channels; + desiredSpec.samples = (mal_uint16)bufferSize; + desiredSpec.callback = mal_audio_callback__sdl; + desiredSpec.userdata = pDevice; + + // Fall back to f32 if we don't have an appropriate mapping between mini_al and SDL. + if (desiredSpec.format == 0) { + desiredSpec.format = MAL_AUDIO_F32; + } + +#ifndef MAL_USE_SDL_1 + if (!pDevice->pContext->sdl.usingSDL1) { + int isCapture = (type == mal_device_type_playback) ? 0 : 1; + + const char* pDeviceName = NULL; + if (pDeviceID != NULL) { + pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pDevice->pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture); + } + + pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE); + if (pDevice->sdl.deviceID == 0) { + return mal_post_error(pDevice, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } else +#endif + { + // SDL1 uses default devices. + (void)pDeviceID; + + // SDL1 only supports playback as far as I can tell. + if (type != mal_device_type_playback) { + return MAL_NO_DEVICE; + } + + // SDL1 does not support floating point formats. + if (desiredSpec.format == MAL_AUDIO_F32) { + desiredSpec.format = MAL_AUDIO_S16; + } + + pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec); + if (pDevice->sdl.deviceID != 0) { + return mal_post_error(pDevice, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + } + + pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format); + pDevice->internalChannels = obtainedSpec.channels; + pDevice->internalSampleRate = (mal_uint32)obtainedSpec.freq; + pDevice->bufferSizeInFrames = obtainedSpec.samples; + pDevice->periods = 1; // SDL doesn't seem to tell us what the period count is. Just set this 1. + +#if 0 + printf("=== SDL CONFIG ===\n"); + printf("REQUESTED -> RECEIVED\n"); + printf(" FORMAT: %s -> %s\n", mal_get_format_name(pConfig->format), mal_get_format_name(pDevice->internalFormat)); + printf(" CHANNELS: %d -> %d\n", desiredSpec.channels, obtainedSpec.channels); + printf(" SAMPLE RATE: %d -> %d\n", desiredSpec.freq, obtainedSpec.freq); + printf(" BUFFER SIZE IN SAMPLES: %d -> %d\n", desiredSpec.samples, obtainedSpec.samples); +#endif + + return MAL_SUCCESS; +} + +static mal_result mal_device__start_backend__sdl(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + +#ifndef MAL_USE_SDL_1 + if (!pDevice->pContext->sdl.usingSDL1) { + ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0); + } else +#endif + { + ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(0); + } + + return MAL_SUCCESS; +} + +static mal_result mal_device__stop_backend__sdl(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + +#ifndef MAL_USE_SDL_1 + if (!pDevice->pContext->sdl.usingSDL1) { + ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1); + } else +#endif + { + ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(1); + } + + return MAL_SUCCESS; +} +#endif // SDL + + + + +mal_bool32 mal__is_channel_map_valid(const mal_channel* channelMap, mal_uint32 channels) +{ + mal_assert(channels > 0); + + // A channel cannot be present in the channel map more than once. + for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { + for (mal_uint32 jChannel = iChannel + 1; jChannel < channels; ++jChannel) { + if (channelMap[iChannel] == channelMap[jChannel]) { + return MAL_FALSE; + } + } + } + + return MAL_TRUE; +} + + +static mal_result mal_device__start_backend(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_result result = MAL_NO_BACKEND; +#ifdef MAL_HAS_WASAPI + if (pDevice->pContext->backend == mal_backend_wasapi) { + result = mal_device__start_backend__wasapi(pDevice); + } +#endif +#ifdef MAL_HAS_DSOUND + if (pDevice->pContext->backend == mal_backend_dsound) { + result = mal_device__start_backend__dsound(pDevice); + } +#endif +#ifdef MAL_HAS_WINMM + if (pDevice->pContext->backend == mal_backend_winmm) { + result = mal_device__start_backend__winmm(pDevice); + } +#endif +#ifdef MAL_HAS_ALSA + if (pDevice->pContext->backend == mal_backend_alsa) { + result = mal_device__start_backend__alsa(pDevice); + } +#endif +#ifdef MAL_HAS_OSS + if (pDevice->pContext->backend == mal_backend_oss) { + result = mal_device__start_backend__oss(pDevice); + } +#endif +#ifdef MAL_HAS_OPENAL + if (pDevice->pContext->backend == mal_backend_openal) { + result = mal_device__start_backend__openal(pDevice); + } +#endif +#ifdef MAL_HAS_NULL + if (pDevice->pContext->backend == mal_backend_null) { + result = mal_device__start_backend__null(pDevice); + } +#endif + + return result; +} + +static mal_result mal_device__stop_backend(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_result result = MAL_NO_BACKEND; +#ifdef MAL_HAS_WASAPI + if (pDevice->pContext->backend == mal_backend_wasapi) { + result = mal_device__stop_backend__wasapi(pDevice); + } +#endif +#ifdef MAL_HAS_DSOUND + if (pDevice->pContext->backend == mal_backend_dsound) { + result = mal_device__stop_backend__dsound(pDevice); + } +#endif +#ifdef MAL_HAS_WINMM + if (pDevice->pContext->backend == mal_backend_winmm) { + result = mal_device__stop_backend__winmm(pDevice); + } +#endif +#ifdef MAL_HAS_ALSA + if (pDevice->pContext->backend == mal_backend_alsa) { + result = mal_device__stop_backend__alsa(pDevice); + } +#endif +#ifdef MAL_HAS_OSS + if (pDevice->pContext->backend == mal_backend_oss) { + result = mal_device__stop_backend__oss(pDevice); + } +#endif +#ifdef MAL_HAS_OPENAL + if (pDevice->pContext->backend == mal_backend_openal) { + result = mal_device__stop_backend__openal(pDevice); + } +#endif +#ifdef MAL_HAS_NULL + if (pDevice->pContext->backend == mal_backend_null) { + result = mal_device__stop_backend__null(pDevice); + } +#endif + + return result; +} + +static mal_result mal_device__break_main_loop(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_result result = MAL_NO_BACKEND; +#ifdef MAL_HAS_WASAPI + if (pDevice->pContext->backend == mal_backend_wasapi) { + result = mal_device__break_main_loop__wasapi(pDevice); + } +#endif +#ifdef MAL_HAS_DSOUND + if (pDevice->pContext->backend == mal_backend_dsound) { + result = mal_device__break_main_loop__dsound(pDevice); + } +#endif +#ifdef MAL_HAS_WINMM + if (pDevice->pContext->backend == mal_backend_winmm) { + result = mal_device__break_main_loop__winmm(pDevice); + } +#endif +#ifdef MAL_HAS_ALSA + if (pDevice->pContext->backend == mal_backend_alsa) { + result = mal_device__break_main_loop__alsa(pDevice); + } +#endif +#ifdef MAL_HAS_OSS + if (pDevice->pContext->backend == mal_backend_oss) { + result = mal_device__break_main_loop__oss(pDevice); + } +#endif +#ifdef MAL_HAS_OPENAL + if (pDevice->pContext->backend == mal_backend_openal) { + result = mal_device__break_main_loop__openal(pDevice); + } +#endif +#ifdef MAL_HAS_NULL + if (pDevice->pContext->backend == mal_backend_null) { + result = mal_device__break_main_loop__null(pDevice); + } +#endif + + return result; +} + +static mal_result mal_device__main_loop(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_result result = MAL_NO_BACKEND; +#ifdef MAL_HAS_WASAPI + if (pDevice->pContext->backend == mal_backend_wasapi) { + result = mal_device__main_loop__wasapi(pDevice); + } +#endif +#ifdef MAL_HAS_DSOUND + if (pDevice->pContext->backend == mal_backend_dsound) { + result = mal_device__main_loop__dsound(pDevice); + } +#endif +#ifdef MAL_HAS_WINMM + if (pDevice->pContext->backend == mal_backend_winmm) { + result = mal_device__main_loop__winmm(pDevice); + } +#endif +#ifdef MAL_HAS_ALSA + if (pDevice->pContext->backend == mal_backend_alsa) { + result = mal_device__main_loop__alsa(pDevice); + } +#endif +#ifdef MAL_HAS_OSS + if (pDevice->pContext->backend == mal_backend_oss) { + result = mal_device__main_loop__oss(pDevice); + } +#endif +#ifdef MAL_HAS_OPENAL + if (pDevice->pContext->backend == mal_backend_openal) { + result = mal_device__main_loop__openal(pDevice); + } +#endif +#ifdef MAL_HAS_NULL + if (pDevice->pContext->backend == mal_backend_null) { + result = mal_device__main_loop__null(pDevice); + } +#endif + + return result; +} + +mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData) +{ + mal_device* pDevice = (mal_device*)pData; + mal_assert(pDevice != NULL); + +#ifdef MAL_WIN32 + mal_CoInitializeEx(pDevice->pContext, NULL, 0); // 0 = COINIT_MULTITHREADED +#endif + + // This is only used to prevent posting onStop() when the device is first initialized. + mal_bool32 skipNextStopEvent = MAL_TRUE; + + for (;;) { + // At the start of iteration the device is stopped - we must explicitly mark it as such. + mal_device__stop_backend(pDevice); + + if (!skipNextStopEvent) { + mal_stop_proc onStop = pDevice->onStop; + if (onStop) { + onStop(pDevice); + } + } else { + skipNextStopEvent = MAL_FALSE; + } + + + // Let the other threads know that the device has stopped. + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + mal_event_signal(&pDevice->stopEvent); + + // We use an event to wait for a request to wake up. + mal_event_wait(&pDevice->wakeupEvent); + + // Default result code. + pDevice->workResult = MAL_SUCCESS; + + // Just break if we're terminating. + if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) { + break; + } + + + // Getting here means we just started the device and we need to wait for the device to + // either deliver us data (recording) or request more data (playback). + mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTING); + + pDevice->workResult = mal_device__start_backend(pDevice); + if (pDevice->workResult != MAL_SUCCESS) { + mal_event_signal(&pDevice->startEvent); + continue; + } + + // The thread that requested the device to start playing is waiting for this thread to start the + // device for real, which is now. + mal_device__set_state(pDevice, MAL_STATE_STARTED); + mal_event_signal(&pDevice->startEvent); + + // Now we just enter the main loop. The main loop can be broken with mal_device__break_main_loop(). + mal_device__main_loop(pDevice); + } + + // Make sure we aren't continuously waiting on a stop event. + mal_event_signal(&pDevice->stopEvent); // <-- Is this still needed? + +#ifdef MAL_WIN32 + mal_CoUninitialize(pDevice->pContext); +#endif + + return (mal_thread_result)0; +} + + +// Helper for determining whether or not the given device is initialized. +mal_bool32 mal_device__is_initialized(mal_device* pDevice) +{ + if (pDevice == NULL) return MAL_FALSE; + return mal_device__get_state(pDevice) != MAL_STATE_UNINITIALIZED; +} + + +#ifdef MAL_WIN32 +mal_result mal_context_uninit_backend_apis__win32(mal_context* pContext) +{ + mal_CoUninitialize(pContext); + mal_dlclose(pContext->win32.hUser32DLL); + mal_dlclose(pContext->win32.hOle32DLL); + + return MAL_SUCCESS; +} + +mal_result mal_context_init_backend_apis__win32(mal_context* pContext) +{ +#ifdef MAL_WIN32_DESKTOP + // Ole32.dll + pContext->win32.hOle32DLL = mal_dlopen("ole32.dll"); + if (pContext->win32.hOle32DLL == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.CoInitializeEx = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "CoInitializeEx"); + pContext->win32.CoUninitialize = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "CoUninitialize"); + pContext->win32.CoCreateInstance = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "CoCreateInstance"); + pContext->win32.CoTaskMemFree = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "CoTaskMemFree"); + pContext->win32.PropVariantClear = (mal_proc)mal_dlsym(pContext->win32.hOle32DLL, "PropVariantClear"); + + + // User32.dll + pContext->win32.hUser32DLL = mal_dlopen("user32.dll"); + if (pContext->win32.hUser32DLL == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.GetForegroundWindow = (mal_proc)mal_dlsym(pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (mal_proc)mal_dlsym(pContext->win32.hUser32DLL, "GetDesktopWindow"); +#endif + + mal_CoInitializeEx(pContext, NULL, 0); // 0 = COINIT_MULTITHREADED + return MAL_SUCCESS; +} +#else +mal_result mal_context_uninit_backend_apis__nix(mal_context* pContext) +{ + mal_dlclose(pContext->posix.pthreadSO); + + return MAL_SUCCESS; +} + +mal_result mal_context_init_backend_apis__nix(mal_context* pContext) +{ + // pthread +#if !defined(MAL_NO_RUNTIME_LINKING) + const char* libpthreadFileNames[] = { + "libpthread.so", + "libpthread.so.0", + "libpthread.dylib" + }; + + for (size_t i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) { + pContext->posix.pthreadSO = mal_dlopen(libpthreadFileNames[i]); + if (pContext->posix.pthreadSO != NULL) { + break; + } + } + + if (pContext->posix.pthreadSO == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + pContext->posix.pthread_create = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_create"); + pContext->posix.pthread_join = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_join"); + pContext->posix.pthread_mutex_init = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_init"); + pContext->posix.pthread_mutex_destroy = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_destroy"); + pContext->posix.pthread_mutex_lock = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_lock"); + pContext->posix.pthread_mutex_unlock = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_mutex_unlock"); + pContext->posix.pthread_cond_init = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_init"); + pContext->posix.pthread_cond_destroy = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_destroy"); + pContext->posix.pthread_cond_wait = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_wait"); + pContext->posix.pthread_cond_signal = (mal_proc)mal_dlsym(pContext->posix.pthreadSO, "pthread_cond_signal"); +#else + pContext->posix.pthread_create = (mal_proc)pthread_create; + pContext->posix.pthread_join = (mal_proc)pthread_join; + pContext->posix.pthread_mutex_init = (mal_proc)pthread_mutex_init; + pContext->posix.pthread_mutex_destroy = (mal_proc)pthread_mutex_destroy; + pContext->posix.pthread_mutex_lock = (mal_proc)pthread_mutex_lock; + pContext->posix.pthread_mutex_unlock = (mal_proc)pthread_mutex_unlock; + pContext->posix.pthread_cond_init = (mal_proc)pthread_cond_init; + pContext->posix.pthread_cond_destroy = (mal_proc)pthread_cond_destroy; + pContext->posix.pthread_cond_wait = (mal_proc)pthread_cond_wait; + pContext->posix.pthread_cond_signal = (mal_proc)pthread_cond_signal; +#endif + + return MAL_SUCCESS; +} +#endif + +mal_result mal_context_init_backend_apis(mal_context* pContext) +{ + mal_result result = MAL_NO_BACKEND; +#ifdef MAL_WIN32 + result = mal_context_init_backend_apis__win32(pContext); +#else + result = mal_context_init_backend_apis__nix(pContext); +#endif + + return result; +} + +mal_result mal_context_uninit_backend_apis(mal_context* pContext) +{ + mal_result result = MAL_NO_BACKEND; +#ifdef MAL_WIN32 + result = mal_context_uninit_backend_apis__win32(pContext); +#else + result = mal_context_uninit_backend_apis__nix(pContext); +#endif + + return result; +} + +mal_result mal_context_init(mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pConfig, mal_context* pContext) +{ + if (pContext == NULL) return MAL_INVALID_ARGS; + mal_zero_object(pContext); + + // Always make sure the config is set first to ensure properties are available as soon as possible. + if (pConfig != NULL) { + pContext->config = *pConfig; + } else { + pContext->config = mal_context_config_init(NULL); + } + + // Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. + mal_result result = mal_context_init_backend_apis(pContext); + if (result != MAL_SUCCESS) { + return result; + } + + static mal_backend defaultBackends[] = { + mal_backend_wasapi, + mal_backend_dsound, + mal_backend_winmm, + mal_backend_alsa, + mal_backend_oss, + mal_backend_opensl, + mal_backend_openal, + mal_backend_sdl, + mal_backend_null + }; + + if (backends == NULL) { + backends = defaultBackends; + backendCount = sizeof(defaultBackends) / sizeof(defaultBackends[0]); + } + + mal_assert(backends != NULL); + + for (mal_uint32 iBackend = 0; iBackend < backendCount; ++iBackend) { + mal_backend backend = backends[iBackend]; + + result = MAL_NO_BACKEND; + switch (backend) { + #ifdef MAL_HAS_WASAPI + case mal_backend_wasapi: + { + result = mal_context_init__wasapi(pContext); + } break; + #endif + #ifdef MAL_HAS_DSOUND + case mal_backend_dsound: + { + result = mal_context_init__dsound(pContext); + } break; + #endif + #ifdef MAL_HAS_WINMM + case mal_backend_winmm: + { + result = mal_context_init__winmm(pContext); + } break; + #endif + #ifdef MAL_HAS_ALSA + case mal_backend_alsa: + { + result = mal_context_init__alsa(pContext); + } break; + #endif + #ifdef MAL_HAS_OSS + case mal_backend_oss: + { + result = mal_context_init__oss(pContext); + } break; + #endif + #ifdef MAL_HAS_OPENSL + case mal_backend_opensl: + { + result = mal_context_init__opensl(pContext); + } break; + #endif + #ifdef MAL_HAS_OPENAL + case mal_backend_openal: + { + result = mal_context_init__openal(pContext); + } break; + #endif + #ifdef MAL_HAS_SDL + case mal_backend_sdl: + { + result = mal_context_init__sdl(pContext); + } break; + #endif + #ifdef MAL_HAS_NULL + case mal_backend_null: + { + result = mal_context_init__null(pContext); + } break; + #endif + + default: break; + } + + // If this iteration was successful, return. + if (result == MAL_SUCCESS) { + pContext->backend = backend; + return result; + } + } + + mal_zero_object(pContext); // Safety. + return MAL_NO_BACKEND; +} + +mal_result mal_context_uninit(mal_context* pContext) +{ + if (pContext == NULL) return MAL_INVALID_ARGS; + + switch (pContext->backend) { + #ifdef MAL_HAS_WASAPI + case mal_backend_wasapi: + { + return mal_context_uninit__wasapi(pContext); + } break; + #endif + #ifdef MAL_HAS_DSOUND + case mal_backend_dsound: + { + return mal_context_uninit__dsound(pContext); + } break; + #endif + #ifdef MAL_HAS_WINMM + case mal_backend_winmm: + { + return mal_context_uninit__winmm(pContext); + } break; + #endif + #ifdef MAL_HAS_ALSA + case mal_backend_alsa: + { + return mal_context_uninit__alsa(pContext); + } break; + #endif + #ifdef MAL_HAS_OSS + case mal_backend_oss: + { + return mal_context_uninit__oss(pContext); + } break; + #endif + #ifdef MAL_HAS_OPENSL + case mal_backend_opensl: + { + return mal_context_uninit__opensl(pContext); + } break; + #endif + #ifdef MAL_HAS_OPENAL + case mal_backend_openal: + { + return mal_context_uninit__openal(pContext); + } break; + #endif + #ifdef MAL_HAS_SDL + case mal_backend_sdl: + { + return mal_context_uninit__sdl(pContext); + } break; + #endif + #ifdef MAL_HAS_NULL + case mal_backend_null: + { + return mal_context_uninit__null(pContext); + } break; + #endif + + default: break; + } + + mal_context_uninit_backend_apis(pContext); + + mal_assert(MAL_FALSE); + return MAL_NO_BACKEND; +} + + +mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, mal_uint32* pCount, mal_device_info* pInfo) +{ + if (pCount == NULL) return mal_post_error(NULL, "mal_enumerate_devices() called with invalid arguments (pCount == 0).", MAL_INVALID_ARGS); + + // The output buffer needs to be initialized to zero. + if (pInfo != NULL) { + mal_zero_memory(pInfo, (*pCount) * sizeof(*pInfo)); + } + + switch (pContext->backend) + { + #ifdef MAL_HAS_WASAPI + case mal_backend_wasapi: + { + return mal_enumerate_devices__wasapi(pContext, type, pCount, pInfo); + } break; + #endif + #ifdef MAL_HAS_DSOUND + case mal_backend_dsound: + { + return mal_enumerate_devices__dsound(pContext, type, pCount, pInfo); + } break; + #endif + #ifdef MAL_HAS_WINMM + case mal_backend_winmm: + { + return mal_enumerate_devices__winmm(pContext, type, pCount, pInfo); + } break; + #endif + #ifdef MAL_HAS_ALSA + case mal_backend_alsa: + { + return mal_enumerate_devices__alsa(pContext, type, pCount, pInfo); + } break; + #endif + #ifdef MAL_HAS_OSS + case mal_backend_oss: + { + return mal_enumerate_devices__oss(pContext, type, pCount, pInfo); + } break; + #endif + #ifdef MAL_HAS_OPENSL + case mal_backend_opensl: + { + return mal_enumerate_devices__opensl(pContext, type, pCount, pInfo); + } break; + #endif + #ifdef MAL_HAS_OPENAL + case mal_backend_openal: + { + return mal_enumerate_devices__openal(pContext, type, pCount, pInfo); + } break; + #endif + #ifdef MAL_HAS_SDL + case mal_backend_sdl: + { + return mal_enumerate_devices__sdl(pContext, type, pCount, pInfo); + } break; + #endif + #ifdef MAL_HAS_NULL + case mal_backend_null: + { + return mal_enumerate_devices__null(pContext, type, pCount, pInfo); + } break; + #endif + + default: break; + } + + mal_assert(MAL_FALSE); + return MAL_NO_BACKEND; +} + +mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice) +{ + if (pDevice == NULL) { + return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); + } + if (pConfig == NULL) { + return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pConfig == NULL).", MAL_INVALID_ARGS); + } + + // Make a copy of the config to ensure we don't override the caller's object. + mal_device_config config = *pConfig; + + mal_zero_object(pDevice); + pDevice->pContext = pContext; + + // Set the user data and log callback ASAP to ensure it is available for the entire initialization process. + pDevice->pUserData = pUserData; + pDevice->onStop = config.onStopCallback; + pDevice->onSend = config.onSendCallback; + pDevice->onRecv = config.onRecvCallback; + + if (((size_t)pDevice % sizeof(pDevice)) != 0) { + if (pContext->config.onLog) { + pContext->config.onLog(pContext, pDevice, "WARNING: mal_device_init() called for a device that is not properly aligned. Thread safety is not supported."); + } + } + + + if (pContext == NULL) { + return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pContext == NULL).", MAL_INVALID_ARGS); + } + + + // Basic config validation. + if (config.channels == 0) { + return mal_post_error(pDevice, "mal_device_init() called with an invalid config. Channel count must be greater than 0.", MAL_INVALID_DEVICE_CONFIG); + } + if (config.channels > MAL_MAX_CHANNELS) { + return mal_post_error(pDevice, "mal_device_init() called with an invalid config. Channel count cannot exceed 18.", MAL_INVALID_DEVICE_CONFIG); + } + + if (config.sampleRate == 0) { + return mal_post_error(pDevice, "mal_device_init() called with an invalid config. Sample rate must be greater than 0.", MAL_INVALID_DEVICE_CONFIG); + } + + if (!mal__is_channel_map_valid(pConfig->channelMap, pConfig->channels)) { + return mal_post_error(pDevice, "mal_device_init() called with invalid arguments. Channel map is invalid.", MAL_INVALID_DEVICE_CONFIG); + } + + + // Default buffer size and periods. + if (config.bufferSizeInFrames == 0) { + config.bufferSizeInFrames = (config.sampleRate/1000) * MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS; + pDevice->usingDefaultBufferSize = MAL_TRUE; + } + if (config.periods == 0) { + config.periods = MAL_DEFAULT_PERIODS; + pDevice->usingDefaultPeriods = MAL_TRUE; + } + + pDevice->type = type; + pDevice->format = config.format; + pDevice->channels = config.channels; + mal_copy_memory(config.channelMap, config.channelMap, sizeof(config.channelMap[0]) * config.channels); + pDevice->sampleRate = config.sampleRate; + pDevice->bufferSizeInFrames = config.bufferSizeInFrames; + pDevice->periods = config.periods; + + // The internal format, channel count and sample rate can be modified by the backend. + pDevice->internalFormat = pDevice->format; + pDevice->internalChannels = pDevice->channels; + pDevice->internalSampleRate = pDevice->sampleRate; + mal_copy_memory(pDevice->internalChannelMap, pDevice->channelMap, sizeof(pDevice->channelMap)); + + if (mal_mutex_init(pContext, &pDevice->lock) != MAL_SUCCESS) { + return mal_post_error(pDevice, "Failed to create mutex.", MAL_FAILED_TO_CREATE_MUTEX); + } + + // When the device is started, the worker thread is the one that does the actual startup of the backend device. We + // use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. + // + // Each of these semaphores is released internally by the worker thread when the work is completed. The start + // semaphore is also used to wake up the worker thread. + if (mal_event_init(pContext, &pDevice->wakeupEvent) != MAL_SUCCESS) { + mal_mutex_uninit(&pDevice->lock); + return mal_post_error(pDevice, "Failed to create worker thread wakeup event.", MAL_FAILED_TO_CREATE_EVENT); + } + if (mal_event_init(pContext, &pDevice->startEvent) != MAL_SUCCESS) { + mal_event_uninit(&pDevice->wakeupEvent); + mal_mutex_uninit(&pDevice->lock); + return mal_post_error(pDevice, "Failed to create worker thread start event.", MAL_FAILED_TO_CREATE_EVENT); + } + if (mal_event_init(pContext, &pDevice->stopEvent) != MAL_SUCCESS) { + mal_event_uninit(&pDevice->startEvent); + mal_event_uninit(&pDevice->wakeupEvent); + mal_mutex_uninit(&pDevice->lock); + return mal_post_error(pDevice, "Failed to create worker thread stop event.", MAL_FAILED_TO_CREATE_EVENT); + } + + + mal_result result = MAL_NO_BACKEND; + switch (pContext->backend) + { + #ifdef MAL_HAS_WASAPI + case mal_backend_wasapi: + { + result = mal_device_init__wasapi(pContext, type, pDeviceID, &config, pDevice); + } break; + #endif + #ifdef MAL_HAS_DSOUND + case mal_backend_dsound: + { + result = mal_device_init__dsound(pContext, type, pDeviceID, &config, pDevice); + } break; + #endif + #ifdef MAL_HAS_WINMM + case mal_backend_winmm: + { + result = mal_device_init__winmm(pContext, type, pDeviceID, &config, pDevice); + } break; + #endif + #ifdef MAL_HAS_ALSA + case mal_backend_alsa: + { + result = mal_device_init__alsa(pContext, type, pDeviceID, &config, pDevice); + } break; + #endif + #ifdef MAL_HAS_OSS + case mal_backend_oss: + { + result = mal_device_init__oss(pContext, type, pDeviceID, &config, pDevice); + } break; + #endif + #ifdef MAL_HAS_OPENSL + case mal_backend_opensl: + { + result = mal_device_init__opensl(pContext, type, pDeviceID, &config, pDevice); + } break; + #endif + #ifdef MAL_HAS_OPENAL + case mal_backend_openal: + { + result = mal_device_init__openal(pContext, type, pDeviceID, &config, pDevice); + } break; + #endif + #ifdef MAL_HAS_SDL + case mal_backend_sdl: + { + result = mal_device_init__sdl(pContext, type, pDeviceID, &config, pDevice); + } break; + #endif + #ifdef MAL_HAS_NULL + case mal_backend_null: + { + result = mal_device_init__null(pContext, type, pDeviceID, &config, pDevice); + } break; + #endif + + default: break; + } + + if (result != MAL_SUCCESS) { + return MAL_NO_BACKEND; // The error message will have been posted with mal_post_error() by the source of the error so don't bother calling it here. + } + + + // If the backend did not fill out a name for the device, try a generic method. + if (pDevice->name[0] == '\0') { + if (mal_context__try_get_device_name_by_id(pContext, type, pDeviceID, pDevice->name, sizeof(pDevice->name)) != MAL_SUCCESS) { + // We failed to get the device name, so fall back to some generic names. + if (pDeviceID == NULL) { + if (type == mal_device_type_playback) { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Default Playback Device", (size_t)-1); + } else { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Default Capture Device", (size_t)-1); + } + } else { + if (type == mal_device_type_playback) { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Playback Device", (size_t)-1); + } else { + mal_strncpy_s(pDevice->name, sizeof(pDevice->name), "Capture Device", (size_t)-1); + } + } + } + } + + + // We need a DSP object which is where samples are moved through in order to convert them to the + // format required by the backend. + mal_dsp_config dspConfig; + dspConfig.cacheSizeInFrames = pDevice->bufferSizeInFrames; + if (type == mal_device_type_playback) { + dspConfig.formatIn = pDevice->format; + dspConfig.channelsIn = pDevice->channels; + dspConfig.sampleRateIn = pDevice->sampleRate; + mal_copy_memory(dspConfig.channelMapIn, pDevice->channelMap, sizeof(dspConfig.channelMapIn)); + dspConfig.formatOut = pDevice->internalFormat; + dspConfig.channelsOut = pDevice->internalChannels; + dspConfig.sampleRateOut = pDevice->internalSampleRate; + mal_copy_memory(dspConfig.channelMapOut, pDevice->internalChannelMap, sizeof(dspConfig.channelMapOut)); + mal_dsp_init(&dspConfig, mal_device__on_read_from_client, pDevice, &pDevice->dsp); + } else { + dspConfig.formatIn = pDevice->internalFormat; + dspConfig.channelsIn = pDevice->internalChannels; + dspConfig.sampleRateIn = pDevice->internalSampleRate; + mal_copy_memory(dspConfig.channelMapIn, pDevice->internalChannelMap, sizeof(dspConfig.channelMapIn)); + dspConfig.formatOut = pDevice->format; + dspConfig.channelsOut = pDevice->channels; + dspConfig.sampleRateOut = pDevice->sampleRate; + mal_copy_memory(dspConfig.channelMapOut, pDevice->channelMap, sizeof(dspConfig.channelMapOut)); + mal_dsp_init(&dspConfig, mal_device__on_read_from_device, pDevice, &pDevice->dsp); + } + + + + + // Some backends don't require the worker thread. + if (pContext->backend != mal_backend_opensl && pContext->backend != mal_backend_sdl) { + // The worker thread. + if (mal_thread_create(pContext, &pDevice->thread, mal_worker_thread, pDevice) != MAL_SUCCESS) { + mal_device_uninit(pDevice); + return mal_post_error(pDevice, "Failed to create worker thread.", MAL_FAILED_TO_CREATE_THREAD); + } + + // Wait for the worker thread to put the device into it's stopped state for real. + mal_event_wait(&pDevice->stopEvent); + } else { + mal_device__set_state(pDevice, MAL_STATE_STOPPED); + } + + mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STOPPED); + return MAL_SUCCESS; +} + +void mal_device_uninit(mal_device* pDevice) +{ + if (!mal_device__is_initialized(pDevice)) return; + + // Make sure the device is stopped first. The backends will probably handle this naturally, + // but I like to do it explicitly for my own sanity. + if (mal_device_is_started(pDevice)) { + while (mal_device_stop(pDevice) == MAL_DEVICE_BUSY) { + mal_sleep(1); + } + } + + // Putting the device into an uninitialized state will make the worker thread return. + mal_device__set_state(pDevice, MAL_STATE_UNINITIALIZED); + + // Wake up the worker thread and wait for it to properly terminate. + if (pDevice->pContext->backend != mal_backend_opensl && pDevice->pContext->backend != mal_backend_sdl) { + mal_event_signal(&pDevice->wakeupEvent); + mal_thread_wait(&pDevice->thread); + } + + mal_event_uninit(&pDevice->stopEvent); + mal_event_uninit(&pDevice->startEvent); + mal_event_uninit(&pDevice->wakeupEvent); + mal_mutex_uninit(&pDevice->lock); + +#ifdef MAL_HAS_WASAPI + if (pDevice->pContext->backend == mal_backend_wasapi) { + mal_device_uninit__wasapi(pDevice); + } +#endif +#ifdef MAL_HAS_DSOUND + if (pDevice->pContext->backend == mal_backend_dsound) { + mal_device_uninit__dsound(pDevice); + } +#endif +#ifdef MAL_HAS_WINMM + if (pDevice->pContext->backend == mal_backend_winmm) { + mal_device_uninit__winmm(pDevice); + } +#endif +#ifdef MAL_HAS_ALSA + if (pDevice->pContext->backend == mal_backend_alsa) { + mal_device_uninit__alsa(pDevice); + } +#endif +#ifdef MAL_HAS_OSS + if (pDevice->pContext->backend == mal_backend_oss) { + mal_device_uninit__oss(pDevice); + } +#endif +#ifdef MAL_HAS_OPENSL + if (pDevice->pContext->backend == mal_backend_opensl) { + mal_device_uninit__opensl(pDevice); + } +#endif +#ifdef MAL_HAS_OPENAL + if (pDevice->pContext->backend == mal_backend_openal) { + mal_device_uninit__openal(pDevice); + } +#endif +#ifdef MAL_HAS_SDL + if (pDevice->pContext->backend == mal_backend_sdl) { + mal_device_uninit__sdl(pDevice); + } +#endif +#ifdef MAL_HAS_NULL + if (pDevice->pContext->backend == mal_backend_null) { + mal_device_uninit__null(pDevice); + } +#endif + + mal_zero_object(pDevice); +} + +void mal_device_set_recv_callback(mal_device* pDevice, mal_recv_proc proc) +{ + if (pDevice == NULL) return; + mal_atomic_exchange_ptr(&pDevice->onRecv, proc); +} + +void mal_device_set_send_callback(mal_device* pDevice, mal_send_proc proc) +{ + if (pDevice == NULL) return; + mal_atomic_exchange_ptr(&pDevice->onSend, proc); +} + +void mal_device_set_stop_callback(mal_device* pDevice, mal_stop_proc proc) +{ + if (pDevice == NULL) return; + mal_atomic_exchange_ptr(&pDevice->onStop, proc); +} + +mal_result mal_device_start(mal_device* pDevice) +{ + if (pDevice == NULL) return mal_post_error(pDevice, "mal_device_start() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); + if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); + + mal_result result = MAL_ERROR; + mal_mutex_lock(&pDevice->lock); + { + // Be a bit more descriptive if the device is already started or is already in the process of starting. This is likely + // a bug with the application. + if (mal_device__get_state(pDevice) == MAL_STATE_STARTING) { + mal_mutex_unlock(&pDevice->lock); + return mal_post_error(pDevice, "mal_device_start() called while another thread is already starting it.", MAL_DEVICE_ALREADY_STARTING); + } + if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { + mal_mutex_unlock(&pDevice->lock); + return mal_post_error(pDevice, "mal_device_start() called for a device that's already started.", MAL_DEVICE_ALREADY_STARTED); + } + + // The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy. + if (mal_device__get_state(pDevice) != MAL_STATE_STOPPED) { + mal_mutex_unlock(&pDevice->lock); + return mal_post_error(pDevice, "mal_device_start() called while another thread is in the process of stopping it.", MAL_DEVICE_BUSY); + } + + mal_device__set_state(pDevice, MAL_STATE_STARTING); + + // Asynchronous backends need to be handled differently. +#ifdef MAL_HAS_OPENSL + if (pDevice->pContext->backend == mal_backend_opensl) { + result = mal_device__start_backend__opensl(pDevice); + if (result == MAL_SUCCESS) { + mal_device__set_state(pDevice, MAL_STATE_STARTED); + } + } else +#endif +#ifdef MAL_HAS_SDL + if (pDevice->pContext->backend == mal_backend_sdl) { + result = mal_device__start_backend__sdl(pDevice); + if (result == MAL_SUCCESS) { + mal_device__set_state(pDevice, MAL_STATE_STARTED); + } + } else +#endif + // Synchronous backends. + { + mal_event_signal(&pDevice->wakeupEvent); + + // Wait for the worker thread to finish starting the device. Note that the worker thread will be the one + // who puts the device into the started state. Don't call mal_device__set_state() here. + mal_event_wait(&pDevice->startEvent); + result = pDevice->workResult; + } + } + mal_mutex_unlock(&pDevice->lock); + + return result; +} + +mal_result mal_device_stop(mal_device* pDevice) +{ + if (pDevice == NULL) return mal_post_error(pDevice, "mal_device_stop() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); + if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); + + mal_result result = MAL_ERROR; + mal_mutex_lock(&pDevice->lock); + { + // Be a bit more descriptive if the device is already stopped or is already in the process of stopping. This is likely + // a bug with the application. + if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) { + mal_mutex_unlock(&pDevice->lock); + return mal_post_error(pDevice, "mal_device_stop() called while another thread is already stopping it.", MAL_DEVICE_ALREADY_STOPPING); + } + if (mal_device__get_state(pDevice) == MAL_STATE_STOPPED) { + mal_mutex_unlock(&pDevice->lock); + return mal_post_error(pDevice, "mal_device_stop() called for a device that's already stopped.", MAL_DEVICE_ALREADY_STOPPED); + } + + // The device needs to be in a started state. If it's not, we just let the caller know the device is busy. + if (mal_device__get_state(pDevice) != MAL_STATE_STARTED) { + mal_mutex_unlock(&pDevice->lock); + return mal_post_error(pDevice, "mal_device_stop() called while another thread is in the process of starting it.", MAL_DEVICE_BUSY); + } + + mal_device__set_state(pDevice, MAL_STATE_STOPPING); + + // There's no need to wake up the thread like we do when starting. + + // Asynchronous backends need to be handled differently. +#ifdef MAL_HAS_OPENSL + if (pDevice->pContext->backend == mal_backend_opensl) { + mal_device__stop_backend__opensl(pDevice); + } else +#endif +#ifdef MAL_HAS_SDL + if (pDevice->pContext->backend == mal_backend_sdl) { + mal_device__stop_backend__sdl(pDevice); + } else +#endif + // Synchronous backends. + { + // When we get here the worker thread is likely in a wait state while waiting for the backend device to deliver or request + // audio data. We need to force these to return as quickly as possible. + mal_device__break_main_loop(pDevice); + + // We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be + // the one who puts the device into the stopped state. Don't call mal_device__set_state() here. + mal_event_wait(&pDevice->stopEvent); + result = MAL_SUCCESS; + } + } + mal_mutex_unlock(&pDevice->lock); + + return result; +} + +mal_bool32 mal_device_is_started(mal_device* pDevice) +{ + if (pDevice == NULL) return MAL_FALSE; + return mal_device__get_state(pDevice) == MAL_STATE_STARTED; +} + +mal_uint32 mal_device_get_buffer_size_in_bytes(mal_device* pDevice) +{ + if (pDevice == NULL) return 0; + return pDevice->bufferSizeInFrames * pDevice->channels * mal_get_sample_size_in_bytes(pDevice->format); +} + +mal_uint32 mal_get_sample_size_in_bytes(mal_format format) +{ + mal_uint32 sizes[] = { + 0, // unknown + 1, // u8 + 2, // s16 + 3, // s24 + 4, // s32 + 4, // f32 + }; + return sizes[format]; +} + +mal_context_config mal_context_config_init(mal_log_proc onLog) +{ + mal_context_config config; + mal_zero_object(&config); + + config.onLog = onLog; + + return config; +} + +mal_device_config mal_device_config_init(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_recv_proc onRecvCallback, mal_send_proc onSendCallback) +{ + mal_device_config config; + mal_zero_object(&config); + + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.onRecvCallback = onRecvCallback; + config.onSendCallback = onSendCallback; + + switch (channels) + { + case 1: + { + config.channelMap[0] = MAL_CHANNEL_FRONT_CENTER; + } break; + + case 2: + { + config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + } break; + + case 3: + { + config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + config.channelMap[2] = MAL_CHANNEL_LFE; + } break; + + case 4: + { + config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + config.channelMap[2] = MAL_CHANNEL_BACK_LEFT; + config.channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 5: + { + config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + config.channelMap[2] = MAL_CHANNEL_BACK_LEFT; + config.channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + config.channelMap[4] = MAL_CHANNEL_LFE; + } break; + + case 6: + { + config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + config.channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + config.channelMap[3] = MAL_CHANNEL_LFE; + config.channelMap[4] = MAL_CHANNEL_BACK_LEFT; + config.channelMap[5] = MAL_CHANNEL_BACK_RIGHT; + } break; + + case 8: + { + config.channelMap[0] = MAL_CHANNEL_FRONT_LEFT; + config.channelMap[1] = MAL_CHANNEL_FRONT_RIGHT; + config.channelMap[2] = MAL_CHANNEL_FRONT_CENTER; + config.channelMap[3] = MAL_CHANNEL_LFE; + config.channelMap[4] = MAL_CHANNEL_BACK_LEFT; + config.channelMap[5] = MAL_CHANNEL_BACK_RIGHT; + config.channelMap[6] = MAL_CHANNEL_SIDE_LEFT; + config.channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + } break; + + default: + { + // Just leave it all blank in this case. This will use the same mapping as the device's native mapping. + } break; + } + + return config; +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// SRC +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void mal_src_cache_init(mal_src* pSRC, mal_src_cache* pCache) +{ + mal_assert(pSRC != NULL); + mal_assert(pCache != NULL); + + pCache->pSRC = pSRC; + pCache->cachedFrameCount = 0; + pCache->iNextFrame = 0; +} + +mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCount, float* pFramesOut) +{ + mal_assert(pCache != NULL); + mal_assert(pCache->pSRC != NULL); + mal_assert(pCache->pSRC->onRead != NULL); + mal_assert(frameCount > 0); + mal_assert(pFramesOut != NULL); + + mal_uint32 channels = pCache->pSRC->config.channels; + + mal_uint32 totalFramesRead = 0; + while (frameCount > 0) { + // If there's anything in memory go ahead and copy that over first. + mal_uint32 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame; + mal_uint32 framesToReadFromMemory = frameCount; + if (framesToReadFromMemory > framesRemainingInMemory) { + framesToReadFromMemory = framesRemainingInMemory; + } + + mal_copy_memory(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, framesToReadFromMemory * channels * sizeof(float)); + pCache->iNextFrame += framesToReadFromMemory; + + totalFramesRead += framesToReadFromMemory; + frameCount -= framesToReadFromMemory; + if (frameCount == 0) { + break; + } + + + // At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data. + mal_assert(frameCount > 0); + pFramesOut += framesToReadFromMemory * channels; + + pCache->iNextFrame = 0; + pCache->cachedFrameCount = 0; + if (pCache->pSRC->config.formatIn == mal_format_f32) { + // No need for a conversion - read straight into the cache. + mal_uint32 framesToReadFromClient = mal_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels; + if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) { + framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; + } + + pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData); + } else { + // A format conversion is required which means we need to use an intermediary buffer. + mal_uint8 pIntermediaryBuffer[sizeof(pCache->pCachedFrames)]; + mal_uint32 framesToReadFromClient = mal_min(mal_buffer_frame_capacity(pIntermediaryBuffer, channels, pCache->pSRC->config.formatIn), mal_buffer_frame_capacity(pCache->pCachedFrames, channels, mal_format_f32)); + if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) { + framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; + } + + pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pIntermediaryBuffer, pCache->pSRC->pUserData); + + // Convert to f32. + mal_pcm_convert(pCache->pCachedFrames, mal_format_f32, pIntermediaryBuffer, pCache->pSRC->config.formatIn, pCache->cachedFrameCount * channels); + } + + + // Get out of this loop if nothing was able to be retrieved. + if (pCache->cachedFrameCount == 0) { + break; + } + } + + return totalFramesRead; +} + + +mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); +mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); + +mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC) +{ + if (pSRC == NULL) return MAL_INVALID_ARGS; + mal_zero_object(pSRC); + + if (pConfig == NULL || onRead == NULL) return MAL_INVALID_ARGS; + if (pConfig->channels == 0 || pConfig->channels > MAL_MAX_CHANNELS) return MAL_INVALID_ARGS; + + pSRC->config = *pConfig; + pSRC->onRead = onRead; + pSRC->pUserData = pUserData; + + if (pSRC->config.cacheSizeInFrames > MAL_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) { + pSRC->config.cacheSizeInFrames = MAL_SRC_CACHE_SIZE_IN_FRAMES; + } + + mal_src_cache_init(pSRC, &pSRC->cache); + return MAL_SUCCESS; +} + +mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut) +{ + if (pSRC == NULL) return MAL_INVALID_ARGS; + + // Must have a sample rate of > 0. + if (sampleRateOut == 0) { + return MAL_INVALID_ARGS; + } + + pSRC->config.sampleRateOut = sampleRateOut; + return MAL_SUCCESS; +} + +mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut) +{ + return mal_src_read_frames_ex(pSRC, frameCount, pFramesOut, MAL_FALSE); +} + +mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) +{ + if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0; + + mal_src_algorithm algorithm = pSRC->config.algorithm; + + // Always use passthrough if the sample rates are the same. + if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) { + algorithm = mal_src_algorithm_none; + } + + // Could just use a function pointer instead of a switch for this... + switch (algorithm) + { + case mal_src_algorithm_none: return mal_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush); + case mal_src_algorithm_linear: return mal_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush); + default: return 0; + } +} + +mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) +{ + mal_assert(pSRC != NULL); + mal_assert(frameCount > 0); + mal_assert(pFramesOut != NULL); + + (void)flush; // Passthrough need not care about flushing. + + // Fast path. No need for data conversion - just pass right through. + if (pSRC->config.formatIn == pSRC->config.formatOut) { + return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData); + } + + // Slower path. Need to do a format conversion. + mal_uint32 totalFramesRead = 0; + while (frameCount > 0) { + mal_uint8 pStagingBuffer[MAL_MAX_CHANNELS * 2048]; + mal_uint32 stagingBufferSizeInFrames = sizeof(pStagingBuffer) / mal_get_sample_size_in_bytes(pSRC->config.formatIn) / pSRC->config.channels; + mal_uint32 framesToRead = stagingBufferSizeInFrames; + if (framesToRead > frameCount) { + framesToRead = frameCount; + } + + mal_uint32 framesRead = pSRC->onRead(pSRC, framesToRead, pStagingBuffer, pSRC->pUserData); + if (framesRead == 0) { + break; + } + + mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pStagingBuffer, pSRC->config.formatIn, framesRead * pSRC->config.channels); + + pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut)); + frameCount -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + +mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) +{ + mal_assert(pSRC != NULL); + mal_assert(frameCount > 0); + mal_assert(pFramesOut != NULL); + + // For linear SRC, the bin is only 2 frames: 1 prior, 1 future. + + // Load the bin if necessary. + if (!pSRC->linear.isPrevFramesLoaded) { + mal_uint32 framesRead = mal_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin); + if (framesRead == 0) { + return 0; + } + pSRC->linear.isPrevFramesLoaded = MAL_TRUE; + } + if (!pSRC->linear.isNextFramesLoaded) { + mal_uint32 framesRead = mal_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin + pSRC->config.channels); + if (framesRead == 0) { + return 0; + } + pSRC->linear.isNextFramesLoaded = MAL_TRUE; + } + + float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; + + mal_uint32 totalFramesRead = 0; + while (frameCount > 0) { + // The bin is where the previous and next frames are located. + float* pPrevFrame = pSRC->bin; + float* pNextFrame = pSRC->bin + pSRC->config.channels; + + float pFrame[MAL_MAX_CHANNELS]; + mal_blend_f32(pFrame, pPrevFrame, pNextFrame, pSRC->linear.alpha, pSRC->config.channels); + + pSRC->linear.alpha += factor; + + // The new alpha value is how we determine whether or not we need to read fresh frames. + mal_uint32 framesToReadFromClient = (mal_uint32)pSRC->linear.alpha; + pSRC->linear.alpha = pSRC->linear.alpha - framesToReadFromClient; + + for (mal_uint32 i = 0; i < framesToReadFromClient; ++i) { + for (mal_uint32 j = 0; j < pSRC->config.channels; ++j) { + pPrevFrame[j] = pNextFrame[j]; + } + + mal_uint32 framesRead = mal_src_cache_read_frames(&pSRC->cache, 1, pNextFrame); + if (framesRead == 0) { + for (mal_uint32 j = 0; j < pSRC->config.channels; ++j) { + pNextFrame[j] = 0; + } + + if (pSRC->linear.isNextFramesLoaded) { + pSRC->linear.isNextFramesLoaded = MAL_FALSE; + } else { + if (flush) { + pSRC->linear.isPrevFramesLoaded = MAL_FALSE; + } + } + + break; + } + } + + mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pFrame, mal_format_f32, 1 * pSRC->config.channels); + + pFramesOut = (mal_uint8*)pFramesOut + (1 * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut)); + frameCount -= 1; + totalFramesRead += 1; + + // If there's no frames available we need to get out of this loop. + if (!pSRC->linear.isNextFramesLoaded && (!flush || !pSRC->linear.isPrevFramesLoaded)) { + break; + } + } + + return totalFramesRead; +} + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// FORMAT CONVERSION +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void mal_pcm_u8_to_s16(short* pOut, const unsigned char* pIn, unsigned int count); +void mal_pcm_u8_to_s24(void* pOut, const unsigned char* pIn, unsigned int count); +void mal_pcm_u8_to_s32(int* pOut, const unsigned char* pIn, unsigned int count); +void mal_pcm_u8_to_f32(float* pOut, const unsigned char* pIn, unsigned int count); +void mal_pcm_s16_to_u8(unsigned char* pOut, const short* pIn, unsigned int count); +void mal_pcm_s16_to_s24(void* pOut, const short* pIn, unsigned int count); +void mal_pcm_s16_to_s32(int* pOut, const short* pIn, unsigned int count); +void mal_pcm_s16_to_f32(float* pOut, const short* pIn, unsigned int count); +void mal_pcm_s24_to_u8(unsigned char* pOut, const void* pIn, unsigned int count); +void mal_pcm_s24_to_s16(short* pOut, const void* pIn, unsigned int count); +void mal_pcm_s24_to_s32(int* pOut, const void* pIn, unsigned int count); +void mal_pcm_s24_to_f32(float* pOut, const void* pIn, unsigned int count); +void mal_pcm_s32_to_u8(unsigned char* pOut, const int* pIn, unsigned int count); +void mal_pcm_s32_to_s16(short* pOut, const int* pIn, unsigned int count); +void mal_pcm_s32_to_s24(void* pOut, const int* pIn, unsigned int count); +void mal_pcm_s32_to_f32(float* pOut, const int* pIn, unsigned int count); +void mal_pcm_f32_to_u8(unsigned char* pOut, const float* pIn, unsigned int count); +void mal_pcm_f32_to_s16(short* pOut, const float* pIn, unsigned int count); +void mal_pcm_f32_to_s24(void* pOut, const float* pIn, unsigned int count); +void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count); + +void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, unsigned int sampleCount) +{ + if (formatOut == formatIn) { + mal_copy_memory(pOut, pIn, sampleCount * mal_get_sample_size_in_bytes(formatOut)); + return; + } + + switch (formatIn) + { + case mal_format_u8: + { + switch (formatOut) + { + case mal_format_s16: mal_pcm_u8_to_s16((short*)pOut, (const unsigned char*)pIn, sampleCount); return; + case mal_format_s24: mal_pcm_u8_to_s24( pOut, (const unsigned char*)pIn, sampleCount); return; + case mal_format_s32: mal_pcm_u8_to_s32( (int*)pOut, (const unsigned char*)pIn, sampleCount); return; + case mal_format_f32: mal_pcm_u8_to_f32((float*)pOut, (const unsigned char*)pIn, sampleCount); return; + default: break; + } + } break; + + case mal_format_s16: + { + switch (formatOut) + { + case mal_format_u8: mal_pcm_s16_to_u8( (unsigned char*)pOut, (const short*)pIn, sampleCount); return; + case mal_format_s24: mal_pcm_s16_to_s24( pOut, (const short*)pIn, sampleCount); return; + case mal_format_s32: mal_pcm_s16_to_s32( (int*)pOut, (const short*)pIn, sampleCount); return; + case mal_format_f32: mal_pcm_s16_to_f32( (float*)pOut, (const short*)pIn, sampleCount); return; + default: break; + } + } break; + + case mal_format_s24: + { + switch (formatOut) + { + case mal_format_u8: mal_pcm_s24_to_u8( (unsigned char*)pOut, pIn, sampleCount); return; + case mal_format_s16: mal_pcm_s24_to_s16( (short*)pOut, pIn, sampleCount); return; + case mal_format_s32: mal_pcm_s24_to_s32( (int*)pOut, pIn, sampleCount); return; + case mal_format_f32: mal_pcm_s24_to_f32( (float*)pOut, pIn, sampleCount); return; + default: break; + } + } break; + + case mal_format_s32: + { + switch (formatOut) + { + case mal_format_u8: mal_pcm_s32_to_u8( (unsigned char*)pOut, (const int*)pIn, sampleCount); return; + case mal_format_s16: mal_pcm_s32_to_s16( (short*)pOut, (const int*)pIn, sampleCount); return; + case mal_format_s24: mal_pcm_s32_to_s24( pOut, (const int*)pIn, sampleCount); return; + case mal_format_f32: mal_pcm_s32_to_f32( (float*)pOut, (const int*)pIn, sampleCount); return; + default: break; + } + } break; + + case mal_format_f32: + { + switch (formatOut) + { + case mal_format_u8: mal_pcm_f32_to_u8( (unsigned char*)pOut, (const float*)pIn, sampleCount); return; + case mal_format_s16: mal_pcm_f32_to_s16( (short*)pOut, (const float*)pIn, sampleCount); return; + case mal_format_s24: mal_pcm_f32_to_s24( pOut, (const float*)pIn, sampleCount); return; + case mal_format_s32: mal_pcm_f32_to_s32( (int*)pOut, (const float*)pIn, sampleCount); return; + default: break; + } + } break; + + default: break; + } +} + + +static void mal_rearrange_channels_u8(mal_uint8* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +{ + mal_uint8 temp[MAL_MAX_CHANNELS]; + mal_copy_memory(temp, pFrame, sizeof(temp[0]) * channels); + + switch (channels) { + case 18: pFrame[17] = temp[channelMap[17]]; + case 17: pFrame[16] = temp[channelMap[16]]; + case 16: pFrame[15] = temp[channelMap[15]]; + case 15: pFrame[14] = temp[channelMap[14]]; + case 14: pFrame[13] = temp[channelMap[13]]; + case 13: pFrame[12] = temp[channelMap[12]]; + case 12: pFrame[11] = temp[channelMap[11]]; + case 11: pFrame[10] = temp[channelMap[10]]; + case 10: pFrame[ 9] = temp[channelMap[ 9]]; + case 9: pFrame[ 8] = temp[channelMap[ 8]]; + case 8: pFrame[ 7] = temp[channelMap[ 7]]; + case 7: pFrame[ 6] = temp[channelMap[ 6]]; + case 6: pFrame[ 5] = temp[channelMap[ 5]]; + case 5: pFrame[ 4] = temp[channelMap[ 4]]; + case 4: pFrame[ 3] = temp[channelMap[ 3]]; + case 3: pFrame[ 2] = temp[channelMap[ 2]]; + case 2: pFrame[ 1] = temp[channelMap[ 1]]; + case 1: pFrame[ 0] = temp[channelMap[ 0]]; + } +} + +static void mal_rearrange_channels_s16(mal_int16* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +{ + mal_int16 temp[MAL_MAX_CHANNELS]; + mal_copy_memory(temp, pFrame, sizeof(temp[0]) * channels); + + switch (channels) { + case 18: pFrame[17] = temp[channelMap[17]]; + case 17: pFrame[16] = temp[channelMap[16]]; + case 16: pFrame[15] = temp[channelMap[15]]; + case 15: pFrame[14] = temp[channelMap[14]]; + case 14: pFrame[13] = temp[channelMap[13]]; + case 13: pFrame[12] = temp[channelMap[12]]; + case 12: pFrame[11] = temp[channelMap[11]]; + case 11: pFrame[10] = temp[channelMap[10]]; + case 10: pFrame[ 9] = temp[channelMap[ 9]]; + case 9: pFrame[ 8] = temp[channelMap[ 8]]; + case 8: pFrame[ 7] = temp[channelMap[ 7]]; + case 7: pFrame[ 6] = temp[channelMap[ 6]]; + case 6: pFrame[ 5] = temp[channelMap[ 5]]; + case 5: pFrame[ 4] = temp[channelMap[ 4]]; + case 4: pFrame[ 3] = temp[channelMap[ 3]]; + case 3: pFrame[ 2] = temp[channelMap[ 2]]; + case 2: pFrame[ 1] = temp[channelMap[ 1]]; + case 1: pFrame[ 0] = temp[channelMap[ 0]]; + } +} + +static void mal_rearrange_channels_s32(mal_int32* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +{ + mal_int32 temp[MAL_MAX_CHANNELS]; + mal_copy_memory(temp, pFrame, sizeof(temp[0]) * channels); + + switch (channels) { + case 18: pFrame[17] = temp[channelMap[17]]; + case 17: pFrame[16] = temp[channelMap[16]]; + case 16: pFrame[15] = temp[channelMap[15]]; + case 15: pFrame[14] = temp[channelMap[14]]; + case 14: pFrame[13] = temp[channelMap[13]]; + case 13: pFrame[12] = temp[channelMap[12]]; + case 12: pFrame[11] = temp[channelMap[11]]; + case 11: pFrame[10] = temp[channelMap[10]]; + case 10: pFrame[ 9] = temp[channelMap[ 9]]; + case 9: pFrame[ 8] = temp[channelMap[ 8]]; + case 8: pFrame[ 7] = temp[channelMap[ 7]]; + case 7: pFrame[ 6] = temp[channelMap[ 6]]; + case 6: pFrame[ 5] = temp[channelMap[ 5]]; + case 5: pFrame[ 4] = temp[channelMap[ 4]]; + case 4: pFrame[ 3] = temp[channelMap[ 3]]; + case 3: pFrame[ 2] = temp[channelMap[ 2]]; + case 2: pFrame[ 1] = temp[channelMap[ 1]]; + case 1: pFrame[ 0] = temp[channelMap[ 0]]; + } +} + +static void mal_rearrange_channels_f32(float* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS]) +{ + float temp[MAL_MAX_CHANNELS]; + mal_copy_memory(temp, pFrame, sizeof(temp[0]) * channels); + + switch (channels) { + case 18: pFrame[17] = temp[channelMap[17]]; + case 17: pFrame[16] = temp[channelMap[16]]; + case 16: pFrame[15] = temp[channelMap[15]]; + case 15: pFrame[14] = temp[channelMap[14]]; + case 14: pFrame[13] = temp[channelMap[13]]; + case 13: pFrame[12] = temp[channelMap[12]]; + case 12: pFrame[11] = temp[channelMap[11]]; + case 11: pFrame[10] = temp[channelMap[10]]; + case 10: pFrame[ 9] = temp[channelMap[ 9]]; + case 9: pFrame[ 8] = temp[channelMap[ 8]]; + case 8: pFrame[ 7] = temp[channelMap[ 7]]; + case 7: pFrame[ 6] = temp[channelMap[ 6]]; + case 6: pFrame[ 5] = temp[channelMap[ 5]]; + case 5: pFrame[ 4] = temp[channelMap[ 4]]; + case 4: pFrame[ 3] = temp[channelMap[ 3]]; + case 3: pFrame[ 2] = temp[channelMap[ 2]]; + case 2: pFrame[ 1] = temp[channelMap[ 1]]; + case 1: pFrame[ 0] = temp[channelMap[ 0]]; + } +} + +static void mal_rearrange_channels_generic(void* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS], mal_format format) +{ + mal_uint32 sampleSizeInBytes = mal_get_sample_size_in_bytes(format); + + mal_uint8 temp[MAL_MAX_CHANNELS * 8]; // x8 to ensure it's large enough for all formats. + mal_copy_memory(temp, pFrame, sampleSizeInBytes * channels); + + switch (channels) { + case 18: mal_copy_memory((mal_uint8*)pFrame + (17 * sampleSizeInBytes), &temp[channelMap[17] * sampleSizeInBytes], sampleSizeInBytes); + case 17: mal_copy_memory((mal_uint8*)pFrame + (16 * sampleSizeInBytes), &temp[channelMap[16] * sampleSizeInBytes], sampleSizeInBytes); + case 16: mal_copy_memory((mal_uint8*)pFrame + (15 * sampleSizeInBytes), &temp[channelMap[15] * sampleSizeInBytes], sampleSizeInBytes); + case 15: mal_copy_memory((mal_uint8*)pFrame + (14 * sampleSizeInBytes), &temp[channelMap[14] * sampleSizeInBytes], sampleSizeInBytes); + case 14: mal_copy_memory((mal_uint8*)pFrame + (13 * sampleSizeInBytes), &temp[channelMap[13] * sampleSizeInBytes], sampleSizeInBytes); + case 13: mal_copy_memory((mal_uint8*)pFrame + (12 * sampleSizeInBytes), &temp[channelMap[12] * sampleSizeInBytes], sampleSizeInBytes); + case 12: mal_copy_memory((mal_uint8*)pFrame + (11 * sampleSizeInBytes), &temp[channelMap[11] * sampleSizeInBytes], sampleSizeInBytes); + case 11: mal_copy_memory((mal_uint8*)pFrame + (10 * sampleSizeInBytes), &temp[channelMap[10] * sampleSizeInBytes], sampleSizeInBytes); + case 10: mal_copy_memory((mal_uint8*)pFrame + ( 9 * sampleSizeInBytes), &temp[channelMap[ 9] * sampleSizeInBytes], sampleSizeInBytes); + case 9: mal_copy_memory((mal_uint8*)pFrame + ( 8 * sampleSizeInBytes), &temp[channelMap[ 8] * sampleSizeInBytes], sampleSizeInBytes); + case 8: mal_copy_memory((mal_uint8*)pFrame + ( 7 * sampleSizeInBytes), &temp[channelMap[ 7] * sampleSizeInBytes], sampleSizeInBytes); + case 7: mal_copy_memory((mal_uint8*)pFrame + ( 6 * sampleSizeInBytes), &temp[channelMap[ 6] * sampleSizeInBytes], sampleSizeInBytes); + case 6: mal_copy_memory((mal_uint8*)pFrame + ( 5 * sampleSizeInBytes), &temp[channelMap[ 5] * sampleSizeInBytes], sampleSizeInBytes); + case 5: mal_copy_memory((mal_uint8*)pFrame + ( 4 * sampleSizeInBytes), &temp[channelMap[ 4] * sampleSizeInBytes], sampleSizeInBytes); + case 4: mal_copy_memory((mal_uint8*)pFrame + ( 3 * sampleSizeInBytes), &temp[channelMap[ 3] * sampleSizeInBytes], sampleSizeInBytes); + case 3: mal_copy_memory((mal_uint8*)pFrame + ( 2 * sampleSizeInBytes), &temp[channelMap[ 2] * sampleSizeInBytes], sampleSizeInBytes); + case 2: mal_copy_memory((mal_uint8*)pFrame + ( 1 * sampleSizeInBytes), &temp[channelMap[ 1] * sampleSizeInBytes], sampleSizeInBytes); + case 1: mal_copy_memory((mal_uint8*)pFrame + ( 0 * sampleSizeInBytes), &temp[channelMap[ 0] * sampleSizeInBytes], sampleSizeInBytes); + } +} + +static void mal_rearrange_channels(void* pFrame, mal_uint32 channels, mal_uint8 channelMap[MAL_MAX_CHANNELS], mal_format format) +{ + switch (format) + { + case mal_format_u8: mal_rearrange_channels_u8( (mal_uint8*)pFrame, channels, channelMap); break; + case mal_format_s16: mal_rearrange_channels_s16((mal_int16*)pFrame, channels, channelMap); break; + case mal_format_s32: mal_rearrange_channels_s32((mal_int32*)pFrame, channels, channelMap); break; + case mal_format_f32: mal_rearrange_channels_f32( (float*)pFrame, channels, channelMap); break; + default: mal_rearrange_channels_generic(pFrame, channels, channelMap, format); break; + } +} + +static void mal_dsp_mix_channels__dec(float* pFramesOut, mal_uint32 channelsOut, const mal_uint8 channelMapOut[MAL_MAX_CHANNELS], const float* pFramesIn, mal_uint32 channelsIn, const mal_uint8 channelMapIn[MAL_MAX_CHANNELS], mal_uint32 frameCount, mal_channel_mix_mode mode) +{ + mal_assert(pFramesOut != NULL); + mal_assert(channelsOut > 0); + mal_assert(pFramesIn != NULL); + mal_assert(channelsIn > 0); + mal_assert(channelsOut < channelsIn); + + (void)channelMapOut; + (void)channelMapIn; + + if (mode == mal_channel_mix_mode_basic) { + // Basic mode is where we just drop excess channels. + for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { + switch (channelsOut) { + case 17: pFramesOut[iFrame*channelsOut+16] = pFramesIn[iFrame*channelsIn+16]; + case 16: pFramesOut[iFrame*channelsOut+15] = pFramesIn[iFrame*channelsIn+15]; + case 15: pFramesOut[iFrame*channelsOut+14] = pFramesIn[iFrame*channelsIn+14]; + case 14: pFramesOut[iFrame*channelsOut+13] = pFramesIn[iFrame*channelsIn+13]; + case 13: pFramesOut[iFrame*channelsOut+12] = pFramesIn[iFrame*channelsIn+12]; + case 12: pFramesOut[iFrame*channelsOut+11] = pFramesIn[iFrame*channelsIn+11]; + case 11: pFramesOut[iFrame*channelsOut+10] = pFramesIn[iFrame*channelsIn+10]; + case 10: pFramesOut[iFrame*channelsOut+ 9] = pFramesIn[iFrame*channelsIn+ 9]; + case 9: pFramesOut[iFrame*channelsOut+ 8] = pFramesIn[iFrame*channelsIn+ 8]; + case 8: pFramesOut[iFrame*channelsOut+ 7] = pFramesIn[iFrame*channelsIn+ 7]; + case 7: pFramesOut[iFrame*channelsOut+ 6] = pFramesIn[iFrame*channelsIn+ 6]; + case 6: pFramesOut[iFrame*channelsOut+ 5] = pFramesIn[iFrame*channelsIn+ 5]; + case 5: pFramesOut[iFrame*channelsOut+ 4] = pFramesIn[iFrame*channelsIn+ 4]; + case 4: pFramesOut[iFrame*channelsOut+ 3] = pFramesIn[iFrame*channelsIn+ 3]; + case 3: pFramesOut[iFrame*channelsOut+ 2] = pFramesIn[iFrame*channelsIn+ 2]; + case 2: pFramesOut[iFrame*channelsOut+ 1] = pFramesIn[iFrame*channelsIn+ 1]; + case 1: pFramesOut[iFrame*channelsOut+ 0] = pFramesIn[iFrame*channelsIn+ 0]; + } + } + } else { + // Blend mode is where we just use simple averaging to blend based on spacial locality. + if (channelsOut == 1) { + for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { + float total = 0; + switch (channelsIn) { + case 18: total += pFramesIn[iFrame*channelsIn+17]; + case 17: total += pFramesIn[iFrame*channelsIn+16]; + case 16: total += pFramesIn[iFrame*channelsIn+15]; + case 15: total += pFramesIn[iFrame*channelsIn+14]; + case 14: total += pFramesIn[iFrame*channelsIn+13]; + case 13: total += pFramesIn[iFrame*channelsIn+12]; + case 12: total += pFramesIn[iFrame*channelsIn+11]; + case 11: total += pFramesIn[iFrame*channelsIn+10]; + case 10: total += pFramesIn[iFrame*channelsIn+ 9]; + case 9: total += pFramesIn[iFrame*channelsIn+ 8]; + case 8: total += pFramesIn[iFrame*channelsIn+ 7]; + case 7: total += pFramesIn[iFrame*channelsIn+ 6]; + case 6: total += pFramesIn[iFrame*channelsIn+ 5]; + case 5: total += pFramesIn[iFrame*channelsIn+ 4]; + case 4: total += pFramesIn[iFrame*channelsIn+ 3]; + case 3: total += pFramesIn[iFrame*channelsIn+ 2]; + case 2: total += pFramesIn[iFrame*channelsIn+ 1]; + case 1: total += pFramesIn[iFrame*channelsIn+ 0]; + } + + pFramesOut[iFrame+0] = total / channelsIn; + } + } else if (channelsOut == 2) { + // TODO: Implement proper stereo blending. + mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); + } else { + // Fall back to basic mode. + mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); + } + } +} + +static void mal_dsp_mix_channels__inc(float* pFramesOut, mal_uint32 channelsOut, const mal_uint8 channelMapOut[MAL_MAX_CHANNELS], const float* pFramesIn, mal_uint32 channelsIn, const mal_uint8 channelMapIn[MAL_MAX_CHANNELS], mal_uint32 frameCount, mal_channel_mix_mode mode) +{ + mal_assert(pFramesOut != NULL); + mal_assert(channelsOut > 0); + mal_assert(pFramesIn != NULL); + mal_assert(channelsIn > 0); + mal_assert(channelsOut > channelsIn); + + (void)channelMapOut; + (void)channelMapIn; + + if (mode == mal_channel_mix_mode_basic) {\ + // Basic mode is where we just zero out extra channels. + for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { + switch (channelsIn) { + case 17: pFramesOut[iFrame*channelsOut+16] = pFramesIn[iFrame*channelsIn+16]; + case 16: pFramesOut[iFrame*channelsOut+15] = pFramesIn[iFrame*channelsIn+15]; + case 15: pFramesOut[iFrame*channelsOut+14] = pFramesIn[iFrame*channelsIn+14]; + case 14: pFramesOut[iFrame*channelsOut+13] = pFramesIn[iFrame*channelsIn+13]; + case 13: pFramesOut[iFrame*channelsOut+12] = pFramesIn[iFrame*channelsIn+12]; + case 12: pFramesOut[iFrame*channelsOut+11] = pFramesIn[iFrame*channelsIn+11]; + case 11: pFramesOut[iFrame*channelsOut+10] = pFramesIn[iFrame*channelsIn+10]; + case 10: pFramesOut[iFrame*channelsOut+ 9] = pFramesIn[iFrame*channelsIn+ 9]; + case 9: pFramesOut[iFrame*channelsOut+ 8] = pFramesIn[iFrame*channelsIn+ 8]; + case 8: pFramesOut[iFrame*channelsOut+ 7] = pFramesIn[iFrame*channelsIn+ 7]; + case 7: pFramesOut[iFrame*channelsOut+ 6] = pFramesIn[iFrame*channelsIn+ 6]; + case 6: pFramesOut[iFrame*channelsOut+ 5] = pFramesIn[iFrame*channelsIn+ 5]; + case 5: pFramesOut[iFrame*channelsOut+ 4] = pFramesIn[iFrame*channelsIn+ 4]; + case 4: pFramesOut[iFrame*channelsOut+ 3] = pFramesIn[iFrame*channelsIn+ 3]; + case 3: pFramesOut[iFrame*channelsOut+ 2] = pFramesIn[iFrame*channelsIn+ 2]; + case 2: pFramesOut[iFrame*channelsOut+ 1] = pFramesIn[iFrame*channelsIn+ 1]; + case 1: pFramesOut[iFrame*channelsOut+ 0] = pFramesIn[iFrame*channelsIn+ 0]; + } + + // Zero out extra channels. + switch (channelsOut - channelsIn) { + case 17: pFramesOut[iFrame*channelsOut+16] = 0; + case 16: pFramesOut[iFrame*channelsOut+15] = 0; + case 15: pFramesOut[iFrame*channelsOut+14] = 0; + case 14: pFramesOut[iFrame*channelsOut+13] = 0; + case 13: pFramesOut[iFrame*channelsOut+12] = 0; + case 12: pFramesOut[iFrame*channelsOut+11] = 0; + case 11: pFramesOut[iFrame*channelsOut+10] = 0; + case 10: pFramesOut[iFrame*channelsOut+ 9] = 0; + case 9: pFramesOut[iFrame*channelsOut+ 8] = 0; + case 8: pFramesOut[iFrame*channelsOut+ 7] = 0; + case 7: pFramesOut[iFrame*channelsOut+ 6] = 0; + case 6: pFramesOut[iFrame*channelsOut+ 5] = 0; + case 5: pFramesOut[iFrame*channelsOut+ 4] = 0; + case 4: pFramesOut[iFrame*channelsOut+ 3] = 0; + case 3: pFramesOut[iFrame*channelsOut+ 2] = 0; + case 2: pFramesOut[iFrame*channelsOut+ 1] = 0; + case 1: pFramesOut[iFrame*channelsOut+ 0] = 0; + } + } + } else { + // Using blended mixing mode. Basically this is just the mode where audio is distributed across all channels + // based on spacial locality. + if (channelsIn == 1) { + for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { + switch (channelsOut) { + case 18: pFramesOut[iFrame*channelsOut+17] = pFramesIn[iFrame*channelsIn+0]; + case 17: pFramesOut[iFrame*channelsOut+16] = pFramesIn[iFrame*channelsIn+0]; + case 16: pFramesOut[iFrame*channelsOut+15] = pFramesIn[iFrame*channelsIn+0]; + case 15: pFramesOut[iFrame*channelsOut+14] = pFramesIn[iFrame*channelsIn+0]; + case 14: pFramesOut[iFrame*channelsOut+13] = pFramesIn[iFrame*channelsIn+0]; + case 13: pFramesOut[iFrame*channelsOut+12] = pFramesIn[iFrame*channelsIn+0]; + case 12: pFramesOut[iFrame*channelsOut+11] = pFramesIn[iFrame*channelsIn+0]; + case 11: pFramesOut[iFrame*channelsOut+10] = pFramesIn[iFrame*channelsIn+0]; + case 10: pFramesOut[iFrame*channelsOut+ 9] = pFramesIn[iFrame*channelsIn+0]; + case 9: pFramesOut[iFrame*channelsOut+ 8] = pFramesIn[iFrame*channelsIn+0]; + case 8: pFramesOut[iFrame*channelsOut+ 7] = pFramesIn[iFrame*channelsIn+0]; + case 7: pFramesOut[iFrame*channelsOut+ 6] = pFramesIn[iFrame*channelsIn+0]; + case 6: pFramesOut[iFrame*channelsOut+ 5] = pFramesIn[iFrame*channelsIn+0]; + case 5: pFramesOut[iFrame*channelsOut+ 4] = pFramesIn[iFrame*channelsIn+0]; + case 4: pFramesOut[iFrame*channelsOut+ 3] = pFramesIn[iFrame*channelsIn+0]; + case 3: pFramesOut[iFrame*channelsOut+ 2] = pFramesIn[iFrame*channelsIn+0]; + case 2: pFramesOut[iFrame*channelsOut+ 1] = pFramesIn[iFrame*channelsIn+0]; + case 1: pFramesOut[iFrame*channelsOut+ 0] = pFramesIn[iFrame*channelsIn+0]; + } + } + } else if (channelsIn == 2) { + // TODO: Implement an optimized stereo conversion. + mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); + } else { + // Fall back to basic mixing mode. + mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); + } + } +} + +static void mal_dsp_mix_channels(float* pFramesOut, mal_uint32 channelsOut, const mal_uint8 channelMapOut[MAL_MAX_CHANNELS], const float* pFramesIn, mal_uint32 channelsIn, const mal_uint8 channelMapIn[MAL_MAX_CHANNELS], mal_uint32 frameCount, mal_channel_mix_mode mode) +{ + if (channelsIn < channelsOut) { + // Increasing the channel count. + mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mode); + } else { + // Decreasing the channel count. + mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mode); + } +} + + +mal_uint32 mal_dsp__src_on_read(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + (void)pSRC; + + mal_dsp* pDSP = (mal_dsp*)pUserData; + mal_assert(pDSP != NULL); + + return pDSP->onRead(pDSP, frameCount, pFramesOut, pDSP->pUserDataForOnRead); +} + +mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp_read_proc onRead, void* pUserData, mal_dsp* pDSP) +{ + if (pDSP == NULL) return MAL_INVALID_ARGS; + mal_zero_object(pDSP); + pDSP->config = *pConfig; + pDSP->onRead = onRead; + pDSP->pUserDataForOnRead = pUserData; + + if (pDSP->config.cacheSizeInFrames > MAL_SRC_CACHE_SIZE_IN_FRAMES || pDSP->config.cacheSizeInFrames == 0) { + pDSP->config.cacheSizeInFrames = MAL_SRC_CACHE_SIZE_IN_FRAMES; + } + + if (pConfig->sampleRateIn != pConfig->sampleRateOut) { + pDSP->isSRCRequired = MAL_TRUE; + + mal_src_config srcConfig; + srcConfig.sampleRateIn = pConfig->sampleRateIn; + srcConfig.sampleRateOut = pConfig->sampleRateOut; + srcConfig.formatIn = pConfig->formatIn; + srcConfig.formatOut = mal_format_f32; + srcConfig.channels = pConfig->channelsIn; + srcConfig.algorithm = mal_src_algorithm_linear; + srcConfig.cacheSizeInFrames = pConfig->cacheSizeInFrames; + mal_result result = mal_src_init(&srcConfig, mal_dsp__src_on_read, pDSP, &pDSP->src); + if (result != MAL_SUCCESS) { + return result; + } + } + + + + pDSP->isChannelMappingRequired = MAL_FALSE; + if (pConfig->channelMapIn[0] != MAL_CHANNEL_NONE && pConfig->channelMapOut[0] != MAL_CHANNEL_NONE) { // <-- Channel mapping will be ignored if the first channel map is MAL_CHANNEL_NONE. + // When using channel mapping we need to figure out a shuffling table. The first thing to do is convert the input channel map + // so that it contains the same number of channels as the output channel count. + mal_uint32 iChannel; + mal_uint32 channelsMin = mal_min(pConfig->channelsIn, pConfig->channelsOut); + for (iChannel = 0; iChannel < channelsMin; ++iChannel) { + pDSP->channelMapInPostMix[iChannel] = pConfig->channelMapIn[iChannel]; + } + + // Any excess channels need to be filled with the relevant channels from the output channel map. Currently we're justing filling it with + // the first channels that are not present in the input channel map. + if (pConfig->channelsOut > pConfig->channelsIn) { + for (iChannel = pConfig->channelsIn; iChannel < pConfig->channelsOut; ++iChannel) { + mal_uint8 newChannel = MAL_CHANNEL_NONE; + for (mal_uint32 iChannelOut = 0; iChannelOut < pConfig->channelsOut; ++iChannelOut) { + mal_bool32 exists = MAL_FALSE; + for (mal_uint32 iChannelIn = 0; iChannelIn < pConfig->channelsIn; ++iChannelIn) { + if (pConfig->channelMapOut[iChannelOut] == pConfig->channelMapIn[iChannelIn]) { + exists = MAL_TRUE; + break; + } + } + + if (!exists) { + newChannel = pConfig->channelMapOut[iChannelOut]; + break; + } + } + + pDSP->channelMapInPostMix[iChannel] = newChannel; + } + } + + // We only need to do a channel mapping if the map after mixing is different to the final output map. + for (iChannel = 0; iChannel < pConfig->channelsOut; ++iChannel) { + if (pDSP->channelMapInPostMix[iChannel] != pConfig->channelMapOut[iChannel]) { + pDSP->isChannelMappingRequired = MAL_TRUE; + break; + } + } + + // Now we need to create the shuffling table. + if (pDSP->isChannelMappingRequired) { + for (mal_uint32 iChannelIn = 0; iChannelIn < pConfig->channelsOut; ++iChannelIn) { + for (mal_uint32 iChannelOut = 0; iChannelOut < pConfig->channelsOut; ++iChannelOut) { + if (pDSP->channelMapInPostMix[iChannelOut] == pConfig->channelMapOut[iChannelIn]) { + pDSP->channelShuffleTable[iChannelOut] = (mal_uint8)iChannelIn; + } + } + } + } + } + + if (pConfig->formatIn == pConfig->formatOut && pConfig->channelsIn == pConfig->channelsOut && pConfig->sampleRateIn == pConfig->sampleRateOut && !pDSP->isChannelMappingRequired) { + pDSP->isPassthrough = MAL_TRUE; + } else { + pDSP->isPassthrough = MAL_FALSE; + } + + return MAL_SUCCESS; +} + +mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut) +{ + if (pDSP == NULL) return MAL_INVALID_ARGS; + + // Must have a sample rate of > 0. + if (sampleRateOut == 0) { + return MAL_INVALID_ARGS; + } + + pDSP->config.sampleRateOut = sampleRateOut; + + // If we already have an SRC pipeline initialized we do _not_ want to re-create it. Instead we adjust it. If we didn't previously + // have an SRC pipeline in place we'll need to initialize it. + if (pDSP->isSRCRequired) { + if (pDSP->config.sampleRateIn != pDSP->config.sampleRateOut) { + mal_src_set_output_sample_rate(&pDSP->src, sampleRateOut); + } else { + pDSP->isSRCRequired = MAL_FALSE; + } + } else { + // We may need a new SRC pipeline. + if (pDSP->config.sampleRateIn != pDSP->config.sampleRateOut) { + pDSP->isSRCRequired = MAL_TRUE; + + mal_src_config srcConfig; + srcConfig.sampleRateIn = pDSP->config.sampleRateIn; + srcConfig.sampleRateOut = pDSP->config.sampleRateOut; + srcConfig.formatIn = pDSP->config.formatIn; + srcConfig.formatOut = mal_format_f32; + srcConfig.channels = pDSP->config.channelsIn; + srcConfig.algorithm = mal_src_algorithm_linear; + srcConfig.cacheSizeInFrames = pDSP->config.cacheSizeInFrames; + mal_result result = mal_src_init(&srcConfig, mal_dsp__src_on_read, pDSP, &pDSP->src); + if (result != MAL_SUCCESS) { + return result; + } + } else { + pDSP->isSRCRequired = MAL_FALSE; + } + } + + // Update whether or not the pipeline is a passthrough. + if (pDSP->config.formatIn == pDSP->config.formatOut && pDSP->config.channelsIn == pDSP->config.channelsOut && pDSP->config.sampleRateIn == pDSP->config.sampleRateOut && !pDSP->isChannelMappingRequired) { + pDSP->isPassthrough = MAL_TRUE; + } else { + pDSP->isPassthrough = MAL_FALSE; + } + + return MAL_SUCCESS; +} + +mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut) +{ + return mal_dsp_read_frames_ex(pDSP, frameCount, pFramesOut, MAL_FALSE); +} + +mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) +{ + if (pDSP == NULL || pFramesOut == NULL) return 0; + + // Fast path. + if (pDSP->isPassthrough) { + return pDSP->onRead(pDSP, frameCount, pFramesOut, pDSP->pUserDataForOnRead); + } + + + // Slower path - where the real work is done. + mal_uint8 pFrames[2][MAL_MAX_CHANNELS * 512 * MAL_MAX_SAMPLE_SIZE_IN_BYTES]; + mal_format pFramesFormat[2]; + mal_uint32 iFrames = 0; // <-- Used as an index into pFrames and cycles between 0 and 1. + + mal_uint32 totalFramesRead = 0; + while (frameCount > 0) { + iFrames = 0; + + mal_uint32 framesToRead = mal_countof(pFrames[0]) / (mal_max(pDSP->config.channelsIn, pDSP->config.channelsOut) * MAL_MAX_SAMPLE_SIZE_IN_BYTES); + if (framesToRead > frameCount) { + framesToRead = frameCount; + } + + // The initial filling of sample data depends on whether or not we are using SRC. + mal_uint32 framesRead = 0; + if (pDSP->isSRCRequired) { + framesRead = mal_src_read_frames_ex(&pDSP->src, framesToRead, pFrames[iFrames], flush); + pFramesFormat[iFrames] = pDSP->src.config.formatOut; // Should always be f32. + } else { + framesRead = pDSP->onRead(pDSP, framesToRead, pFrames[iFrames], pDSP->pUserDataForOnRead); + pFramesFormat[iFrames] = pDSP->config.formatIn; + } + + if (framesRead == 0) { + break; + } + + + // Channel mixing. The input format must be in f32 which may require a conversion. + if (pDSP->config.channelsIn != pDSP->config.channelsOut) { + if (pFramesFormat[iFrames] != mal_format_f32) { + mal_pcm_convert(pFrames[(iFrames + 1) % 2], mal_format_f32, pFrames[iFrames], pDSP->config.formatIn, framesRead * pDSP->config.channelsIn); + iFrames = (iFrames + 1) % 2; + pFramesFormat[iFrames] = mal_format_f32; + } + + mal_dsp_mix_channels((float*)(pFrames[(iFrames + 1) % 2]), pDSP->config.channelsOut, pDSP->config.channelMapOut, (const float*)(pFrames[iFrames]), pDSP->config.channelsIn, pDSP->config.channelMapIn, framesRead, mal_channel_mix_mode_blend); + iFrames = (iFrames + 1) % 2; + pFramesFormat[iFrames] = mal_format_f32; + } + + + // Channel mapping. + if (pDSP->isChannelMappingRequired) { + for (mal_uint32 i = 0; i < framesRead; ++i) { + mal_rearrange_channels(pFrames[iFrames] + (i * pDSP->config.channelsOut * mal_get_sample_size_in_bytes(pFramesFormat[iFrames])), pDSP->config.channelsOut, pDSP->channelShuffleTable, pFramesFormat[iFrames]); + } + } + + + // Final conversion to output format. + mal_pcm_convert(pFramesOut, pDSP->config.formatOut, pFrames[iFrames], pFramesFormat[iFrames], framesRead * pDSP->config.channelsOut); + + pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pDSP->config.channelsOut * mal_get_sample_size_in_bytes(pDSP->config.formatOut)); + frameCount -= framesRead; + totalFramesRead += framesRead; + } + + return totalFramesRead; +} + + +mal_uint32 mal_calculate_frame_count_after_src(mal_uint32 sampleRateOut, mal_uint32 sampleRateIn, mal_uint32 frameCountIn) +{ + double srcRatio = (double)sampleRateOut / sampleRateIn; + double frameCountOutF = frameCountIn * srcRatio; + + mal_uint32 frameCountOut = (mal_uint32)frameCountOutF; + + // If the output frame count is fractional, make sure we add an extra frame to ensure there's enough room for that last sample. + if ((frameCountOutF - frameCountOut) > 0.0) { + frameCountOut += 1; + } + + return frameCountOut; +} + +typedef struct +{ + const void* pDataIn; + mal_format formatIn; + mal_uint32 channelsIn; + mal_uint32 totalFrameCount; + mal_uint32 iNextFrame; +} mal_convert_frames__data; + +mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) +{ + (void)pDSP; + + mal_convert_frames__data* pData = (mal_convert_frames__data*)pUserData; + mal_assert(pData != NULL); + mal_assert(pData->totalFrameCount >= pData->iNextFrame); + + mal_uint32 framesToRead = frameCount; + mal_uint32 framesRemaining = (pData->totalFrameCount - pData->iNextFrame); + if (framesToRead > framesRemaining) { + framesToRead = framesRemaining; + } + + mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(pData->formatIn) * pData->channelsIn; + mal_copy_memory(pFramesOut, (const mal_uint8*)pData->pDataIn + (frameSizeInBytes * pData->iNextFrame), frameSizeInBytes * framesToRead); + + pData->iNextFrame += framesToRead; + return framesToRead; +} + +mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint32 frameCountIn) +{ + if (frameCountIn == 0) { + return 0; + } + + mal_uint32 frameCountOut = mal_calculate_frame_count_after_src(sampleRateOut, sampleRateIn, frameCountIn); + if (pOut == NULL) { + return frameCountOut; + } + + mal_convert_frames__data data; + data.pDataIn = pIn; + data.formatIn = formatIn; + data.channelsIn = channelsIn; + data.totalFrameCount = frameCountIn; + data.iNextFrame = 0; + + mal_dsp_config config; + mal_zero_object(&config); + config.formatIn = formatIn; + config.channelsIn = channelsIn; + config.sampleRateIn = sampleRateIn; + config.formatOut = formatOut; + config.channelsOut = channelsOut; + config.sampleRateOut = sampleRateOut; + + mal_dsp dsp; + if (mal_dsp_init(&config, mal_convert_frames__on_read, &data, &dsp) != MAL_SUCCESS) { + return 0; + } + + return mal_dsp_read_frames_ex(&dsp, frameCountOut, pOut, MAL_TRUE); +} + +mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut) +{ + mal_dsp_config config; + mal_zero_object(&config); + config.formatIn = formatIn; + config.channelsIn = channelsIn; + config.sampleRateIn = sampleRateIn; + config.formatOut = formatOut; + config.channelsOut = channelsOut; + config.sampleRateOut = sampleRateOut; + + return config; +} + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Miscellaneous Helpers +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const char* mal_get_backend_name(mal_backend backend) +{ + switch (backend) + { + case mal_backend_null: return "Null"; + case mal_backend_wasapi: return "WASAPI"; + case mal_backend_dsound: return "DirectSound"; + case mal_backend_winmm: return "WinMM"; + case mal_backend_alsa: return "ALSA"; + //case mal_backend_pulse: return "PulseAudio"; + //case mal_backend_jack: return "JACK"; + //case mal_backend_coreaudio: return "Core Audio"; + case mal_backend_oss: return "OSS"; + case mal_backend_opensl: return "OpenSL|ES"; + case mal_backend_openal: return "OpenAL"; + case mal_backend_sdl: return "SDL"; + default: return "Unknown"; + } +} + +const char* mal_get_format_name(mal_format format) +{ + switch (format) + { + case mal_format_unknown: return "Unknown"; + case mal_format_u8: return "8-bit Unsigned Integer"; + case mal_format_s16: return "16-bit Signed Integer"; + case mal_format_s24: return "24-bit Signed Integer (Tightly Packed)"; + case mal_format_s32: return "32-bit Signed Integer"; + case mal_format_f32: return "32-bit IEEE Floating Point"; + default: return "Invalid"; + } +} + +void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels) +{ + for (mal_uint32 i = 0; i < channels; ++i) { + pOut[i] = mal_mix_f32(pInA[i], pInB[i], factor); + } +} + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// +// AUTO-GENERATED +// +// +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// FORMAT CONVERSION +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void mal_pcm_u8_to_s16(short* pOut, const unsigned char* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x - 128; + r = r << 8; + pOut[i] = (short)r; + } +} + +void mal_pcm_u8_to_s24(void* pOut, const unsigned char* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x - 128; + r = r << 16; + ((unsigned char*)pOut)[(i*3)+0] = (unsigned char)(r & 0xFF); ((unsigned char*)pOut)[(i*3)+1] = (unsigned char)((r & 0xFF00) >> 8); ((unsigned char*)pOut)[(i*3)+2] = (unsigned char)((r & 0xFF0000) >> 16); + } +} + +void mal_pcm_u8_to_s32(int* pOut, const unsigned char* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x - 128; + r = r << 24; + pOut[i] = (int)r; + } +} + +void mal_pcm_u8_to_f32(float* pOut, const unsigned char* pIn, unsigned int count) +{ + float r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x * 0.00784313725490196078f; + r = r - 1; + pOut[i] = (float)r; + } +} + +void mal_pcm_s16_to_u8(unsigned char* pOut, const short* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x >> 8; + r = r + 128; + pOut[i] = (unsigned char)r; + } +} + +void mal_pcm_s16_to_s24(void* pOut, const short* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x << 8; + ((unsigned char*)pOut)[(i*3)+0] = (unsigned char)(r & 0xFF); ((unsigned char*)pOut)[(i*3)+1] = (unsigned char)((r & 0xFF00) >> 8); ((unsigned char*)pOut)[(i*3)+2] = (unsigned char)((r & 0xFF0000) >> 16); + } +} + +void mal_pcm_s16_to_s32(int* pOut, const short* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x << 16; + pOut[i] = (int)r; + } +} + +void mal_pcm_s16_to_f32(float* pOut, const short* pIn, unsigned int count) +{ + float r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = (float)(x + 32768); + r = r * 0.00003051804379339284f; + r = r - 1; + pOut[i] = (float)r; + } +} + +void mal_pcm_s24_to_u8(unsigned char* pOut, const void* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8; + r = x >> 16; + r = r + 128; + pOut[i] = (unsigned char)r; + } +} + +void mal_pcm_s24_to_s16(short* pOut, const void* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8; + r = x >> 8; + pOut[i] = (short)r; + } +} + +void mal_pcm_s24_to_s32(int* pOut, const void* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8; + r = x << 8; + pOut[i] = (int)r; + } +} + +void mal_pcm_s24_to_f32(float* pOut, const void* pIn, unsigned int count) +{ + float r; + for (unsigned int i = 0; i < count; ++i) { + int x = ((int)(((unsigned int)(((unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((unsigned char*)pIn)[i*3+2])) << 24)) >> 8; + r = (float)(x + 8388608); + r = r * 0.00000011920929665621f; + r = r - 1; + pOut[i] = (float)r; + } +} + +void mal_pcm_s32_to_u8(unsigned char* pOut, const int* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x >> 24; + r = r + 128; + pOut[i] = (unsigned char)r; + } +} + +void mal_pcm_s32_to_s16(short* pOut, const int* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x >> 16; + pOut[i] = (short)r; + } +} + +void mal_pcm_s32_to_s24(void* pOut, const int* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + r = x >> 8; + ((unsigned char*)pOut)[(i*3)+0] = (unsigned char)(r & 0xFF); ((unsigned char*)pOut)[(i*3)+1] = (unsigned char)((r & 0xFF00) >> 8); ((unsigned char*)pOut)[(i*3)+2] = (unsigned char)((r & 0xFF0000) >> 16); + } +} + +void mal_pcm_s32_to_f32(float* pOut, const int* pIn, unsigned int count) +{ + float r; + for (unsigned int i = 0; i < count; ++i) { + int x = pIn[i]; + double t; + t = (double)(x + 2147483647); + t = t + 1; + t = t * 0.0000000004656612873077392578125; + r = (float)(t - 1); + pOut[i] = (float)r; + } +} + +void mal_pcm_f32_to_u8(unsigned char* pOut, const float* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + float x = pIn[i]; + float c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 127.5f); + pOut[i] = (unsigned char)r; + } +} + +void mal_pcm_f32_to_s16(short* pOut, const float* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + float x = pIn[i]; + float c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5f); + r = r - 32768; + pOut[i] = (short)r; + } +} + +void mal_pcm_f32_to_s24(void* pOut, const float* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + float x = pIn[i]; + float c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 8388607.5f); + r = r - 8388608; + ((unsigned char*)pOut)[(i*3)+0] = (unsigned char)(r & 0xFF); ((unsigned char*)pOut)[(i*3)+1] = (unsigned char)((r & 0xFF00) >> 8); ((unsigned char*)pOut)[(i*3)+2] = (unsigned char)((r & 0xFF0000) >> 16); + } +} + +void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count) +{ + int r; + for (unsigned int i = 0; i < count; ++i) { + float x = pIn[i]; + float c; + mal_int64 t; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + t = (mal_int64)(c * 2147483647.5); + t = t - 2147483647; + r = (int)(t - 1); + pOut[i] = (int)r; + } +} + +#endif + + +// REVISION HISTORY +// ================ +// +// v0.x - 2017-xx-xx +// - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll +// need to update. +// - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and mal_dsp object respectively. +// - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent. +// - Add support for SDL and Emscripten. +// - Simplify the build system further for when development packages for various backends are not installed. +// With this change, when the compiler supports __has_include, backends without the relevant development +// packages installed will be ignored. This fixes the build for old versions of MinGW. +// - Fixes to the Android build. +// - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of +// audio data to a different format. +// - Improvements to f32 -> u8/s16/s24/s32 conversion routines. +// - Fix a bug where the wrong value is returned from mal_device_start() for the OpenSL and SDL backends. +// - Fixes and improvements for Raspberry Pi. +// - Warning fixes. +// +// v0.5 - 2017-11-11 +// - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for +// configuring the context. The works in the same kind of way as the device config. The rationale for this +// change is to give applications better control over context-level properties, add support for backend- +// specific configurations, and support extensibility without breaking the API. +// - API CHANGE: The alsa.preferPlugHW device config variable has been removed since it's not really useful for +// anything anymore. +// - ALSA: By default, device enumeration will now only enumerate over unique card/device pairs. Applications +// can enable verbose device enumeration by setting the alsa.useVerboseDeviceEnumeration context config +// variable. +// - ALSA: When opening a device in shared mode (the default), the dmix/dsnoop plugin will be prioritized. If +// this fails it will fall back to the hw plugin. With this change the preferExclusiveMode config is now +// honored. Note that this does not happen when alsa.useVerboseDeviceEnumeration is set to true (see above) +// which is by design. +// - ALSA: Add support for excluding the "null" device using the alsa.excludeNullDevice context config variable. +// - ALSA: Fix a bug with channel mapping which causes an assertion to fail. +// - Fix errors with enumeration when pInfo is set to NULL. +// - OSS: Fix a bug when starting a device when the client sends 0 samples for the initial buffer fill. +// +// v0.4 - 2017-11-05 +// - API CHANGE: The log callback is now per-context rather than per-device and as is thus now passed to +// mal_context_init(). The rationale for this change is that it allows applications to capture diagnostic +// messages at the context level. Previously this was only available at the device level. +// - API CHANGE: The device config passed to mal_device_init() is now const. +// - Added support for OSS which enables support on BSD platforms. +// - Added support for WinMM (waveOut/waveIn). +// - Added support for UWP (Universal Windows Platform) applications. Currently C++ only. +// - Added support for exclusive mode for selected backends. Currently supported on WASAPI. +// - POSIX builds no longer require explicit linking to libpthread (-lpthread). +// - ALSA: Explicit linking to libasound (-lasound) is no longer required. +// - ALSA: Latency improvements. +// - ALSA: Use MMAP mode where available. This can be disabled with the alsa.noMMap config. +// - ALSA: Use "hw" devices instead of "plughw" devices by default. This can be disabled with the +// alsa.preferPlugHW config. +// - WASAPI is now the highest priority backend on Windows platforms. +// - Fixed an error with sample rate conversion which was causing crackling when capturing. +// - Improved error handling. +// - Improved compiler support. +// - Miscellaneous bug fixes. +// +// v0.3 - 2017-06-19 +// - API CHANGE: Introduced the notion of a context. The context is the highest level object and is required for +// enumerating and creating devices. Now, applications must first create a context, and then use that to +// enumerate and create devices. The reason for this change is to ensure device enumeration and creation is +// tied to the same backend. In addition, some backends are better suited to this design. +// - API CHANGE: Removed the rewinding APIs because they're too inconsistent across the different backends, hard +// to test and maintain, and just generally unreliable. +// - Added helper APIs for initializing mal_device_config objects. +// - Null Backend: Fixed a crash when recording. +// - Fixed build for UWP. +// - Added support for f32 formats to the OpenSL|ES backend. +// - Added initial implementation of the WASAPI backend. +// - Added initial implementation of the OpenAL backend. +// - Added support for low quality linear sample rate conversion. +// - Added early support for basic channel mapping. +// +// v0.2 - 2016-10-28 +// - API CHANGE: Add user data pointer as the last parameter to mal_device_init(). The rationale for this +// change is to ensure the logging callback has access to the user data during initialization. +// - API CHANGE: Have device configuration properties be passed to mal_device_init() via a structure. Rationale: +// 1) The number of parameters is just getting too much. +// 2) It makes it a bit easier to add new configuration properties in the future. In particular, there's a +// chance there will be support added for backend-specific properties. +// - Dropped support for f64, A-law and Mu-law formats since they just aren't common enough to justify the +// added maintenance cost. +// - DirectSound: Increased the default buffer size for capture devices. +// - Added initial implementation of the OpenSL|ES backend. +// +// v0.1 - 2016-10-21 +// - Initial versioned release. + + +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ diff --git a/raylib/gestures.h b/raylib/gestures.h index 60d5172..68bdc11 100644 --- a/raylib/gestures.h +++ b/raylib/gestures.h @@ -140,17 +140,25 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang #if defined(GESTURES_IMPLEMENTATION) -#include // Required for: atan2(), sqrt() -#include // Required for: uint64_t - #if defined(_WIN32) // Functions required to query time on Windows int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount); int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency); #elif defined(__linux__) - //#define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. + #if _POSIX_C_SOURCE < 199309L + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. + #endif #include // Required for: timespec #include // Required for: clock_gettime() + + #include // Required for: atan2(), sqrt() + #include // Required for: uint64_t +#endif + +#if defined(__APPLE__) // macOS also defines __MACH__ + #include // Required for: clock_get_time() + #include // Required for: mach_timespec_t #endif //---------------------------------------------------------------------------------- @@ -529,6 +537,22 @@ static double GetCurrentTime(void) time = ((double)nowTime/1000000.0); // Time in miliseconds #endif +#if defined(__APPLE__) + //#define CLOCK_REALTIME CALENDAR_CLOCK + //#define CLOCK_MONOTONIC SYSTEM_CLOCK + + clock_serv_t cclock; + mach_timespec_t now; + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + + // NOTE: OS X does not have clock_gettime(), using clock_get_time() + clock_get_time(cclock, &now); + mach_port_deallocate(mach_task_self(), cclock); + uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds + + time = ((double)nowTime/1000000.0); // Time in miliseconds +#endif + return time; } diff --git a/raylib/mini_al.c b/raylib/mini_al.c new file mode 100644 index 0000000..7b43785 --- /dev/null +++ b/raylib/mini_al.c @@ -0,0 +1,4 @@ +// The implementation of mini_al needs to #include windows.h which means it needs to go into +// it's own translation unit. Not doing this will cause conflicts with CloseWindow(), etc. +#define MAL_IMPLEMENTATION +#include "mini_al.h" \ No newline at end of file diff --git a/raylib/raylib.h b/raylib/raylib.h index e5ad8a9..a3914c4 100644 --- a/raylib/raylib.h +++ b/raylib/raylib.h @@ -1,23 +1,23 @@ /********************************************************************************************** * -* raylib v1.8.0 +* raylib v1.9-dev * * A simple and easy-to-use library to learn videogames programming (www.raylib.com) * * FEATURES: * - Written in plain C code (C99) in PascalCase/camelCase notation -* - Multiple platforms support: Windows, Linux, Mac, Android, Raspberry Pi and HTML5 -* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3 or ES 2.0) +* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3 or ES2 - choose at compile) * - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] -* - Powerful fonts module with SpriteFonts support (XNA bitmap fonts, AngelCode fonts, TTF) -* - Outstanding texture formats support, including compressed formats (DXT, ETC, PVRT, ASTC) -* - Basic 3d support for Geometrics, Models, Billboards, Heightmaps and Cubicmaps +* - Powerful fonts module with SpriteFonts support (XNA fonts, AngelCode fonts, TTF) +* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) +* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! * - Flexible Materials system, supporting classic maps and PBR maps * - Shaders support, including Model shaders and Postprocessing shaders -* - Powerful math module for Vector2, Vector3, Matrix and Quaternion operations: [raymath] -* - Audio loading and playing with streaming support and mixing channels: [audio] -* - VR stereo rendering support with configurable HMD device parameters -* - Minimal external dependencies (GLFW3, OpenGL, OpenAL) +* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] +* - Audio loading and playing with streaming support (WAV, OGG, FLAC, XM, MOD) +* - Multiple platforms support: Windows, Linux, FreeBSD, MacOS, UWP, Android, Raspberry Pi, HTML5. +* - VR stereo rendering with configurable HMD device parameters +* - NO external dependencies, all required libraries included with raylib * - Complete bindings to LUA (raylib-lua) and Go (raylib-go) * * NOTES: @@ -25,17 +25,17 @@ * If using OpenGL 3.3 or ES2, one default shader is loaded automatically (internally defined) [rlgl] * If using OpenGL 3.3 or ES2, several vertex buffers (VAO/VBO) are created to manage lines-triangles-quads * -* DEPENDENCIES: -* GLFW3 (www.glfw.org) for window/context management and input [core] -* GLAD for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP) [rlgl] -* OpenAL Soft for audio device/context management [audio] +* DEPENDENCIES (included): +* rglfw (github.com/glfw/glfw) for window/context management and input (only PLATFORM_DESKTOP) [core] +* glad (github.com/Dav1dde/glad) for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP) [rlgl] +* mini_al (github.com/dr-soft/mini_al) for audio device/context management [audio] * -* OPTIONAL DEPENDENCIES: -* stb_image (Sean Barret) for images loading (JPEG, PNG, BMP, TGA) [textures] +* OPTIONAL DEPENDENCIES (included): +* stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) [textures] * stb_image_resize (Sean Barret) for image resizing algorythms [textures] * stb_image_write (Sean Barret) for image writting (PNG) [utils] * stb_truetype (Sean Barret) for ttf fonts loading [text] -* stb_vorbis (Sean Barret) for ogg audio loading [audio] +* stb_vorbis (Sean Barret) for OGG audio loading [audio] * stb_perlin (Sean Barret) for Perlin noise image generation [textures] * par_shapes (Philip Rideout) for parametric 3d shapes generation [models] * jar_xm (Joshua Reisenauer) for XM audio module loading [audio] @@ -50,7 +50,7 @@ * raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: * -* Copyright (c) 2013-2017 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2018 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -470,6 +470,8 @@ typedef struct Wave { // Sound source type typedef struct Sound { + void* audioBuffer; // A pointer to internal data used by the audio system. + unsigned int source; // OpenAL audio source id unsigned int buffer; // OpenAL audio buffer id int format; // OpenAL audio format specifier @@ -486,6 +488,8 @@ typedef struct AudioStream { unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) unsigned int channels; // Number of channels (1-mono, 2-stereo) + void* audioBuffer; // A pointer to internal data used by the audio system. + int format; // OpenAL audio format specifier unsigned int source; // OpenAL audio source id unsigned int buffers[2]; // OpenAL audio buffers (double buffering) @@ -1126,7 +1130,7 @@ RLAPI void ResumeMusicStream(Music music); // Resume RLAPI bool IsMusicPlaying(Music music); // Check if music is playing RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) -RLAPI void SetMusicLoopCount(Music music, float count); // Set music loop count (loop repeats) +RLAPI void SetMusicLoopCount(Music music, int count); // Set music loop count (loop repeats) RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds) RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) @@ -1139,7 +1143,10 @@ RLAPI bool IsAudioBufferProcessed(AudioStream stream); // Check i RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream +RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream +RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) +RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) #ifdef __cplusplus } diff --git a/raylib/raymath.h b/raylib/raymath.h index fe0b894..decd02c 100644 --- a/raylib/raymath.h +++ b/raylib/raymath.h @@ -191,8 +191,8 @@ RMDEF void QuaternionNormalize(Quaternion *q); // Normalize pro RMDEF void QuaternionInvert(Quaternion *quat); // Invert provided quaternion RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2); // Calculate two quaternion multiplication RMDEF Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount); // Calculate linear interpolation between two quaternions -RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount); // Calculates spherical linear interpolation between two quaternions RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount); // Calculate slerp-optimized interpolation between two quaternions +RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount); // Calculates spherical linear interpolation between two quaternions RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to); // Calculate quaternion based on the rotation from one vector to another RMDEF Quaternion QuaternionFromMatrix(Matrix matrix); // Returns a quaternion for a given rotation matrix RMDEF Matrix QuaternionToMatrix(Quaternion q); // Returns a matrix for a given quaternion @@ -1083,6 +1083,15 @@ RMDEF Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) return result; } +// Calculate slerp-optimized interpolation between two quaternions +RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = QuaternionLerp(q1, q2, amount); + QuaternionNormalize(&result); + + return result; +} + // Calculates spherical linear interpolation between two quaternions RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) { @@ -1119,15 +1128,6 @@ RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) return result; } -// Calculate slerp-optimized interpolation between two quaternions -RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) -{ - Quaternion result = QuaternionLerp(q1, q2, amount); - QuaternionNormalize(&result); - - return result; -} - // Calculate quaternion based on the rotation from one vector to another RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) { diff --git a/raylib/rlgl.c b/raylib/rlgl.c index f273e55..6ae7df4 100644 --- a/raylib/rlgl.c +++ b/raylib/rlgl.c @@ -3362,8 +3362,8 @@ static void SetShaderDefaultLocations(Shader *shader) // Get handles to GLSL uniform locations (fragment shader) shader->locs[LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader->id, "colDiffuse"); shader->locs[LOC_MAP_DIFFUSE] = glGetUniformLocation(shader->id, "texture0"); - shader->locs[LOC_MAP_NORMAL] = glGetUniformLocation(shader->id, "texture1"); - shader->locs[LOC_MAP_SPECULAR] = glGetUniformLocation(shader->id, "texture2"); + shader->locs[LOC_MAP_SPECULAR] = glGetUniformLocation(shader->id, "texture1"); + shader->locs[LOC_MAP_NORMAL] = glGetUniformLocation(shader->id, "texture2"); } // Unload default shader diff --git a/raylib/text.c b/raylib/text.c index ff55ae6..8db2fc9 100644 --- a/raylib/text.c +++ b/raylib/text.c @@ -883,11 +883,10 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int charsCount, in scale = stbtt_ScaleForPixelHeight(&fontInfo, fontSize); stbtt_GetFontVMetrics(&fontInfo, &ascent, 0, 0); baseline = (int)(ascent*scale); - if (fontChars[0] != 32) TraceLog(LOG_WARNING, "TTF spritefont loading: first character is not SPACE(32) character"); - // NOTE: Using stb_truetype crappy packing method, no guarante the font fits the image... + // NOTE: Using stb_truetype crappy packing method, no guarantee the font fits the image... // TODO: Replace this function by a proper packing method and support random chars order, // we already receive a list (fontChars) with the ordered expected characters int result = stbtt_BakeFontBitmap(ttfBuffer, 0, fontSize, dataBitmap, textureSize, textureSize, fontChars[0], charsCount, charData); diff --git a/raylib/textures.c b/raylib/textures.c index 814c302..090de24 100644 --- a/raylib/textures.c +++ b/raylib/textures.c @@ -58,6 +58,8 @@ #define SUPPORT_FILEFORMAT_PNG #define SUPPORT_FILEFORMAT_DDS #define SUPPORT_FILEFORMAT_HDR +#define SUPPORT_FILEFORMAT_KTX +#define SUPPORT_FILEFORMAT_ASTC #define SUPPORT_IMAGE_MANIPULATION #define SUPPORT_IMAGE_GENERATION //------------------------------------------------- @@ -536,14 +538,13 @@ Image GetTextureData(Texture2D texture) { image.width = texture.width; image.height = texture.height; + image.format = texture.format; image.mipmaps = 1; - if (rlGetVersion() == OPENGL_ES_20) - { - // NOTE: Data retrieved on OpenGL ES 2.0 comes as RGBA (from framebuffer) - image.format = UNCOMPRESSED_R8G8B8A8; - } - else image.format = texture.format; + // NOTE: Data retrieved on OpenGL ES 2.0 should be RGBA + // coming from FBO color buffer, but it seems original + // texture format is retrieved on RPI... weird... + //image.format = UNCOMPRESSED_R8G8B8A8; TraceLog(LOG_INFO, "Texture pixel data obtained successfully"); } @@ -622,9 +623,9 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0; i < image->width*image->height; i++) { - r = (unsigned char)(round((float)pixels[k].r*31/255)); - g = (unsigned char)(round((float)pixels[k].g*63/255)); - b = (unsigned char)(round((float)pixels[k].b*31/255)); + r = (unsigned char)(round((float)pixels[i].r*31.0f/255)); + g = (unsigned char)(round((float)pixels[i].g*63.0f/255)); + b = (unsigned char)(round((float)pixels[i].b*31.0f/255)); ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b; } @@ -655,9 +656,9 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0; i < image->width*image->height; i++) { - r = (unsigned char)(round((float)pixels[i].r*31/255)); - g = (unsigned char)(round((float)pixels[i].g*31/255)); - b = (unsigned char)(round((float)pixels[i].b*31/255)); + r = (unsigned char)(round((float)pixels[i].r*31.0f/255)); + g = (unsigned char)(round((float)pixels[i].g*31.0f/255)); + b = (unsigned char)(round((float)pixels[i].b*31.0f/255)); a = (pixels[i].a > ALPHA_THRESHOLD) ? 1 : 0; ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a; @@ -675,12 +676,12 @@ void ImageFormat(Image *image, int newFormat) for (int i = 0; i < image->width*image->height; i++) { - r = (unsigned char)(round((float)pixels[i].r*15/255)); - g = (unsigned char)(round((float)pixels[i].g*15/255)); - b = (unsigned char)(round((float)pixels[i].b*15/255)); - a = (unsigned char)(round((float)pixels[i].a*15/255)); - - ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8| (unsigned short)b << 4| (unsigned short)a; + r = (unsigned char)(round((float)pixels[i].r*15.0f/255)); + g = (unsigned char)(round((float)pixels[i].g*15.0f/255)); + b = (unsigned char)(round((float)pixels[i].b*15.0f/255)); + a = (unsigned char)(round((float)pixels[i].a*15.0f/255)); + + ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a; } } break; @@ -801,7 +802,7 @@ void ImageToPOT(Image *image, Color fillColor) // Copy an image to a new image Image ImageCopy(Image image) { - Image newImage; + Image newImage = { 0 }; int byteSize = image.width*image.height; @@ -1087,7 +1088,8 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing ImageDraw(&imText, imFont, letter.rec, (Rectangle){ posX + letter.offsetX, letter.offsetY, letter.rec.width, letter.rec.height }); - posX += letter.advanceX + spacing; + if (letter.advanceX == 0) posX += letter.rec.width + spacing; + else posX += letter.advanceX + spacing; } UnloadImage(imFont); From 1c128741fa78b0387df7b2422a116a8720cc6aed Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 5 Dec 2017 19:52:58 +0100 Subject: [PATCH 28/48] Add alsa dev packages --- .travis.yml | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d8aa708..bb40629 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,5 @@ go: install: - sudo apt-get update -y - - sudo apt-get install libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev -y + - sudo apt-get install libasound2-dev libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev -y - go get -t ./... diff --git a/README.md b/README.md index 4fd2650..ba06017 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ Golang bindings for [raylib](http://www.raylib.com/), a simple and easy-to-use l ##### Ubuntu - apt-get install libgl1-mesa-dev libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev + apt-get install libasound2-dev libgl1-mesa-dev libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev ##### Fedora - dnf install mesa-libGL-devel libXi-devel libXcursor-devel libXrandr-devel libXinerama-devel + dnf install alsa-lib-devel mesa-libGL-devel libXi-devel libXcursor-devel libXrandr-devel libXinerama-devel ##### macOS From 753e09ba2b5f549d6b17b04381e39229baa80f90 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Wed, 6 Dec 2017 22:30:31 +0100 Subject: [PATCH 29/48] Test OS X build --- .travis.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bb40629..57eb5a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,17 @@ language: go +os: + - linux + - osx + go: - 1.9.x +before_install: + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then + sudo apt-get update -y; + sudo apt-get install libasound2-dev libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev -y; + fi + install: - - sudo apt-get update -y - - sudo apt-get install libasound2-dev libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev -y - go get -t ./... From 9dd006a2c991dedd90a7ea0cd42375e1f54c5ac7 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 7 Dec 2017 15:49:19 +0100 Subject: [PATCH 30/48] Unexport New*FromPointer functions --- raylib/audio.go | 24 +++++++++++----------- raylib/core.go | 46 +++++++++++++++++++++--------------------- raylib/gestures.go | 4 ++-- raylib/models.go | 46 +++++++++++++++++++++--------------------- raylib/shaders.go | 26 ++++++++++++------------ raylib/shapes.go | 2 +- raylib/text.go | 16 +++++++-------- raylib/textures.go | 50 +++++++++++++++++++++++----------------------- 8 files changed, 107 insertions(+), 107 deletions(-) diff --git a/raylib/audio.go b/raylib/audio.go index 989eceb..73eda11 100644 --- a/raylib/audio.go +++ b/raylib/audio.go @@ -35,8 +35,8 @@ func NewWave(sampleCount, sampleRate, sampleSize, channels uint32, data unsafe.P return Wave{sampleCount, sampleRate, sampleSize, channels, data} } -// NewWaveFromPointer - Returns new Wave from pointer -func NewWaveFromPointer(ptr unsafe.Pointer) Wave { +// newWaveFromPointer - Returns new Wave from pointer +func newWaveFromPointer(ptr unsafe.Pointer) Wave { return *(*Wave)(ptr) } @@ -59,8 +59,8 @@ func NewSound(source, buffer uint32, format int32) Sound { return Sound{source, buffer, format} } -// NewSoundFromPointer - Returns new Sound from pointer -func NewSoundFromPointer(ptr unsafe.Pointer) Sound { +// newSoundFromPointer - Returns new Sound from pointer +func newSoundFromPointer(ptr unsafe.Pointer) Sound { return *(*Sound)(ptr) } @@ -94,8 +94,8 @@ func NewAudioStream(sampleRate, sampleSize, channels uint32, format int32, sourc return AudioStream{sampleRate, sampleSize, channels, format, source, buffers} } -// NewAudioStreamFromPointer - Returns new AudioStream from pointer -func NewAudioStreamFromPointer(ptr unsafe.Pointer) AudioStream { +// newAudioStreamFromPointer - Returns new AudioStream from pointer +func newAudioStreamFromPointer(ptr unsafe.Pointer) AudioStream { return *(*AudioStream)(ptr) } @@ -127,7 +127,7 @@ func LoadWave(fileName string) Wave { cfileName := C.CString(fileName) defer C.free(unsafe.Pointer(cfileName)) ret := C.LoadWave(cfileName) - v := NewWaveFromPointer(unsafe.Pointer(&ret)) + v := newWaveFromPointer(unsafe.Pointer(&ret)) return v } @@ -139,7 +139,7 @@ func LoadWaveEx(data []byte, sampleCount int32, sampleRate int32, sampleSize int csampleSize := (C.int)(sampleSize) cchannels := (C.int)(channels) ret := C.LoadWaveEx(cdata, csampleCount, csampleRate, csampleSize, cchannels) - v := NewWaveFromPointer(unsafe.Pointer(&ret)) + v := newWaveFromPointer(unsafe.Pointer(&ret)) return v } @@ -148,7 +148,7 @@ func LoadSound(fileName string) Sound { cfileName := C.CString(fileName) defer C.free(unsafe.Pointer(cfileName)) ret := C.LoadSound(cfileName) - v := NewSoundFromPointer(unsafe.Pointer(&ret)) + v := newSoundFromPointer(unsafe.Pointer(&ret)) return v } @@ -156,7 +156,7 @@ func LoadSound(fileName string) Sound { func LoadSoundFromWave(wave Wave) Sound { cwave := wave.cptr() ret := C.LoadSoundFromWave(*cwave) - v := NewSoundFromPointer(unsafe.Pointer(&ret)) + v := newSoundFromPointer(unsafe.Pointer(&ret)) return v } @@ -239,7 +239,7 @@ func WaveFormat(wave Wave, sampleRate int32, sampleSize int32, channels int32) { func WaveCopy(wave Wave) Wave { cwave := wave.cptr() ret := C.WaveCopy(*cwave) - v := NewWaveFromPointer(unsafe.Pointer(&ret)) + v := newWaveFromPointer(unsafe.Pointer(&ret)) return v } @@ -362,7 +362,7 @@ func InitAudioStream(sampleRate uint32, sampleSize uint32, channels uint32) Audi csampleSize := (C.uint)(sampleSize) cchannels := (C.uint)(channels) ret := C.InitAudioStream(csampleRate, csampleSize, cchannels) - v := NewAudioStreamFromPointer(unsafe.Pointer(&ret)) + v := newAudioStreamFromPointer(unsafe.Pointer(&ret)) return v } diff --git a/raylib/core.go b/raylib/core.go index 9aa0a7c..6e5a932 100644 --- a/raylib/core.go +++ b/raylib/core.go @@ -251,8 +251,8 @@ func NewVector2(x, y float32) Vector2 { return Vector2{x, y} } -// NewVector2FromPointer - Returns new Vector2 from pointer -func NewVector2FromPointer(ptr unsafe.Pointer) Vector2 { +// newVector2FromPointer - Returns new Vector2 from pointer +func newVector2FromPointer(ptr unsafe.Pointer) Vector2 { return *(*Vector2)(ptr) } @@ -272,8 +272,8 @@ func NewVector3(X, Y, Z float32) Vector3 { return Vector3{X, Y, Z} } -// NewVector3FromPointer - Returns new Vector3 from pointer -func NewVector3FromPointer(ptr unsafe.Pointer) Vector3 { +// newVector3FromPointer - Returns new Vector3 from pointer +func newVector3FromPointer(ptr unsafe.Pointer) Vector3 { return *(*Vector3)(ptr) } @@ -294,8 +294,8 @@ func NewMatrix(m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, return Matrix{m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, m15} } -// NewMatrixFromPointer - Returns new Matrix from pointer -func NewMatrixFromPointer(ptr unsafe.Pointer) Matrix { +// newMatrixFromPointer - Returns new Matrix from pointer +func newMatrixFromPointer(ptr unsafe.Pointer) Matrix { return *(*Matrix)(ptr) } @@ -342,8 +342,8 @@ func NewColor(r, g, b, a uint8) Color { return Color{r, g, b, a} } -// NewColorFromPointer - Returns new Color from pointer -func NewColorFromPointer(ptr unsafe.Pointer) Color { +// newColorFromPointer - Returns new Color from pointer +func newColorFromPointer(ptr unsafe.Pointer) Color { return *(*Color)(ptr) } @@ -364,8 +364,8 @@ func NewRectangle(x, y, width, height int32) Rectangle { return Rectangle{x, y, width, height} } -// NewRectangleFromPointer - Returns new Rectangle from pointer -func NewRectangleFromPointer(ptr unsafe.Pointer) Rectangle { +// newRectangleFromPointer - Returns new Rectangle from pointer +func newRectangleFromPointer(ptr unsafe.Pointer) Rectangle { return *(*Rectangle)(ptr) } @@ -390,8 +390,8 @@ func NewCamera(pos, target, up Vector3, fovy float32) Camera { return Camera{pos, target, up, fovy} } -// NewCameraFromPointer - Returns new Camera from pointer -func NewCameraFromPointer(ptr unsafe.Pointer) Camera { +// newCameraFromPointer - Returns new Camera from pointer +func newCameraFromPointer(ptr unsafe.Pointer) Camera { return *(*Camera)(ptr) } @@ -416,8 +416,8 @@ func NewCamera2D(offset, target Vector2, rotation, zoom float32) Camera2D { return Camera2D{offset, target, rotation, zoom} } -// NewCamera2DFromPointer - Returns new Camera2D from pointer -func NewCamera2DFromPointer(ptr unsafe.Pointer) Camera2D { +// newCamera2DFromPointer - Returns new Camera2D from pointer +func newCamera2DFromPointer(ptr unsafe.Pointer) Camera2D { return *(*Camera2D)(ptr) } @@ -438,8 +438,8 @@ func NewBoundingBox(min, max Vector3) BoundingBox { return BoundingBox{min, max} } -// NewBoundingBoxFromPointer - Returns new BoundingBox from pointer -func NewBoundingBoxFromPointer(ptr unsafe.Pointer) BoundingBox { +// newBoundingBoxFromPointer - Returns new BoundingBox from pointer +func newBoundingBoxFromPointer(ptr unsafe.Pointer) BoundingBox { return *(*BoundingBox)(ptr) } @@ -567,7 +567,7 @@ func GetMouseRay(mousePosition Vector2, camera Camera) Ray { cmousePosition := mousePosition.cptr() ccamera := camera.cptr() ret := C.GetMouseRay(*cmousePosition, *ccamera) - v := NewRayFromPointer(unsafe.Pointer(&ret)) + v := newRayFromPointer(unsafe.Pointer(&ret)) return v } @@ -576,7 +576,7 @@ func GetWorldToScreen(position Vector3, camera Camera) Vector2 { cposition := position.cptr() ccamera := camera.cptr() ret := C.GetWorldToScreen(*cposition, *ccamera) - v := NewVector2FromPointer(unsafe.Pointer(&ret)) + v := newVector2FromPointer(unsafe.Pointer(&ret)) return v } @@ -584,7 +584,7 @@ func GetWorldToScreen(position Vector3, camera Camera) Vector2 { func GetCameraMatrix(camera Camera) Matrix { ccamera := camera.cptr() ret := C.GetCameraMatrix(*ccamera) - v := NewMatrixFromPointer(unsafe.Pointer(&ret)) + v := newMatrixFromPointer(unsafe.Pointer(&ret)) return v } @@ -612,7 +612,7 @@ func GetFrameTime() float32 { func GetColor(hexValue int32) Color { chexValue := (C.int)(hexValue) ret := C.GetColor(chexValue) - v := NewColorFromPointer(unsafe.Pointer(&ret)) + v := newColorFromPointer(unsafe.Pointer(&ret)) return v } @@ -683,7 +683,7 @@ func Fade(color Color, alpha float32) Color { ccolor := color.cptr() calpha := (C.float)(alpha) ret := C.Fade(*ccolor, calpha) - v := NewColorFromPointer(unsafe.Pointer(&ret)) + v := newColorFromPointer(unsafe.Pointer(&ret)) return v } @@ -900,7 +900,7 @@ func GetMouseY() int32 { // GetMousePosition - Returns mouse position XY func GetMousePosition() Vector2 { ret := C.GetMousePosition() - v := NewVector2FromPointer(unsafe.Pointer(&ret)) + v := newVector2FromPointer(unsafe.Pointer(&ret)) return v } @@ -935,6 +935,6 @@ func GetTouchY() int32 { func GetTouchPosition(index int32) Vector2 { cindex := (C.int)(index) ret := C.GetTouchPosition(cindex) - v := NewVector2FromPointer(unsafe.Pointer(&ret)) + v := newVector2FromPointer(unsafe.Pointer(&ret)) return v } diff --git a/raylib/gestures.go b/raylib/gestures.go index 74bece1..c1a137f 100644 --- a/raylib/gestures.go +++ b/raylib/gestures.go @@ -63,7 +63,7 @@ func GetGestureHoldDuration() float32 { // GetGestureDragVector - Get gesture drag vector func GetGestureDragVector() Vector2 { ret := C.GetGestureDragVector() - v := NewVector2FromPointer(unsafe.Pointer(&ret)) + v := newVector2FromPointer(unsafe.Pointer(&ret)) return v } @@ -77,7 +77,7 @@ func GetGestureDragAngle() float32 { // GetGesturePinchVector - Get gesture pinch delta func GetGesturePinchVector() Vector2 { ret := C.GetGesturePinchVector() - v := NewVector2FromPointer(unsafe.Pointer(&ret)) + v := newVector2FromPointer(unsafe.Pointer(&ret)) return v } diff --git a/raylib/models.go b/raylib/models.go index bbb0e11..03a1605 100644 --- a/raylib/models.go +++ b/raylib/models.go @@ -106,8 +106,8 @@ func NewMesh(vertexCount, triangleCount int32, vertices, texcoords, texcoords2, return Mesh{vertexCount, triangleCount, vertices, texcoords, texcoords2, normals, tangents, colors, indices, vaoID, vboID} } -// NewMeshFromPointer - Returns new Mesh from pointer -func NewMeshFromPointer(ptr unsafe.Pointer) Mesh { +// newMeshFromPointer - Returns new Mesh from pointer +func newMeshFromPointer(ptr unsafe.Pointer) Mesh { return *(*Mesh)(ptr) } @@ -132,8 +132,8 @@ func NewMaterial(shader Shader, maps [MaxMaterialMaps]MaterialMap, params *[]flo return Material{shader, maps, [4]byte{}, params} } -// NewMaterialFromPointer - Returns new Material from pointer -func NewMaterialFromPointer(ptr unsafe.Pointer) Material { +// newMaterialFromPointer - Returns new Material from pointer +func newMaterialFromPointer(ptr unsafe.Pointer) Material { return *(*Material)(ptr) } @@ -168,8 +168,8 @@ func NewModel(mesh Mesh, transform Matrix, material Material) Model { return Model{mesh, transform, material, [4]byte{}} } -// NewModelFromPointer - Returns new Model from pointer -func NewModelFromPointer(ptr unsafe.Pointer) Model { +// newModelFromPointer - Returns new Model from pointer +func newModelFromPointer(ptr unsafe.Pointer) Model { return *(*Model)(ptr) } @@ -190,8 +190,8 @@ func NewRay(position, direction Vector3) Ray { return Ray{position, direction} } -// NewRayFromPointer - Returns new Ray from pointer -func NewRayFromPointer(ptr unsafe.Pointer) Ray { +// newRayFromPointer - Returns new Ray from pointer +func newRayFromPointer(ptr unsafe.Pointer) Ray { return *(*Ray)(ptr) } @@ -335,7 +335,7 @@ func LoadMesh(fileName string) Mesh { cfileName := C.CString(fileName) defer C.free(unsafe.Pointer(cfileName)) ret := C.LoadMesh(cfileName) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -344,7 +344,7 @@ func LoadModel(fileName string) Model { cfileName := C.CString(fileName) defer C.free(unsafe.Pointer(cfileName)) ret := C.LoadModel(cfileName) - v := NewModelFromPointer(unsafe.Pointer(&ret)) + v := newModelFromPointer(unsafe.Pointer(&ret)) return v } @@ -352,7 +352,7 @@ func LoadModel(fileName string) Model { func LoadModelFromMesh(data Mesh) Model { cdata := data.cptr() ret := C.LoadModelFromMesh(*cdata) - v := NewModelFromPointer(unsafe.Pointer(&ret)) + v := newModelFromPointer(unsafe.Pointer(&ret)) return v } @@ -376,7 +376,7 @@ func GenMeshPlane(width, length float32, resX, resZ int) Mesh { cresZ := (C.int)(resZ) ret := C.GenMeshPlane(cwidth, clength, cresX, cresZ) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -387,7 +387,7 @@ func GenMeshCube(width, height, length float32) Mesh { clength := (C.float)(length) ret := C.GenMeshCube(cwidth, cheight, clength) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -398,7 +398,7 @@ func GenMeshSphere(radius float32, rings, slices int) Mesh { cslices := (C.int)(slices) ret := C.GenMeshSphere(cradius, crings, cslices) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -409,7 +409,7 @@ func GenMeshHemiSphere(radius float32, rings, slices int) Mesh { cslices := (C.int)(slices) ret := C.GenMeshHemiSphere(cradius, crings, cslices) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -420,7 +420,7 @@ func GenMeshCylinder(radius, height float32, slices int) Mesh { cslices := (C.int)(slices) ret := C.GenMeshCylinder(cradius, cheight, cslices) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -432,7 +432,7 @@ func GenMeshTorus(radius, size float32, radSeg, sides int) Mesh { csides := (C.int)(sides) ret := C.GenMeshTorus(cradius, csize, cradSeg, csides) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -444,7 +444,7 @@ func GenMeshKnot(radius, size float32, radSeg, sides int) Mesh { csides := (C.int)(sides) ret := C.GenMeshKnot(cradius, csize, cradSeg, csides) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -454,7 +454,7 @@ func GenMeshHeightmap(heightmap Image, size Vector3) Mesh { csize := size.cptr() ret := C.GenMeshHeightmap(*cheightmap, *csize) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -464,7 +464,7 @@ func GenMeshCubicmap(cubicmap Image, size Vector3) Mesh { csize := size.cptr() ret := C.GenMeshCubicmap(*ccubicmap, *csize) - v := NewMeshFromPointer(unsafe.Pointer(&ret)) + v := newMeshFromPointer(unsafe.Pointer(&ret)) return v } @@ -473,14 +473,14 @@ func LoadMaterial(fileName string) Material { cfileName := C.CString(fileName) defer C.free(unsafe.Pointer(cfileName)) ret := C.LoadMaterial(cfileName) - v := NewMaterialFromPointer(unsafe.Pointer(&ret)) + v := newMaterialFromPointer(unsafe.Pointer(&ret)) return v } // LoadMaterialDefault - Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) func LoadMaterialDefault() Material { ret := C.LoadMaterialDefault() - v := NewMaterialFromPointer(unsafe.Pointer(&ret)) + v := newMaterialFromPointer(unsafe.Pointer(&ret)) return v } @@ -562,7 +562,7 @@ func DrawBillboardRec(camera Camera, texture Texture2D, sourceRec Rectangle, cen func CalculateBoundingBox(mesh Mesh) BoundingBox { cmesh := mesh.cptr() ret := C.CalculateBoundingBox(*cmesh) - v := NewBoundingBoxFromPointer(unsafe.Pointer(&ret)) + v := newBoundingBoxFromPointer(unsafe.Pointer(&ret)) return v } diff --git a/raylib/shaders.go b/raylib/shaders.go index e5482a4..feee45a 100644 --- a/raylib/shaders.go +++ b/raylib/shaders.go @@ -57,8 +57,8 @@ func NewVrDeviceInfo(hResolution, vResolution int, hScreenSize, vScreenSize, vSc lensSeparationDistance, interpupillaryDistance, lensDistortionValues, chromaAbCorrection} } -// NewVrDeviceInfoFromPointer - Returns new VrDeviceInfo from pointer -func NewVrDeviceInfoFromPointer(ptr unsafe.Pointer) VrDeviceInfo { +// newVrDeviceInfoFromPointer - Returns new VrDeviceInfo from pointer +func newVrDeviceInfoFromPointer(ptr unsafe.Pointer) VrDeviceInfo { return *(*VrDeviceInfo)(ptr) } @@ -89,8 +89,8 @@ func NewShader(id uint32, locs [MaxShaderLocations]int32) Shader { return Shader{id, locs} } -// NewShaderFromPointer - Returns new Shader from pointer -func NewShaderFromPointer(ptr unsafe.Pointer) Shader { +// newShaderFromPointer - Returns new Shader from pointer +func newShaderFromPointer(ptr unsafe.Pointer) Shader { return *(*Shader)(ptr) } @@ -105,10 +105,10 @@ func LoadShader(vsFileName string, fsFileName string) Shader { var v Shader if vsFileName == "" { ret := C.LoadShader(nil, cfsFileName) - v = NewShaderFromPointer(unsafe.Pointer(&ret)) + v = newShaderFromPointer(unsafe.Pointer(&ret)) } else { ret := C.LoadShader(cvsFileName, cfsFileName) - v = NewShaderFromPointer(unsafe.Pointer(&ret)) + v = newShaderFromPointer(unsafe.Pointer(&ret)) } return v @@ -123,14 +123,14 @@ func UnloadShader(shader Shader) { // GetShaderDefault - Get default shader func GetShaderDefault() Shader { ret := C.GetShaderDefault() - v := NewShaderFromPointer(unsafe.Pointer(&ret)) + v := newShaderFromPointer(unsafe.Pointer(&ret)) return v } // GetTextureDefault - Get default texture func GetTextureDefault() *Texture2D { ret := C.GetTextureDefault() - v := NewTexture2DFromPointer(unsafe.Pointer(&ret)) + v := newTexture2DFromPointer(unsafe.Pointer(&ret)) return &v } @@ -190,7 +190,7 @@ func GenTextureCubemap(shader Shader, skyHDR Texture2D, size int) Texture2D { csize := (C.int)(size) ret := C.GenTextureCubemap(*cshader, *cskyHDR, csize) - v := NewTexture2DFromPointer(unsafe.Pointer(&ret)) + v := newTexture2DFromPointer(unsafe.Pointer(&ret)) return v } @@ -201,7 +201,7 @@ func GenTextureIrradiance(shader Shader, cubemap Texture2D, size int) Texture2D csize := (C.int)(size) ret := C.GenTextureIrradiance(*cshader, *ccubemap, csize) - v := NewTexture2DFromPointer(unsafe.Pointer(&ret)) + v := newTexture2DFromPointer(unsafe.Pointer(&ret)) return v } @@ -212,7 +212,7 @@ func GenTexturePrefilter(shader Shader, cubemap Texture2D, size int) Texture2D { csize := (C.int)(size) ret := C.GenTexturePrefilter(*cshader, *ccubemap, csize) - v := NewTexture2DFromPointer(unsafe.Pointer(&ret)) + v := newTexture2DFromPointer(unsafe.Pointer(&ret)) return v } @@ -223,7 +223,7 @@ func GenTextureBRDF(shader Shader, cubemap Texture2D, size int) Texture2D { csize := (C.int)(size) ret := C.GenTextureBRDF(*cshader, *ccubemap, csize) - v := NewTexture2DFromPointer(unsafe.Pointer(&ret)) + v := newTexture2DFromPointer(unsafe.Pointer(&ret)) return v } @@ -253,7 +253,7 @@ func EndBlendMode() { func GetVrDeviceInfo(vrDevice VrDevice) VrDeviceInfo { cvrDevice := (C.int)(vrDevice) ret := C.GetVrDeviceInfo(cvrDevice) - v := NewVrDeviceInfoFromPointer(unsafe.Pointer(&ret)) + v := newVrDeviceInfoFromPointer(unsafe.Pointer(&ret)) return v } diff --git a/raylib/shapes.go b/raylib/shapes.go index 1a74b34..5deed7b 100644 --- a/raylib/shapes.go +++ b/raylib/shapes.go @@ -259,7 +259,7 @@ func GetCollisionRec(rec1, rec2 Rectangle) Rectangle { crec1 := rec1.cptr() crec2 := rec2.cptr() ret := C.GetCollisionRec(*crec1, *crec2) - v := NewRectangleFromPointer(unsafe.Pointer(&ret)) + v := newRectangleFromPointer(unsafe.Pointer(&ret)) return v } diff --git a/raylib/text.go b/raylib/text.go index 5188a8a..5f7693c 100644 --- a/raylib/text.go +++ b/raylib/text.go @@ -30,8 +30,8 @@ func NewCharInfo(value int32, rec Rectangle, offsetX, offsetY, advanceX int32) C return CharInfo{value, rec, offsetX, offsetY, advanceX} } -// NewCharInfoFromPointer - Returns new SpriteFont from pointer -func NewCharInfoFromPointer(ptr unsafe.Pointer) CharInfo { +// newCharInfoFromPointer - Returns new SpriteFont from pointer +func newCharInfoFromPointer(ptr unsafe.Pointer) CharInfo { return *(*CharInfo)(ptr) } @@ -56,15 +56,15 @@ func NewSpriteFont(texture Texture2D, baseSize, charsCount int32, chars *CharInf return SpriteFont{texture, baseSize, charsCount, chars} } -// NewSpriteFontFromPointer - Returns new SpriteFont from pointer -func NewSpriteFontFromPointer(ptr unsafe.Pointer) SpriteFont { +// newSpriteFontFromPointer - Returns new SpriteFont from pointer +func newSpriteFontFromPointer(ptr unsafe.Pointer) SpriteFont { return *(*SpriteFont)(ptr) } // GetDefaultFont - Get the default SpriteFont func GetDefaultFont() SpriteFont { ret := C.GetDefaultFont() - v := NewSpriteFontFromPointer(unsafe.Pointer(&ret)) + v := newSpriteFontFromPointer(unsafe.Pointer(&ret)) return v } @@ -73,7 +73,7 @@ func LoadSpriteFont(fileName string) SpriteFont { cfileName := C.CString(fileName) defer C.free(unsafe.Pointer(cfileName)) ret := C.LoadSpriteFont(cfileName) - v := NewSpriteFontFromPointer(unsafe.Pointer(&ret)) + v := newSpriteFontFromPointer(unsafe.Pointer(&ret)) return v } @@ -85,7 +85,7 @@ func LoadSpriteFontEx(fileName string, fontSize int32, charsCount int32, fontCha ccharsCount := (C.int)(charsCount) cfontChars := (*C.int)(unsafe.Pointer(fontChars)) ret := C.LoadSpriteFontEx(cfileName, cfontSize, ccharsCount, cfontChars) - v := NewSpriteFontFromPointer(unsafe.Pointer(&ret)) + v := newSpriteFontFromPointer(unsafe.Pointer(&ret)) return v } @@ -136,7 +136,7 @@ func MeasureTextEx(spriteFont SpriteFont, text string, fontSize float32, spacing cfontSize := (C.float)(fontSize) cspacing := (C.int)(spacing) ret := C.MeasureTextEx(*cspriteFont, ctext, cfontSize, cspacing) - v := NewVector2FromPointer(unsafe.Pointer(&ret)) + v := newVector2FromPointer(unsafe.Pointer(&ret)) return v } diff --git a/raylib/textures.go b/raylib/textures.go index 2fe8ac5..67e8d8f 100644 --- a/raylib/textures.go +++ b/raylib/textures.go @@ -122,8 +122,8 @@ func NewImage(data unsafe.Pointer, width, height, mipmaps int32, format TextureF return &Image{data, width, height, mipmaps, format} } -// NewImageFromPointer - Returns new Image from pointer -func NewImageFromPointer(ptr unsafe.Pointer) *Image { +// newImageFromPointer - Returns new Image from pointer +func newImageFromPointer(ptr unsafe.Pointer) *Image { return (*Image)(ptr) } @@ -167,8 +167,8 @@ func NewTexture2D(id uint32, width, height, mipmaps int32, format TextureFormat) return Texture2D{id, width, height, mipmaps, format} } -// NewTexture2DFromPointer - Returns new Texture2D from pointer -func NewTexture2DFromPointer(ptr unsafe.Pointer) Texture2D { +// newTexture2DFromPointer - Returns new Texture2D from pointer +func newTexture2DFromPointer(ptr unsafe.Pointer) Texture2D { return *(*Texture2D)(ptr) } @@ -191,8 +191,8 @@ func NewRenderTexture2D(id uint32, texture, depth Texture2D) RenderTexture2D { return RenderTexture2D{id, texture, depth} } -// NewRenderTexture2DFromPointer - Returns new RenderTexture2D from pointer -func NewRenderTexture2DFromPointer(ptr unsafe.Pointer) RenderTexture2D { +// newRenderTexture2DFromPointer - Returns new RenderTexture2D from pointer +func newRenderTexture2DFromPointer(ptr unsafe.Pointer) RenderTexture2D { return *(*RenderTexture2D)(ptr) } @@ -201,7 +201,7 @@ func LoadImage(fileName string) *Image { cfileName := C.CString(fileName) defer C.free(unsafe.Pointer(cfileName)) ret := C.LoadImage(cfileName) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -211,7 +211,7 @@ func LoadImageEx(pixels []Color, width, height int32) *Image { cwidth := (C.int)(width) cheight := (C.int)(height) ret := C.LoadImageEx(cpixels, cwidth, cheight) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -222,7 +222,7 @@ func LoadImagePro(data []byte, width, height int32, format TextureFormat) *Image cheight := (C.int)(height) cformat := (C.int)(format) ret := C.LoadImagePro(cdata, cwidth, cheight, cformat) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -235,7 +235,7 @@ func LoadImageRaw(fileName string, width, height int32, format TextureFormat, he cformat := (C.int)(format) cheaderSize := (C.int)(headerSize) ret := C.LoadImageRaw(cfileName, cwidth, cheight, cformat, cheaderSize) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -244,7 +244,7 @@ func LoadTexture(fileName string) Texture2D { cfileName := C.CString(fileName) defer C.free(unsafe.Pointer(cfileName)) ret := C.LoadTexture(cfileName) - v := NewTexture2DFromPointer(unsafe.Pointer(&ret)) + v := newTexture2DFromPointer(unsafe.Pointer(&ret)) return v } @@ -252,7 +252,7 @@ func LoadTexture(fileName string) Texture2D { func LoadTextureFromImage(image *Image) Texture2D { cimage := image.cptr() ret := C.LoadTextureFromImage(*cimage) - v := NewTexture2DFromPointer(unsafe.Pointer(&ret)) + v := newTexture2DFromPointer(unsafe.Pointer(&ret)) return v } @@ -261,7 +261,7 @@ func LoadRenderTexture(width, height int32) RenderTexture2D { cwidth := (C.int)(width) cheight := (C.int)(height) ret := C.LoadRenderTexture(cwidth, cheight) - v := NewRenderTexture2DFromPointer(unsafe.Pointer(&ret)) + v := newRenderTexture2DFromPointer(unsafe.Pointer(&ret)) return v } @@ -294,7 +294,7 @@ func GetImageData(image *Image) unsafe.Pointer { func GetTextureData(texture Texture2D) *Image { ctexture := texture.cptr() ret := C.GetTextureData(*ctexture) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -349,7 +349,7 @@ func ImageDither(image *Image, rBpp, gBpp, bBpp, aBpp int32) { func ImageCopy(image *Image) *Image { cimage := image.cptr() ret := C.ImageCopy(*cimage) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -383,7 +383,7 @@ func ImageText(text string, fontSize int32, color Color) *Image { cfontSize := (C.int)(fontSize) ccolor := color.cptr() ret := C.ImageText(ctext, cfontSize, *ccolor) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -396,7 +396,7 @@ func ImageTextEx(font SpriteFont, text string, fontSize float32, spacing int32, cspacing := (C.int)(spacing) ctint := tint.cptr() ret := C.ImageTextEx(*cfont, ctext, cfontSize, cspacing, *ctint) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -485,7 +485,7 @@ func GenImageColor(width, height int, color Color) *Image { ccolor := color.cptr() ret := C.GenImageColor(cwidth, cheight, *ccolor) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -497,7 +497,7 @@ func GenImageGradientV(width, height int, top, bottom Color) *Image { cbottom := bottom.cptr() ret := C.GenImageGradientV(cwidth, cheight, *ctop, *cbottom) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -509,7 +509,7 @@ func GenImageGradientH(width, height int, left, right Color) *Image { cright := right.cptr() ret := C.GenImageGradientH(cwidth, cheight, *cleft, *cright) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -522,7 +522,7 @@ func GenImageGradientRadial(width, height int, density float32, inner, outer Col couter := outer.cptr() ret := C.GenImageGradientRadial(cwidth, cheight, cdensity, *cinner, *couter) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -536,7 +536,7 @@ func GenImageChecked(width, height, checksX, checksY int, col1, col2 Color) *Ima ccol2 := col2.cptr() ret := C.GenImageChecked(cwidth, cheight, cchecksX, cchecksY, *ccol1, *ccol2) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -547,7 +547,7 @@ func GenImageWhiteNoise(width, height int, factor float32) *Image { cfactor := (C.float)(factor) ret := C.GenImageWhiteNoise(cwidth, cheight, cfactor) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -558,7 +558,7 @@ func GenImagePerlinNoise(width, height int, scale float32) *Image { cscale := (C.float)(scale) ret := C.GenImagePerlinNoise(cwidth, cheight, cscale) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } @@ -569,7 +569,7 @@ func GenImageCellular(width, height, tileSize int) *Image { ctileSize := (C.int)(tileSize) ret := C.GenImageCellular(cwidth, cheight, ctileSize) - v := NewImageFromPointer(unsafe.Pointer(&ret)) + v := newImageFromPointer(unsafe.Pointer(&ret)) return v } From 8b0dcc549f7ec1d1db9a42a0eb1dea2f7df4255d Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 7 Dec 2017 17:04:09 +0100 Subject: [PATCH 31/48] Proper Music type --- raylib/audio.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/raylib/audio.go b/raylib/audio.go index 73eda11..9cf51a1 100644 --- a/raylib/audio.go +++ b/raylib/audio.go @@ -66,7 +66,18 @@ func newSoundFromPointer(ptr unsafe.Pointer) Sound { // Music type (file streaming from memory) // NOTE: Anything longer than ~10 seconds should be streamed -type Music C.Music +type Music struct { + CtxType uint32 + _ [4]byte + ctxOgg unsafe.Pointer + ctxFlac unsafe.Pointer + ctxXm unsafe.Pointer + ctxMod unsafe.Pointer + Stream AudioStream + LoopCount int32 + TotalSamples uint32 + SamplesLeft uint32 +} // AudioStream type // NOTE: Useful to create custom audio streams not bound to a specific file From 0206cd3cd64ca26598b4c0718d70acb0b71b9c03 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 7 Dec 2017 17:22:43 +0100 Subject: [PATCH 32/48] Use []byte instead of unsafe.Pointer --- raylib/textures.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/raylib/textures.go b/raylib/textures.go index 67e8d8f..c5545b7 100644 --- a/raylib/textures.go +++ b/raylib/textures.go @@ -112,7 +112,7 @@ func (i *Image) ToImage() image.Image { // Get pixel data from image (RGBA 32bit) pixels := GetImageData(i) - img.Pix = (*[1 << 30]uint8)(pixels)[:] + img.Pix = pixels return img } @@ -284,10 +284,10 @@ func UnloadRenderTexture(target RenderTexture2D) { } // GetImageData - Get pixel data from image -func GetImageData(image *Image) unsafe.Pointer { +func GetImageData(image *Image) []byte { cimage := image.cptr() ret := C.GetImageData(*cimage) - return unsafe.Pointer(ret) + return (*[1 << 30]uint8)(unsafe.Pointer(ret))[:] } // GetTextureData - Get pixel data from GPU texture and return an Image @@ -299,9 +299,9 @@ func GetTextureData(texture Texture2D) *Image { } // UpdateTexture - Update GPU texture with new data -func UpdateTexture(texture Texture2D, pixels unsafe.Pointer) { +func UpdateTexture(texture Texture2D, pixels []byte) { ctexture := texture.cptr() - cpixels := (unsafe.Pointer)(unsafe.Pointer(pixels)) + cpixels := unsafe.Pointer(&pixels[0]) C.UpdateTexture(*ctexture, cpixels) } From 6fc955d098046a2175d9eb1cf7dc3d2b62349a06 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 7 Dec 2017 18:30:07 +0100 Subject: [PATCH 33/48] Use []byte instead of unsafe.Pointer --- examples/audio/raw_stream/main.go | 3 +-- raylib/audio.go | 15 ++++++++------- raylib/textures.go | 7 ++++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/examples/audio/raw_stream/main.go b/examples/audio/raw_stream/main.go index b1e28d0..39870f9 100644 --- a/examples/audio/raw_stream/main.go +++ b/examples/audio/raw_stream/main.go @@ -2,7 +2,6 @@ package main import ( "math" - "unsafe" "github.com/gen2brain/raylib-go/raylib" ) @@ -48,7 +47,7 @@ func main() { numSamples = samplesLeft } - raylib.UpdateAudioStream(stream, unsafe.Pointer(&data[totalSamples-samplesLeft]), numSamples) + raylib.UpdateAudioStream(stream, data[totalSamples-samplesLeft:], numSamples) samplesLeft -= numSamples diff --git a/raylib/audio.go b/raylib/audio.go index 9cf51a1..1badcbe 100644 --- a/raylib/audio.go +++ b/raylib/audio.go @@ -23,7 +23,7 @@ type Wave struct { // Number of channels (1-mono, 2-stereo) Channels uint32 // Buffer data pointer - Data unsafe.Pointer + data unsafe.Pointer } func (w *Wave) cptr() *C.Wave { @@ -31,8 +31,9 @@ func (w *Wave) cptr() *C.Wave { } // NewWave - Returns new Wave -func NewWave(sampleCount, sampleRate, sampleSize, channels uint32, data unsafe.Pointer) Wave { - return Wave{sampleCount, sampleRate, sampleSize, channels, data} +func NewWave(sampleCount, sampleRate, sampleSize, channels uint32, data []byte) Wave { + d := unsafe.Pointer(&data[0]) + return Wave{sampleCount, sampleRate, sampleSize, channels, d} } // newWaveFromPointer - Returns new Wave from pointer @@ -172,9 +173,9 @@ func LoadSoundFromWave(wave Wave) Sound { } // UpdateSound - Update sound buffer with new data -func UpdateSound(sound Sound, data unsafe.Pointer, samplesCount int32) { +func UpdateSound(sound Sound, data []byte, samplesCount int32) { csound := sound.cptr() - cdata := (unsafe.Pointer)(unsafe.Pointer(data)) + cdata := unsafe.Pointer(&data[0]) csamplesCount := (C.int)(samplesCount) C.UpdateSound(*csound, cdata, csamplesCount) } @@ -378,9 +379,9 @@ func InitAudioStream(sampleRate uint32, sampleSize uint32, channels uint32) Audi } // UpdateAudioStream - Update audio stream buffers with data -func UpdateAudioStream(stream AudioStream, data unsafe.Pointer, samplesCount int32) { +func UpdateAudioStream(stream AudioStream, data []float32, samplesCount int32) { cstream := stream.cptr() - cdata := (unsafe.Pointer)(unsafe.Pointer(data)) + cdata := unsafe.Pointer(&data[0]) csamplesCount := (C.int)(samplesCount) C.UpdateAudioStream(*cstream, cdata, csamplesCount) } diff --git a/raylib/textures.go b/raylib/textures.go index c5545b7..b7b5261 100644 --- a/raylib/textures.go +++ b/raylib/textures.go @@ -90,7 +90,7 @@ const ( // NOTE: Data stored in CPU memory (RAM) type Image struct { // Image raw data - Data unsafe.Pointer + data unsafe.Pointer // Image base width Width int32 // Image base height @@ -118,8 +118,9 @@ func (i *Image) ToImage() image.Image { } // NewImage - Returns new Image -func NewImage(data unsafe.Pointer, width, height, mipmaps int32, format TextureFormat) *Image { - return &Image{data, width, height, mipmaps, format} +func NewImage(data []byte, width, height, mipmaps int32, format TextureFormat) *Image { + d := unsafe.Pointer(&data[0]) + return &Image{d, width, height, mipmaps, format} } // newImageFromPointer - Returns new Image from pointer From 6f72c4f5af1bafe64a10dc6495c7ed891eb99162 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 7 Dec 2017 19:52:53 +0100 Subject: [PATCH 34/48] Don't use C for constants --- raylib/camera.go | 10 ++++----- raylib/gestures.go | 22 +++++++++---------- raylib/shaders.go | 6 +++--- raylib/textures.go | 54 +++++++++++++++++++++++----------------------- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/raylib/camera.go b/raylib/camera.go index b098038..419c431 100644 --- a/raylib/camera.go +++ b/raylib/camera.go @@ -12,11 +12,11 @@ type CameraMode int32 // Camera system modes const ( - CameraCustom CameraMode = C.CAMERA_CUSTOM - CameraFree CameraMode = C.CAMERA_FREE - CameraOrbital CameraMode = C.CAMERA_ORBITAL - CameraFirstPerson CameraMode = C.CAMERA_FIRST_PERSON - CameraThirdPerson CameraMode = C.CAMERA_THIRD_PERSON + CameraCustom CameraMode = iota + CameraFree + CameraOrbital + CameraFirstPerson + CameraThirdPerson ) // SetCameraMode - Set camera mode (multiple camera modes available) diff --git a/raylib/gestures.go b/raylib/gestures.go index c1a137f..0742751 100644 --- a/raylib/gestures.go +++ b/raylib/gestures.go @@ -12,17 +12,17 @@ type Gestures int32 // Gestures types // NOTE: It could be used as flags to enable only some gestures const ( - GestureNone Gestures = C.GESTURE_NONE - GestureTap Gestures = C.GESTURE_TAP - GestureDoubletap Gestures = C.GESTURE_DOUBLETAP - GestureHold Gestures = C.GESTURE_HOLD - GestureDrag Gestures = C.GESTURE_DRAG - GestureSwipeRight Gestures = C.GESTURE_SWIPE_RIGHT - GestureSwipeLeft Gestures = C.GESTURE_SWIPE_LEFT - GestureSwipeUp Gestures = C.GESTURE_SWIPE_UP - GestureSwipeDown Gestures = C.GESTURE_SWIPE_DOWN - GesturePinchIn Gestures = C.GESTURE_PINCH_IN - GesturePinchOut Gestures = C.GESTURE_PINCH_OUT + GestureNone Gestures = 0 + GestureTap Gestures = 1 + GestureDoubletap Gestures = 2 + GestureHold Gestures = 4 + GestureDrag Gestures = 8 + GestureSwipeRight Gestures = 16 + GestureSwipeLeft Gestures = 32 + GestureSwipeUp Gestures = 64 + GestureSwipeDown Gestures = 128 + GesturePinchIn Gestures = 256 + GesturePinchOut Gestures = 512 ) // SetGesturesEnabled - Enable a set of gestures using flags diff --git a/raylib/shaders.go b/raylib/shaders.go index feee45a..ae36c2c 100644 --- a/raylib/shaders.go +++ b/raylib/shaders.go @@ -67,9 +67,9 @@ type BlendMode int32 // Color blending modes (pre-defined) const ( - BlendAlpha BlendMode = C.BLEND_ALPHA - BlendAdditive BlendMode = C.BLEND_ADDITIVE - BlendMultiplied BlendMode = C.BLEND_MULTIPLIED + BlendAlpha BlendMode = iota + BlendAdditive + BlendMultiplied ) // Shader type (generic shader) diff --git a/raylib/textures.go b/raylib/textures.go index b7b5261..05c23bc 100644 --- a/raylib/textures.go +++ b/raylib/textures.go @@ -18,41 +18,41 @@ type TextureFormat int32 // NOTE: Support depends on OpenGL version and platform const ( // 8 bit per pixel (no alpha) - UncompressedGrayscale TextureFormat = C.UNCOMPRESSED_GRAYSCALE + UncompressedGrayscale TextureFormat = iota + 1 // 16 bpp (2 channels) - UncompressedGrayAlpha TextureFormat = C.UNCOMPRESSED_GRAY_ALPHA + UncompressedGrayAlpha // 16 bpp - UncompressedR5g6b5 TextureFormat = C.UNCOMPRESSED_R5G6B5 + UncompressedR5g6b5 // 24 bpp - UncompressedR8g8b8 TextureFormat = C.UNCOMPRESSED_R8G8B8 + UncompressedR8g8b8 // 16 bpp (1 bit alpha) - UncompressedR5g5b5a1 TextureFormat = C.UNCOMPRESSED_R5G5B5A1 + UncompressedR5g5b5a1 // 16 bpp (4 bit alpha) - UncompressedR4g4b4a4 TextureFormat = C.UNCOMPRESSED_R4G4B4A4 + UncompressedR4g4b4a4 // 32 bpp - UncompressedR8g8b8a8 TextureFormat = C.UNCOMPRESSED_R8G8B8A8 + UncompressedR8g8b8a8 // 4 bpp (no alpha) - CompressedDxt1Rgb TextureFormat = C.COMPRESSED_DXT1_RGB + CompressedDxt1Rgb // 4 bpp (1 bit alpha) - CompressedDxt1Rgba TextureFormat = C.COMPRESSED_DXT1_RGBA + CompressedDxt1Rgba // 8 bpp - CompressedDxt3Rgba TextureFormat = C.COMPRESSED_DXT3_RGBA + CompressedDxt3Rgba // 8 bpp - CompressedDxt5Rgba TextureFormat = C.COMPRESSED_DXT5_RGBA + CompressedDxt5Rgba // 4 bpp - CompressedEtc1Rgb TextureFormat = C.COMPRESSED_ETC1_RGB + CompressedEtc1Rgb // 4 bpp - CompressedEtc2Rgb TextureFormat = C.COMPRESSED_ETC2_RGB + CompressedEtc2Rgb // 8 bpp - CompressedEtc2EacRgba TextureFormat = C.COMPRESSED_ETC2_EAC_RGBA + CompressedEtc2EacRgba // 4 bpp - CompressedPvrtRgb TextureFormat = C.COMPRESSED_PVRT_RGB + CompressedPvrtRgb // 4 bpp - CompressedPvrtRgba TextureFormat = C.COMPRESSED_PVRT_RGBA + CompressedPvrtRgba // 8 bpp - CompressedAstc4x4Rgba TextureFormat = C.COMPRESSED_ASTC_4x4_RGBA + CompressedAstc4x4Rgba // 2 bpp - CompressedAstc8x8Rgba TextureFormat = C.COMPRESSED_ASTC_8x8_RGBA + CompressedAstc8x8Rgba ) // TextureFilterMode - Texture filter mode @@ -63,17 +63,17 @@ type TextureFilterMode int32 // NOTE 2: Filter is accordingly set for minification and magnification const ( // No filter, just pixel aproximation - FilterPoint TextureFilterMode = C.FILTER_POINT + FilterPoint TextureFilterMode = iota // Linear filtering - FilterBilinear TextureFilterMode = C.FILTER_BILINEAR + FilterBilinear // Trilinear filtering (linear with mipmaps) - FilterTrilinear TextureFilterMode = C.FILTER_TRILINEAR + FilterTrilinear // Anisotropic filtering 4x - FilterAnisotropic4x TextureFilterMode = C.FILTER_ANISOTROPIC_4X + FilterAnisotropic4x // Anisotropic filtering 8x - FilterAnisotropic8x TextureFilterMode = C.FILTER_ANISOTROPIC_8X + FilterAnisotropic8x // Anisotropic filtering 16x - FilterAnisotropic16x TextureFilterMode = C.FILTER_ANISOTROPIC_16X + FilterAnisotropic16x ) // TextureWrapMode - Texture wrap mode @@ -81,9 +81,9 @@ type TextureWrapMode int32 // Texture parameters: wrap mode const ( - WrapRepeat TextureWrapMode = C.WRAP_REPEAT - WrapClamp TextureWrapMode = C.WRAP_CLAMP - WrapMirror TextureWrapMode = C.WRAP_MIRROR + WrapRepeat TextureWrapMode = iota + WrapClamp + WrapMirror ) // Image type, bpp always RGBA (32bit) From f71025719cd36491629cb280a7f7ed937a816883 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 7 Dec 2017 19:56:41 +0100 Subject: [PATCH 35/48] Disable OS X build, it is too crowded --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 57eb5a8..f6ef0d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: go os: - linux - - osx + #- osx go: - 1.9.x From 135a4637a6f1dc3b1681eb1d4639c9897d0fe17c Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 7 Dec 2017 20:32:31 +0100 Subject: [PATCH 36/48] Move all constants and types to raylib.go --- raylib/audio.go | 74 +--- raylib/camera.go | 12 - raylib/core.go | 375 +---------------- raylib/gestures.go | 19 - raylib/models.go | 156 +------ raylib/raylib.go | 895 ++++++++++++++++++++++++++++++++++++++++ raylib/shaders.go | 70 +--- raylib/text.go | 38 +- raylib/textures.go | 163 +------- raylib/utils.go | 8 - raylib/utils_android.go | 8 - raylib/utils_windows.go | 8 - 12 files changed, 916 insertions(+), 910 deletions(-) diff --git a/raylib/audio.go b/raylib/audio.go index 1badcbe..2b7c621 100644 --- a/raylib/audio.go +++ b/raylib/audio.go @@ -12,100 +12,30 @@ import "C" import "unsafe" import "reflect" -// Wave type, defines audio wave data -type Wave struct { - // Number of samples - SampleCount uint32 - // Frequency (samples per second) - SampleRate uint32 - // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - SampleSize uint32 - // Number of channels (1-mono, 2-stereo) - Channels uint32 - // Buffer data pointer - data unsafe.Pointer -} - +// cptr returns C pointer func (w *Wave) cptr() *C.Wave { return (*C.Wave)(unsafe.Pointer(w)) } -// NewWave - Returns new Wave -func NewWave(sampleCount, sampleRate, sampleSize, channels uint32, data []byte) Wave { - d := unsafe.Pointer(&data[0]) - return Wave{sampleCount, sampleRate, sampleSize, channels, d} -} - // newWaveFromPointer - Returns new Wave from pointer func newWaveFromPointer(ptr unsafe.Pointer) Wave { return *(*Wave)(ptr) } -// Sound source type -type Sound struct { - // Audio source id - Source uint32 - // Audio buffer id - Buffer uint32 - // Audio format specifier - Format int32 -} - func (s *Sound) cptr() *C.Sound { return (*C.Sound)(unsafe.Pointer(s)) } -// NewSound - Returns new Sound -func NewSound(source, buffer uint32, format int32) Sound { - return Sound{source, buffer, format} -} - // newSoundFromPointer - Returns new Sound from pointer func newSoundFromPointer(ptr unsafe.Pointer) Sound { return *(*Sound)(ptr) } -// Music type (file streaming from memory) -// NOTE: Anything longer than ~10 seconds should be streamed -type Music struct { - CtxType uint32 - _ [4]byte - ctxOgg unsafe.Pointer - ctxFlac unsafe.Pointer - ctxXm unsafe.Pointer - ctxMod unsafe.Pointer - Stream AudioStream - LoopCount int32 - TotalSamples uint32 - SamplesLeft uint32 -} - -// AudioStream type -// NOTE: Useful to create custom audio streams not bound to a specific file -type AudioStream struct { - // Frequency (samples per second) - SampleRate uint32 - // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - SampleSize uint32 - // Number of channels (1-mono, 2-stereo) - Channels uint32 - // Audio format specifier - Format int32 - // Audio source id - Source uint32 - // Audio buffers (double buffering) - Buffers [2]uint32 -} - +// cptr returns C pointer func (a *AudioStream) cptr() *C.AudioStream { return (*C.AudioStream)(unsafe.Pointer(a)) } -// NewAudioStream - Returns new AudioStream -func NewAudioStream(sampleRate, sampleSize, channels uint32, format int32, source uint32, buffers [2]uint32) AudioStream { - return AudioStream{sampleRate, sampleSize, channels, format, source, buffers} -} - // newAudioStreamFromPointer - Returns new AudioStream from pointer func newAudioStreamFromPointer(ptr unsafe.Pointer) AudioStream { return *(*AudioStream)(ptr) diff --git a/raylib/camera.go b/raylib/camera.go index 419c431..960cf7b 100644 --- a/raylib/camera.go +++ b/raylib/camera.go @@ -7,18 +7,6 @@ package raylib */ import "C" -// CameraMode type -type CameraMode int32 - -// Camera system modes -const ( - CameraCustom CameraMode = iota - CameraFree - CameraOrbital - CameraFirstPerson - CameraThirdPerson -) - // SetCameraMode - Set camera mode (multiple camera modes available) func SetCameraMode(camera Camera, mode CameraMode) { ccamera := camera.cptr() diff --git a/raylib/core.go b/raylib/core.go index 6e5a932..0351705 100644 --- a/raylib/core.go +++ b/raylib/core.go @@ -7,448 +7,89 @@ package raylib import "C" import ( - "io" "unsafe" ) -// Some basic Defines -const ( - Pi = 3.1415927 - Deg2rad = 0.017453292 - Rad2deg = 57.295776 - - // Raylib Config Flags - - // Set to show raylib logo at startup - FlagShowLogo = 1 - // Set to run program in fullscreen - FlagFullscreenMode = 2 - // Set to allow resizable window - FlagWindowResizable = 4 - // Set to show window decoration (frame and buttons) - FlagWindowDecorated = 8 - // Set to allow transparent window - FlagWindowTransparent = 16 - // Set to try enabling MSAA 4X - FlagMsaa4xHint = 32 - // Set to try enabling V-Sync on GPU - FlagVsyncHint = 64 - - // Keyboard Function Keys - KeySpace = 32 - KeyEscape = 256 - KeyEnter = 257 - KeyBackspace = 259 - KeyRight = 262 - KeyLeft = 263 - KeyDown = 264 - KeyUp = 265 - KeyF1 = 290 - KeyF2 = 291 - KeyF3 = 292 - KeyF4 = 293 - KeyF5 = 294 - KeyF6 = 295 - KeyF7 = 296 - KeyF8 = 297 - KeyF9 = 298 - KeyF10 = 299 - KeyF11 = 300 - KeyF12 = 301 - KeyLeftShift = 340 - KeyLeftControl = 341 - KeyLeftAlt = 342 - KeyRightShift = 344 - KeyRightControl = 345 - KeyRightAlt = 346 - - // Keyboard Alpha Numeric Keys - KeyZero = 48 - KeyOne = 49 - KeyTwo = 50 - KeyThree = 51 - KeyFour = 52 - KeyFive = 53 - KeySix = 54 - KeySeven = 55 - KeyEight = 56 - KeyNine = 57 - KeyA = 65 - KeyB = 66 - KeyC = 67 - KeyD = 68 - KeyE = 69 - KeyF = 70 - KeyG = 71 - KeyH = 72 - KeyI = 73 - KeyJ = 74 - KeyK = 75 - KeyL = 76 - KeyM = 77 - KeyN = 78 - KeyO = 79 - KeyP = 80 - KeyQ = 81 - KeyR = 82 - KeyS = 83 - KeyT = 84 - KeyU = 85 - KeyV = 86 - KeyW = 87 - KeyX = 88 - KeyY = 89 - KeyZ = 90 - - // Android keys - KeyBack = 4 - KeyMenu = 82 - KeyVolumeUp = 24 - KeyVolumeDown = 25 - - // Mouse Buttons - MouseLeftButton = 0 - MouseRightButton = 1 - MouseMiddleButton = 2 - - // Touch points registered - MaxTouchPoints = 2 - - // Gamepad Number - GamepadPlayer1 = 0 - GamepadPlayer2 = 1 - GamepadPlayer3 = 2 - GamepadPlayer4 = 3 - - // Gamepad Buttons/Axis - - // PS3 USB Controller Buttons - GamepadPs3ButtonTriangle = 0 - GamepadPs3ButtonCircle = 1 - GamepadPs3ButtonCross = 2 - GamepadPs3ButtonSquare = 3 - GamepadPs3ButtonL1 = 6 - GamepadPs3ButtonR1 = 7 - GamepadPs3ButtonL2 = 4 - GamepadPs3ButtonR2 = 5 - GamepadPs3ButtonStart = 8 - GamepadPs3ButtonSelect = 9 - GamepadPs3ButtonUp = 24 - GamepadPs3ButtonRight = 25 - GamepadPs3ButtonDown = 26 - GamepadPs3ButtonLeft = 27 - GamepadPs3ButtonPs = 12 - - // PS3 USB Controller Axis - GamepadPs3AxisLeftX = 0 - GamepadPs3AxisLeftY = 1 - GamepadPs3AxisRightX = 2 - GamepadPs3AxisRightY = 5 - // [1..-1] (pressure-level) - GamepadPs3AxisL2 = 3 - // [1..-1] (pressure-level) - GamepadPs3AxisR2 = 4 - - // Xbox360 USB Controller Buttons - GamepadXboxButtonA = 0 - GamepadXboxButtonB = 1 - GamepadXboxButtonX = 2 - GamepadXboxButtonY = 3 - GamepadXboxButtonLb = 4 - GamepadXboxButtonRb = 5 - GamepadXboxButtonSelect = 6 - GamepadXboxButtonStart = 7 - GamepadXboxButtonUp = 10 - GamepadXboxButtonRight = 11 - GamepadXboxButtonDown = 12 - GamepadXboxButtonLeft = 13 - GamepadXboxButtonHome = 8 - - // Xbox360 USB Controller Axis - // [-1..1] (left->right) - GamepadXboxAxisLeftX = 0 - // [1..-1] (up->down) - GamepadXboxAxisLeftY = 1 - // [-1..1] (left->right) - GamepadXboxAxisRightX = 2 - // [1..-1] (up->down) - GamepadXboxAxisRightY = 3 - // [-1..1] (pressure-level) - GamepadXboxAxisLt = 4 - // [-1..1] (pressure-level) - GamepadXboxAxisRt = 5 -) - -// Some Basic Colors -// NOTE: Custom raylib color palette for amazing visuals on WHITE background -var ( - // Light Gray - LightGray = NewColor(200, 200, 200, 255) - // Gray - Gray = NewColor(130, 130, 130, 255) - // Dark Gray - DarkGray = NewColor(80, 80, 80, 255) - // Yellow - Yellow = NewColor(253, 249, 0, 255) - // Gold - Gold = NewColor(255, 203, 0, 255) - // Orange - Orange = NewColor(255, 161, 0, 255) - // Pink - Pink = NewColor(255, 109, 194, 255) - // Red - Red = NewColor(230, 41, 55, 255) - // Maroon - Maroon = NewColor(190, 33, 55, 255) - // Green - Green = NewColor(0, 228, 48, 255) - // Lime - Lime = NewColor(0, 158, 47, 255) - // Dark Green - DarkGreen = NewColor(0, 117, 44, 255) - // Sky Blue - SkyBlue = NewColor(102, 191, 255, 255) - // Blue - Blue = NewColor(0, 121, 241, 255) - // Dark Blue - DarkBlue = NewColor(0, 82, 172, 255) - // Purple - Purple = NewColor(200, 122, 255, 255) - // Violet - Violet = NewColor(135, 60, 190, 255) - // Dark Purple - DarkPurple = NewColor(112, 31, 126, 255) - // Beige - Beige = NewColor(211, 176, 131, 255) - // Brown - Brown = NewColor(127, 106, 79, 255) - // Dark Brown - DarkBrown = NewColor(76, 63, 47, 255) - // White - White = NewColor(255, 255, 255, 255) - // Black - Black = NewColor(0, 0, 0, 255) - // Blank (Transparent) - Blank = NewColor(0, 0, 0, 0) - // Magenta - Magenta = NewColor(255, 0, 255, 255) - // Ray White (RayLib Logo White) - RayWhite = NewColor(245, 245, 245, 255) -) - -// Vector2 type -type Vector2 struct { - X float32 - Y float32 -} - +// cptr returns C pointer func (v *Vector2) cptr() *C.Vector2 { return (*C.Vector2)(unsafe.Pointer(v)) } -// NewVector2 - Returns new Vector2 -func NewVector2(x, y float32) Vector2 { - return Vector2{x, y} -} - // newVector2FromPointer - Returns new Vector2 from pointer func newVector2FromPointer(ptr unsafe.Pointer) Vector2 { return *(*Vector2)(ptr) } -// Vector3 type -type Vector3 struct { - X float32 - Y float32 - Z float32 -} - +// cptr returns C pointer func (v *Vector3) cptr() *C.Vector3 { return (*C.Vector3)(unsafe.Pointer(v)) } -// NewVector3 - Returns new Vector3 -func NewVector3(X, Y, Z float32) Vector3 { - return Vector3{X, Y, Z} -} - // newVector3FromPointer - Returns new Vector3 from pointer func newVector3FromPointer(ptr unsafe.Pointer) Vector3 { return *(*Vector3)(ptr) } -// Matrix type (OpenGL style 4x4 - right handed, column major) -type Matrix struct { - M0, M4, M8, M12 float32 - M1, M5, M9, M13 float32 - M2, M6, M10, M14 float32 - M3, M7, M11, M15 float32 -} - +// cptr returns C pointer func (m *Matrix) cptr() *C.Matrix { return (*C.Matrix)(unsafe.Pointer(m)) } -// NewMatrix - Returns new Matrix -func NewMatrix(m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, m15 float32) Matrix { - return Matrix{m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, m15} -} - // newMatrixFromPointer - Returns new Matrix from pointer func newMatrixFromPointer(ptr unsafe.Pointer) Matrix { return *(*Matrix)(ptr) } -// Mat2 type (used for polygon shape rotation matrix) -type Mat2 struct { - M00 float32 - M01 float32 - M10 float32 - M11 float32 -} - -// NewMat2 - Returns new Mat2 -func NewMat2(m0, m1, m10, m11 float32) Mat2 { - return Mat2{m0, m1, m10, m11} -} - -// Quaternion type -type Quaternion struct { - X float32 - Y float32 - Z float32 - W float32 -} - -// NewQuaternion - Returns new Quaternion -func NewQuaternion(x, y, z, w float32) Quaternion { - return Quaternion{x, y, z, w} -} - -// Color type, RGBA (32bit) -type Color struct { - R uint8 - G uint8 - B uint8 - A uint8 -} - +// cptr returns C pointer func (color *Color) cptr() *C.Color { return (*C.Color)(unsafe.Pointer(color)) } -// NewColor - Returns new Color -func NewColor(r, g, b, a uint8) Color { - return Color{r, g, b, a} -} - // newColorFromPointer - Returns new Color from pointer func newColorFromPointer(ptr unsafe.Pointer) Color { return *(*Color)(ptr) } -// Rectangle type -type Rectangle struct { - X int32 - Y int32 - Width int32 - Height int32 -} - +// cptr returns C pointer func (r *Rectangle) cptr() *C.Rectangle { return (*C.Rectangle)(unsafe.Pointer(r)) } -// NewRectangle - Returns new Rectangle -func NewRectangle(x, y, width, height int32) Rectangle { - return Rectangle{x, y, width, height} -} - // newRectangleFromPointer - Returns new Rectangle from pointer func newRectangleFromPointer(ptr unsafe.Pointer) Rectangle { return *(*Rectangle)(ptr) } -// Camera type, defines a camera position/orientation in 3d space -type Camera struct { - // Camera position - Position Vector3 - // Camera target it looks-at - Target Vector3 - // Camera up vector (rotation over its axis) - Up Vector3 - // Camera field-of-view apperture in Y (degrees) - Fovy float32 -} - +// cptr returns C pointer func (c *Camera) cptr() *C.Camera { return (*C.Camera)(unsafe.Pointer(c)) } -// NewCamera - Returns new Camera -func NewCamera(pos, target, up Vector3, fovy float32) Camera { - return Camera{pos, target, up, fovy} -} - // newCameraFromPointer - Returns new Camera from pointer func newCameraFromPointer(ptr unsafe.Pointer) Camera { return *(*Camera)(ptr) } -// Camera2D type, defines a 2d camera -type Camera2D struct { - // Camera offset (displacement from target) - Offset Vector2 - // Camera target (rotation and zoom origin) - Target Vector2 - // Camera rotation in degrees - Rotation float32 - // Camera zoom (scaling), should be 1.0f by default - Zoom float32 -} - +// cptr returns C pointer func (c *Camera2D) cptr() *C.Camera2D { return (*C.Camera2D)(unsafe.Pointer(c)) } -// NewCamera2D - Returns new Camera2D -func NewCamera2D(offset, target Vector2, rotation, zoom float32) Camera2D { - return Camera2D{offset, target, rotation, zoom} -} - // newCamera2DFromPointer - Returns new Camera2D from pointer func newCamera2DFromPointer(ptr unsafe.Pointer) Camera2D { return *(*Camera2D)(ptr) } -// BoundingBox type -type BoundingBox struct { - // Minimum vertex box-corner - Min Vector3 - // Maximum vertex box-corner - Max Vector3 -} - +// cptr returns C pointer func (b *BoundingBox) cptr() *C.BoundingBox { return (*C.BoundingBox)(unsafe.Pointer(b)) } -// NewBoundingBox - Returns new BoundingBox -func NewBoundingBox(min, max Vector3) BoundingBox { - return BoundingBox{min, max} -} - // newBoundingBoxFromPointer - Returns new BoundingBox from pointer func newBoundingBoxFromPointer(ptr unsafe.Pointer) BoundingBox { return *(*BoundingBox)(ptr) } -// Asset file -type Asset interface { - io.ReadSeeker - io.Closer -} - // CloseWindow - Close Window and Terminate Context func CloseWindow() { C.CloseWindow() diff --git a/raylib/gestures.go b/raylib/gestures.go index 0742751..efe34ba 100644 --- a/raylib/gestures.go +++ b/raylib/gestures.go @@ -6,25 +6,6 @@ package raylib import "C" import "unsafe" -// Gestures type -type Gestures int32 - -// Gestures types -// NOTE: It could be used as flags to enable only some gestures -const ( - GestureNone Gestures = 0 - GestureTap Gestures = 1 - GestureDoubletap Gestures = 2 - GestureHold Gestures = 4 - GestureDrag Gestures = 8 - GestureSwipeRight Gestures = 16 - GestureSwipeLeft Gestures = 32 - GestureSwipeUp Gestures = 64 - GestureSwipeDown Gestures = 128 - GesturePinchIn Gestures = 256 - GesturePinchOut Gestures = 512 -) - // SetGesturesEnabled - Enable a set of gestures using flags func SetGesturesEnabled(gestureFlags uint32) { cgestureFlags := (C.uint)(gestureFlags) diff --git a/raylib/models.go b/raylib/models.go index 03a1605..c9b3e19 100644 --- a/raylib/models.go +++ b/raylib/models.go @@ -7,189 +7,41 @@ package raylib import "C" import "unsafe" -// Shader location point type -const ( - LocVertexPosition = iota - LocVertexTexcoord01 - LocVertexTexcoord02 - LocVertexNormal - LocVertexTangent - LocVertexColor - LocMatrixMvp - LocMatrixModel - LocMatrixView - LocMatrixProjection - LocVectorView - LocColorDiffuse - LocColorSpecular - LocColorAmbient - LocMapAlbedo - LocMapMetalness - LocMapNormal - LocMapRoughness - LocMapOccusion - LocMapEmission - LocMapHeight - LocMapCubemap - LocMapIrradiance - LocMapPrefilter - LocMapBrdf -) - -// Material map type -const ( - // MapDiffuse - MapAlbedo = iota - MapMetalness - MapNormal - MapRoughness - MapOcclusion - MapEmission - MapHeight - // NOTE: Uses GL_TEXTURE_CUBE_MAP - MapCubemap - // NOTE: Uses GL_TEXTURE_CUBE_MAP - MapIrradiance - // NOTE: Uses GL_TEXTURE_CUBE_MAP - MapPrefilter - MapBrdf -) - -// Material map type -const ( - MapDiffuse = MapAlbedo - MapSpecular = MapMetalness - LocMapDiffuse = LocMapAlbedo - LocMapSpecular = LocMapMetalness -) - -// Shader and material limits -const ( - // Maximum number of predefined locations stored in shader struct - MaxShaderLocations = 32 - // Maximum number of texture maps stored in shader struct - MaxMaterialMaps = 12 -) - -// Mesh - Vertex data definning a mesh -type Mesh struct { - // Number of vertices stored in arrays - VertexCount int32 - // Number of triangles stored (indexed or not) - TriangleCount int32 - // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) - Vertices *[]float32 - // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) - Texcoords *[]float32 - // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) - Texcoords2 *[]float32 - // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) - Normals *[]float32 - // Vertex tangents (XYZ - 3 components per vertex) (shader-location = 4) - Tangents *[]float32 - // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) - Colors *[]uint8 - // Vertex indices (in case vertex data comes indexed) - Indices *[]uint16 - // OpenGL Vertex Array Object id - VaoID uint32 - // OpenGL Vertex Buffer Objects id (7 types of vertex data) - VboID [7]uint32 -} - +// cptr returns C pointer func (m *Mesh) cptr() *C.Mesh { return (*C.Mesh)(unsafe.Pointer(m)) } -// NewMesh - Returns new Mesh -func NewMesh(vertexCount, triangleCount int32, vertices, texcoords, texcoords2, normals, tangents *[]float32, colors *[]uint8, indices *[]uint16, vaoID uint32, vboID [7]uint32) Mesh { - return Mesh{vertexCount, triangleCount, vertices, texcoords, texcoords2, normals, tangents, colors, indices, vaoID, vboID} -} - // newMeshFromPointer - Returns new Mesh from pointer func newMeshFromPointer(ptr unsafe.Pointer) Mesh { return *(*Mesh)(ptr) } -// Material type -type Material struct { - // Shader - Shader Shader - // Maps - Maps [MaxMaterialMaps]MaterialMap - // Padding - _ [4]byte - // Generic parameters (if required) - Params *[]float32 -} - +// cptr returns C pointer func (m *Material) cptr() *C.Material { return (*C.Material)(unsafe.Pointer(m)) } -// NewMaterial - Returns new Material -func NewMaterial(shader Shader, maps [MaxMaterialMaps]MaterialMap, params *[]float32) Material { - return Material{shader, maps, [4]byte{}, params} -} - // newMaterialFromPointer - Returns new Material from pointer func newMaterialFromPointer(ptr unsafe.Pointer) Material { return *(*Material)(ptr) } -// MaterialMap type -type MaterialMap struct { - // Texture - Texture Texture2D - // Color - Color Color - // Value - Value float32 -} - -// Model type -type Model struct { - // Vertex data buffers (RAM and VRAM) - Mesh Mesh - // Local transform matrix - Transform Matrix - // Shader and textures data - Material Material - // Padding - _ [4]byte -} - +// cptr returns C pointer func (m *Model) cptr() *C.Model { return (*C.Model)(unsafe.Pointer(m)) } -// NewModel - Returns new Model -func NewModel(mesh Mesh, transform Matrix, material Material) Model { - return Model{mesh, transform, material, [4]byte{}} -} - // newModelFromPointer - Returns new Model from pointer func newModelFromPointer(ptr unsafe.Pointer) Model { return *(*Model)(ptr) } -// Ray type (useful for raycast) -type Ray struct { - // Ray position (origin) - Position Vector3 - // Ray direction - Direction Vector3 -} - +// cptr returns C pointer func (r *Ray) cptr() *C.Ray { return (*C.Ray)(unsafe.Pointer(r)) } -// NewRay - Returns new Ray -func NewRay(position, direction Vector3) Ray { - return Ray{position, direction} -} - // newRayFromPointer - Returns new Ray from pointer func newRayFromPointer(ptr unsafe.Pointer) Ray { return *(*Ray)(ptr) diff --git a/raylib/raylib.go b/raylib/raylib.go index 1c65186..7fd5cac 100644 --- a/raylib/raylib.go +++ b/raylib/raylib.go @@ -36,10 +36,905 @@ Example: package raylib import ( + "image" + "io" "runtime" + "unsafe" ) func init() { // Make sure the main goroutine is bound to the main thread. runtime.LockOSThread() } + +// Wave type, defines audio wave data +type Wave struct { + // Number of samples + SampleCount uint32 + // Frequency (samples per second) + SampleRate uint32 + // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + SampleSize uint32 + // Number of channels (1-mono, 2-stereo) + Channels uint32 + // Buffer data pointer + data unsafe.Pointer +} + +// NewWave - Returns new Wave +func NewWave(sampleCount, sampleRate, sampleSize, channels uint32, data []byte) Wave { + d := unsafe.Pointer(&data[0]) + return Wave{sampleCount, sampleRate, sampleSize, channels, d} +} + +// Sound source type +type Sound struct { + // Audio source id + Source uint32 + // Audio buffer id + Buffer uint32 + // Audio format specifier + Format int32 +} + +// NewSound - Returns new Sound +func NewSound(source, buffer uint32, format int32) Sound { + return Sound{source, buffer, format} +} + +// Music type (file streaming from memory) +// NOTE: Anything longer than ~10 seconds should be streamed +type Music struct { + CtxType uint32 + _ [4]byte + ctxOgg unsafe.Pointer + ctxFlac unsafe.Pointer + ctxXm unsafe.Pointer + ctxMod unsafe.Pointer + Stream AudioStream + LoopCount int32 + TotalSamples uint32 + SamplesLeft uint32 +} + +// AudioStream type +// NOTE: Useful to create custom audio streams not bound to a specific file +type AudioStream struct { + // Frequency (samples per second) + SampleRate uint32 + // Bit depth (bits per sample): 8, 16, 32 (24 not supported) + SampleSize uint32 + // Number of channels (1-mono, 2-stereo) + Channels uint32 + // Audio format specifier + Format int32 + // Audio source id + Source uint32 + // Audio buffers (double buffering) + Buffers [2]uint32 +} + +// NewAudioStream - Returns new AudioStream +func NewAudioStream(sampleRate, sampleSize, channels uint32, format int32, source uint32, buffers [2]uint32) AudioStream { + return AudioStream{sampleRate, sampleSize, channels, format, source, buffers} +} + +// CameraMode type +type CameraMode int32 + +// Camera system modes +const ( + CameraCustom CameraMode = iota + CameraFree + CameraOrbital + CameraFirstPerson + CameraThirdPerson +) + +// Some basic Defines +const ( + Pi = 3.1415927 + Deg2rad = 0.017453292 + Rad2deg = 57.295776 + + // Raylib Config Flags + + // Set to show raylib logo at startup + FlagShowLogo = 1 + // Set to run program in fullscreen + FlagFullscreenMode = 2 + // Set to allow resizable window + FlagWindowResizable = 4 + // Set to show window decoration (frame and buttons) + FlagWindowDecorated = 8 + // Set to allow transparent window + FlagWindowTransparent = 16 + // Set to try enabling MSAA 4X + FlagMsaa4xHint = 32 + // Set to try enabling V-Sync on GPU + FlagVsyncHint = 64 + + // Keyboard Function Keys + KeySpace = 32 + KeyEscape = 256 + KeyEnter = 257 + KeyBackspace = 259 + KeyRight = 262 + KeyLeft = 263 + KeyDown = 264 + KeyUp = 265 + KeyF1 = 290 + KeyF2 = 291 + KeyF3 = 292 + KeyF4 = 293 + KeyF5 = 294 + KeyF6 = 295 + KeyF7 = 296 + KeyF8 = 297 + KeyF9 = 298 + KeyF10 = 299 + KeyF11 = 300 + KeyF12 = 301 + KeyLeftShift = 340 + KeyLeftControl = 341 + KeyLeftAlt = 342 + KeyRightShift = 344 + KeyRightControl = 345 + KeyRightAlt = 346 + + // Keyboard Alpha Numeric Keys + KeyZero = 48 + KeyOne = 49 + KeyTwo = 50 + KeyThree = 51 + KeyFour = 52 + KeyFive = 53 + KeySix = 54 + KeySeven = 55 + KeyEight = 56 + KeyNine = 57 + KeyA = 65 + KeyB = 66 + KeyC = 67 + KeyD = 68 + KeyE = 69 + KeyF = 70 + KeyG = 71 + KeyH = 72 + KeyI = 73 + KeyJ = 74 + KeyK = 75 + KeyL = 76 + KeyM = 77 + KeyN = 78 + KeyO = 79 + KeyP = 80 + KeyQ = 81 + KeyR = 82 + KeyS = 83 + KeyT = 84 + KeyU = 85 + KeyV = 86 + KeyW = 87 + KeyX = 88 + KeyY = 89 + KeyZ = 90 + + // Android keys + KeyBack = 4 + KeyMenu = 82 + KeyVolumeUp = 24 + KeyVolumeDown = 25 + + // Mouse Buttons + MouseLeftButton = 0 + MouseRightButton = 1 + MouseMiddleButton = 2 + + // Touch points registered + MaxTouchPoints = 2 + + // Gamepad Number + GamepadPlayer1 = 0 + GamepadPlayer2 = 1 + GamepadPlayer3 = 2 + GamepadPlayer4 = 3 + + // Gamepad Buttons/Axis + + // PS3 USB Controller Buttons + GamepadPs3ButtonTriangle = 0 + GamepadPs3ButtonCircle = 1 + GamepadPs3ButtonCross = 2 + GamepadPs3ButtonSquare = 3 + GamepadPs3ButtonL1 = 6 + GamepadPs3ButtonR1 = 7 + GamepadPs3ButtonL2 = 4 + GamepadPs3ButtonR2 = 5 + GamepadPs3ButtonStart = 8 + GamepadPs3ButtonSelect = 9 + GamepadPs3ButtonUp = 24 + GamepadPs3ButtonRight = 25 + GamepadPs3ButtonDown = 26 + GamepadPs3ButtonLeft = 27 + GamepadPs3ButtonPs = 12 + + // PS3 USB Controller Axis + GamepadPs3AxisLeftX = 0 + GamepadPs3AxisLeftY = 1 + GamepadPs3AxisRightX = 2 + GamepadPs3AxisRightY = 5 + // [1..-1] (pressure-level) + GamepadPs3AxisL2 = 3 + // [1..-1] (pressure-level) + GamepadPs3AxisR2 = 4 + + // Xbox360 USB Controller Buttons + GamepadXboxButtonA = 0 + GamepadXboxButtonB = 1 + GamepadXboxButtonX = 2 + GamepadXboxButtonY = 3 + GamepadXboxButtonLb = 4 + GamepadXboxButtonRb = 5 + GamepadXboxButtonSelect = 6 + GamepadXboxButtonStart = 7 + GamepadXboxButtonUp = 10 + GamepadXboxButtonRight = 11 + GamepadXboxButtonDown = 12 + GamepadXboxButtonLeft = 13 + GamepadXboxButtonHome = 8 + + // Xbox360 USB Controller Axis + // [-1..1] (left->right) + GamepadXboxAxisLeftX = 0 + // [1..-1] (up->down) + GamepadXboxAxisLeftY = 1 + // [-1..1] (left->right) + GamepadXboxAxisRightX = 2 + // [1..-1] (up->down) + GamepadXboxAxisRightY = 3 + // [-1..1] (pressure-level) + GamepadXboxAxisLt = 4 + // [-1..1] (pressure-level) + GamepadXboxAxisRt = 5 +) + +// Some Basic Colors +// NOTE: Custom raylib color palette for amazing visuals on WHITE background +var ( + // Light Gray + LightGray = NewColor(200, 200, 200, 255) + // Gray + Gray = NewColor(130, 130, 130, 255) + // Dark Gray + DarkGray = NewColor(80, 80, 80, 255) + // Yellow + Yellow = NewColor(253, 249, 0, 255) + // Gold + Gold = NewColor(255, 203, 0, 255) + // Orange + Orange = NewColor(255, 161, 0, 255) + // Pink + Pink = NewColor(255, 109, 194, 255) + // Red + Red = NewColor(230, 41, 55, 255) + // Maroon + Maroon = NewColor(190, 33, 55, 255) + // Green + Green = NewColor(0, 228, 48, 255) + // Lime + Lime = NewColor(0, 158, 47, 255) + // Dark Green + DarkGreen = NewColor(0, 117, 44, 255) + // Sky Blue + SkyBlue = NewColor(102, 191, 255, 255) + // Blue + Blue = NewColor(0, 121, 241, 255) + // Dark Blue + DarkBlue = NewColor(0, 82, 172, 255) + // Purple + Purple = NewColor(200, 122, 255, 255) + // Violet + Violet = NewColor(135, 60, 190, 255) + // Dark Purple + DarkPurple = NewColor(112, 31, 126, 255) + // Beige + Beige = NewColor(211, 176, 131, 255) + // Brown + Brown = NewColor(127, 106, 79, 255) + // Dark Brown + DarkBrown = NewColor(76, 63, 47, 255) + // White + White = NewColor(255, 255, 255, 255) + // Black + Black = NewColor(0, 0, 0, 255) + // Blank (Transparent) + Blank = NewColor(0, 0, 0, 0) + // Magenta + Magenta = NewColor(255, 0, 255, 255) + // Ray White (RayLib Logo White) + RayWhite = NewColor(245, 245, 245, 255) +) + +// Vector2 type +type Vector2 struct { + X float32 + Y float32 +} + +// NewVector2 - Returns new Vector2 +func NewVector2(x, y float32) Vector2 { + return Vector2{x, y} +} + +// Vector3 type +type Vector3 struct { + X float32 + Y float32 + Z float32 +} + +// NewVector3 - Returns new Vector3 +func NewVector3(X, Y, Z float32) Vector3 { + return Vector3{X, Y, Z} +} + +// Matrix type (OpenGL style 4x4 - right handed, column major) +type Matrix struct { + M0, M4, M8, M12 float32 + M1, M5, M9, M13 float32 + M2, M6, M10, M14 float32 + M3, M7, M11, M15 float32 +} + +// NewMatrix - Returns new Matrix +func NewMatrix(m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, m15 float32) Matrix { + return Matrix{m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, m15} +} + +// Mat2 type (used for polygon shape rotation matrix) +type Mat2 struct { + M00 float32 + M01 float32 + M10 float32 + M11 float32 +} + +// NewMat2 - Returns new Mat2 +func NewMat2(m0, m1, m10, m11 float32) Mat2 { + return Mat2{m0, m1, m10, m11} +} + +// Quaternion type +type Quaternion struct { + X float32 + Y float32 + Z float32 + W float32 +} + +// NewQuaternion - Returns new Quaternion +func NewQuaternion(x, y, z, w float32) Quaternion { + return Quaternion{x, y, z, w} +} + +// Color type, RGBA (32bit) +type Color struct { + R uint8 + G uint8 + B uint8 + A uint8 +} + +// NewColor - Returns new Color +func NewColor(r, g, b, a uint8) Color { + return Color{r, g, b, a} +} + +// Rectangle type +type Rectangle struct { + X int32 + Y int32 + Width int32 + Height int32 +} + +// NewRectangle - Returns new Rectangle +func NewRectangle(x, y, width, height int32) Rectangle { + return Rectangle{x, y, width, height} +} + +// Camera type, defines a camera position/orientation in 3d space +type Camera struct { + // Camera position + Position Vector3 + // Camera target it looks-at + Target Vector3 + // Camera up vector (rotation over its axis) + Up Vector3 + // Camera field-of-view apperture in Y (degrees) + Fovy float32 +} + +// NewCamera - Returns new Camera +func NewCamera(pos, target, up Vector3, fovy float32) Camera { + return Camera{pos, target, up, fovy} +} + +// Camera2D type, defines a 2d camera +type Camera2D struct { + // Camera offset (displacement from target) + Offset Vector2 + // Camera target (rotation and zoom origin) + Target Vector2 + // Camera rotation in degrees + Rotation float32 + // Camera zoom (scaling), should be 1.0f by default + Zoom float32 +} + +// NewCamera2D - Returns new Camera2D +func NewCamera2D(offset, target Vector2, rotation, zoom float32) Camera2D { + return Camera2D{offset, target, rotation, zoom} +} + +// BoundingBox type +type BoundingBox struct { + // Minimum vertex box-corner + Min Vector3 + // Maximum vertex box-corner + Max Vector3 +} + +// NewBoundingBox - Returns new BoundingBox +func NewBoundingBox(min, max Vector3) BoundingBox { + return BoundingBox{min, max} +} + +// Asset file +type Asset interface { + io.ReadSeeker + io.Closer +} + +// Gestures type +type Gestures int32 + +// Gestures types +// NOTE: It could be used as flags to enable only some gestures +const ( + GestureNone Gestures = 0 + GestureTap Gestures = 1 + GestureDoubletap Gestures = 2 + GestureHold Gestures = 4 + GestureDrag Gestures = 8 + GestureSwipeRight Gestures = 16 + GestureSwipeLeft Gestures = 32 + GestureSwipeUp Gestures = 64 + GestureSwipeDown Gestures = 128 + GesturePinchIn Gestures = 256 + GesturePinchOut Gestures = 512 +) + +// Shader location point type +const ( + LocVertexPosition = iota + LocVertexTexcoord01 + LocVertexTexcoord02 + LocVertexNormal + LocVertexTangent + LocVertexColor + LocMatrixMvp + LocMatrixModel + LocMatrixView + LocMatrixProjection + LocVectorView + LocColorDiffuse + LocColorSpecular + LocColorAmbient + LocMapAlbedo + LocMapMetalness + LocMapNormal + LocMapRoughness + LocMapOccusion + LocMapEmission + LocMapHeight + LocMapCubemap + LocMapIrradiance + LocMapPrefilter + LocMapBrdf +) + +// Material map type +const ( + // MapDiffuse + MapAlbedo = iota + MapMetalness + MapNormal + MapRoughness + MapOcclusion + MapEmission + MapHeight + // NOTE: Uses GL_TEXTURE_CUBE_MAP + MapCubemap + // NOTE: Uses GL_TEXTURE_CUBE_MAP + MapIrradiance + // NOTE: Uses GL_TEXTURE_CUBE_MAP + MapPrefilter + MapBrdf +) + +// Material map type +const ( + MapDiffuse = MapAlbedo + MapSpecular = MapMetalness + LocMapDiffuse = LocMapAlbedo + LocMapSpecular = LocMapMetalness +) + +// Shader and material limits +const ( + // Maximum number of predefined locations stored in shader struct + MaxShaderLocations = 32 + // Maximum number of texture maps stored in shader struct + MaxMaterialMaps = 12 +) + +// Mesh - Vertex data definning a mesh +type Mesh struct { + // Number of vertices stored in arrays + VertexCount int32 + // Number of triangles stored (indexed or not) + TriangleCount int32 + // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + Vertices *[]float32 + // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + Texcoords *[]float32 + // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) + Texcoords2 *[]float32 + // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) + Normals *[]float32 + // Vertex tangents (XYZ - 3 components per vertex) (shader-location = 4) + Tangents *[]float32 + // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) + Colors *[]uint8 + // Vertex indices (in case vertex data comes indexed) + Indices *[]uint16 + // OpenGL Vertex Array Object id + VaoID uint32 + // OpenGL Vertex Buffer Objects id (7 types of vertex data) + VboID [7]uint32 +} + +// NewMesh - Returns new Mesh +func NewMesh(vertexCount, triangleCount int32, vertices, texcoords, texcoords2, normals, tangents *[]float32, colors *[]uint8, indices *[]uint16, vaoID uint32, vboID [7]uint32) Mesh { + return Mesh{vertexCount, triangleCount, vertices, texcoords, texcoords2, normals, tangents, colors, indices, vaoID, vboID} +} + +// Material type +type Material struct { + // Shader + Shader Shader + // Maps + Maps [MaxMaterialMaps]MaterialMap + // Padding + _ [4]byte + // Generic parameters (if required) + Params *[]float32 +} + +// NewMaterial - Returns new Material +func NewMaterial(shader Shader, maps [MaxMaterialMaps]MaterialMap, params *[]float32) Material { + return Material{shader, maps, [4]byte{}, params} +} + +// MaterialMap type +type MaterialMap struct { + // Texture + Texture Texture2D + // Color + Color Color + // Value + Value float32 +} + +// Model type +type Model struct { + // Vertex data buffers (RAM and VRAM) + Mesh Mesh + // Local transform matrix + Transform Matrix + // Shader and textures data + Material Material + // Padding + _ [4]byte +} + +// NewModel - Returns new Model +func NewModel(mesh Mesh, transform Matrix, material Material) Model { + return Model{mesh, transform, material, [4]byte{}} +} + +// Ray type (useful for raycast) +type Ray struct { + // Ray position (origin) + Position Vector3 + // Ray direction + Direction Vector3 +} + +// NewRay - Returns new Ray +func NewRay(position, direction Vector3) Ray { + return Ray{position, direction} +} + +// VrDevice type +type VrDevice int32 + +// Head Mounted Display devices +const ( + HmdDefaultDevice VrDevice = iota + HmdOculusRiftDk2 + HmdOculusRiftCv1 + HmdOculusGo + HmdValveHtcVive + HmdSonyPsvr +) + +// VrDeviceInfo - Head-Mounted-Display device parameters +type VrDeviceInfo struct { + // HMD horizontal resolution in pixels + hResolution int + // HMD vertical resolution in pixels + vResolution int + // HMD horizontal size in meters + hScreenSize float32 + // HMD vertical size in meters + vScreenSize float32 + // HMD screen center in meters + vScreenCenter float32 + // HMD distance between eye and display in meters + eyeToScreenDistance float32 + // HMD lens separation distance in meters + lensSeparationDistance float32 + // HMD IPD (distance between pupils) in meters + interpupillaryDistance float32 + // HMD lens distortion constant parameters + lensDistortionValues [4]float32 + // HMD chromatic aberration correction parameters + chromaAbCorrection [4]float32 +} + +// NewVrDeviceInfo - Returns new VrDeviceInfo +func NewVrDeviceInfo(hResolution, vResolution int, hScreenSize, vScreenSize, vScreenCenter, eyeToScreenDistance, + lensSeparationDistance, interpupillaryDistance float32, lensDistortionValues, chromaAbCorrection [4]float32) VrDeviceInfo { + + return VrDeviceInfo{hResolution, vResolution, hScreenSize, vScreenSize, vScreenCenter, eyeToScreenDistance, + lensSeparationDistance, interpupillaryDistance, lensDistortionValues, chromaAbCorrection} +} + +// BlendMode type +type BlendMode int32 + +// Color blending modes (pre-defined) +const ( + BlendAlpha BlendMode = iota + BlendAdditive + BlendMultiplied +) + +// Shader type (generic shader) +type Shader struct { + // Shader program id + ID uint32 + // Shader locations array + Locs [MaxShaderLocations]int32 +} + +// NewShader - Returns new Shader +func NewShader(id uint32, locs [MaxShaderLocations]int32) Shader { + return Shader{id, locs} +} + +// CharInfo - SpriteFont character info +type CharInfo struct { + // Character value (Unicode) + Value int32 + // Character rectangle in sprite font + Rec Rectangle + // Character offset X when drawing + OffsetX int32 + // Character offset Y when drawing + OffsetY int32 + // Character advance position X + AdvanceX int32 +} + +// NewCharInfo - Returns new SpriteFont +func NewCharInfo(value int32, rec Rectangle, offsetX, offsetY, advanceX int32) CharInfo { + return CharInfo{value, rec, offsetX, offsetY, advanceX} +} + +// SpriteFont type, includes texture and charSet array data +type SpriteFont struct { + // Font texture + Texture Texture2D + // Base size (default chars height) + BaseSize int32 + // Number of characters + CharsCount int32 + // Characters info data + Chars *CharInfo +} + +// NewSpriteFont - Returns new SpriteFont +func NewSpriteFont(texture Texture2D, baseSize, charsCount int32, chars *CharInfo) SpriteFont { + return SpriteFont{texture, baseSize, charsCount, chars} +} + +// TextureFormat - Texture format +type TextureFormat int32 + +// Texture formats +// NOTE: Support depends on OpenGL version and platform +const ( + // 8 bit per pixel (no alpha) + UncompressedGrayscale TextureFormat = iota + 1 + // 16 bpp (2 channels) + UncompressedGrayAlpha + // 16 bpp + UncompressedR5g6b5 + // 24 bpp + UncompressedR8g8b8 + // 16 bpp (1 bit alpha) + UncompressedR5g5b5a1 + // 16 bpp (4 bit alpha) + UncompressedR4g4b4a4 + // 32 bpp + UncompressedR8g8b8a8 + // 4 bpp (no alpha) + CompressedDxt1Rgb + // 4 bpp (1 bit alpha) + CompressedDxt1Rgba + // 8 bpp + CompressedDxt3Rgba + // 8 bpp + CompressedDxt5Rgba + // 4 bpp + CompressedEtc1Rgb + // 4 bpp + CompressedEtc2Rgb + // 8 bpp + CompressedEtc2EacRgba + // 4 bpp + CompressedPvrtRgb + // 4 bpp + CompressedPvrtRgba + // 8 bpp + CompressedAstc4x4Rgba + // 2 bpp + CompressedAstc8x8Rgba +) + +// TextureFilterMode - Texture filter mode +type TextureFilterMode int32 + +// Texture parameters: filter mode +// NOTE 1: Filtering considers mipmaps if available in the texture +// NOTE 2: Filter is accordingly set for minification and magnification +const ( + // No filter, just pixel aproximation + FilterPoint TextureFilterMode = iota + // Linear filtering + FilterBilinear + // Trilinear filtering (linear with mipmaps) + FilterTrilinear + // Anisotropic filtering 4x + FilterAnisotropic4x + // Anisotropic filtering 8x + FilterAnisotropic8x + // Anisotropic filtering 16x + FilterAnisotropic16x +) + +// TextureWrapMode - Texture wrap mode +type TextureWrapMode int32 + +// Texture parameters: wrap mode +const ( + WrapRepeat TextureWrapMode = iota + WrapClamp + WrapMirror +) + +// Image type, bpp always RGBA (32bit) +// NOTE: Data stored in CPU memory (RAM) +type Image struct { + // Image raw data + data unsafe.Pointer + // Image base width + Width int32 + // Image base height + Height int32 + // Mipmap levels, 1 by default + Mipmaps int32 + // Data format (TextureFormat) + Format TextureFormat +} + +// ToImage converts a Image to Go image.Image +func (i *Image) ToImage() image.Image { + img := image.NewRGBA(image.Rect(0, 0, int(i.Width), int(i.Height))) + + // Get pixel data from image (RGBA 32bit) + pixels := GetImageData(i) + + img.Pix = pixels + + return img +} + +// NewImage - Returns new Image +func NewImage(data []byte, width, height, mipmaps int32, format TextureFormat) *Image { + d := unsafe.Pointer(&data[0]) + return &Image{d, width, height, mipmaps, format} +} + +// NewImageFromImage - Returns new Image from Go image.Image +func NewImageFromImage(img image.Image) *Image { + size := img.Bounds().Size() + pixels := make([]Color, size.X*size.Y) + + for y := 0; y < size.Y; y++ { + for x := 0; x < size.X; x++ { + color := img.At(x, y) + r, g, b, a := color.RGBA() + pixels[x+y*size.Y] = NewColor(uint8(r), uint8(g), uint8(b), uint8(a)) + } + } + + return LoadImageEx(pixels, int32(size.X), int32(size.Y)) +} + +// Texture2D type, bpp always RGBA (32bit) +// NOTE: Data stored in GPU memory +type Texture2D struct { + // OpenGL texture id + ID uint32 + // Texture base width + Width int32 + // Texture base height + Height int32 + // Mipmap levels, 1 by default + Mipmaps int32 + // Data format (TextureFormat) + Format TextureFormat +} + +// NewTexture2D - Returns new Texture2D +func NewTexture2D(id uint32, width, height, mipmaps int32, format TextureFormat) Texture2D { + return Texture2D{id, width, height, mipmaps, format} +} + +// RenderTexture2D type, for texture rendering +type RenderTexture2D struct { + // Render texture (fbo) id + ID uint32 + // Color buffer attachment texture + Texture Texture2D + // Depth buffer attachment texture + Depth Texture2D +} + +// NewRenderTexture2D - Returns new RenderTexture2D +func NewRenderTexture2D(id uint32, texture, depth Texture2D) RenderTexture2D { + return RenderTexture2D{id, texture, depth} +} + +// Log message types +const ( + LogInfo = iota + LogWarning + LogError + LogDebug +) diff --git a/raylib/shaders.go b/raylib/shaders.go index ae36c2c..ae11b6c 100644 --- a/raylib/shaders.go +++ b/raylib/shaders.go @@ -8,87 +8,21 @@ import "C" import "unsafe" import "reflect" -// VrDevice type -type VrDevice int32 - -// Head Mounted Display devices -const ( - HmdDefaultDevice VrDevice = iota - HmdOculusRiftDk2 - HmdOculusRiftCv1 - HmdOculusGo - HmdValveHtcVive - HmdSonyPsvr -) - -// VrDeviceInfo - Head-Mounted-Display device parameters -type VrDeviceInfo struct { - // HMD horizontal resolution in pixels - hResolution int - // HMD vertical resolution in pixels - vResolution int - // HMD horizontal size in meters - hScreenSize float32 - // HMD vertical size in meters - vScreenSize float32 - // HMD screen center in meters - vScreenCenter float32 - // HMD distance between eye and display in meters - eyeToScreenDistance float32 - // HMD lens separation distance in meters - lensSeparationDistance float32 - // HMD IPD (distance between pupils) in meters - interpupillaryDistance float32 - // HMD lens distortion constant parameters - lensDistortionValues [4]float32 - // HMD chromatic aberration correction parameters - chromaAbCorrection [4]float32 -} - +// cptr returns C pointer func (v *VrDeviceInfo) cptr() *C.VrDeviceInfo { return (*C.VrDeviceInfo)(unsafe.Pointer(v)) } -// NewVrDeviceInfo - Returns new VrDeviceInfo -func NewVrDeviceInfo(hResolution, vResolution int, hScreenSize, vScreenSize, vScreenCenter, eyeToScreenDistance, - lensSeparationDistance, interpupillaryDistance float32, lensDistortionValues, chromaAbCorrection [4]float32) VrDeviceInfo { - - return VrDeviceInfo{hResolution, vResolution, hScreenSize, vScreenSize, vScreenCenter, eyeToScreenDistance, - lensSeparationDistance, interpupillaryDistance, lensDistortionValues, chromaAbCorrection} -} - // newVrDeviceInfoFromPointer - Returns new VrDeviceInfo from pointer func newVrDeviceInfoFromPointer(ptr unsafe.Pointer) VrDeviceInfo { return *(*VrDeviceInfo)(ptr) } -// BlendMode type -type BlendMode int32 - -// Color blending modes (pre-defined) -const ( - BlendAlpha BlendMode = iota - BlendAdditive - BlendMultiplied -) - -// Shader type (generic shader) -type Shader struct { - // Shader program id - ID uint32 - // Shader locations array - Locs [MaxShaderLocations]int32 -} - +// cptr returns C pointer func (s *Shader) cptr() *C.Shader { return (*C.Shader)(unsafe.Pointer(s)) } -// NewShader - Returns new Shader -func NewShader(id uint32, locs [MaxShaderLocations]int32) Shader { - return Shader{id, locs} -} - // newShaderFromPointer - Returns new Shader from pointer func newShaderFromPointer(ptr unsafe.Pointer) Shader { return *(*Shader)(ptr) diff --git a/raylib/text.go b/raylib/text.go index 5f7693c..94affec 100644 --- a/raylib/text.go +++ b/raylib/text.go @@ -7,55 +7,21 @@ package raylib import "C" import "unsafe" -// CharInfo - SpriteFont character info -type CharInfo struct { - // Character value (Unicode) - Value int32 - // Character rectangle in sprite font - Rec Rectangle - // Character offset X when drawing - OffsetX int32 - // Character offset Y when drawing - OffsetY int32 - // Character advance position X - AdvanceX int32 -} - +// cptr returns C pointer func (c *CharInfo) cptr() *C.CharInfo { return (*C.CharInfo)(unsafe.Pointer(c)) } -// NewCharInfo - Returns new SpriteFont -func NewCharInfo(value int32, rec Rectangle, offsetX, offsetY, advanceX int32) CharInfo { - return CharInfo{value, rec, offsetX, offsetY, advanceX} -} - // newCharInfoFromPointer - Returns new SpriteFont from pointer func newCharInfoFromPointer(ptr unsafe.Pointer) CharInfo { return *(*CharInfo)(ptr) } -// SpriteFont type, includes texture and charSet array data -type SpriteFont struct { - // Font texture - Texture Texture2D - // Base size (default chars height) - BaseSize int32 - // Number of characters - CharsCount int32 - // Characters info data - Chars *CharInfo -} - +// cptr returns C pointer func (s *SpriteFont) cptr() *C.SpriteFont { return (*C.SpriteFont)(unsafe.Pointer(s)) } -// NewSpriteFont - Returns new SpriteFont -func NewSpriteFont(texture Texture2D, baseSize, charsCount int32, chars *CharInfo) SpriteFont { - return SpriteFont{texture, baseSize, charsCount, chars} -} - // newSpriteFontFromPointer - Returns new SpriteFont from pointer func newSpriteFontFromPointer(ptr unsafe.Pointer) SpriteFont { return *(*SpriteFont)(ptr) diff --git a/raylib/textures.go b/raylib/textures.go index 05c23bc..3141eab 100644 --- a/raylib/textures.go +++ b/raylib/textures.go @@ -7,191 +7,34 @@ package raylib import "C" import ( - "image" "unsafe" ) -// TextureFormat - Texture format -type TextureFormat int32 - -// Texture formats -// NOTE: Support depends on OpenGL version and platform -const ( - // 8 bit per pixel (no alpha) - UncompressedGrayscale TextureFormat = iota + 1 - // 16 bpp (2 channels) - UncompressedGrayAlpha - // 16 bpp - UncompressedR5g6b5 - // 24 bpp - UncompressedR8g8b8 - // 16 bpp (1 bit alpha) - UncompressedR5g5b5a1 - // 16 bpp (4 bit alpha) - UncompressedR4g4b4a4 - // 32 bpp - UncompressedR8g8b8a8 - // 4 bpp (no alpha) - CompressedDxt1Rgb - // 4 bpp (1 bit alpha) - CompressedDxt1Rgba - // 8 bpp - CompressedDxt3Rgba - // 8 bpp - CompressedDxt5Rgba - // 4 bpp - CompressedEtc1Rgb - // 4 bpp - CompressedEtc2Rgb - // 8 bpp - CompressedEtc2EacRgba - // 4 bpp - CompressedPvrtRgb - // 4 bpp - CompressedPvrtRgba - // 8 bpp - CompressedAstc4x4Rgba - // 2 bpp - CompressedAstc8x8Rgba -) - -// TextureFilterMode - Texture filter mode -type TextureFilterMode int32 - -// Texture parameters: filter mode -// NOTE 1: Filtering considers mipmaps if available in the texture -// NOTE 2: Filter is accordingly set for minification and magnification -const ( - // No filter, just pixel aproximation - FilterPoint TextureFilterMode = iota - // Linear filtering - FilterBilinear - // Trilinear filtering (linear with mipmaps) - FilterTrilinear - // Anisotropic filtering 4x - FilterAnisotropic4x - // Anisotropic filtering 8x - FilterAnisotropic8x - // Anisotropic filtering 16x - FilterAnisotropic16x -) - -// TextureWrapMode - Texture wrap mode -type TextureWrapMode int32 - -// Texture parameters: wrap mode -const ( - WrapRepeat TextureWrapMode = iota - WrapClamp - WrapMirror -) - -// Image type, bpp always RGBA (32bit) -// NOTE: Data stored in CPU memory (RAM) -type Image struct { - // Image raw data - data unsafe.Pointer - // Image base width - Width int32 - // Image base height - Height int32 - // Mipmap levels, 1 by default - Mipmaps int32 - // Data format (TextureFormat) - Format TextureFormat -} - +// cptr returns C pointer func (i *Image) cptr() *C.Image { return (*C.Image)(unsafe.Pointer(i)) } -// ToImage converts a Image to Go image.Image -func (i *Image) ToImage() image.Image { - img := image.NewRGBA(image.Rect(0, 0, int(i.Width), int(i.Height))) - - // Get pixel data from image (RGBA 32bit) - pixels := GetImageData(i) - - img.Pix = pixels - - return img -} - -// NewImage - Returns new Image -func NewImage(data []byte, width, height, mipmaps int32, format TextureFormat) *Image { - d := unsafe.Pointer(&data[0]) - return &Image{d, width, height, mipmaps, format} -} - // newImageFromPointer - Returns new Image from pointer func newImageFromPointer(ptr unsafe.Pointer) *Image { return (*Image)(ptr) } -// NewImageFromImage - Returns new Image from Go image.Image -func NewImageFromImage(img image.Image) *Image { - size := img.Bounds().Size() - pixels := make([]Color, size.X*size.Y) - - for y := 0; y < size.Y; y++ { - for x := 0; x < size.X; x++ { - color := img.At(x, y) - r, g, b, a := color.RGBA() - pixels[x+y*size.Y] = NewColor(uint8(r), uint8(g), uint8(b), uint8(a)) - } - } - - return LoadImageEx(pixels, int32(size.X), int32(size.Y)) -} - -// Texture2D type, bpp always RGBA (32bit) -// NOTE: Data stored in GPU memory -type Texture2D struct { - // OpenGL texture id - ID uint32 - // Texture base width - Width int32 - // Texture base height - Height int32 - // Mipmap levels, 1 by default - Mipmaps int32 - // Data format (TextureFormat) - Format TextureFormat -} - +// cptr returns C pointer func (t *Texture2D) cptr() *C.Texture2D { return (*C.Texture2D)(unsafe.Pointer(t)) } -// NewTexture2D - Returns new Texture2D -func NewTexture2D(id uint32, width, height, mipmaps int32, format TextureFormat) Texture2D { - return Texture2D{id, width, height, mipmaps, format} -} - // newTexture2DFromPointer - Returns new Texture2D from pointer func newTexture2DFromPointer(ptr unsafe.Pointer) Texture2D { return *(*Texture2D)(ptr) } -// RenderTexture2D type, for texture rendering -type RenderTexture2D struct { - // Render texture (fbo) id - ID uint32 - // Color buffer attachment texture - Texture Texture2D - // Depth buffer attachment texture - Depth Texture2D -} - +// cptr returns C pointer func (r *RenderTexture2D) cptr() *C.RenderTexture2D { return (*C.RenderTexture2D)(unsafe.Pointer(r)) } -// NewRenderTexture2D - Returns new RenderTexture2D -func NewRenderTexture2D(id uint32, texture, depth Texture2D) RenderTexture2D { - return RenderTexture2D{id, texture, depth} -} - // newRenderTexture2DFromPointer - Returns new RenderTexture2D from pointer func newRenderTexture2DFromPointer(ptr unsafe.Pointer) RenderTexture2D { return *(*RenderTexture2D)(ptr) diff --git a/raylib/utils.go b/raylib/utils.go index 761351b..2ca8842 100644 --- a/raylib/utils.go +++ b/raylib/utils.go @@ -7,14 +7,6 @@ import ( "os" ) -// Log message types -const ( - LogInfo = iota - LogWarning - LogError - LogDebug -) - var traceDebugMsgs = false // SetDebug - Set debug messages diff --git a/raylib/utils_android.go b/raylib/utils_android.go index 191a1f4..0863cf0 100644 --- a/raylib/utils_android.go +++ b/raylib/utils_android.go @@ -20,14 +20,6 @@ import ( "unsafe" ) -// Log message types -const ( - LogInfo = iota - LogError - LogWarning - LogDebug -) - var traceDebugMsgs = false // SetDebug - Set debug messages diff --git a/raylib/utils_windows.go b/raylib/utils_windows.go index 7374074..acc6a81 100644 --- a/raylib/utils_windows.go +++ b/raylib/utils_windows.go @@ -7,14 +7,6 @@ import ( "os" ) -// Log message types -const ( - LogInfo = iota - LogError - LogWarning - LogDebug -) - var traceDebugMsgs = false // SetDebug - Set debug messages From 5db89e7c35071bc2777d03626ae49643d25bfcc4 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 7 Dec 2017 20:37:09 +0100 Subject: [PATCH 37/48] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba06017..b82a772 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ On macOS you need Xcode or Command Line Tools for Xcode. ##### Windows -On Windows you need C compiler, like [https://mingw-w64.org](Mingw-w64) or [http://tdm-gcc.tdragon.net/](TDM-GCC). +On Windows you need C compiler, like [Mingw-w64](https://mingw-w64.org) or [TDM-GCC](http://tdm-gcc.tdragon.net/). You can also build binary in [MSYS2](https://msys2.github.io/) shell. ##### Android From 9ddb7731e9fc4c6881d50ca5abbb3e247e845f88 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Thu, 7 Dec 2017 22:09:16 +0100 Subject: [PATCH 38/48] Add GopherJS build constraints --- raylib/audio.c | 2 +- raylib/audio.go | 2 +- raylib/camera.go | 2 +- raylib/cgo.go | 2 ++ raylib/cgo_android.go | 2 +- raylib/cgo_darwin.go | 2 +- raylib/cgo_linux.go | 2 +- raylib/cgo_linux_arm.go | 2 +- raylib/cgo_windows.go | 2 +- raylib/core.c | 2 ++ raylib/core.go | 2 ++ raylib/gestures.go | 2 ++ raylib/mini_al.c | 4 +++- raylib/models.c | 2 ++ raylib/models.go | 2 ++ raylib/platform_android.c | 2 +- raylib/platform_android.go | 2 +- raylib/platform_arm.go | 2 +- raylib/platform_desktop.go | 2 +- raylib/rlgl.c | 2 ++ raylib/shaders.go | 2 ++ raylib/shapes.c | 2 ++ raylib/shapes.go | 2 ++ raylib/text.c | 2 ++ raylib/text.go | 2 ++ raylib/textures.c | 2 ++ raylib/textures.go | 2 ++ raylib/utils.c | 2 ++ raylib/utils.go | 2 +- raylib/utils_android.c | 2 +- raylib/utils_android.go | 2 +- raylib/utils_windows.go | 2 +- 32 files changed, 49 insertions(+), 17 deletions(-) diff --git a/raylib/audio.c b/raylib/audio.c index 5efca74..23a834d 100644 --- a/raylib/audio.c +++ b/raylib/audio.c @@ -1,4 +1,4 @@ -// +build !noaudio +// +build !noaudio,!js /********************************************************************************************** * diff --git a/raylib/audio.go b/raylib/audio.go index 2b7c621..fd5079f 100644 --- a/raylib/audio.go +++ b/raylib/audio.go @@ -1,4 +1,4 @@ -// +build !noaudio +// +build !noaudio,!js package raylib diff --git a/raylib/camera.go b/raylib/camera.go index 960cf7b..e224dcd 100644 --- a/raylib/camera.go +++ b/raylib/camera.go @@ -1,4 +1,4 @@ -// +build !android +// +build !android,!js package raylib diff --git a/raylib/cgo.go b/raylib/cgo.go index 18f493c..3046677 100644 --- a/raylib/cgo.go +++ b/raylib/cgo.go @@ -1,3 +1,5 @@ +// +build !js + package raylib /* diff --git a/raylib/cgo_android.go b/raylib/cgo_android.go index e56b130..50459f5 100644 --- a/raylib/cgo_android.go +++ b/raylib/cgo_android.go @@ -1,4 +1,4 @@ -// +build android +// +build android,!js package raylib diff --git a/raylib/cgo_darwin.go b/raylib/cgo_darwin.go index 8b7bf07..8c96fea 100644 --- a/raylib/cgo_darwin.go +++ b/raylib/cgo_darwin.go @@ -1,4 +1,4 @@ -// +build darwin +// +build darwin,!js package raylib diff --git a/raylib/cgo_linux.go b/raylib/cgo_linux.go index 5297216..2675bc6 100644 --- a/raylib/cgo_linux.go +++ b/raylib/cgo_linux.go @@ -1,4 +1,4 @@ -// +build linux,!arm,!arm64 +// +build linux,!arm,!arm64,!js package raylib diff --git a/raylib/cgo_linux_arm.go b/raylib/cgo_linux_arm.go index 3326212..739d508 100644 --- a/raylib/cgo_linux_arm.go +++ b/raylib/cgo_linux_arm.go @@ -1,4 +1,4 @@ -// +build linux,arm,!android +// +build linux,arm,!android,!js package raylib diff --git a/raylib/cgo_windows.go b/raylib/cgo_windows.go index 6615cc2..4245ba4 100644 --- a/raylib/cgo_windows.go +++ b/raylib/cgo_windows.go @@ -1,4 +1,4 @@ -// +build windows +// +build windows,!js package raylib diff --git a/raylib/core.c b/raylib/core.c index 36b9de6..1d57704 100644 --- a/raylib/core.c +++ b/raylib/core.c @@ -1,3 +1,5 @@ +// +build !js + /********************************************************************************************** * * raylib.core - Basic functions to manage windows, OpenGL context and input on multiple platforms diff --git a/raylib/core.go b/raylib/core.go index 0351705..bcf1e5a 100644 --- a/raylib/core.go +++ b/raylib/core.go @@ -1,3 +1,5 @@ +// +build !js + package raylib /* diff --git a/raylib/gestures.go b/raylib/gestures.go index efe34ba..e1ec0cf 100644 --- a/raylib/gestures.go +++ b/raylib/gestures.go @@ -1,3 +1,5 @@ +// +build !js + package raylib /* diff --git a/raylib/mini_al.c b/raylib/mini_al.c index 7b43785..6475476 100644 --- a/raylib/mini_al.c +++ b/raylib/mini_al.c @@ -1,4 +1,6 @@ +// +build !js + // The implementation of mini_al needs to #include windows.h which means it needs to go into // it's own translation unit. Not doing this will cause conflicts with CloseWindow(), etc. #define MAL_IMPLEMENTATION -#include "mini_al.h" \ No newline at end of file +#include "mini_al.h" diff --git a/raylib/models.c b/raylib/models.c index 4b8a673..1fd3999 100644 --- a/raylib/models.c +++ b/raylib/models.c @@ -1,3 +1,5 @@ +// +build !js + /********************************************************************************************** * * raylib.models - Basic functions to deal with 3d shapes and 3d models diff --git a/raylib/models.go b/raylib/models.go index c9b3e19..7c88250 100644 --- a/raylib/models.go +++ b/raylib/models.go @@ -1,3 +1,5 @@ +// +build !js + package raylib /* diff --git a/raylib/platform_android.c b/raylib/platform_android.c index 8212e0c..021f27f 100644 --- a/raylib/platform_android.c +++ b/raylib/platform_android.c @@ -1,4 +1,4 @@ -// +build android +// +build android,!js #include "_cgo_export.h" diff --git a/raylib/platform_android.go b/raylib/platform_android.go index bba458f..31f3841 100644 --- a/raylib/platform_android.go +++ b/raylib/platform_android.go @@ -1,4 +1,4 @@ -// +build android +// +build android,!js package raylib diff --git a/raylib/platform_arm.go b/raylib/platform_arm.go index 0139729..241d733 100644 --- a/raylib/platform_arm.go +++ b/raylib/platform_arm.go @@ -1,4 +1,4 @@ -// +build !android,arm +// +build !android,arm,!js package raylib diff --git a/raylib/platform_desktop.go b/raylib/platform_desktop.go index 3994cd8..cedc2d4 100644 --- a/raylib/platform_desktop.go +++ b/raylib/platform_desktop.go @@ -1,4 +1,4 @@ -// +build !android,!arm +// +build !android,!arm,!js package raylib diff --git a/raylib/rlgl.c b/raylib/rlgl.c index 6ae7df4..0319a17 100644 --- a/raylib/rlgl.c +++ b/raylib/rlgl.c @@ -1,3 +1,5 @@ +// +build !js + /********************************************************************************************** * * rlgl - raylib OpenGL abstraction layer diff --git a/raylib/shaders.go b/raylib/shaders.go index ae11b6c..61893f9 100644 --- a/raylib/shaders.go +++ b/raylib/shaders.go @@ -1,3 +1,5 @@ +// +build !js + package raylib /* diff --git a/raylib/shapes.c b/raylib/shapes.c index 0b34f92..970b7fe 100644 --- a/raylib/shapes.c +++ b/raylib/shapes.c @@ -1,3 +1,5 @@ +// +build !js + /********************************************************************************************** * * raylib.shapes - Basic functions to draw 2d Shapes and check collisions diff --git a/raylib/shapes.go b/raylib/shapes.go index 5deed7b..8f81df3 100644 --- a/raylib/shapes.go +++ b/raylib/shapes.go @@ -1,3 +1,5 @@ +// +build !js + package raylib /* diff --git a/raylib/text.c b/raylib/text.c index 8db2fc9..5998978 100644 --- a/raylib/text.c +++ b/raylib/text.c @@ -1,3 +1,5 @@ +// +build !js + /********************************************************************************************** * * raylib.text - Basic functions to load SpriteFonts and draw Text diff --git a/raylib/text.go b/raylib/text.go index 94affec..253fd11 100644 --- a/raylib/text.go +++ b/raylib/text.go @@ -1,3 +1,5 @@ +// +build !js + package raylib /* diff --git a/raylib/textures.c b/raylib/textures.c index 090de24..d24a72d 100644 --- a/raylib/textures.c +++ b/raylib/textures.c @@ -1,3 +1,5 @@ +// +build !js + /********************************************************************************************** * * raylib.textures - Basic functions to load and draw Textures (2d) diff --git a/raylib/textures.go b/raylib/textures.go index 3141eab..ab05b50 100644 --- a/raylib/textures.go +++ b/raylib/textures.go @@ -1,3 +1,5 @@ +// +build !js + package raylib /* diff --git a/raylib/utils.c b/raylib/utils.c index 967ed91..5c91f4c 100644 --- a/raylib/utils.c +++ b/raylib/utils.c @@ -1,3 +1,5 @@ +// +build !js + /********************************************************************************************** * * raylib.utils - Some common utility functions diff --git a/raylib/utils.go b/raylib/utils.go index 2ca8842..15f4033 100644 --- a/raylib/utils.go +++ b/raylib/utils.go @@ -1,4 +1,4 @@ -// +build !android,!windows +// +build !android,!windows,!js package raylib diff --git a/raylib/utils_android.c b/raylib/utils_android.c index 9f290f2..26dad23 100644 --- a/raylib/utils_android.c +++ b/raylib/utils_android.c @@ -1,4 +1,4 @@ -// +build android +// +build android,!js #include "_cgo_export.h" #include diff --git a/raylib/utils_android.go b/raylib/utils_android.go index 0863cf0..7f797c9 100644 --- a/raylib/utils_android.go +++ b/raylib/utils_android.go @@ -1,4 +1,4 @@ -// +build android +// +build android,!js package raylib diff --git a/raylib/utils_windows.go b/raylib/utils_windows.go index acc6a81..7814c97 100644 --- a/raylib/utils_windows.go +++ b/raylib/utils_windows.go @@ -1,4 +1,4 @@ -// +build windows +// +build windows,!js package raylib From 6de595f39600dea897754e1849cefbd9bd56a432 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 8 Dec 2017 01:08:50 +0100 Subject: [PATCH 39/48] Generate raylib.js with emscripten --- raylib/external/scripts/emcc-generate-js.sh | 13 +++++++++++++ raylib/raylib_js.go | 5 +++++ 2 files changed, 18 insertions(+) create mode 100755 raylib/external/scripts/emcc-generate-js.sh create mode 100644 raylib/raylib_js.go diff --git a/raylib/external/scripts/emcc-generate-js.sh b/raylib/external/scripts/emcc-generate-js.sh new file mode 100755 index 0000000..be71a38 --- /dev/null +++ b/raylib/external/scripts/emcc-generate-js.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +EMCC_EXPORT="['_InitWindow','_CloseWindow','_WindowShouldClose','_IsWindowMinimized','_ToggleFullscreen','_SetWindowIcon','_SetWindowTitle','_SetWindowPosition','_SetWindowMonitor','_SetWindowMinSize','_GetScreenWidth','_GetScreenHeight','_ShowCursor','_HideCursor','_IsCursorHidden','_EnableCursor','_DisableCursor','_ClearBackground','_BeginDrawing','_EndDrawing','_Begin2dMode','_End2dMode','_Begin3dMode','_End3dMode','_BeginTextureMode','_EndTextureMode','_GetMouseRay','_GetWorldToScreen','_GetCameraMatrix','_SetTargetFPS','_GetFPS','_GetFrameTime','_GetHexValue','_GetColor','_ColorToFloat','_Vector3ToFloat','_MatrixToFloat','_Vector3Zero','_Vector3One','_MatrixIdentity','_ShowLogo','_SetConfigFlags','_TraceLog','_TakeScreenshot','_GetRandomValue','_StorageSaveValue','_StorageLoadValue','_IsKeyPressed','_IsKeyDown','_IsKeyReleased','_IsKeyUp','_GetKeyPressed','_SetExitKey','_IsGamepadAvailable','_IsGamepadName','_IsGamepadButtonPressed','_IsGamepadButtonDown','_IsGamepadButtonReleased','_IsGamepadButtonUp','_GetGamepadButtonPressed','_GetGamepadAxisCount','_GetGamepadAxisMovement','_IsMouseButtonPressed','_IsMouseButtonDown','_IsMouseButtonReleased','_IsMouseButtonUp','_GetMouseX','_GetMouseY','_GetMousePosition','_SetMousePosition','_GetMouseWheelMove','_GetTouchX','_GetTouchY','_GetTouchPosition','_SetGesturesEnabled','_IsGestureDetected','_GetGestureDetected','_GetTouchPointsCount','_GetGestureHoldDuration','_GetGestureDragVector','_GetGestureDragAngle','_GetGesturePinchVector','_GetGesturePinchAngle','_SetCameraMode','_UpdateCamera','_SetCameraPanControl','_SetCameraAltControl','_SetCameraSmoothZoomControl','_SetCameraMoveControls','_DrawPixel','_DrawPixelV','_DrawLine','_DrawLineV','_DrawLineEx','_DrawLineBezier','_DrawCircle','_DrawCircleGradient','_DrawCircleV','_DrawCircleLines','_DrawRectangle','_DrawRectangleRec','_DrawRectanglePro','_DrawRectangleGradientV','_DrawRectangleGradientH','_DrawRectangleGradientEx','_DrawRectangleV','_DrawRectangleLines','_DrawRectangleT','_DrawTriangle','_DrawTriangleLines','_DrawPoly','_DrawPolyEx','_DrawPolyExLines','_CheckCollisionRecs','_CheckCollisionCircles','_CheckCollisionCircleRec','_GetCollisionRec','_CheckCollisionPointRec','_CheckCollisionPointCircle','_CheckCollisionPointTriangle','_LoadImage','_LoadImageEx','_LoadImagePro','_LoadImageRaw','_LoadTexture','_LoadTextureFromImage','_LoadRenderTexture','_UnloadImage','_UnloadTexture','_UnloadRenderTexture','_GetImageData','_GetTextureData','_UpdateTexture','_SaveImageAs','_ImageToPOT','_ImageFormat','_ImageAlphaMask','_ImageDither','_ImageCopy','_ImageCrop','_ImageResize','_ImageResizeNN','_ImageText','_ImageTextEx','_ImageDraw','_ImageDrawText','_ImageDrawTextEx','_ImageFlipVertical','_ImageFlipHorizontal','_ImageColorTint','_ImageColorInvert','_ImageColorGrayscale','_ImageColorContrast','_ImageColorBrightness','_GenImageColor','_GenImageGradientV','_GenImageGradientH','_GenImageGradientRadial','_GenImageChecked','_GenImageWhiteNoise','_GenImagePerlinNoise','_GenImageCellular','_GenTextureMipmaps','_SetTextureFilter','_SetTextureWrap','_DrawTexture','_DrawTextureV','_DrawTextureEx','_DrawTextureRec','_DrawTexturePro','_GetDefaultFont','_LoadSpriteFont','_LoadSpriteFontEx','_UnloadSpriteFont','_DrawFPS','_DrawText','_DrawTextEx','_MeasureText','_MeasureTextEx','_DrawLine3D','_DrawCircle3D','_DrawCube','_DrawCubeV','_DrawCubeWires','_DrawCubeTexture','_DrawSphere','_DrawSphereEx','_DrawSphereWires','_DrawCylinder','_DrawCylinderWires','_DrawPlane','_DrawRay','_DrawGrid','_DrawGizmo','_LoadModel','_LoadModelFromMesh','_UnloadModel','_LoadMesh','_UnloadMesh','_GenMeshPlane','_GenMeshCube','_GenMeshSphere','_GenMeshHemiSphere','_GenMeshCylinder','_GenMeshTorus','_GenMeshKnot','_GenMeshHeightmap','_GenMeshCubicmap','_LoadMaterial','_LoadMaterialDefault','_UnloadMaterial','_DrawModel','_DrawModelEx','_DrawModelWires','_DrawModelWiresEx','_DrawBoundingBox','_DrawBillboard','_DrawBillboardRec','_CalculateBoundingBox','_CheckCollisionSpheres','_CheckCollisionBoxes','_CheckCollisionBoxSphere','_CheckCollisionRaySphere','_CheckCollisionRaySphereEx','_CheckCollisionRayBox','_GetCollisionRayMesh','_GetCollisionRayTriangle','_GetCollisionRayGround','_LoadText','_LoadShader','_UnloadShader','_GetShaderDefault','_GetTextureDefault','_GetShaderLocation','_SetShaderValue','_SetShaderValuei','_SetShaderValueMatrix','_SetMatrixProjection','_SetMatrixModelview','_GenTextureCubemap','_GenTextureIrradiance','_GenTexturePrefilter','_GenTextureBRDF','_BeginShaderMode','_EndShaderMode','_BeginBlendMode','_EndBlendMode','_GetVrDeviceInfo','_InitVrSimulator','_CloseVrSimulator','_IsVrSimulatorReady','_SetVrDistortionShader','_UpdateVrTracking','_ToggleVrMode','_BeginVrDrawing','_EndVrDrawing','_InitAudioDevice','_CloseAudioDevice','_IsAudioDeviceReady','_SetMasterVolume','_LoadWave','_LoadWaveEx','_LoadSound','_LoadSoundFromWave','_UpdateSound','_UnloadWave','_UnloadSound','_PlaySound','_PauseSound','_ResumeSound','_StopSound','_IsSoundPlaying','_SetSoundVolume','_SetSoundPitch','_WaveFormat','_WaveCopy','_WaveCrop','_GetWaveData','_LoadMusicStream','_UnloadMusicStream','_PlayMusicStream','_UpdateMusicStream','_StopMusicStream','_PauseMusicStream','_ResumeMusicStream','_IsMusicPlaying','_SetMusicVolume','_SetMusicPitch','_SetMusicLoopCount','_GetMusicTimeLength','_GetMusicTimePlayed','_InitAudioStream','_UpdateAudioStream','_CloseAudioStream','_IsAudioBufferProcessed','_PlayAudioStream','_PauseAudioStream','_ResumeAudioStream','_IsAudioStreamPlaying','_StopAudioStream','_SetAudioStreamVolume','_SetAudioStreamPitch']" +RAYLIB_SRC=( "audio.c" "core.c" "mini_al.c" "models.c" "rlgl.c" "shapes.c" "text.c" "textures.c" "utils.c" "external/stb_vorbis.c" ) +RAYLIB_OBJ=( "audio.o" "core.o" "mini_al.o" "models.o" "rlgl.o" "shapes.o" "text.o" "textures.o" "utils.o" "external/stb_vorbis.o" ) + +for i in "${RAYLIB_SRC[@]}"; do + emcc "${i}" -o "${i/.c}.o" -O1 -std=c99 -D_DEFAULT_SOURCE -fgnu89-inline -Wno-missing-braces -Wno-unused-function -Wno-unused-variable -Iexternal -Iexternal/glfw/include -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2 +done + +emcc `echo "${RAYLIB_OBJ[*]}"` -o raylib.js -g3 -s USE_GLFW=3 -s EXPORTED_FUNCTIONS=$EMCC_EXPORT + +rm "${GOPATH}"/src/github.com/gen2brain/raylib-go/raylib/*.o "${GOPATH}"/src/github.com/gen2brain/raylib-go/raylib/external/*.o diff --git a/raylib/raylib_js.go b/raylib/raylib_js.go new file mode 100644 index 0000000..35b91d9 --- /dev/null +++ b/raylib/raylib_js.go @@ -0,0 +1,5 @@ +// +build js + +//go:generate ./external/scripts/emcc-generate-js.sh + +package raylib From 2bfca1f330ea35bac4b37a1120a4a0d4cafed8f8 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 8 Dec 2017 05:38:13 +0100 Subject: [PATCH 40/48] Add GopherJS bindings for raylib.js --- raylib/audio.go | 15 - raylib/core.go | 40 -- raylib/models.go | 20 - raylib/raylib.go | 115 ++++ raylib/raylib_js.go | 1476 +++++++++++++++++++++++++++++++++++++++++++ raylib/shaders.go | 10 - raylib/text.go | 10 - raylib/textures.go | 15 - raylib/utils.go | 2 +- 9 files changed, 1592 insertions(+), 111 deletions(-) diff --git a/raylib/audio.go b/raylib/audio.go index fd5079f..3b2f412 100644 --- a/raylib/audio.go +++ b/raylib/audio.go @@ -17,30 +17,15 @@ func (w *Wave) cptr() *C.Wave { return (*C.Wave)(unsafe.Pointer(w)) } -// newWaveFromPointer - Returns new Wave from pointer -func newWaveFromPointer(ptr unsafe.Pointer) Wave { - return *(*Wave)(ptr) -} - func (s *Sound) cptr() *C.Sound { return (*C.Sound)(unsafe.Pointer(s)) } -// newSoundFromPointer - Returns new Sound from pointer -func newSoundFromPointer(ptr unsafe.Pointer) Sound { - return *(*Sound)(ptr) -} - // cptr returns C pointer func (a *AudioStream) cptr() *C.AudioStream { return (*C.AudioStream)(unsafe.Pointer(a)) } -// newAudioStreamFromPointer - Returns new AudioStream from pointer -func newAudioStreamFromPointer(ptr unsafe.Pointer) AudioStream { - return *(*AudioStream)(ptr) -} - // InitAudioDevice - Initialize audio device and context func InitAudioDevice() { C.InitAudioDevice() diff --git a/raylib/core.go b/raylib/core.go index bcf1e5a..3b44dcf 100644 --- a/raylib/core.go +++ b/raylib/core.go @@ -17,81 +17,41 @@ func (v *Vector2) cptr() *C.Vector2 { return (*C.Vector2)(unsafe.Pointer(v)) } -// newVector2FromPointer - Returns new Vector2 from pointer -func newVector2FromPointer(ptr unsafe.Pointer) Vector2 { - return *(*Vector2)(ptr) -} - // cptr returns C pointer func (v *Vector3) cptr() *C.Vector3 { return (*C.Vector3)(unsafe.Pointer(v)) } -// newVector3FromPointer - Returns new Vector3 from pointer -func newVector3FromPointer(ptr unsafe.Pointer) Vector3 { - return *(*Vector3)(ptr) -} - // cptr returns C pointer func (m *Matrix) cptr() *C.Matrix { return (*C.Matrix)(unsafe.Pointer(m)) } -// newMatrixFromPointer - Returns new Matrix from pointer -func newMatrixFromPointer(ptr unsafe.Pointer) Matrix { - return *(*Matrix)(ptr) -} - // cptr returns C pointer func (color *Color) cptr() *C.Color { return (*C.Color)(unsafe.Pointer(color)) } -// newColorFromPointer - Returns new Color from pointer -func newColorFromPointer(ptr unsafe.Pointer) Color { - return *(*Color)(ptr) -} - // cptr returns C pointer func (r *Rectangle) cptr() *C.Rectangle { return (*C.Rectangle)(unsafe.Pointer(r)) } -// newRectangleFromPointer - Returns new Rectangle from pointer -func newRectangleFromPointer(ptr unsafe.Pointer) Rectangle { - return *(*Rectangle)(ptr) -} - // cptr returns C pointer func (c *Camera) cptr() *C.Camera { return (*C.Camera)(unsafe.Pointer(c)) } -// newCameraFromPointer - Returns new Camera from pointer -func newCameraFromPointer(ptr unsafe.Pointer) Camera { - return *(*Camera)(ptr) -} - // cptr returns C pointer func (c *Camera2D) cptr() *C.Camera2D { return (*C.Camera2D)(unsafe.Pointer(c)) } -// newCamera2DFromPointer - Returns new Camera2D from pointer -func newCamera2DFromPointer(ptr unsafe.Pointer) Camera2D { - return *(*Camera2D)(ptr) -} - // cptr returns C pointer func (b *BoundingBox) cptr() *C.BoundingBox { return (*C.BoundingBox)(unsafe.Pointer(b)) } -// newBoundingBoxFromPointer - Returns new BoundingBox from pointer -func newBoundingBoxFromPointer(ptr unsafe.Pointer) BoundingBox { - return *(*BoundingBox)(ptr) -} - // CloseWindow - Close Window and Terminate Context func CloseWindow() { C.CloseWindow() diff --git a/raylib/models.go b/raylib/models.go index 7c88250..5a35f07 100644 --- a/raylib/models.go +++ b/raylib/models.go @@ -14,41 +14,21 @@ func (m *Mesh) cptr() *C.Mesh { return (*C.Mesh)(unsafe.Pointer(m)) } -// newMeshFromPointer - Returns new Mesh from pointer -func newMeshFromPointer(ptr unsafe.Pointer) Mesh { - return *(*Mesh)(ptr) -} - // cptr returns C pointer func (m *Material) cptr() *C.Material { return (*C.Material)(unsafe.Pointer(m)) } -// newMaterialFromPointer - Returns new Material from pointer -func newMaterialFromPointer(ptr unsafe.Pointer) Material { - return *(*Material)(ptr) -} - // cptr returns C pointer func (m *Model) cptr() *C.Model { return (*C.Model)(unsafe.Pointer(m)) } -// newModelFromPointer - Returns new Model from pointer -func newModelFromPointer(ptr unsafe.Pointer) Model { - return *(*Model)(ptr) -} - // cptr returns C pointer func (r *Ray) cptr() *C.Ray { return (*C.Ray)(unsafe.Pointer(r)) } -// newRayFromPointer - Returns new Ray from pointer -func newRayFromPointer(ptr unsafe.Pointer) Ray { - return *(*Ray)(ptr) -} - // DrawLine3D - Draw a line in 3D world space func DrawLine3D(startPos Vector3, endPos Vector3, color Color) { cstartPos := startPos.cptr() diff --git a/raylib/raylib.go b/raylib/raylib.go index 7fd5cac..7641a6e 100644 --- a/raylib/raylib.go +++ b/raylib/raylib.go @@ -67,6 +67,11 @@ func NewWave(sampleCount, sampleRate, sampleSize, channels uint32, data []byte) return Wave{sampleCount, sampleRate, sampleSize, channels, d} } +// newWaveFromPointer - Returns new Wave from pointer +func newWaveFromPointer(ptr unsafe.Pointer) Wave { + return *(*Wave)(ptr) +} + // Sound source type type Sound struct { // Audio source id @@ -82,6 +87,11 @@ func NewSound(source, buffer uint32, format int32) Sound { return Sound{source, buffer, format} } +// newSoundFromPointer - Returns new Sound from pointer +func newSoundFromPointer(ptr unsafe.Pointer) Sound { + return *(*Sound)(ptr) +} + // Music type (file streaming from memory) // NOTE: Anything longer than ~10 seconds should be streamed type Music struct { @@ -97,6 +107,11 @@ type Music struct { SamplesLeft uint32 } +// newMusicFromPointer - Returns new Music from pointer +func newMusicFromPointer(ptr unsafe.Pointer) Music { + return *(*Music)(ptr) +} + // AudioStream type // NOTE: Useful to create custom audio streams not bound to a specific file type AudioStream struct { @@ -119,6 +134,11 @@ func NewAudioStream(sampleRate, sampleSize, channels uint32, format int32, sourc return AudioStream{sampleRate, sampleSize, channels, format, source, buffers} } +// newAudioStreamFromPointer - Returns new AudioStream from pointer +func newAudioStreamFromPointer(ptr unsafe.Pointer) AudioStream { + return *(*AudioStream)(ptr) +} + // CameraMode type type CameraMode int32 @@ -367,6 +387,11 @@ func NewVector2(x, y float32) Vector2 { return Vector2{x, y} } +// newVector2FromPointer - Returns new Vector2 from pointer +func newVector2FromPointer(ptr unsafe.Pointer) Vector2 { + return *(*Vector2)(ptr) +} + // Vector3 type type Vector3 struct { X float32 @@ -379,6 +404,11 @@ func NewVector3(X, Y, Z float32) Vector3 { return Vector3{X, Y, Z} } +// newVector3FromPointer - Returns new Vector3 from pointer +func newVector3FromPointer(ptr unsafe.Pointer) Vector3 { + return *(*Vector3)(ptr) +} + // Matrix type (OpenGL style 4x4 - right handed, column major) type Matrix struct { M0, M4, M8, M12 float32 @@ -392,6 +422,11 @@ func NewMatrix(m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, return Matrix{m0, m4, m8, m12, m1, m5, m9, m13, m2, m6, m10, m14, m3, m7, m11, m15} } +// newMatrixFromPointer - Returns new Matrix from pointer +func newMatrixFromPointer(ptr unsafe.Pointer) Matrix { + return *(*Matrix)(ptr) +} + // Mat2 type (used for polygon shape rotation matrix) type Mat2 struct { M00 float32 @@ -431,6 +466,11 @@ func NewColor(r, g, b, a uint8) Color { return Color{r, g, b, a} } +// newColorFromPointer - Returns new Color from pointer +func newColorFromPointer(ptr unsafe.Pointer) Color { + return *(*Color)(ptr) +} + // Rectangle type type Rectangle struct { X int32 @@ -444,6 +484,11 @@ func NewRectangle(x, y, width, height int32) Rectangle { return Rectangle{x, y, width, height} } +// newRectangleFromPointer - Returns new Rectangle from pointer +func newRectangleFromPointer(ptr unsafe.Pointer) Rectangle { + return *(*Rectangle)(ptr) +} + // Camera type, defines a camera position/orientation in 3d space type Camera struct { // Camera position @@ -461,6 +506,11 @@ func NewCamera(pos, target, up Vector3, fovy float32) Camera { return Camera{pos, target, up, fovy} } +// newCameraFromPointer - Returns new Camera from pointer +func newCameraFromPointer(ptr unsafe.Pointer) Camera { + return *(*Camera)(ptr) +} + // Camera2D type, defines a 2d camera type Camera2D struct { // Camera offset (displacement from target) @@ -478,6 +528,11 @@ func NewCamera2D(offset, target Vector2, rotation, zoom float32) Camera2D { return Camera2D{offset, target, rotation, zoom} } +// newCamera2DFromPointer - Returns new Camera2D from pointer +func newCamera2DFromPointer(ptr unsafe.Pointer) Camera2D { + return *(*Camera2D)(ptr) +} + // BoundingBox type type BoundingBox struct { // Minimum vertex box-corner @@ -491,6 +546,11 @@ func NewBoundingBox(min, max Vector3) BoundingBox { return BoundingBox{min, max} } +// newBoundingBoxFromPointer - Returns new BoundingBox from pointer +func newBoundingBoxFromPointer(ptr unsafe.Pointer) BoundingBox { + return *(*BoundingBox)(ptr) +} + // Asset file type Asset interface { io.ReadSeeker @@ -611,6 +671,11 @@ func NewMesh(vertexCount, triangleCount int32, vertices, texcoords, texcoords2, return Mesh{vertexCount, triangleCount, vertices, texcoords, texcoords2, normals, tangents, colors, indices, vaoID, vboID} } +// newMeshFromPointer - Returns new Mesh from pointer +func newMeshFromPointer(ptr unsafe.Pointer) Mesh { + return *(*Mesh)(ptr) +} + // Material type type Material struct { // Shader @@ -628,6 +693,11 @@ func NewMaterial(shader Shader, maps [MaxMaterialMaps]MaterialMap, params *[]flo return Material{shader, maps, [4]byte{}, params} } +// newMaterialFromPointer - Returns new Material from pointer +func newMaterialFromPointer(ptr unsafe.Pointer) Material { + return *(*Material)(ptr) +} + // MaterialMap type type MaterialMap struct { // Texture @@ -655,6 +725,11 @@ func NewModel(mesh Mesh, transform Matrix, material Material) Model { return Model{mesh, transform, material, [4]byte{}} } +// newModelFromPointer - Returns new Model from pointer +func newModelFromPointer(ptr unsafe.Pointer) Model { + return *(*Model)(ptr) +} + // Ray type (useful for raycast) type Ray struct { // Ray position (origin) @@ -668,6 +743,11 @@ func NewRay(position, direction Vector3) Ray { return Ray{position, direction} } +// newRayFromPointer - Returns new Ray from pointer +func newRayFromPointer(ptr unsafe.Pointer) Ray { + return *(*Ray)(ptr) +} + // VrDevice type type VrDevice int32 @@ -713,6 +793,11 @@ func NewVrDeviceInfo(hResolution, vResolution int, hScreenSize, vScreenSize, vSc lensSeparationDistance, interpupillaryDistance, lensDistortionValues, chromaAbCorrection} } +// newVrDeviceInfoFromPointer - Returns new VrDeviceInfo from pointer +func newVrDeviceInfoFromPointer(ptr unsafe.Pointer) VrDeviceInfo { + return *(*VrDeviceInfo)(ptr) +} + // BlendMode type type BlendMode int32 @@ -736,6 +821,11 @@ func NewShader(id uint32, locs [MaxShaderLocations]int32) Shader { return Shader{id, locs} } +// newShaderFromPointer - Returns new Shader from pointer +func newShaderFromPointer(ptr unsafe.Pointer) Shader { + return *(*Shader)(ptr) +} + // CharInfo - SpriteFont character info type CharInfo struct { // Character value (Unicode) @@ -755,6 +845,11 @@ func NewCharInfo(value int32, rec Rectangle, offsetX, offsetY, advanceX int32) C return CharInfo{value, rec, offsetX, offsetY, advanceX} } +// newCharInfoFromPointer - Returns new SpriteFont from pointer +func newCharInfoFromPointer(ptr unsafe.Pointer) CharInfo { + return *(*CharInfo)(ptr) +} + // SpriteFont type, includes texture and charSet array data type SpriteFont struct { // Font texture @@ -772,6 +867,11 @@ func NewSpriteFont(texture Texture2D, baseSize, charsCount int32, chars *CharInf return SpriteFont{texture, baseSize, charsCount, chars} } +// newSpriteFontFromPointer - Returns new SpriteFont from pointer +func newSpriteFontFromPointer(ptr unsafe.Pointer) SpriteFont { + return *(*SpriteFont)(ptr) +} + // TextureFormat - Texture format type TextureFormat int32 @@ -880,6 +980,11 @@ func NewImage(data []byte, width, height, mipmaps int32, format TextureFormat) * return &Image{d, width, height, mipmaps, format} } +// newImageFromPointer - Returns new Image from pointer +func newImageFromPointer(ptr unsafe.Pointer) *Image { + return (*Image)(ptr) +} + // NewImageFromImage - Returns new Image from Go image.Image func NewImageFromImage(img image.Image) *Image { size := img.Bounds().Size() @@ -916,6 +1021,11 @@ func NewTexture2D(id uint32, width, height, mipmaps int32, format TextureFormat) return Texture2D{id, width, height, mipmaps, format} } +// newTexture2DFromPointer - Returns new Texture2D from pointer +func newTexture2DFromPointer(ptr unsafe.Pointer) Texture2D { + return *(*Texture2D)(ptr) +} + // RenderTexture2D type, for texture rendering type RenderTexture2D struct { // Render texture (fbo) id @@ -931,6 +1041,11 @@ func NewRenderTexture2D(id uint32, texture, depth Texture2D) RenderTexture2D { return RenderTexture2D{id, texture, depth} } +// newRenderTexture2DFromPointer - Returns new RenderTexture2D from pointer +func newRenderTexture2DFromPointer(ptr unsafe.Pointer) RenderTexture2D { + return *(*RenderTexture2D)(ptr) +} + // Log message types const ( LogInfo = iota diff --git a/raylib/raylib_js.go b/raylib/raylib_js.go index 35b91d9..4eb13a1 100644 --- a/raylib/raylib_js.go +++ b/raylib/raylib_js.go @@ -3,3 +3,1479 @@ //go:generate ./external/scripts/emcc-generate-js.sh package raylib + +import ( + "unsafe" + + "github.com/gopherjs/gopherjs/js" +) + +// InitWindow - Initialize Window and OpenGL Graphics +func InitWindow(width int32, height int32, t interface{}) { + js.Global.Get("Module").Call("_InitWindow", width, height, t.(string)) +} + +// SetCallbackFunc - Sets callback function +func SetCallbackFunc(func(unsafe.Pointer)) { +} + +// ShowCursor - Shows cursor +func ShowCursor() { +} + +// HideCursor - Hides cursor +func HideCursor() { +} + +// IsCursorHidden - Returns true if cursor is not visible +func IsCursorHidden() bool { + return false +} + +// EnableCursor - Enables cursor +func EnableCursor() { +} + +// DisableCursor - Disables cursor +func DisableCursor() { +} + +// IsFileDropped - Check if a file have been dropped into window +func IsFileDropped() bool { + return false +} + +// GetDroppedFiles - Retrieve dropped files into window +func GetDroppedFiles(count *int32) (f []string) { + return +} + +// ClearDroppedFiles - Clear dropped files paths buffer +func ClearDroppedFiles() { +} + +// InitAudioDevice - Initialize audio device and context +func InitAudioDevice() { + js.Global.Get("Module").Call("_InitAudioDevice") +} + +// CloseAudioDevice - Close the audio device and context +func CloseAudioDevice() { + js.Global.Get("Module").Call("_CloseAudioDevice") +} + +// IsAudioDeviceReady - Check if audio device has been initialized successfully +func IsAudioDeviceReady() bool { + return js.Global.Get("Module").Call("_IsAudioDeviceReady").Bool() +} + +// SetMasterVolume - Set master volume (listener) +func SetMasterVolume(volume float32) { + js.Global.Get("Module").Call("_SetMasterVolume", volume) +} + +// LoadWave - Load wave data from file into RAM +func LoadWave(fileName string) Wave { + return newWaveFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadWave", fileName).Unsafe())) +} + +// LoadWaveEx - Load wave data from float array data (32bit) +func LoadWaveEx(data []byte, sampleCount int32, sampleRate int32, sampleSize int32, channels int32) Wave { + return newWaveFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadWaveEx", data, sampleCount, sampleRate, sampleSize, channels).Unsafe())) +} + +// LoadSound - Load sound to memory +func LoadSound(fileName string) Sound { + return newSoundFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadSound", fileName).Unsafe())) +} + +// LoadSoundFromWave - Load sound to memory from wave data +func LoadSoundFromWave(wave Wave) Sound { + return newSoundFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadSoundFromWave", wave).Unsafe())) +} + +// UpdateSound - Update sound buffer with new data +func UpdateSound(sound Sound, data []byte, samplesCount int32) { + js.Global.Get("Module").Call("_UpdateSound", sound, data, samplesCount) +} + +// UnloadWave - Unload wave data +func UnloadWave(wave Wave) { + js.Global.Get("Module").Call("_UnloadWave", wave) +} + +// UnloadSound - Unload sound +func UnloadSound(sound Sound) { + js.Global.Get("Module").Call("_UnloadSound", sound) +} + +// PlaySound - Play a sound +func PlaySound(sound Sound) { + js.Global.Get("Module").Call("_PlaySound", sound) +} + +// PauseSound - Pause a sound +func PauseSound(sound Sound) { + js.Global.Get("Module").Call("_PauseSound", sound) +} + +// ResumeSound - Resume a paused sound +func ResumeSound(sound Sound) { + js.Global.Get("Module").Call("_ResumeSound", sound) +} + +// StopSound - Stop playing a sound +func StopSound(sound Sound) { + js.Global.Get("Module").Call("_StopSound", sound) +} + +// IsSoundPlaying - Check if a sound is currently playing +func IsSoundPlaying(sound Sound) bool { + return js.Global.Get("Module").Call("_IsSoundPlaying", sound).Bool() +} + +// SetSoundVolume - Set volume for a sound (1.0 is max level) +func SetSoundVolume(sound Sound, volume float32) { + js.Global.Get("Module").Call("_SetSoundVolume", sound, volume) +} + +// SetSoundPitch - Set pitch for a sound (1.0 is base level) +func SetSoundPitch(sound Sound, pitch float32) { + js.Global.Get("Module").Call("_SetSoundPitch", sound, pitch) +} + +// WaveFormat - Convert wave data to desired format +func WaveFormat(wave Wave, sampleRate int32, sampleSize int32, channels int32) { + js.Global.Get("Module").Call("_WaveFormat", wave, sampleRate, sampleSize, channels) +} + +// WaveCopy - Copy a wave to a new wave +func WaveCopy(wave Wave) Wave { + return newWaveFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_WaveCopy", wave).Unsafe())) +} + +// WaveCrop - Crop a wave to defined samples range +func WaveCrop(wave Wave, initSample int32, finalSample int32) { + js.Global.Get("Module").Call("_WaveCrop", wave, initSample, finalSample) +} + +// GetWaveData - Get samples data from wave as a floats array +func GetWaveData(wave Wave) []float32 { + return js.Global.Get("Module").Call("_GetWaveData", wave).Interface().([]float32) +} + +// LoadMusicStream - Load music stream from file +func LoadMusicStream(fileName string) Music { + return newMusicFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadMusicStream", fileName).Unsafe())) +} + +// UnloadMusicStream - Unload music stream +func UnloadMusicStream(music Music) { + js.Global.Get("Module").Call("_UnloadMusicStream", music) +} + +// PlayMusicStream - Start music playing +func PlayMusicStream(music Music) { + js.Global.Get("Module").Call("_PlayMusicStream", music) +} + +// UpdateMusicStream - Updates buffers for music streaming +func UpdateMusicStream(music Music) { + js.Global.Get("Module").Call("_UpdateMusicStream", music) +} + +// StopMusicStream - Stop music playing +func StopMusicStream(music Music) { + js.Global.Get("Module").Call("_StopMusicStream", music) +} + +// PauseMusicStream - Pause music playing +func PauseMusicStream(music Music) { + js.Global.Get("Module").Call("_PauseMusicStream", music) +} + +// ResumeMusicStream - Resume playing paused music +func ResumeMusicStream(music Music) { + js.Global.Get("Module").Call("_ResumeMusicStream", music) +} + +// IsMusicPlaying - Check if music is playing +func IsMusicPlaying(music Music) bool { + return js.Global.Get("Module").Call("_IsMusicPlaying", music).Bool() +} + +// SetMusicVolume - Set volume for music (1.0 is max level) +func SetMusicVolume(music Music, volume float32) { + js.Global.Get("Module").Call("_SetMusicVolume", music, volume) +} + +// SetMusicPitch - Set pitch for a music (1.0 is base level) +func SetMusicPitch(music Music, pitch float32) { + js.Global.Get("Module").Call("_SetMusicPitch", music, pitch) +} + +// NOTE: If set to -1, means infinite loop +func SetMusicLoopCount(music Music, count int32) { + js.Global.Get("Module").Call("_SetMusicLoopCount", music, count) +} + +// GetMusicTimeLength - Get music time length (in seconds) +func GetMusicTimeLength(music Music) float32 { + return float32(js.Global.Get("Module").Call("_GetMusicTimeLength", music).Float()) +} + +// GetMusicTimePlayed - Get current music time played (in seconds) +func GetMusicTimePlayed(music Music) float32 { + return float32(js.Global.Get("Module").Call("_GetMusicTimePlayed", music).Float()) +} + +// InitAudioStream - Init audio stream (to stream raw audio pcm data) +func InitAudioStream(sampleRate uint32, sampleSize uint32, channels uint32) AudioStream { + return newAudioStreamFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_InitAudioStream", sampleRate, sampleSize, channels).Unsafe())) +} + +// UpdateAudioStream - Update audio stream buffers with data +func UpdateAudioStream(stream AudioStream, data []float32, samplesCount int32) { + js.Global.Get("Module").Call("_UpdateAudioStream", stream, data, samplesCount) +} + +// CloseAudioStream - Close audio stream and free memory +func CloseAudioStream(stream AudioStream) { + js.Global.Get("Module").Call("_CloseAudioStream", stream) +} + +// IsAudioBufferProcessed - Check if any audio stream buffers requires refill +func IsAudioBufferProcessed(stream AudioStream) bool { + return js.Global.Get("Module").Call("_IsAudioBufferProcessed", stream).Bool() +} + +// PlayAudioStream - Play audio stream +func PlayAudioStream(stream AudioStream) { + js.Global.Get("Module").Call("_PlayAudioStream", stream) +} + +// PauseAudioStream - Pause audio stream +func PauseAudioStream(stream AudioStream) { + js.Global.Get("Module").Call("_PauseAudioStream", stream) +} + +// ResumeAudioStream - Resume audio stream +func ResumeAudioStream(stream AudioStream) { + js.Global.Get("Module").Call("_ResumeAudioStream", stream) +} + +// StopAudioStream - Stop audio stream +func StopAudioStream(stream AudioStream) { + js.Global.Get("Module").Call("_StopAudioStream", stream) +} + +// SetCameraMode - Set camera mode (multiple camera modes available) +func SetCameraMode(camera Camera, mode CameraMode) { + js.Global.Get("Module").Call("_SetCameraMode", camera, mode) +} + +// UpdateCamera - Update camera position for selected mode +func UpdateCamera(camera *Camera) { + js.Global.Get("Module").Call("_UpdateCamera", camera) +} + +// SetCameraPanControl - Set camera pan key to combine with mouse movement (free camera) +func SetCameraPanControl(panKey int32) { + js.Global.Get("Module").Call("_SetCameraPanControl", panKey) +} + +// SetCameraAltControl - Set camera alt key to combine with mouse movement (free camera) +func SetCameraAltControl(altKey int32) { + js.Global.Get("Module").Call("_SetCameraAltControl", altKey) +} + +// SetCameraSmoothZoomControl - Set camera smooth zoom key to combine with mouse (free camera) +func SetCameraSmoothZoomControl(szKey int32) { + js.Global.Get("Module").Call("_SetCameraSmoothZoomControl", szKey) +} + +// SetCameraMoveControls - Set camera move controls (1st person and 3rd person cameras) +func SetCameraMoveControls(frontKey int32, backKey int32, rightKey int32, leftKey int32, upKey int32, downKey int32) { + js.Global.Get("Module").Call("_SetCameraMoveControls", frontKey, backKey, rightKey, leftKey, upKey, downKey) +} + +// CloseWindow - Close Window and Terminate Context +func CloseWindow() { + js.Global.Get("Module").Call("_CloseWindow") +} + +// WindowShouldClose - Detect if KEY_ESCAPE pressed or Close icon pressed +func WindowShouldClose() bool { + return js.Global.Get("Module").Call("_WindowShouldClose").Bool() +} + +// IsWindowMinimized - Detect if window has been minimized (or lost focus) +func IsWindowMinimized() bool { + return js.Global.Get("Module").Call("_IsWindowMinimized").Bool() +} + +// ToggleFullscreen - Fullscreen toggle (only PLATFORM_DESKTOP) +func ToggleFullscreen() { + js.Global.Get("Module").Call("_ToggleFullscreen") +} + +// SetWindowIcon - Set icon for window (only PLATFORM_DESKTOP) +func SetWindowIcon(image Image) { + js.Global.Get("Module").Call("_SetWindowIcon", image) +} + +// SetWindowTitle - Set title for window (only PLATFORM_DESKTOP) +func SetWindowTitle(title string) { + js.Global.Get("Module").Call("_SetWindowTitle", title) +} + +// SetWindowPosition - Set window position on screen (only PLATFORM_DESKTOP) +func SetWindowPosition(x, y int32) { + js.Global.Get("Module").Call("_SetWindowPosition", x, y) +} + +// SetWindowMonitor - Set monitor for the current window (fullscreen mode) +func SetWindowMonitor(monitor int32) { + js.Global.Get("Module").Call("_SetWindowMonitor", monitor) +} + +// GetScreenWidth - Get current screen width +func GetScreenWidth() int32 { + return int32(js.Global.Get("Module").Call("_GetScreenWidth").Int()) +} + +// GetScreenHeight - Get current screen height +func GetScreenHeight() int32 { + return int32(js.Global.Get("Module").Call("_GetScreenHeight").Int()) +} + +// ClearBackground - Sets Background Color +func ClearBackground(color Color) { + js.Global.Get("Module").Call("_ClearBackground", color) +} + +// BeginDrawing - Setup drawing canvas to start drawing +func BeginDrawing() { + js.Global.Get("Module").Call("_BeginDrawing") +} + +// EndDrawing - End canvas drawing and Swap Buffers (Double Buffering) +func EndDrawing() { + js.Global.Get("Module").Call("_EndDrawing") +} + +// Begin2dMode - Initialize 2D mode with custom camera +func Begin2dMode(camera Camera2D) { + js.Global.Get("Module").Call("_Begin2dMode", camera) +} + +// End2dMode - Ends 2D mode custom camera usage +func End2dMode() { + js.Global.Get("Module").Call("_End2dMode") +} + +// Begin3dMode - Initializes 3D mode for drawing (Camera setup) +func Begin3dMode(camera Camera) { + js.Global.Get("Module").Call("_Begin3dMode", camera) +} + +// End3dMode - Ends 3D mode and returns to default 2D orthographic mode +func End3dMode() { + js.Global.Get("Module").Call("_End3dMode") +} + +// BeginTextureMode - Initializes render texture for drawing +func BeginTextureMode(target RenderTexture2D) { + js.Global.Get("Module").Call("_BeginTextureMode", target) +} + +// EndTextureMode - Ends drawing to render texture +func EndTextureMode() { + js.Global.Get("Module").Call("_EndTextureMode") +} + +// GetMouseRay - Returns a ray trace from mouse position +func GetMouseRay(mousePosition Vector2, camera Camera) Ray { + return newRayFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetMouseRay", mousePosition, camera).Unsafe())) +} + +// GetWorldToScreen - Returns the screen space position from a 3d world space position +func GetWorldToScreen(position Vector3, camera Camera) Vector2 { + return newVector2FromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetWorldToScreen", position, camera).Unsafe())) +} + +// GetCameraMatrix - Returns camera transform matrix (view matrix) +func GetCameraMatrix(camera Camera) Matrix { + return newMatrixFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetCameraMatrix", camera).Unsafe())) +} + +// SetTargetFPS - Set target FPS (maximum) +func SetTargetFPS(fps int32) { + js.Global.Get("Module").Call("_SetTargetFPS") +} + +// GetFPS - Returns current FPS +func GetFPS() float32 { + return float32(js.Global.Get("Module").Call("_GetFPS").Float()) +} + +// GetFrameTime - Returns time in seconds for one frame +func GetFrameTime() float32 { + return float32(js.Global.Get("Module").Call("_GetFrameTime").Float()) +} + +// GetColor - Returns a Color struct from hexadecimal value +func GetColor(hexValue int32) Color { + return newColorFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetColor", hexValue).Unsafe())) +} + +// GetHexValue - Returns hexadecimal value for a Color +func GetHexValue(color Color) int32 { + return int32(js.Global.Get("Module").Call("_GetHexValue", color).Int()) +} + +// ColorToFloat - Converts Color to float32 slice and normalizes +func ColorToFloat(color Color) []float32 { + return js.Global.Get("Module").Call("_ColorToFloat", color).Interface().([]float32) +} + +// Vector3ToFloat - Converts Vector3 to float32 slice +func Vector3ToFloat(vec Vector3) []float32 { + return js.Global.Get("Module").Call("_Vector3ToFloat", vec).Interface().([]float32) +} + +// MatrixToFloat - Converts Matrix to float32 slice +func MatrixToFloat(mat Matrix) []float32 { + return js.Global.Get("Module").Call("_MatrixToFloat", mat).Interface().([]float32) +} + +// GetRandomValue - Returns a random value between min and max (both included) +func GetRandomValue(min, max int32) int32 { + return int32(js.Global.Get("Module").Call("_GetRandomValue", min, max).Int()) +} + +// Fade - Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +func Fade(color Color, alpha float32) Color { + return newColorFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_Fade", color, alpha).Unsafe())) +} + +// ShowLogo - Activates raylib logo at startup (can be done with flags) +func ShowLogo() { + js.Global.Get("Module").Call("_ShowLogo") +} + +// SetConfigFlags - Setup some window configuration flags +func SetConfigFlags(flags byte) { + js.Global.Get("Module").Call("_SetConfigFlags", flags) +} + +// TakeScreenshot - Takes a screenshot of current screen (saved a .png) +func TakeScreenshot(name string) { + js.Global.Get("Module").Call("_TakeScreenshot", name) +} + +// StorageSaveValue - Storage save integer value (to defined position) +func StorageSaveValue(position, value int32) { + js.Global.Get("Module").Call("_StorageSaveValue", position, value) +} + +// StorageLoadValue - Storage load integer value (from defined position) +func StorageLoadValue(position int32) int32 { + return int32(js.Global.Get("Module").Call("_StorageLoadValue", position).Int()) +} + +// IsKeyPressed - Detect if a key has been pressed once +func IsKeyPressed(key int32) bool { + return js.Global.Get("Module").Call("_IsKeyPressed", key).Bool() +} + +// IsKeyDown - Detect if a key is being pressed +func IsKeyDown(key int32) bool { + return js.Global.Get("Module").Call("_IsKeyDown", key).Bool() +} + +// IsKeyReleased - Detect if a key has been released once +func IsKeyReleased(key int32) bool { + return js.Global.Get("Module").Call("_IsKeyReleased", key).Bool() +} + +// IsKeyUp - Detect if a key is NOT being pressed +func IsKeyUp(key int32) bool { + return js.Global.Get("Module").Call("_IsKeyUp", key).Bool() +} + +// GetKeyPressed - Get latest key pressed +func GetKeyPressed() int32 { + return int32(js.Global.Get("Module").Call("_GetKeyPressed").Int()) +} + +// SetExitKey - Set a custom key to exit program (default is ESC) +func SetExitKey(key int32) { + js.Global.Get("Module").Call("_SetExitKey", key) +} + +// IsGamepadAvailable - Detect if a gamepad is available +func IsGamepadAvailable(gamepad int32) bool { + return js.Global.Get("Module").Call("_IsGamepadAvailable", gamepad).Bool() +} + +// IsGamepadName - Check gamepad name (if available) +func IsGamepadName(gamepad int32, name string) bool { + return js.Global.Get("Module").Call("_IsGamepadName", gamepad, name).Bool() +} + +// GetGamepadName - Return gamepad internal name id +func GetGamepadName(gamepad int32) string { + return js.Global.Get("Module").Call("_GetGamepadName", gamepad).String() +} + +// IsGamepadButtonPressed - Detect if a gamepad button has been pressed once +func IsGamepadButtonPressed(gamepad, button int32) bool { + return js.Global.Get("Module").Call("_IsGamepadButtonPressed", gamepad, button).Bool() +} + +// IsGamepadButtonDown - Detect if a gamepad button is being pressed +func IsGamepadButtonDown(gamepad, button int32) bool { + return js.Global.Get("Module").Call("_IsGamepadButtonDown", gamepad, button).Bool() +} + +// IsGamepadButtonReleased - Detect if a gamepad button has been released once +func IsGamepadButtonReleased(gamepad, button int32) bool { + return js.Global.Get("Module").Call("_IsGamepadButtonReleased", gamepad, button).Bool() +} + +// IsGamepadButtonUp - Detect if a gamepad button is NOT being pressed +func IsGamepadButtonUp(gamepad, button int32) bool { + return js.Global.Get("Module").Call("_IsGamepadButtonUp", gamepad, button).Bool() +} + +// GetGamepadButtonPressed - Get the last gamepad button pressed +func GetGamepadButtonPressed() int32 { + return int32(js.Global.Get("Module").Call("_GetGamepadButtonPressed").Int()) +} + +// GetGamepadAxisCount - Return gamepad axis count for a gamepad +func GetGamepadAxisCount(gamepad int32) int32 { + return int32(js.Global.Get("Module").Call("_GetGamepadAxisCount", gamepad).Int()) +} + +// GetGamepadAxisMovement - Return axis movement value for a gamepad axis +func GetGamepadAxisMovement(gamepad, axis int32) float32 { + return float32(js.Global.Get("Module").Call("_GetGamepadAxisMovement", gamepad, axis).Float()) +} + +// IsMouseButtonPressed - Detect if a mouse button has been pressed once +func IsMouseButtonPressed(button int32) bool { + return js.Global.Get("Module").Call("_IsMouseButtonPressed", button).Bool() +} + +// IsMouseButtonDown - Detect if a mouse button is being pressed +func IsMouseButtonDown(button int32) bool { + return js.Global.Get("Module").Call("_IsMouseButtonDown", button).Bool() +} + +// IsMouseButtonReleased - Detect if a mouse button has been released once +func IsMouseButtonReleased(button int32) bool { + return js.Global.Get("Module").Call("_IsMouseButtonReleased", button).Bool() +} + +// IsMouseButtonUp - Detect if a mouse button is NOT being pressed +func IsMouseButtonUp(button int32) bool { + return js.Global.Get("Module").Call("_IsMouseButtonUp", button).Bool() +} + +// GetMouseX - Returns mouse position X +func GetMouseX() int32 { + return int32(js.Global.Get("Module").Call("_GetMouseX").Int()) +} + +// GetMouseY - Returns mouse position Y +func GetMouseY() int32 { + return int32(js.Global.Get("Module").Call("_GetMouseY").Int()) +} + +// GetMousePosition - Returns mouse position XY +func GetMousePosition() Vector2 { + return newVector2FromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetMousePosition").Unsafe())) +} + +// SetMousePosition - Set mouse position XY +func SetMousePosition(position Vector2) { + js.Global.Get("Module").Call("_SetMousePosition", position) +} + +// GetMouseWheelMove - Returns mouse wheel movement Y +func GetMouseWheelMove() int32 { + return int32(js.Global.Get("Module").Call("_GetMouseWheelMove").Int()) +} + +// GetTouchX - Returns touch position X for touch point 0 (relative to screen size) +func GetTouchX() int32 { + return int32(js.Global.Get("Module").Call("_GetTouchX").Int()) +} + +// GetTouchY - Returns touch position Y for touch point 0 (relative to screen size) +func GetTouchY() int32 { + return int32(js.Global.Get("Module").Call("_GetTouchX").Int()) +} + +// GetTouchPosition - Returns touch position XY for a touch point index (relative to screen size) +func GetTouchPosition(index int32) Vector2 { + return newVector2FromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetTouchPosition", index).Unsafe())) +} + +// SetGesturesEnabled - Enable a set of gestures using flags +func SetGesturesEnabled(gestureFlags uint32) { + js.Global.Get("Module").Call("_SetGesturesEnabled", gestureFlags) +} + +// IsGestureDetected - Check if a gesture have been detected +func IsGestureDetected(gesture Gestures) bool { + return js.Global.Get("Module").Call("_IsGestureDetected", gesture).Bool() +} + +// GetGestureDetected - Get latest detected gesture +func GetGestureDetected() Gestures { + return Gestures(js.Global.Get("Module").Call("_GetGestureDetected").Int()) +} + +// GetTouchPointsCount - Get touch points count +func GetTouchPointsCount() int32 { + return int32(js.Global.Get("Module").Call("_GetTouchPointsCount").Int()) +} + +// GetGestureHoldDuration - Get gesture hold time in milliseconds +func GetGestureHoldDuration() float32 { + return float32(js.Global.Get("Module").Call("_GetGestureHoldDuration").Float()) +} + +// GetGestureDragVector - Get gesture drag vector +func GetGestureDragVector() Vector2 { + return newVector2FromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetGestureDragVector").Unsafe())) +} + +// GetGestureDragAngle - Get gesture drag angle +func GetGestureDragAngle() float32 { + return float32(js.Global.Get("Module").Call("_GetGestureDragAngle").Float()) +} + +// GetGesturePinchVector - Get gesture pinch delta +func GetGesturePinchVector() Vector2 { + return newVector2FromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetGesturePinchVector").Unsafe())) +} + +// GetGesturePinchAngle - Get gesture pinch angle +func GetGesturePinchAngle() float32 { + return float32(js.Global.Get("Module").Call("_GetGesturePinchAngle").Float()) +} + +// DrawLine3D - Draw a line in 3D world space +func DrawLine3D(startPos Vector3, endPos Vector3, color Color) { + js.Global.Get("Module").Call("_DrawLine3D", startPos, endPos, color) +} + +// DrawCircle3D - Draw a circle in 3D world space +func DrawCircle3D(center Vector3, radius float32, rotationAxis Vector3, rotationAngle float32, color Color) { + js.Global.Get("Module").Call("_DrawCircle3D", center, radius, rotationAxis, rotationAngle, color) +} + +// DrawCube - Draw cube +func DrawCube(position Vector3, width float32, height float32, length float32, color Color) { + js.Global.Get("Module").Call("_DrawCube", position, width, height, length, color) +} + +// DrawCubeV - Draw cube (Vector version) +func DrawCubeV(position Vector3, size Vector3, color Color) { + js.Global.Get("Module").Call("_DrawCubeV", position, size, color) +} + +// DrawCubeWires - Draw cube wires +func DrawCubeWires(position Vector3, width float32, height float32, length float32, color Color) { + js.Global.Get("Module").Call("_DrawCubeWires", position, width, height, length, color) +} + +// DrawCubeTexture - Draw cube textured +func DrawCubeTexture(texture Texture2D, position Vector3, width float32, height float32, length float32, color Color) { + js.Global.Get("Module").Call("_DrawCubeTexture", texture, position, width, height, length, color) +} + +// DrawSphere - Draw sphere +func DrawSphere(centerPos Vector3, radius float32, color Color) { + js.Global.Get("Module").Call("_DrawSphere", centerPos, radius, color) +} + +// DrawSphereEx - Draw sphere with extended parameters +func DrawSphereEx(centerPos Vector3, radius float32, rings int32, slices int32, color Color) { + js.Global.Get("Module").Call("_DrawSphereEx", centerPos, radius, rings, slices, color) +} + +// DrawSphereWires - Draw sphere wires +func DrawSphereWires(centerPos Vector3, radius float32, rings int32, slices int32, color Color) { + js.Global.Get("Module").Call("_DrawSphereWires", centerPos, radius, rings, slices, color) +} + +// DrawCylinder - Draw a cylinder/cone +func DrawCylinder(position Vector3, radiusTop float32, radiusBottom float32, height float32, slices int32, color Color) { + js.Global.Get("Module").Call("_DrawCylinder", position, radiusTop, radiusBottom, height, slices, color) +} + +// DrawCylinderWires - Draw a cylinder/cone wires +func DrawCylinderWires(position Vector3, radiusTop float32, radiusBottom float32, height float32, slices int32, color Color) { + js.Global.Get("Module").Call("_DrawCylinderWires", position, radiusTop, radiusBottom, height, slices, color) +} + +// DrawPlane - Draw a plane XZ +func DrawPlane(centerPos Vector3, size Vector2, color Color) { + js.Global.Get("Module").Call("_DrawPlane", centerPos, size, color) +} + +// DrawRay - Draw a ray line +func DrawRay(ray Ray, color Color) { + js.Global.Get("Module").Call("_DrawRay", ray, color) +} + +// DrawGrid - Draw a grid (centered at (0, 0, 0)) +func DrawGrid(slices int32, spacing float32) { + js.Global.Get("Module").Call("_DrawGrid", slices, spacing) +} + +// DrawGizmo - Draw simple gizmo +func DrawGizmo(position Vector3) { + js.Global.Get("Module").Call("_DrawGizmo", position) +} + +// LoadMesh - Load mesh from file +func LoadMesh(fileName string) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadMesh", fileName).Unsafe())) +} + +// LoadModel - Load model from file +func LoadModel(fileName string) Model { + return newModelFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadModel", fileName).Unsafe())) +} + +// LoadModelFromMesh - Load model from mesh data +func LoadModelFromMesh(data Mesh) Model { + return newModelFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadModelFromMesh", data).Unsafe())) +} + +// UnloadModel - Unload model from memory (RAM and/or VRAM) +func UnloadModel(model Model) { + js.Global.Get("Module").Call("_UnloadModel", model) +} + +// UnloadMesh - Unload mesh from memory (RAM and/or VRAM) +func UnloadMesh(mesh *Mesh) { + js.Global.Get("Module").Call("_UnloadMesh", mesh) +} + +// GenMeshPlane - Generate plane mesh (with subdivisions) +func GenMeshPlane(width, length float32, resX, resZ int) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenMeshPlane", width, length, resX, resZ).Unsafe())) +} + +// GenMeshCube - Generate cuboid mesh +func GenMeshCube(width, height, length float32) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenMeshCube", width, height, length).Unsafe())) +} + +// GenMeshSphere - Generate sphere mesh (standard sphere) +func GenMeshSphere(radius float32, rings, slices int) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenMeshSphere", radius, rings, slices).Unsafe())) +} + +// GenMeshHemiSphere - Generate half-sphere mesh (no bottom cap) +func GenMeshHemiSphere(radius float32, rings, slices int) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenMeshHemiSphere", radius, rings, slices).Unsafe())) +} + +// GenMeshCylinder - Generate cylinder mesh +func GenMeshCylinder(radius, height float32, slices int) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenMeshCylinder", radius, height, slices).Unsafe())) +} + +// GenMeshTorus - Generate torus mesh +func GenMeshTorus(radius, size float32, radSeg, sides int) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenMeshTorus", radius, size, radSeg, sides).Unsafe())) +} + +// GenMeshKnot - Generate trefoil knot mesh +func GenMeshKnot(radius, size float32, radSeg, sides int) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenMeshKnot", radius, size, radSeg, sides).Unsafe())) +} + +// GenMeshHeightmap - Generate heightmap mesh from image data +func GenMeshHeightmap(heightmap Image, size Vector3) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenMeshHeightmap", heightmap, size).Unsafe())) +} + +// GenMeshCubicmap - Generate cubes-based map mesh from image data +func GenMeshCubicmap(cubicmap Image, size Vector3) Mesh { + return newMeshFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenMeshCubicmap", cubicmap, size).Unsafe())) +} + +// LoadMaterial - Load material data (.MTL) +func LoadMaterial(fileName string) Material { + return newMaterialFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadMaterial", fileName).Unsafe())) +} + +// LoadMaterialDefault - Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +func LoadMaterialDefault() Material { + return newMaterialFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadMaterialDefault").Unsafe())) +} + +// UnloadMaterial - Unload material textures from VRAM +func UnloadMaterial(material Material) { + js.Global.Get("Module").Call("_UnloadMaterial", material) +} + +// DrawModel - Draw a model (with texture if set) +func DrawModel(model Model, position Vector3, scale float32, tint Color) { + js.Global.Get("Module").Call("_DrawModel", model, position, scale, tint) +} + +// DrawModelEx - Draw a model with extended parameters +func DrawModelEx(model Model, position Vector3, rotationAxis Vector3, rotationAngle float32, scale Vector3, tint Color) { + js.Global.Get("Module").Call("_DrawModelEx", model, position, rotationAxis, rotationAngle, scale, tint) +} + +// DrawModelWires - Draw a model wires (with texture if set) +func DrawModelWires(model Model, position Vector3, scale float32, tint Color) { + js.Global.Get("Module").Call("_DrawModelWires", model, position, scale, tint) +} + +// DrawModelWiresEx - Draw a model wires (with texture if set) with extended parameters +func DrawModelWiresEx(model Model, position Vector3, rotationAxis Vector3, rotationAngle float32, scale Vector3, tint Color) { + js.Global.Get("Module").Call("_DrawModelWiresEx", model, position, rotationAxis, rotationAngle, scale, tint) +} + +// DrawBoundingBox - Draw bounding box (wires) +func DrawBoundingBox(box BoundingBox, color Color) { + js.Global.Get("Module").Call("_DrawBoundingBox", box, color) +} + +// DrawBillboard - Draw a billboard texture +func DrawBillboard(camera Camera, texture Texture2D, center Vector3, size float32, tint Color) { + js.Global.Get("Module").Call("_DrawBillboard", camera, texture, center, size, tint) +} + +// DrawBillboardRec - Draw a billboard texture defined by sourceRec +func DrawBillboardRec(camera Camera, texture Texture2D, sourceRec Rectangle, center Vector3, size float32, tint Color) { + js.Global.Get("Module").Call("_DrawBillboardRec", camera, texture, sourceRec, center, size, tint) +} + +// CalculateBoundingBox - Calculate mesh bounding box limits +func CalculateBoundingBox(mesh Mesh) BoundingBox { + return newBoundingBoxFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_CalculateBoundingBox").Unsafe())) +} + +// CheckCollisionSpheres - Detect collision between two spheres +func CheckCollisionSpheres(centerA Vector3, radiusA float32, centerB Vector3, radiusB float32) bool { + return js.Global.Get("Module").Call("_CheckCollisionSpheres", centerA, radiusA, centerB, radiusB).Bool() +} + +// CheckCollisionBoxes - Detect collision between two bounding boxes +func CheckCollisionBoxes(box1 BoundingBox, box2 BoundingBox) bool { + return js.Global.Get("Module").Call("_CheckCollisionBoxes", box1, box2).Bool() +} + +// CheckCollisionBoxSphere - Detect collision between box and sphere +func CheckCollisionBoxSphere(box BoundingBox, centerSphere Vector3, radiusSphere float32) bool { + return js.Global.Get("Module").Call("_CheckCollisionBoxSphere", box, centerSphere, radiusSphere).Bool() +} + +// CheckCollisionRaySphere - Detect collision between ray and sphere +func CheckCollisionRaySphere(ray Ray, spherePosition Vector3, sphereRadius float32) bool { + return js.Global.Get("Module").Call("_CheckCollisionRaySphere", ray, spherePosition, sphereRadius).Bool() +} + +// CheckCollisionRaySphereEx - Detect collision between ray and sphere with extended parameters and collision point detection +func CheckCollisionRaySphereEx(ray Ray, spherePosition Vector3, sphereRadius float32, collisionPoint Vector3) bool { + return js.Global.Get("Module").Call("_CheckCollisionRaySphereEx", ray, spherePosition, sphereRadius, collisionPoint).Bool() +} + +// CheckCollisionRayBox - Detect collision between ray and box +func CheckCollisionRayBox(ray Ray, box BoundingBox) bool { + return js.Global.Get("Module").Call("_CheckCollisionRayBox", ray, box).Bool() +} + +// LoadShader - Load a custom shader and bind default locations +func LoadShader(vsFileName string, fsFileName string) Shader { + return newShaderFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadShader", vsFileName, fsFileName).Unsafe())) +} + +// UnloadShader - Unload a custom shader from memory +func UnloadShader(shader Shader) { + js.Global.Get("Module").Call("_UnloadShader", shader) +} + +// GetShaderDefault - Get default shader +func GetShaderDefault() Shader { + return newShaderFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetShaderDefault").Unsafe())) +} + +// GetTextureDefault - Get default texture +func GetTextureDefault() *Texture2D { + v := newTexture2DFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetTextureDefault").Unsafe())) + return &v +} + +// GetShaderLocation - Get shader uniform location +func GetShaderLocation(shader Shader, uniformName string) int32 { + return int32(js.Global.Get("Module").Call("_GetShaderLocation", shader, uniformName).Int()) +} + +// SetShaderValue - Set shader uniform value (float) +func SetShaderValue(shader Shader, uniformLoc int32, value []float32, size int32) { + js.Global.Get("Module").Call("_SetShaderValue", shader, uniformLoc, value, size) +} + +// SetShaderValuei - Set shader uniform value (int) +func SetShaderValuei(shader Shader, uniformLoc int32, value []int32, size int32) { + js.Global.Get("Module").Call("_SetShaderValuei", shader, uniformLoc, value, size) +} + +// SetShaderValueMatrix - Set shader uniform value (matrix 4x4) +func SetShaderValueMatrix(shader Shader, uniformLoc int32, mat Matrix) { + js.Global.Get("Module").Call("_SetShaderValueMatrix", shader, uniformLoc, mat) +} + +// SetMatrixProjection - Set a custom projection matrix (replaces internal projection matrix) +func SetMatrixProjection(proj Matrix) { + js.Global.Get("Module").Call("_SetMatrixProjection", proj) +} + +// SetMatrixModelview - Set a custom modelview matrix (replaces internal modelview matrix) +func SetMatrixModelview(view Matrix) { + js.Global.Get("Module").Call("_SetMatrixModelview", view) +} + +// GenTextureCubemap - Generate cubemap texture from HDR texture +func GenTextureCubemap(shader Shader, skyHDR Texture2D, size int) Texture2D { + return newTexture2DFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenTextureCubemap", shader, skyHDR, size).Unsafe())) +} + +// GenTextureIrradiance - Generate irradiance texture using cubemap data +func GenTextureIrradiance(shader Shader, cubemap Texture2D, size int) Texture2D { + return newTexture2DFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenTextureIrradiance", shader, cubemap, size).Unsafe())) +} + +// GenTexturePrefilter - Generate prefilter texture using cubemap data +func GenTexturePrefilter(shader Shader, cubemap Texture2D, size int) Texture2D { + return newTexture2DFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenTexturePrefilter", shader, cubemap, size).Unsafe())) +} + +// GenTextureBRDF - Generate BRDF texture using cubemap data +func GenTextureBRDF(shader Shader, cubemap Texture2D, size int) Texture2D { + return newTexture2DFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenTextureBRDF", shader, cubemap, size).Unsafe())) +} + +// BeginShaderMode - Begin custom shader drawing +func BeginShaderMode(shader Shader) { + js.Global.Get("Module").Call("_BeginShaderMode", shader) +} + +// EndShaderMode - End custom shader drawing (use default shader) +func EndShaderMode() { + js.Global.Get("Module").Call("_EndShaderMode") +} + +// BeginBlendMode - Begin blending mode (alpha, additive, multiplied) +func BeginBlendMode(mode BlendMode) { + js.Global.Get("Module").Call("_BeginBlendMode", mode) +} + +// EndBlendMode - End blending mode (reset to default: alpha blending) +func EndBlendMode() { + js.Global.Get("Module").Call("_EndBlendMode") +} + +// GetVrDeviceInfo - Get VR device information for some standard devices +func GetVrDeviceInfo(vrDevice VrDevice) VrDeviceInfo { + return newVrDeviceInfoFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetVrDeviceInfo", vrDevice).Unsafe())) +} + +// InitVrSimulator - Init VR simulator for selected device +func InitVrSimulator(vrDeviceInfo VrDeviceInfo) { + js.Global.Get("Module").Call("_InitVrSimulator", vrDeviceInfo) +} + +// CloseVrSimulator - Close VR simulator for current device +func CloseVrSimulator() { + js.Global.Get("Module").Call("_CloseVrSimulator") +} + +// IsVrSimulatorReady - Detect if VR simulator is ready +func IsVrSimulatorReady() bool { + return js.Global.Get("Module").Call("_IsVrSimulatorReady").Bool() +} + +// UpdateVrTracking - Update VR tracking (position and orientation) and camera +func UpdateVrTracking(camera *Camera) { + js.Global.Get("Module").Call("_UpdateVrTracking", camera) +} + +// ToggleVrMode - Enable/Disable VR experience (device or simulator) +func ToggleVrMode() { + js.Global.Get("Module").Call("_ToggleVrMode") +} + +// BeginVrDrawing - Begin VR simulator stereo rendering +func BeginVrDrawing() { + js.Global.Get("Module").Call("_BeginVrDrawing") +} + +// EndVrDrawing - End VR simulator stereo rendering +func EndVrDrawing() { + js.Global.Get("Module").Call("_EndVrDrawing") +} + +// DrawPixel - Draw a pixel +func DrawPixel(posX, posY int32, color Color) { + js.Global.Get("Module").Call("_DrawPixel", posX, posY, color) +} + +// DrawPixelV - Draw a pixel (Vector version) +func DrawPixelV(position Vector2, color Color) { + js.Global.Get("Module").Call("_DrawPixelV", position, color) +} + +// DrawLine - Draw a line +func DrawLine(startPosX, startPosY, endPosX, endPosY int32, color Color) { + js.Global.Get("Module").Call("_DrawLine", startPosX, startPosY, endPosX, endPosY, color) +} + +// DrawLineV - Draw a line (Vector version) +func DrawLineV(startPos, endPos Vector2, color Color) { + js.Global.Get("Module").Call("_DrawLineV", startPos, endPos, color) +} + +// DrawLineEx - Draw a line defining thickness +func DrawLineEx(startPos, endPos Vector2, thick float32, color Color) { + js.Global.Get("Module").Call("_DrawLineEx", startPos, endPos, thick, color) +} + +// DrawLineBezier - Draw a line using cubic-bezier curves in-out +func DrawLineBezier(startPos, endPos Vector2, thick float32, color Color) { + js.Global.Get("Module").Call("_DrawLineBezier", startPos, endPos, thick, color) +} + +// DrawCircle - Draw a color-filled circle +func DrawCircle(centerX, centerY int32, radius float32, color Color) { + js.Global.Get("Module").Call("_DrawCircle", centerX, centerY, radius, color) +} + +// DrawCircleGradient - Draw a gradient-filled circle +func DrawCircleGradient(centerX, centerY int32, radius float32, color1, color2 Color) { + js.Global.Get("Module").Call("_DrawCircleGradient", centerX, centerY, radius, color1, color2) +} + +// DrawCircleV - Draw a color-filled circle (Vector version) +func DrawCircleV(center Vector2, radius float32, color Color) { + js.Global.Get("Module").Call("_DrawCircleV", center, radius, color) +} + +// DrawCircleLines - Draw circle outline +func DrawCircleLines(centerX, centerY int32, radius float32, color Color) { + js.Global.Get("Module").Call("_DrawCircleLines", centerX, centerY, radius, color) +} + +// DrawRectangle - Draw a color-filled rectangle +func DrawRectangle(posX, posY, width, height int32, color Color) { + js.Global.Get("Module").Call("_DrawRectangle", posX, posY, width, height, color) +} + +// DrawRectangleRec - Draw a color-filled rectangle +func DrawRectangleRec(rec Rectangle, color Color) { + js.Global.Get("Module").Call("_DrawRectangleRec", rec, color) +} + +// DrawRectanglePro - Draw a color-filled rectangle with pro parameters +func DrawRectanglePro(rec Rectangle, origin Vector2, rotation float32, color Color) { + js.Global.Get("Module").Call("_DrawRectanglePro", rec, origin, rotation, color) +} + +// DrawRectangleGradientV - Draw a vertical-gradient-filled rectangle +func DrawRectangleGradientV(posX, posY, width, height int32, color1, color2 Color) { + js.Global.Get("Module").Call("_DrawRectangleGradientV", posX, posY, width, height, color1, color2) +} + +// DrawRectangleGradientH - Draw a horizontal-gradient-filled rectangle +func DrawRectangleGradientH(posX, posY, width, height int32, color1, color2 Color) { + js.Global.Get("Module").Call("_DrawRectangleGradientH", posX, posY, width, height, color1, color2) +} + +// DrawRectangleGradientEx - Draw a gradient-filled rectangle with custom vertex colors +func DrawRectangleGradientEx(rec Rectangle, color1, color2, color3, color4 Color) { + js.Global.Get("Module").Call("_DrawRectangleGradientEx", rec, color1, color2, color3, color4) +} + +// DrawRectangleV - Draw a color-filled rectangle (Vector version) +func DrawRectangleV(position Vector2, size Vector2, color Color) { + js.Global.Get("Module").Call("_DrawRectangleV", position, size, color) +} + +// DrawRectangleLines - Draw rectangle outline +func DrawRectangleLines(posX, posY, width, height int32, color Color) { + js.Global.Get("Module").Call("_DrawRectangleLines", posX, posY, width, height, color) +} + +// DrawRectangleT - Draw rectangle using text character +func DrawRectangleT(posX, posY, width, height int32, color Color) { + js.Global.Get("Module").Call("_DrawRectangleT", posX, posY, width, height, color) +} + +// DrawTriangle - Draw a color-filled triangle +func DrawTriangle(v1, v2, v3 Vector2, color Color) { + js.Global.Get("Module").Call("_DrawTriangle", v1, v2, v3, color) +} + +// DrawTriangleLines - Draw triangle outline +func DrawTriangleLines(v1, v2, v3 Vector2, color Color) { + js.Global.Get("Module").Call("_DrawTriangleLines", v1, v2, v3, color) +} + +// DrawPoly - Draw a regular polygon (Vector version) +func DrawPoly(center Vector2, sides int32, radius, rotation float32, color Color) { + js.Global.Get("Module").Call("_DrawPoly", center, sides, radius, rotation, color) +} + +// DrawPolyEx - Draw a closed polygon defined by points +func DrawPolyEx(points []Vector2, numPoints int32, color Color) { + js.Global.Get("Module").Call("_DrawPolyEx", points, numPoints, color) +} + +// DrawPolyExLines - Draw polygon lines +func DrawPolyExLines(points []Vector2, numPoints int32, color Color) { + js.Global.Get("Module").Call("_DrawPolyExLines", points, numPoints, color) +} + +// CheckCollisionRecs - Check collision between two rectangles +func CheckCollisionRecs(rec1, rec2 Rectangle) bool { + return js.Global.Get("Module").Call("_CheckCollisionRecs", rec1, rec2).Bool() +} + +// CheckCollisionCircles - Check collision between two circles +func CheckCollisionCircles(center1 Vector2, radius1 float32, center2 Vector2, radius2 float32) bool { + return js.Global.Get("Module").Call("_CheckCollisionCircles", center1, radius1, center2, radius2).Bool() +} + +// CheckCollisionCircleRec - Check collision between circle and rectangle +func CheckCollisionCircleRec(center Vector2, radius float32, rec Rectangle) bool { + return js.Global.Get("Module").Call("_CheckCollisionCircleRec", center, radius, rec).Bool() +} + +// GetCollisionRec - Get collision rectangle for two rectangles collision +func GetCollisionRec(rec1, rec2 Rectangle) Rectangle { + return newRectangleFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetCollisionRec", rec1, rec2).Unsafe())) +} + +// CheckCollisionPointRec - Check if point is inside rectangle +func CheckCollisionPointRec(point Vector2, rec Rectangle) bool { + return js.Global.Get("Module").Call("_CheckCollisionPointRec", point, rec).Bool() +} + +// CheckCollisionPointCircle - Check if point is inside circle +func CheckCollisionPointCircle(point Vector2, center Vector2, radius float32) bool { + return js.Global.Get("Module").Call("_CheckCollisionPointCircle", point, center, radius).Bool() +} + +// CheckCollisionPointTriangle - Check if point is inside a triangle +func CheckCollisionPointTriangle(point, p1, p2, p3 Vector2) bool { + return js.Global.Get("Module").Call("_CheckCollisionPointTriangle", point, p1, p2, p3).Bool() +} + +// GetDefaultFont - Get the default SpriteFont +func GetDefaultFont() SpriteFont { + return newSpriteFontFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetDefaultFont").Unsafe())) +} + +// LoadSpriteFont - Load a SpriteFont image into GPU memory (VRAM) +func LoadSpriteFont(fileName string) SpriteFont { + return newSpriteFontFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadSpriteFont", fileName).Unsafe())) +} + +// LoadSpriteFontEx - Load SpriteFont from file with extended parameters +func LoadSpriteFontEx(fileName string, fontSize int32, charsCount int32, fontChars *int32) SpriteFont { + return newSpriteFontFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadSpriteFontEx", fileName, fontSize, charsCount, fontChars).Unsafe())) +} + +// UnloadSpriteFont - Unload SpriteFont from GPU memory (VRAM) +func UnloadSpriteFont(spriteFont SpriteFont) { + js.Global.Get("Module").Call("_UnloadSpriteFont", spriteFont) +} + +// DrawText - Draw text (using default font) +func DrawText(text string, posX int32, posY int32, fontSize int32, color Color) { + js.Global.Get("Module").Call("_DrawText", text, posX, posY, fontSize, color) +} + +// DrawTextEx - Draw text using SpriteFont and additional parameters +func DrawTextEx(spriteFont SpriteFont, text string, position Vector2, fontSize float32, spacing int32, tint Color) { + js.Global.Get("Module").Call("_DrawTextEx", spriteFont, text, position, fontSize, spacing, tint) +} + +// MeasureText - Measure string width for default font +func MeasureText(text string, fontSize int32) int32 { + return int32(js.Global.Get("Module").Call("_MeasureText", text, fontSize).Int()) +} + +// MeasureTextEx - Measure string size for SpriteFont +func MeasureTextEx(spriteFont SpriteFont, text string, fontSize float32, spacing int32) Vector2 { + return newVector2FromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_MeasureTextEx", spriteFont, text, fontSize, spacing).Unsafe())) +} + +// DrawFPS - Shows current FPS +func DrawFPS(posX int32, posY int32) { + js.Global.Get("Module").Call("_DrawFPS", posX, posY) +} + +// LoadImage - Load an image into CPU memory (RAM) +func LoadImage(fileName string) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadImage", fileName).Unsafe())) +} + +// LoadImageEx - Load image data from Color array data (RGBA - 32bit) +func LoadImageEx(pixels []Color, width, height int32) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadImageEx", pixels, width, height).Unsafe())) +} + +// LoadImagePro - Load image from raw data with parameters +func LoadImagePro(data []byte, width, height int32, format TextureFormat) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadImagePro", data, width, height, format).Unsafe())) +} + +// LoadImageRaw - Load image data from RAW file +func LoadImageRaw(fileName string, width, height int32, format TextureFormat, headerSize int32) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadImageRaw", fileName, width, height, format, headerSize).Unsafe())) +} + +// LoadTexture - Load an image as texture into GPU memory +func LoadTexture(fileName string) Texture2D { + return newTexture2DFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadTexture", fileName).Unsafe())) +} + +// LoadTextureFromImage - Load a texture from image data +func LoadTextureFromImage(image *Image) Texture2D { + return newTexture2DFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadTextureFromImage", image).Unsafe())) +} + +// LoadRenderTexture - Load a texture to be used for rendering +func LoadRenderTexture(width, height int32) RenderTexture2D { + return newRenderTexture2DFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_LoadRenderTexture", width, height).Unsafe())) +} + +// UnloadImage - Unload image from CPU memory (RAM) +func UnloadImage(image *Image) { + js.Global.Get("Module").Call("_UnloadImage", image) +} + +// UnloadTexture - Unload texture from GPU memory +func UnloadTexture(texture Texture2D) { + js.Global.Get("Module").Call("_UnloadTexture", texture) +} + +// UnloadRenderTexture - Unload render texture from GPU memory +func UnloadRenderTexture(target RenderTexture2D) { + js.Global.Get("Module").Call("_UnloadRenderTexture", target) +} + +// GetImageData - Get pixel data from image +func GetImageData(image *Image) []byte { + return js.Global.Get("Module").Call("_GetImageData", image).Interface().([]byte) +} + +// GetTextureData - Get pixel data from GPU texture and return an Image +func GetTextureData(texture Texture2D) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GetTextureData", texture).Unsafe())) +} + +// UpdateTexture - Update GPU texture with new data +func UpdateTexture(texture Texture2D, pixels []byte) { + js.Global.Get("Module").Call("_UpdateTexture", texture, pixels) +} + +// SaveImageAs - Save image to a PNG file +func SaveImageAs(name string, image Image) { + js.Global.Get("Module").Call("_SaveImageAs", name, image) +} + +// ImageToPOT - Convert image to POT (power-of-two) +func ImageToPOT(image *Image, fillColor Color) { + js.Global.Get("Module").Call("_ImageToPot", image, fillColor) +} + +// ImageFormat - Convert image data to desired format +func ImageFormat(image *Image, newFormat int32) { + js.Global.Get("Module").Call("_ImageFormat", image, newFormat) +} + +// ImageAlphaMask - Apply alpha mask to image +func ImageAlphaMask(image, alphaMask *Image) { + js.Global.Get("Module").Call("_ImageAlphaMask", image, alphaMask) +} + +// ImageDither - Dither image data to 16bpp or lower (Floyd-Steinberg dithering) +func ImageDither(image *Image, rBpp, gBpp, bBpp, aBpp int32) { + js.Global.Get("Module").Call("_ImageDither", image, rBpp, gBpp, bBpp, aBpp) +} + +// ImageCopy - Create an image duplicate (useful for transformations) +func ImageCopy(image *Image) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_ImageCopy", image).Unsafe())) +} + +// ImageCrop - Crop an image to a defined rectangle +func ImageCrop(image *Image, crop Rectangle) { + js.Global.Get("Module").Call("_ImageCrop", image, crop) +} + +// ImageResize - Resize an image (bilinear filtering) +func ImageResize(image *Image, newWidth, newHeight int32) { + js.Global.Get("Module").Call("_ImageResize", image, newWidth, newHeight) +} + +// ImageResizeNN - Resize an image (Nearest-Neighbor scaling algorithm) +func ImageResizeNN(image *Image, newWidth, newHeight int32) { + js.Global.Get("Module").Call("_ImageResizeNN", image, newWidth, newHeight) +} + +// ImageText - Create an image from text (default font) +func ImageText(text string, fontSize int32, color Color) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_ImageText", text, fontSize, color).Unsafe())) +} + +// ImageTextEx - Create an image from text (custom sprite font) +func ImageTextEx(font SpriteFont, text string, fontSize float32, spacing int32, tint Color) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_ImageTextEx", font, text, fontSize, spacing, tint).Unsafe())) +} + +// ImageDraw - Draw a source image within a destination image +func ImageDraw(dst, src *Image, srcRec, dstRec Rectangle) { + js.Global.Get("Module").Call("_ImageDraw", dst, src, srcRec, dstRec) +} + +// ImageDrawText - Draw text (default font) within an image (destination) +func ImageDrawText(dst *Image, position Vector2, text string, fontSize int32, color Color) { + js.Global.Get("Module").Call("_ImageDrawText", dst, position, text, fontSize, color) +} + +// ImageDrawTextEx - Draw text (custom sprite font) within an image (destination) +func ImageDrawTextEx(dst *Image, position Vector2, font SpriteFont, text string, fontSize float32, spacing int32, color Color) { + js.Global.Get("Module").Call("_ImageDrawTextEx", dst, position, font, text, fontSize, spacing, color) +} + +// ImageFlipVertical - Flip image vertically +func ImageFlipVertical(image *Image) { + js.Global.Get("Module").Call("_ImageFlipVertical", image) +} + +// ImageFlipHorizontal - Flip image horizontally +func ImageFlipHorizontal(image *Image) { + js.Global.Get("Module").Call("_ImageFlipHorizontal", image) +} + +// ImageColorTint - Modify image color: tint +func ImageColorTint(image *Image, color Color) { + js.Global.Get("Module").Call("_ImageColorTint", image, color) +} + +// ImageColorInvert - Modify image color: invert +func ImageColorInvert(image *Image) { + js.Global.Get("Module").Call("_ImageColorInvert", image) +} + +// ImageColorGrayscale - Modify image color: grayscale +func ImageColorGrayscale(image *Image) { + js.Global.Get("Module").Call("_ImageColorGrayscale", image) +} + +// ImageColorContrast - Modify image color: contrast (-100 to 100) +func ImageColorContrast(image *Image, contrast float32) { + js.Global.Get("Module").Call("_ImageColorContrast", image, contrast) +} + +// ImageColorBrightness - Modify image color: brightness (-255 to 255) +func ImageColorBrightness(image *Image, brightness int32) { + js.Global.Get("Module").Call("_ImageColorBrightness", image, brightness) +} + +// GenImageColor - Generate image: plain color +func GenImageColor(width, height int, color Color) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenImageColor", width, height, color).Unsafe())) +} + +// GenImageGradientV - Generate image: vertical gradient +func GenImageGradientV(width, height int, top, bottom Color) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenImageGradientV", width, height, top, bottom).Unsafe())) +} + +// GenImageGradientH - Generate image: horizontal gradient +func GenImageGradientH(width, height int, left, right Color) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenImageGradientH", width, height, left, right).Unsafe())) +} + +// GenImageGradientRadial - Generate image: radial gradient +func GenImageGradientRadial(width, height int, density float32, inner, outer Color) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenImageGradientRadial", width, height, density, inner, outer).Unsafe())) +} + +// GenImageChecked - Generate image: checked +func GenImageChecked(width, height, checksX, checksY int, col1, col2 Color) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenImageChecked", width, height, checksX, checksY, col1, col2).Unsafe())) +} + +// GenImageWhiteNoise - Generate image: white noise +func GenImageWhiteNoise(width, height int, factor float32) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenImageWhiteNoise", width, height, factor).Unsafe())) +} + +// GenImagePerlinNoise - Generate image: perlin noise +func GenImagePerlinNoise(width, height int, scale float32) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenImagePerlinNoise", width, height, scale).Unsafe())) +} + +// GenImageCellular - Generate image: cellular algorithm. Bigger tileSize means bigger cells +func GenImageCellular(width, height, tileSize int) *Image { + return newImageFromPointer(unsafe.Pointer(js.Global.Get("Module").Call("_GenImageCellular", width, height, tileSize).Unsafe())) +} + +// GenTextureMipmaps - Generate GPU mipmaps for a texture +func GenTextureMipmaps(texture *Texture2D) { + js.Global.Get("Module").Call("_GenTextureMipmaps", texture) +} + +// SetTextureFilter - Set texture scaling filter mode +func SetTextureFilter(texture Texture2D, filterMode TextureFilterMode) { + js.Global.Get("Module").Call("_SetTextureFilter", texture, filterMode) +} + +// SetTextureWrap - Set texture wrapping mode +func SetTextureWrap(texture Texture2D, wrapMode TextureWrapMode) { + js.Global.Get("Module").Call("_SetTextureWrap", texture, wrapMode) +} + +// DrawTexture - Draw a Texture2D +func DrawTexture(texture Texture2D, posX int32, posY int32, tint Color) { + js.Global.Get("Module").Call("_DrawTexture", texture, posX, posY, tint) +} + +// DrawTextureV - Draw a Texture2D with position defined as Vector2 +func DrawTextureV(texture Texture2D, position Vector2, tint Color) { + js.Global.Get("Module").Call("_DrawTextureV", texture, position, tint) +} + +// DrawTextureEx - Draw a Texture2D with extended parameters +func DrawTextureEx(texture Texture2D, position Vector2, rotation, scale float32, tint Color) { + js.Global.Get("Module").Call("_DrawTextureEx", texture, position, rotation, scale, tint) +} + +// DrawTextureRec - Draw a part of a texture defined by a rectangle +func DrawTextureRec(texture Texture2D, sourceRec Rectangle, position Vector2, tint Color) { + js.Global.Get("Module").Call("_DrawTextureRec", texture, sourceRec, position, tint) +} + +// DrawTexturePro - Draw a part of a texture defined by a rectangle with 'pro' parameters +func DrawTexturePro(texture Texture2D, sourceRec, destRec Rectangle, origin Vector2, rotation float32, tint Color) { + js.Global.Get("Module").Call("_DrawTexturePro", texture, sourceRec, destRec, origin, rotation, tint) +} diff --git a/raylib/shaders.go b/raylib/shaders.go index 61893f9..a147a23 100644 --- a/raylib/shaders.go +++ b/raylib/shaders.go @@ -15,21 +15,11 @@ func (v *VrDeviceInfo) cptr() *C.VrDeviceInfo { return (*C.VrDeviceInfo)(unsafe.Pointer(v)) } -// newVrDeviceInfoFromPointer - Returns new VrDeviceInfo from pointer -func newVrDeviceInfoFromPointer(ptr unsafe.Pointer) VrDeviceInfo { - return *(*VrDeviceInfo)(ptr) -} - // cptr returns C pointer func (s *Shader) cptr() *C.Shader { return (*C.Shader)(unsafe.Pointer(s)) } -// newShaderFromPointer - Returns new Shader from pointer -func newShaderFromPointer(ptr unsafe.Pointer) Shader { - return *(*Shader)(ptr) -} - // LoadShader - Load a custom shader and bind default locations func LoadShader(vsFileName string, fsFileName string) Shader { cvsFileName := C.CString(vsFileName) diff --git a/raylib/text.go b/raylib/text.go index 253fd11..8800774 100644 --- a/raylib/text.go +++ b/raylib/text.go @@ -14,21 +14,11 @@ func (c *CharInfo) cptr() *C.CharInfo { return (*C.CharInfo)(unsafe.Pointer(c)) } -// newCharInfoFromPointer - Returns new SpriteFont from pointer -func newCharInfoFromPointer(ptr unsafe.Pointer) CharInfo { - return *(*CharInfo)(ptr) -} - // cptr returns C pointer func (s *SpriteFont) cptr() *C.SpriteFont { return (*C.SpriteFont)(unsafe.Pointer(s)) } -// newSpriteFontFromPointer - Returns new SpriteFont from pointer -func newSpriteFontFromPointer(ptr unsafe.Pointer) SpriteFont { - return *(*SpriteFont)(ptr) -} - // GetDefaultFont - Get the default SpriteFont func GetDefaultFont() SpriteFont { ret := C.GetDefaultFont() diff --git a/raylib/textures.go b/raylib/textures.go index ab05b50..438a48d 100644 --- a/raylib/textures.go +++ b/raylib/textures.go @@ -17,31 +17,16 @@ func (i *Image) cptr() *C.Image { return (*C.Image)(unsafe.Pointer(i)) } -// newImageFromPointer - Returns new Image from pointer -func newImageFromPointer(ptr unsafe.Pointer) *Image { - return (*Image)(ptr) -} - // cptr returns C pointer func (t *Texture2D) cptr() *C.Texture2D { return (*C.Texture2D)(unsafe.Pointer(t)) } -// newTexture2DFromPointer - Returns new Texture2D from pointer -func newTexture2DFromPointer(ptr unsafe.Pointer) Texture2D { - return *(*Texture2D)(ptr) -} - // cptr returns C pointer func (r *RenderTexture2D) cptr() *C.RenderTexture2D { return (*C.RenderTexture2D)(unsafe.Pointer(r)) } -// newRenderTexture2DFromPointer - Returns new RenderTexture2D from pointer -func newRenderTexture2DFromPointer(ptr unsafe.Pointer) RenderTexture2D { - return *(*RenderTexture2D)(ptr) -} - // LoadImage - Load an image into CPU memory (RAM) func LoadImage(fileName string) *Image { cfileName := C.CString(fileName) diff --git a/raylib/utils.go b/raylib/utils.go index 15f4033..2ca8842 100644 --- a/raylib/utils.go +++ b/raylib/utils.go @@ -1,4 +1,4 @@ -// +build !android,!windows,!js +// +build !android,!windows package raylib From 1fec9fe2f08178c376b538f8a56eba7e612d583b Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 8 Dec 2017 06:17:42 +0100 Subject: [PATCH 41/48] Add SetMainLoop() function --- raylib/platform_android.go | 4 ++++ raylib/platform_arm.go | 4 ++++ raylib/platform_desktop.go | 4 ++++ raylib/raylib_js.go | 5 +++++ 4 files changed, 17 insertions(+) diff --git a/raylib/platform_android.go b/raylib/platform_android.go index 31f3841..51f30d7 100644 --- a/raylib/platform_android.go +++ b/raylib/platform_android.go @@ -41,6 +41,10 @@ func SetCallbackFunc(callback func(unsafe.Pointer)) { callbackHolder = callback } +// SetMainLoop - Sets main loop function +func SetMainLoop(f func(), fps, simulateInfiniteLoop int) { +} + //export androidMain func androidMain(app *C.struct_android_app) { if callbackHolder != nil { diff --git a/raylib/platform_arm.go b/raylib/platform_arm.go index 241d733..08f22c0 100644 --- a/raylib/platform_arm.go +++ b/raylib/platform_arm.go @@ -32,6 +32,10 @@ func SetCallbackFunc(func(unsafe.Pointer)) { return } +// SetMainLoop - Sets main loop function +func SetMainLoop(f func(), fps, simulateInfiniteLoop int) { +} + // ShowCursor - Shows cursor func ShowCursor() { C.ShowCursor() diff --git a/raylib/platform_desktop.go b/raylib/platform_desktop.go index cedc2d4..ebf9a84 100644 --- a/raylib/platform_desktop.go +++ b/raylib/platform_desktop.go @@ -32,6 +32,10 @@ func SetCallbackFunc(func(unsafe.Pointer)) { return } +// SetMainLoop - Sets main loop function +func SetMainLoop(f func(), fps, simulateInfiniteLoop int) { +} + // ShowCursor - Shows cursor func ShowCursor() { C.ShowCursor() diff --git a/raylib/raylib_js.go b/raylib/raylib_js.go index 4eb13a1..dbe95eb 100644 --- a/raylib/raylib_js.go +++ b/raylib/raylib_js.go @@ -19,6 +19,11 @@ func InitWindow(width int32, height int32, t interface{}) { func SetCallbackFunc(func(unsafe.Pointer)) { } +// SetMainLoop - Sets main loop function +func SetMainLoop(f func(), fps, simulateInfiniteLoop int) { + js.Global.Get("Module").Call("_emscripten_set_main_loop", f, fps, simulateInfiniteLoop) +} + // ShowCursor - Shows cursor func ShowCursor() { } From 8759f0d9bd77e31c314da17227f192095a2f52b7 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 8 Dec 2017 15:23:52 +0100 Subject: [PATCH 42/48] Move some functions to platform_web.go --- raylib/platform_web.go | 69 ++++++++++++++++++++++++++++++++++++++++++ raylib/raylib_js.go | 49 ------------------------------ 2 files changed, 69 insertions(+), 49 deletions(-) create mode 100644 raylib/platform_web.go diff --git a/raylib/platform_web.go b/raylib/platform_web.go new file mode 100644 index 0000000..85b813e --- /dev/null +++ b/raylib/platform_web.go @@ -0,0 +1,69 @@ +// +build js + +package raylib + +import ( + "os" + "unsafe" + + "github.com/gopherjs/gopherjs/js" +) + +// InitWindow - Initialize Window and OpenGL Graphics +func InitWindow(width int32, height int32, t interface{}) { + js.Global.Get("Module").Call("_InitWindow", width, height, t.(string)) +} + +// SetCallbackFunc - Sets callback function +func SetCallbackFunc(func(unsafe.Pointer)) { +} + +// SetMainLoop - Sets main loop function +func SetMainLoop(fn func(), fps int, infinite bool) { + js.Global.Set("_go_update_function", fn) + js.Global.Get("Module").Call("_emscripten_set_main_loop_go", fps, infinite) +} + +// ShowCursor - Shows cursor +func ShowCursor() { +} + +// HideCursor - Hides cursor +func HideCursor() { +} + +// IsCursorHidden - Returns true if cursor is not visible +func IsCursorHidden() bool { + return false +} + +// EnableCursor - Enables cursor +func EnableCursor() { +} + +// DisableCursor - Disables cursor +func DisableCursor() { +} + +// IsFileDropped - Check if a file have been dropped into window +func IsFileDropped() bool { + return false +} + +// GetDroppedFiles - Retrieve dropped files into window +func GetDroppedFiles(count *int32) (f []string) { + return +} + +// ClearDroppedFiles - Clear dropped files paths buffer +func ClearDroppedFiles() { +} + +// OpenAsset - Open asset +func OpenAsset(name string) (Asset, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + return f, nil +} diff --git a/raylib/raylib_js.go b/raylib/raylib_js.go index dbe95eb..72610c5 100644 --- a/raylib/raylib_js.go +++ b/raylib/raylib_js.go @@ -10,55 +10,6 @@ import ( "github.com/gopherjs/gopherjs/js" ) -// InitWindow - Initialize Window and OpenGL Graphics -func InitWindow(width int32, height int32, t interface{}) { - js.Global.Get("Module").Call("_InitWindow", width, height, t.(string)) -} - -// SetCallbackFunc - Sets callback function -func SetCallbackFunc(func(unsafe.Pointer)) { -} - -// SetMainLoop - Sets main loop function -func SetMainLoop(f func(), fps, simulateInfiniteLoop int) { - js.Global.Get("Module").Call("_emscripten_set_main_loop", f, fps, simulateInfiniteLoop) -} - -// ShowCursor - Shows cursor -func ShowCursor() { -} - -// HideCursor - Hides cursor -func HideCursor() { -} - -// IsCursorHidden - Returns true if cursor is not visible -func IsCursorHidden() bool { - return false -} - -// EnableCursor - Enables cursor -func EnableCursor() { -} - -// DisableCursor - Disables cursor -func DisableCursor() { -} - -// IsFileDropped - Check if a file have been dropped into window -func IsFileDropped() bool { - return false -} - -// GetDroppedFiles - Retrieve dropped files into window -func GetDroppedFiles(count *int32) (f []string) { - return -} - -// ClearDroppedFiles - Clear dropped files paths buffer -func ClearDroppedFiles() { -} - // InitAudioDevice - Initialize audio device and context func InitAudioDevice() { js.Global.Get("Module").Call("_InitAudioDevice") From da8f828e2d8be389a06047715aaa44f02c41083b Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Fri, 8 Dec 2017 15:26:54 +0100 Subject: [PATCH 43/48] Add web template and mainloop js --- raylib/external/scripts/emcc-generate-js.sh | 5 +- raylib/external/web/index.html | 240 ++++++++++++++++++++ raylib/external/web/mainloop.js | 7 + 3 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 raylib/external/web/index.html create mode 100644 raylib/external/web/mainloop.js diff --git a/raylib/external/scripts/emcc-generate-js.sh b/raylib/external/scripts/emcc-generate-js.sh index be71a38..bea2a78 100755 --- a/raylib/external/scripts/emcc-generate-js.sh +++ b/raylib/external/scripts/emcc-generate-js.sh @@ -1,5 +1,6 @@ #!/bin/sh +BASE_DIR="${GOPATH}/src/github.com/gen2brain/raylib-go/raylib" EMCC_EXPORT="['_InitWindow','_CloseWindow','_WindowShouldClose','_IsWindowMinimized','_ToggleFullscreen','_SetWindowIcon','_SetWindowTitle','_SetWindowPosition','_SetWindowMonitor','_SetWindowMinSize','_GetScreenWidth','_GetScreenHeight','_ShowCursor','_HideCursor','_IsCursorHidden','_EnableCursor','_DisableCursor','_ClearBackground','_BeginDrawing','_EndDrawing','_Begin2dMode','_End2dMode','_Begin3dMode','_End3dMode','_BeginTextureMode','_EndTextureMode','_GetMouseRay','_GetWorldToScreen','_GetCameraMatrix','_SetTargetFPS','_GetFPS','_GetFrameTime','_GetHexValue','_GetColor','_ColorToFloat','_Vector3ToFloat','_MatrixToFloat','_Vector3Zero','_Vector3One','_MatrixIdentity','_ShowLogo','_SetConfigFlags','_TraceLog','_TakeScreenshot','_GetRandomValue','_StorageSaveValue','_StorageLoadValue','_IsKeyPressed','_IsKeyDown','_IsKeyReleased','_IsKeyUp','_GetKeyPressed','_SetExitKey','_IsGamepadAvailable','_IsGamepadName','_IsGamepadButtonPressed','_IsGamepadButtonDown','_IsGamepadButtonReleased','_IsGamepadButtonUp','_GetGamepadButtonPressed','_GetGamepadAxisCount','_GetGamepadAxisMovement','_IsMouseButtonPressed','_IsMouseButtonDown','_IsMouseButtonReleased','_IsMouseButtonUp','_GetMouseX','_GetMouseY','_GetMousePosition','_SetMousePosition','_GetMouseWheelMove','_GetTouchX','_GetTouchY','_GetTouchPosition','_SetGesturesEnabled','_IsGestureDetected','_GetGestureDetected','_GetTouchPointsCount','_GetGestureHoldDuration','_GetGestureDragVector','_GetGestureDragAngle','_GetGesturePinchVector','_GetGesturePinchAngle','_SetCameraMode','_UpdateCamera','_SetCameraPanControl','_SetCameraAltControl','_SetCameraSmoothZoomControl','_SetCameraMoveControls','_DrawPixel','_DrawPixelV','_DrawLine','_DrawLineV','_DrawLineEx','_DrawLineBezier','_DrawCircle','_DrawCircleGradient','_DrawCircleV','_DrawCircleLines','_DrawRectangle','_DrawRectangleRec','_DrawRectanglePro','_DrawRectangleGradientV','_DrawRectangleGradientH','_DrawRectangleGradientEx','_DrawRectangleV','_DrawRectangleLines','_DrawRectangleT','_DrawTriangle','_DrawTriangleLines','_DrawPoly','_DrawPolyEx','_DrawPolyExLines','_CheckCollisionRecs','_CheckCollisionCircles','_CheckCollisionCircleRec','_GetCollisionRec','_CheckCollisionPointRec','_CheckCollisionPointCircle','_CheckCollisionPointTriangle','_LoadImage','_LoadImageEx','_LoadImagePro','_LoadImageRaw','_LoadTexture','_LoadTextureFromImage','_LoadRenderTexture','_UnloadImage','_UnloadTexture','_UnloadRenderTexture','_GetImageData','_GetTextureData','_UpdateTexture','_SaveImageAs','_ImageToPOT','_ImageFormat','_ImageAlphaMask','_ImageDither','_ImageCopy','_ImageCrop','_ImageResize','_ImageResizeNN','_ImageText','_ImageTextEx','_ImageDraw','_ImageDrawText','_ImageDrawTextEx','_ImageFlipVertical','_ImageFlipHorizontal','_ImageColorTint','_ImageColorInvert','_ImageColorGrayscale','_ImageColorContrast','_ImageColorBrightness','_GenImageColor','_GenImageGradientV','_GenImageGradientH','_GenImageGradientRadial','_GenImageChecked','_GenImageWhiteNoise','_GenImagePerlinNoise','_GenImageCellular','_GenTextureMipmaps','_SetTextureFilter','_SetTextureWrap','_DrawTexture','_DrawTextureV','_DrawTextureEx','_DrawTextureRec','_DrawTexturePro','_GetDefaultFont','_LoadSpriteFont','_LoadSpriteFontEx','_UnloadSpriteFont','_DrawFPS','_DrawText','_DrawTextEx','_MeasureText','_MeasureTextEx','_DrawLine3D','_DrawCircle3D','_DrawCube','_DrawCubeV','_DrawCubeWires','_DrawCubeTexture','_DrawSphere','_DrawSphereEx','_DrawSphereWires','_DrawCylinder','_DrawCylinderWires','_DrawPlane','_DrawRay','_DrawGrid','_DrawGizmo','_LoadModel','_LoadModelFromMesh','_UnloadModel','_LoadMesh','_UnloadMesh','_GenMeshPlane','_GenMeshCube','_GenMeshSphere','_GenMeshHemiSphere','_GenMeshCylinder','_GenMeshTorus','_GenMeshKnot','_GenMeshHeightmap','_GenMeshCubicmap','_LoadMaterial','_LoadMaterialDefault','_UnloadMaterial','_DrawModel','_DrawModelEx','_DrawModelWires','_DrawModelWiresEx','_DrawBoundingBox','_DrawBillboard','_DrawBillboardRec','_CalculateBoundingBox','_CheckCollisionSpheres','_CheckCollisionBoxes','_CheckCollisionBoxSphere','_CheckCollisionRaySphere','_CheckCollisionRaySphereEx','_CheckCollisionRayBox','_GetCollisionRayMesh','_GetCollisionRayTriangle','_GetCollisionRayGround','_LoadText','_LoadShader','_UnloadShader','_GetShaderDefault','_GetTextureDefault','_GetShaderLocation','_SetShaderValue','_SetShaderValuei','_SetShaderValueMatrix','_SetMatrixProjection','_SetMatrixModelview','_GenTextureCubemap','_GenTextureIrradiance','_GenTexturePrefilter','_GenTextureBRDF','_BeginShaderMode','_EndShaderMode','_BeginBlendMode','_EndBlendMode','_GetVrDeviceInfo','_InitVrSimulator','_CloseVrSimulator','_IsVrSimulatorReady','_SetVrDistortionShader','_UpdateVrTracking','_ToggleVrMode','_BeginVrDrawing','_EndVrDrawing','_InitAudioDevice','_CloseAudioDevice','_IsAudioDeviceReady','_SetMasterVolume','_LoadWave','_LoadWaveEx','_LoadSound','_LoadSoundFromWave','_UpdateSound','_UnloadWave','_UnloadSound','_PlaySound','_PauseSound','_ResumeSound','_StopSound','_IsSoundPlaying','_SetSoundVolume','_SetSoundPitch','_WaveFormat','_WaveCopy','_WaveCrop','_GetWaveData','_LoadMusicStream','_UnloadMusicStream','_PlayMusicStream','_UpdateMusicStream','_StopMusicStream','_PauseMusicStream','_ResumeMusicStream','_IsMusicPlaying','_SetMusicVolume','_SetMusicPitch','_SetMusicLoopCount','_GetMusicTimeLength','_GetMusicTimePlayed','_InitAudioStream','_UpdateAudioStream','_CloseAudioStream','_IsAudioBufferProcessed','_PlayAudioStream','_PauseAudioStream','_ResumeAudioStream','_IsAudioStreamPlaying','_StopAudioStream','_SetAudioStreamVolume','_SetAudioStreamPitch']" RAYLIB_SRC=( "audio.c" "core.c" "mini_al.c" "models.c" "rlgl.c" "shapes.c" "text.c" "textures.c" "utils.c" "external/stb_vorbis.c" ) RAYLIB_OBJ=( "audio.o" "core.o" "mini_al.o" "models.o" "rlgl.o" "shapes.o" "text.o" "textures.o" "utils.o" "external/stb_vorbis.o" ) @@ -8,6 +9,6 @@ for i in "${RAYLIB_SRC[@]}"; do emcc "${i}" -o "${i/.c}.o" -O1 -std=c99 -D_DEFAULT_SOURCE -fgnu89-inline -Wno-missing-braces -Wno-unused-function -Wno-unused-variable -Iexternal -Iexternal/glfw/include -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2 done -emcc `echo "${RAYLIB_OBJ[*]}"` -o raylib.js -g3 -s USE_GLFW=3 -s EXPORTED_FUNCTIONS=$EMCC_EXPORT +emcc `echo "${RAYLIB_OBJ[*]}"` -o raylib.js --post-js "${BASE_DIR}/external/web/mainloop.js" -g3 -s USE_GLFW=3 -s EXPORTED_FUNCTIONS=$EMCC_EXPORT -rm "${GOPATH}"/src/github.com/gen2brain/raylib-go/raylib/*.o "${GOPATH}"/src/github.com/gen2brain/raylib-go/raylib/external/*.o +rm "${BASE_DIR}"/*.o "${BASE_DIR}"/external/*.o diff --git a/raylib/external/web/index.html b/raylib/external/web/index.html new file mode 100644 index 0000000..f61ee38 --- /dev/null +++ b/raylib/external/web/index.html @@ -0,0 +1,240 @@ + + + + + + + raylib-go HTML5 + + + + + + + + + + + + + +
+ + +
+
Downloading...
+ + + + + +
+ +
+
+ +
+ +
+ + + + + + + + + diff --git a/raylib/external/web/mainloop.js b/raylib/external/web/mainloop.js new file mode 100644 index 0000000..9c3a3d0 --- /dev/null +++ b/raylib/external/web/mainloop.js @@ -0,0 +1,7 @@ +function _emscripten_set_main_loop_go(fps, simulateInfiniteLoop) { + if (_go_update_function !== undefined && typeof _go_update_function == 'function') { + _emscripten_set_main_loop(_go_update_function, fps, simulateInfiniteLoop); + } +} + +Module["_emscripten_set_main_loop_go"] = _emscripten_set_main_loop_go; From f0ea8218a7bb23cb38abdc5b01f3a8c3ab84da02 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Sat, 9 Dec 2017 18:18:56 +0100 Subject: [PATCH 44/48] Add _emscripten_set_main_loop to EMCC_EXPORT --- raylib/external/scripts/emcc-generate-js.sh | 9 ++++++--- raylib/external/web/mainloop.js | 7 ------- raylib/platform_web.go | 3 +-- 3 files changed, 7 insertions(+), 12 deletions(-) delete mode 100644 raylib/external/web/mainloop.js diff --git a/raylib/external/scripts/emcc-generate-js.sh b/raylib/external/scripts/emcc-generate-js.sh index bea2a78..9889ad7 100755 --- a/raylib/external/scripts/emcc-generate-js.sh +++ b/raylib/external/scripts/emcc-generate-js.sh @@ -1,7 +1,7 @@ #!/bin/sh BASE_DIR="${GOPATH}/src/github.com/gen2brain/raylib-go/raylib" -EMCC_EXPORT="['_InitWindow','_CloseWindow','_WindowShouldClose','_IsWindowMinimized','_ToggleFullscreen','_SetWindowIcon','_SetWindowTitle','_SetWindowPosition','_SetWindowMonitor','_SetWindowMinSize','_GetScreenWidth','_GetScreenHeight','_ShowCursor','_HideCursor','_IsCursorHidden','_EnableCursor','_DisableCursor','_ClearBackground','_BeginDrawing','_EndDrawing','_Begin2dMode','_End2dMode','_Begin3dMode','_End3dMode','_BeginTextureMode','_EndTextureMode','_GetMouseRay','_GetWorldToScreen','_GetCameraMatrix','_SetTargetFPS','_GetFPS','_GetFrameTime','_GetHexValue','_GetColor','_ColorToFloat','_Vector3ToFloat','_MatrixToFloat','_Vector3Zero','_Vector3One','_MatrixIdentity','_ShowLogo','_SetConfigFlags','_TraceLog','_TakeScreenshot','_GetRandomValue','_StorageSaveValue','_StorageLoadValue','_IsKeyPressed','_IsKeyDown','_IsKeyReleased','_IsKeyUp','_GetKeyPressed','_SetExitKey','_IsGamepadAvailable','_IsGamepadName','_IsGamepadButtonPressed','_IsGamepadButtonDown','_IsGamepadButtonReleased','_IsGamepadButtonUp','_GetGamepadButtonPressed','_GetGamepadAxisCount','_GetGamepadAxisMovement','_IsMouseButtonPressed','_IsMouseButtonDown','_IsMouseButtonReleased','_IsMouseButtonUp','_GetMouseX','_GetMouseY','_GetMousePosition','_SetMousePosition','_GetMouseWheelMove','_GetTouchX','_GetTouchY','_GetTouchPosition','_SetGesturesEnabled','_IsGestureDetected','_GetGestureDetected','_GetTouchPointsCount','_GetGestureHoldDuration','_GetGestureDragVector','_GetGestureDragAngle','_GetGesturePinchVector','_GetGesturePinchAngle','_SetCameraMode','_UpdateCamera','_SetCameraPanControl','_SetCameraAltControl','_SetCameraSmoothZoomControl','_SetCameraMoveControls','_DrawPixel','_DrawPixelV','_DrawLine','_DrawLineV','_DrawLineEx','_DrawLineBezier','_DrawCircle','_DrawCircleGradient','_DrawCircleV','_DrawCircleLines','_DrawRectangle','_DrawRectangleRec','_DrawRectanglePro','_DrawRectangleGradientV','_DrawRectangleGradientH','_DrawRectangleGradientEx','_DrawRectangleV','_DrawRectangleLines','_DrawRectangleT','_DrawTriangle','_DrawTriangleLines','_DrawPoly','_DrawPolyEx','_DrawPolyExLines','_CheckCollisionRecs','_CheckCollisionCircles','_CheckCollisionCircleRec','_GetCollisionRec','_CheckCollisionPointRec','_CheckCollisionPointCircle','_CheckCollisionPointTriangle','_LoadImage','_LoadImageEx','_LoadImagePro','_LoadImageRaw','_LoadTexture','_LoadTextureFromImage','_LoadRenderTexture','_UnloadImage','_UnloadTexture','_UnloadRenderTexture','_GetImageData','_GetTextureData','_UpdateTexture','_SaveImageAs','_ImageToPOT','_ImageFormat','_ImageAlphaMask','_ImageDither','_ImageCopy','_ImageCrop','_ImageResize','_ImageResizeNN','_ImageText','_ImageTextEx','_ImageDraw','_ImageDrawText','_ImageDrawTextEx','_ImageFlipVertical','_ImageFlipHorizontal','_ImageColorTint','_ImageColorInvert','_ImageColorGrayscale','_ImageColorContrast','_ImageColorBrightness','_GenImageColor','_GenImageGradientV','_GenImageGradientH','_GenImageGradientRadial','_GenImageChecked','_GenImageWhiteNoise','_GenImagePerlinNoise','_GenImageCellular','_GenTextureMipmaps','_SetTextureFilter','_SetTextureWrap','_DrawTexture','_DrawTextureV','_DrawTextureEx','_DrawTextureRec','_DrawTexturePro','_GetDefaultFont','_LoadSpriteFont','_LoadSpriteFontEx','_UnloadSpriteFont','_DrawFPS','_DrawText','_DrawTextEx','_MeasureText','_MeasureTextEx','_DrawLine3D','_DrawCircle3D','_DrawCube','_DrawCubeV','_DrawCubeWires','_DrawCubeTexture','_DrawSphere','_DrawSphereEx','_DrawSphereWires','_DrawCylinder','_DrawCylinderWires','_DrawPlane','_DrawRay','_DrawGrid','_DrawGizmo','_LoadModel','_LoadModelFromMesh','_UnloadModel','_LoadMesh','_UnloadMesh','_GenMeshPlane','_GenMeshCube','_GenMeshSphere','_GenMeshHemiSphere','_GenMeshCylinder','_GenMeshTorus','_GenMeshKnot','_GenMeshHeightmap','_GenMeshCubicmap','_LoadMaterial','_LoadMaterialDefault','_UnloadMaterial','_DrawModel','_DrawModelEx','_DrawModelWires','_DrawModelWiresEx','_DrawBoundingBox','_DrawBillboard','_DrawBillboardRec','_CalculateBoundingBox','_CheckCollisionSpheres','_CheckCollisionBoxes','_CheckCollisionBoxSphere','_CheckCollisionRaySphere','_CheckCollisionRaySphereEx','_CheckCollisionRayBox','_GetCollisionRayMesh','_GetCollisionRayTriangle','_GetCollisionRayGround','_LoadText','_LoadShader','_UnloadShader','_GetShaderDefault','_GetTextureDefault','_GetShaderLocation','_SetShaderValue','_SetShaderValuei','_SetShaderValueMatrix','_SetMatrixProjection','_SetMatrixModelview','_GenTextureCubemap','_GenTextureIrradiance','_GenTexturePrefilter','_GenTextureBRDF','_BeginShaderMode','_EndShaderMode','_BeginBlendMode','_EndBlendMode','_GetVrDeviceInfo','_InitVrSimulator','_CloseVrSimulator','_IsVrSimulatorReady','_SetVrDistortionShader','_UpdateVrTracking','_ToggleVrMode','_BeginVrDrawing','_EndVrDrawing','_InitAudioDevice','_CloseAudioDevice','_IsAudioDeviceReady','_SetMasterVolume','_LoadWave','_LoadWaveEx','_LoadSound','_LoadSoundFromWave','_UpdateSound','_UnloadWave','_UnloadSound','_PlaySound','_PauseSound','_ResumeSound','_StopSound','_IsSoundPlaying','_SetSoundVolume','_SetSoundPitch','_WaveFormat','_WaveCopy','_WaveCrop','_GetWaveData','_LoadMusicStream','_UnloadMusicStream','_PlayMusicStream','_UpdateMusicStream','_StopMusicStream','_PauseMusicStream','_ResumeMusicStream','_IsMusicPlaying','_SetMusicVolume','_SetMusicPitch','_SetMusicLoopCount','_GetMusicTimeLength','_GetMusicTimePlayed','_InitAudioStream','_UpdateAudioStream','_CloseAudioStream','_IsAudioBufferProcessed','_PlayAudioStream','_PauseAudioStream','_ResumeAudioStream','_IsAudioStreamPlaying','_StopAudioStream','_SetAudioStreamVolume','_SetAudioStreamPitch']" +EMCC_EXPORT="['_InitWindow','_CloseWindow','_WindowShouldClose','_IsWindowMinimized','_ToggleFullscreen','_SetWindowIcon','_SetWindowTitle','_SetWindowPosition','_SetWindowMonitor','_SetWindowMinSize','_GetScreenWidth','_GetScreenHeight','_ShowCursor','_HideCursor','_IsCursorHidden','_EnableCursor','_DisableCursor','_ClearBackground','_BeginDrawing','_EndDrawing','_Begin2dMode','_End2dMode','_Begin3dMode','_End3dMode','_BeginTextureMode','_EndTextureMode','_GetMouseRay','_GetWorldToScreen','_GetCameraMatrix','_SetTargetFPS','_GetFPS','_GetFrameTime','_GetHexValue','_GetColor','_ColorToFloat','_Vector3ToFloat','_MatrixToFloat','_Vector3Zero','_Vector3One','_MatrixIdentity','_ShowLogo','_SetConfigFlags','_TraceLog','_TakeScreenshot','_GetRandomValue','_StorageSaveValue','_StorageLoadValue','_IsKeyPressed','_IsKeyDown','_IsKeyReleased','_IsKeyUp','_GetKeyPressed','_SetExitKey','_IsGamepadAvailable','_IsGamepadName','_IsGamepadButtonPressed','_IsGamepadButtonDown','_IsGamepadButtonReleased','_IsGamepadButtonUp','_GetGamepadButtonPressed','_GetGamepadAxisCount','_GetGamepadAxisMovement','_IsMouseButtonPressed','_IsMouseButtonDown','_IsMouseButtonReleased','_IsMouseButtonUp','_GetMouseX','_GetMouseY','_GetMousePosition','_SetMousePosition','_GetMouseWheelMove','_GetTouchX','_GetTouchY','_GetTouchPosition','_SetGesturesEnabled','_IsGestureDetected','_GetGestureDetected','_GetTouchPointsCount','_GetGestureHoldDuration','_GetGestureDragVector','_GetGestureDragAngle','_GetGesturePinchVector','_GetGesturePinchAngle','_SetCameraMode','_UpdateCamera','_SetCameraPanControl','_SetCameraAltControl','_SetCameraSmoothZoomControl','_SetCameraMoveControls','_DrawPixel','_DrawPixelV','_DrawLine','_DrawLineV','_DrawLineEx','_DrawLineBezier','_DrawCircle','_DrawCircleGradient','_DrawCircleV','_DrawCircleLines','_DrawRectangle','_DrawRectangleRec','_DrawRectanglePro','_DrawRectangleGradientV','_DrawRectangleGradientH','_DrawRectangleGradientEx','_DrawRectangleV','_DrawRectangleLines','_DrawRectangleT','_DrawTriangle','_DrawTriangleLines','_DrawPoly','_DrawPolyEx','_DrawPolyExLines','_CheckCollisionRecs','_CheckCollisionCircles','_CheckCollisionCircleRec','_GetCollisionRec','_CheckCollisionPointRec','_CheckCollisionPointCircle','_CheckCollisionPointTriangle','_LoadImage','_LoadImageEx','_LoadImagePro','_LoadImageRaw','_LoadTexture','_LoadTextureFromImage','_LoadRenderTexture','_UnloadImage','_UnloadTexture','_UnloadRenderTexture','_GetImageData','_GetTextureData','_UpdateTexture','_SaveImageAs','_ImageToPOT','_ImageFormat','_ImageAlphaMask','_ImageDither','_ImageCopy','_ImageCrop','_ImageResize','_ImageResizeNN','_ImageText','_ImageTextEx','_ImageDraw','_ImageDrawText','_ImageDrawTextEx','_ImageFlipVertical','_ImageFlipHorizontal','_ImageColorTint','_ImageColorInvert','_ImageColorGrayscale','_ImageColorContrast','_ImageColorBrightness','_GenImageColor','_GenImageGradientV','_GenImageGradientH','_GenImageGradientRadial','_GenImageChecked','_GenImageWhiteNoise','_GenImagePerlinNoise','_GenImageCellular','_GenTextureMipmaps','_SetTextureFilter','_SetTextureWrap','_DrawTexture','_DrawTextureV','_DrawTextureEx','_DrawTextureRec','_DrawTexturePro','_GetDefaultFont','_LoadSpriteFont','_LoadSpriteFontEx','_UnloadSpriteFont','_DrawFPS','_DrawText','_DrawTextEx','_MeasureText','_MeasureTextEx','_DrawLine3D','_DrawCircle3D','_DrawCube','_DrawCubeV','_DrawCubeWires','_DrawCubeTexture','_DrawSphere','_DrawSphereEx','_DrawSphereWires','_DrawCylinder','_DrawCylinderWires','_DrawPlane','_DrawRay','_DrawGrid','_DrawGizmo','_LoadModel','_LoadModelFromMesh','_UnloadModel','_LoadMesh','_UnloadMesh','_GenMeshPlane','_GenMeshCube','_GenMeshSphere','_GenMeshHemiSphere','_GenMeshCylinder','_GenMeshTorus','_GenMeshKnot','_GenMeshHeightmap','_GenMeshCubicmap','_LoadMaterial','_LoadMaterialDefault','_UnloadMaterial','_DrawModel','_DrawModelEx','_DrawModelWires','_DrawModelWiresEx','_DrawBoundingBox','_DrawBillboard','_DrawBillboardRec','_CalculateBoundingBox','_CheckCollisionSpheres','_CheckCollisionBoxes','_CheckCollisionBoxSphere','_CheckCollisionRaySphere','_CheckCollisionRaySphereEx','_CheckCollisionRayBox','_GetCollisionRayMesh','_GetCollisionRayTriangle','_GetCollisionRayGround','_LoadText','_LoadShader','_UnloadShader','_GetShaderDefault','_GetTextureDefault','_GetShaderLocation','_SetShaderValue','_SetShaderValuei','_SetShaderValueMatrix','_SetMatrixProjection','_SetMatrixModelview','_GenTextureCubemap','_GenTextureIrradiance','_GenTexturePrefilter','_GenTextureBRDF','_BeginShaderMode','_EndShaderMode','_BeginBlendMode','_EndBlendMode','_GetVrDeviceInfo','_InitVrSimulator','_CloseVrSimulator','_IsVrSimulatorReady','_SetVrDistortionShader','_UpdateVrTracking','_ToggleVrMode','_BeginVrDrawing','_EndVrDrawing','_InitAudioDevice','_CloseAudioDevice','_IsAudioDeviceReady','_SetMasterVolume','_LoadWave','_LoadWaveEx','_LoadSound','_LoadSoundFromWave','_UpdateSound','_UnloadWave','_UnloadSound','_PlaySound','_PauseSound','_ResumeSound','_StopSound','_IsSoundPlaying','_SetSoundVolume','_SetSoundPitch','_WaveFormat','_WaveCopy','_WaveCrop','_GetWaveData','_LoadMusicStream','_UnloadMusicStream','_PlayMusicStream','_UpdateMusicStream','_StopMusicStream','_PauseMusicStream','_ResumeMusicStream','_IsMusicPlaying','_SetMusicVolume','_SetMusicPitch','_SetMusicLoopCount','_GetMusicTimeLength','_GetMusicTimePlayed','_InitAudioStream','_UpdateAudioStream','_CloseAudioStream','_IsAudioBufferProcessed','_PlayAudioStream','_PauseAudioStream','_ResumeAudioStream','_IsAudioStreamPlaying','_StopAudioStream','_SetAudioStreamVolume','_SetAudioStreamPitch','_emscripten_set_main_loop']" RAYLIB_SRC=( "audio.c" "core.c" "mini_al.c" "models.c" "rlgl.c" "shapes.c" "text.c" "textures.c" "utils.c" "external/stb_vorbis.c" ) RAYLIB_OBJ=( "audio.o" "core.o" "mini_al.o" "models.o" "rlgl.o" "shapes.o" "text.o" "textures.o" "utils.o" "external/stb_vorbis.o" ) @@ -9,6 +9,9 @@ for i in "${RAYLIB_SRC[@]}"; do emcc "${i}" -o "${i/.c}.o" -O1 -std=c99 -D_DEFAULT_SOURCE -fgnu89-inline -Wno-missing-braces -Wno-unused-function -Wno-unused-variable -Iexternal -Iexternal/glfw/include -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2 done -emcc `echo "${RAYLIB_OBJ[*]}"` -o raylib.js --post-js "${BASE_DIR}/external/web/mainloop.js" -g3 -s USE_GLFW=3 -s EXPORTED_FUNCTIONS=$EMCC_EXPORT +emcc `echo "${RAYLIB_OBJ[*]}"` -o raylib.js -g3 -s USE_GLFW=3 -s EXPORTED_FUNCTIONS=$EMCC_EXPORT -rm "${BASE_DIR}"/*.o "${BASE_DIR}"/external/*.o +sed --quiet -i "/Module\['dynCall_vi'\]/c\ func(arg);" raylib.js +sed --quiet -i "/Module\['dynCall_v'\]/c\ func();" raylib.js + +rm -f "${BASE_DIR}"/*.o "${BASE_DIR}"/external/*.o diff --git a/raylib/external/web/mainloop.js b/raylib/external/web/mainloop.js deleted file mode 100644 index 9c3a3d0..0000000 --- a/raylib/external/web/mainloop.js +++ /dev/null @@ -1,7 +0,0 @@ -function _emscripten_set_main_loop_go(fps, simulateInfiniteLoop) { - if (_go_update_function !== undefined && typeof _go_update_function == 'function') { - _emscripten_set_main_loop(_go_update_function, fps, simulateInfiniteLoop); - } -} - -Module["_emscripten_set_main_loop_go"] = _emscripten_set_main_loop_go; diff --git a/raylib/platform_web.go b/raylib/platform_web.go index 85b813e..2175b52 100644 --- a/raylib/platform_web.go +++ b/raylib/platform_web.go @@ -20,8 +20,7 @@ func SetCallbackFunc(func(unsafe.Pointer)) { // SetMainLoop - Sets main loop function func SetMainLoop(fn func(), fps int, infinite bool) { - js.Global.Set("_go_update_function", fn) - js.Global.Get("Module").Call("_emscripten_set_main_loop_go", fps, infinite) + js.Global.Get("Module").Call("_emscripten_set_main_loop", fn, fps, infinite) } // ShowCursor - Shows cursor From 2272effbcef7e44134ca4570a898b193e2f45b5e Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Sat, 9 Dec 2017 18:27:53 +0100 Subject: [PATCH 45/48] Fix sed --- raylib/external/scripts/emcc-generate-js.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/raylib/external/scripts/emcc-generate-js.sh b/raylib/external/scripts/emcc-generate-js.sh index 9889ad7..f20b6b1 100755 --- a/raylib/external/scripts/emcc-generate-js.sh +++ b/raylib/external/scripts/emcc-generate-js.sh @@ -11,7 +11,7 @@ done emcc `echo "${RAYLIB_OBJ[*]}"` -o raylib.js -g3 -s USE_GLFW=3 -s EXPORTED_FUNCTIONS=$EMCC_EXPORT -sed --quiet -i "/Module\['dynCall_vi'\]/c\ func(arg);" raylib.js -sed --quiet -i "/Module\['dynCall_v'\]/c\ func();" raylib.js +sed -i "/Module\['dynCall_vi'\]/c\ func(arg);" raylib.js +sed -i "/Module\['dynCall_v'\]/c\ func();" raylib.js rm -f "${BASE_DIR}"/*.o "${BASE_DIR}"/external/*.o From 3ea6b47140dc6fa8cbecdde6b6adcad0b217d719 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Sat, 9 Dec 2017 18:37:06 +0100 Subject: [PATCH 46/48] Add raylib.js --- raylib/external/web/raylib.js | 105109 +++++++++++++++++++++++++++++++ 1 file changed, 105109 insertions(+) create mode 100644 raylib/external/web/raylib.js diff --git a/raylib/external/web/raylib.js b/raylib/external/web/raylib.js new file mode 100644 index 0000000..9d28ea0 --- /dev/null +++ b/raylib/external/web/raylib.js @@ -0,0 +1,105109 @@ +// The Module object: Our interface to the outside world. We import +// and export values on it, and do the work to get that through +// closure compiler if necessary. There are various ways Module can be used: +// 1. Not defined. We create it here +// 2. A function parameter, function(Module) { ..generated code.. } +// 3. pre-run appended it, var Module = {}; ..generated code.. +// 4. External script tag defines var Module. +// We need to do an eval in order to handle the closure compiler +// case, where this code here is minified but Module was defined +// elsewhere (e.g. case 4 above). We also need to check if Module +// already exists (e.g. case 3 above). +// Note that if you want to run closure, and also to use Module +// after the generated code, you will need to define var Module = {}; +// before the code. Then that object will be used in the code, and you +// can continue to use Module afterwards as well. +var Module; +if (!Module) Module = (typeof Module !== 'undefined' ? Module : null) || {}; + +// Sometimes an existing Module object exists with properties +// meant to overwrite the default module functionality. Here +// we collect those properties and reapply _after_ we configure +// the current environment's defaults to avoid having to be so +// defensive during initialization. +var moduleOverrides = {}; +for (var key in Module) { + if (Module.hasOwnProperty(key)) { + moduleOverrides[key] = Module[key]; + } +} + +// The environment setup code below is customized to use Module. +// *** Environment setup code *** +var ENVIRONMENT_IS_WEB = false; +var ENVIRONMENT_IS_WORKER = false; +var ENVIRONMENT_IS_NODE = false; +var ENVIRONMENT_IS_SHELL = false; + +// Three configurations we can be running in: +// 1) We could be the application main() thread running in the main JS UI thread. (ENVIRONMENT_IS_WORKER == false and ENVIRONMENT_IS_PTHREAD == false) +// 2) We could be the application main() thread proxied to worker. (with Emscripten -s PROXY_TO_WORKER=1) (ENVIRONMENT_IS_WORKER == true, ENVIRONMENT_IS_PTHREAD == false) +// 3) We could be an application pthread running in a worker. (ENVIRONMENT_IS_WORKER == true and ENVIRONMENT_IS_PTHREAD == true) + +if (Module['ENVIRONMENT']) { + if (Module['ENVIRONMENT'] === 'WEB') { + ENVIRONMENT_IS_WEB = true; + } else if (Module['ENVIRONMENT'] === 'WORKER') { + ENVIRONMENT_IS_WORKER = true; + } else if (Module['ENVIRONMENT'] === 'NODE') { + ENVIRONMENT_IS_NODE = true; + } else if (Module['ENVIRONMENT'] === 'SHELL') { + ENVIRONMENT_IS_SHELL = true; + } else { + throw new Error('The provided Module[\'ENVIRONMENT\'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL.'); + } +} else { + ENVIRONMENT_IS_WEB = typeof window === 'object'; + ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; + ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof require === 'function' && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER; + ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; +} + + +if (ENVIRONMENT_IS_NODE) { + // Expose functionality in the same simple way that the shells work + // Note that we pollute the global namespace here, otherwise we break in node + if (!Module['print']) Module['print'] = console.log; + if (!Module['printErr']) Module['printErr'] = console.warn; + + var nodeFS; + var nodePath; + + Module['read'] = function shell_read(filename, binary) { + if (!nodeFS) nodeFS = require('fs'); + if (!nodePath) nodePath = require('path'); + filename = nodePath['normalize'](filename); + var ret = nodeFS['readFileSync'](filename); + return binary ? ret : ret.toString(); + }; + + Module['readBinary'] = function readBinary(filename) { + var ret = Module['read'](filename, true); + if (!ret.buffer) { + ret = new Uint8Array(ret); + } + assert(ret.buffer); + return ret; + }; + + Module['load'] = function load(f) { + globalEval(read(f)); + }; + + if (!Module['thisProgram']) { + if (process['argv'].length > 1) { + Module['thisProgram'] = process['argv'][1].replace(/\\/g, '/'); + } else { + Module['thisProgram'] = 'unknown-program'; + } + } + + Module['arguments'] = process['argv'].slice(2); + + if (typeof module !== 'undefined') { + module['exports'] = Module; + } + + process['on']('uncaughtException', function(ex) { + // suppress ExitStatus exceptions from showing an error + if (!(ex instanceof ExitStatus)) { + throw ex; + } + }); + + Module['inspect'] = function () { return '[Emscripten Module object]'; }; +} +else if (ENVIRONMENT_IS_SHELL) { + if (!Module['print']) Module['print'] = print; + if (typeof printErr != 'undefined') Module['printErr'] = printErr; // not present in v8 or older sm + + if (typeof read != 'undefined') { + Module['read'] = read; + } else { + Module['read'] = function shell_read() { throw 'no read() available' }; + } + + Module['readBinary'] = function readBinary(f) { + if (typeof readbuffer === 'function') { + return new Uint8Array(readbuffer(f)); + } + var data = read(f, 'binary'); + assert(typeof data === 'object'); + return data; + }; + + if (typeof scriptArgs != 'undefined') { + Module['arguments'] = scriptArgs; + } else if (typeof arguments != 'undefined') { + Module['arguments'] = arguments; + } + + if (typeof quit === 'function') { + Module['quit'] = function(status, toThrow) { + quit(status); + } + } + +} +else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + Module['read'] = function shell_read(url) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.send(null); + return xhr.responseText; + }; + + if (ENVIRONMENT_IS_WORKER) { + Module['readBinary'] = function readBinary(url) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.responseType = 'arraybuffer'; + xhr.send(null); + return new Uint8Array(xhr.response); + }; + } + + Module['readAsync'] = function readAsync(url, onload, onerror) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = function xhr_onload() { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 + onload(xhr.response); + } else { + onerror(); + } + }; + xhr.onerror = onerror; + xhr.send(null); + }; + + if (typeof arguments != 'undefined') { + Module['arguments'] = arguments; + } + + if (typeof console !== 'undefined') { + if (!Module['print']) Module['print'] = function shell_print(x) { + console.log(x); + }; + if (!Module['printErr']) Module['printErr'] = function shell_printErr(x) { + console.warn(x); + }; + } else { + // Probably a worker, and without console.log. We can do very little here... + var TRY_USE_DUMP = false; + if (!Module['print']) Module['print'] = (TRY_USE_DUMP && (typeof(dump) !== "undefined") ? (function(x) { + dump(x); + }) : (function(x) { + // self.postMessage(x); // enable this if you want stdout to be sent as messages + })); + } + + if (ENVIRONMENT_IS_WORKER) { + Module['load'] = importScripts; + } + + if (typeof Module['setWindowTitle'] === 'undefined') { + Module['setWindowTitle'] = function(title) { document.title = title }; + } +} +else { + // Unreachable because SHELL is dependant on the others + throw 'Unknown runtime environment. Where are we?'; +} + +function globalEval(x) { + eval.call(null, x); +} +if (!Module['load'] && Module['read']) { + Module['load'] = function load(f) { + globalEval(Module['read'](f)); + }; +} +if (!Module['print']) { + Module['print'] = function(){}; +} +if (!Module['printErr']) { + Module['printErr'] = Module['print']; +} +if (!Module['arguments']) { + Module['arguments'] = []; +} +if (!Module['thisProgram']) { + Module['thisProgram'] = './this.program'; +} +if (!Module['quit']) { + Module['quit'] = function(status, toThrow) { + throw toThrow; + } +} + +// *** Environment setup code *** + +// Closure helpers +Module.print = Module['print']; +Module.printErr = Module['printErr']; + +// Callbacks +Module['preRun'] = []; +Module['postRun'] = []; + +// Merge back in the overrides +for (var key in moduleOverrides) { + if (moduleOverrides.hasOwnProperty(key)) { + Module[key] = moduleOverrides[key]; + } +} +// Free the object hierarchy contained in the overrides, this lets the GC +// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. +moduleOverrides = undefined; + + + +// {{PREAMBLE_ADDITIONS}} + +// === Preamble library stuff === + +// Documentation for the public APIs defined in this file must be updated in: +// site/source/docs/api_reference/preamble.js.rst +// A prebuilt local version of the documentation is available at: +// site/build/text/docs/api_reference/preamble.js.txt +// You can also build docs locally as HTML or other formats in site/ +// An online HTML version (which may be of a different version of Emscripten) +// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html + +//======================================== +// Runtime code shared with compiler +//======================================== + +var Runtime = { + setTempRet0: function (value) { + tempRet0 = value; + return value; + }, + getTempRet0: function () { + return tempRet0; + }, + stackSave: function () { + return STACKTOP; + }, + stackRestore: function (stackTop) { + STACKTOP = stackTop; + }, + getNativeTypeSize: function (type) { + switch (type) { + case 'i1': case 'i8': return 1; + case 'i16': return 2; + case 'i32': return 4; + case 'i64': return 8; + case 'float': return 4; + case 'double': return 8; + default: { + if (type[type.length-1] === '*') { + return Runtime.QUANTUM_SIZE; // A pointer + } else if (type[0] === 'i') { + var bits = parseInt(type.substr(1)); + assert(bits % 8 === 0); + return bits/8; + } else { + return 0; + } + } + } + }, + getNativeFieldSize: function (type) { + return Math.max(Runtime.getNativeTypeSize(type), Runtime.QUANTUM_SIZE); + }, + STACK_ALIGN: 16, + prepVararg: function (ptr, type) { + if (type === 'double' || type === 'i64') { + // move so the load is aligned + if (ptr & 7) { + assert((ptr & 7) === 4); + ptr += 4; + } + } else { + assert((ptr & 3) === 0); + } + return ptr; + }, + getAlignSize: function (type, size, vararg) { + // we align i64s and doubles on 64-bit boundaries, unlike x86 + if (!vararg && (type == 'i64' || type == 'double')) return 8; + if (!type) return Math.min(size, 8); // align structures internally to 64 bits + return Math.min(size || (type ? Runtime.getNativeFieldSize(type) : 0), Runtime.QUANTUM_SIZE); + }, + dynCall: function (sig, ptr, args) { + if (args && args.length) { + assert(args.length == sig.length-1); + assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); + return Module['dynCall_' + sig].apply(null, [ptr].concat(args)); + } else { + assert(sig.length == 1); + assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); + return Module['dynCall_' + sig].call(null, ptr); + } + }, + functionPointers: [], + addFunction: function (func) { + for (var i = 0; i < Runtime.functionPointers.length; i++) { + if (!Runtime.functionPointers[i]) { + Runtime.functionPointers[i] = func; + return 2*(1 + i); + } + } + throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.'; + }, + removeFunction: function (index) { + Runtime.functionPointers[(index-2)/2] = null; + }, + warnOnce: function (text) { + if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {}; + if (!Runtime.warnOnce.shown[text]) { + Runtime.warnOnce.shown[text] = 1; + Module.printErr(text); + } + }, + funcWrappers: {}, + getFuncWrapper: function (func, sig) { + if (!func) return; // on null pointer, return undefined + assert(sig); + if (!Runtime.funcWrappers[sig]) { + Runtime.funcWrappers[sig] = {}; + } + var sigCache = Runtime.funcWrappers[sig]; + if (!sigCache[func]) { + // optimize away arguments usage in common cases + if (sig.length === 1) { + sigCache[func] = function dynCall_wrapper() { + return Runtime.dynCall(sig, func); + }; + } else if (sig.length === 2) { + sigCache[func] = function dynCall_wrapper(arg) { + return Runtime.dynCall(sig, func, [arg]); + }; + } else { + // general case + sigCache[func] = function dynCall_wrapper() { + return Runtime.dynCall(sig, func, Array.prototype.slice.call(arguments)); + }; + } + } + return sigCache[func]; + }, + getCompilerSetting: function (name) { + throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work'; + }, + stackAlloc: function (size) { var ret = STACKTOP;STACKTOP = (STACKTOP + size)|0;STACKTOP = (((STACKTOP)+15)&-16);(assert((((STACKTOP|0) < (STACK_MAX|0))|0))|0); return ret; }, + staticAlloc: function (size) { var ret = STATICTOP;STATICTOP = (STATICTOP + (assert(!staticSealed),size))|0;STATICTOP = (((STATICTOP)+15)&-16); return ret; }, + dynamicAlloc: function (size) { assert(DYNAMICTOP_PTR);var ret = HEAP32[DYNAMICTOP_PTR>>2];var end = (((ret + size + 15)|0) & -16);HEAP32[DYNAMICTOP_PTR>>2] = end;if (end >= TOTAL_MEMORY) {var success = enlargeMemory();if (!success) {HEAP32[DYNAMICTOP_PTR>>2] = ret;return 0;}}return ret;}, + alignMemory: function (size,quantum) { var ret = size = Math.ceil((size)/(quantum ? quantum : 16))*(quantum ? quantum : 16); return ret; }, + makeBigInt: function (low,high,unsigned) { var ret = (unsigned ? ((+((low>>>0)))+((+((high>>>0)))*4294967296.0)) : ((+((low>>>0)))+((+((high|0)))*4294967296.0))); return ret; }, + GLOBAL_BASE: 8, + QUANTUM_SIZE: 4, + __dummy__: 0 +} + + + +Module["Runtime"] = Runtime; + + + +//======================================== +// Runtime essentials +//======================================== + +var ABORT = 0; // whether we are quitting the application. no code should run after this. set in exit() and abort() +var EXITSTATUS = 0; + +/** @type {function(*, string=)} */ +function assert(condition, text) { + if (!condition) { + abort('Assertion failed: ' + text); + } +} + +var globalScope = this; + +// Returns the C function with a specified identifier (for C++, you need to do manual name mangling) +function getCFunc(ident) { + var func = Module['_' + ident]; // closure exported function + if (!func) { + try { func = eval('_' + ident); } catch(e) {} + } + assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)'); + return func; +} + +var cwrap, ccall; +(function(){ + var JSfuncs = { + // Helpers for cwrap -- it can't refer to Runtime directly because it might + // be renamed by closure, instead it calls JSfuncs['stackSave'].body to find + // out what the minified function name is. + 'stackSave': function() { + Runtime.stackSave() + }, + 'stackRestore': function() { + Runtime.stackRestore() + }, + // type conversion from js to c + 'arrayToC' : function(arr) { + var ret = Runtime.stackAlloc(arr.length); + writeArrayToMemory(arr, ret); + return ret; + }, + 'stringToC' : function(str) { + var ret = 0; + if (str !== null && str !== undefined && str !== 0) { // null string + // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' + var len = (str.length << 2) + 1; + ret = Runtime.stackAlloc(len); + stringToUTF8(str, ret, len); + } + return ret; + } + }; + // For fast lookup of conversion functions + var toC = {'string' : JSfuncs['stringToC'], 'array' : JSfuncs['arrayToC']}; + + // C calling interface. + ccall = function ccallFunc(ident, returnType, argTypes, args, opts) { + var func = getCFunc(ident); + var cArgs = []; + var stack = 0; + assert(returnType !== 'array', 'Return type should not be "array".'); + if (args) { + for (var i = 0; i < args.length; i++) { + var converter = toC[argTypes[i]]; + if (converter) { + if (stack === 0) stack = Runtime.stackSave(); + cArgs[i] = converter(args[i]); + } else { + cArgs[i] = args[i]; + } + } + } + var ret = func.apply(null, cArgs); + if ((!opts || !opts.async) && typeof EmterpreterAsync === 'object') { + assert(!EmterpreterAsync.state, 'cannot start async op with normal JS calling ccall'); + } + if (opts && opts.async) assert(!returnType, 'async ccalls cannot return values'); + if (returnType === 'string') ret = Pointer_stringify(ret); + if (stack !== 0) { + if (opts && opts.async) { + EmterpreterAsync.asyncFinalizers.push(function() { + Runtime.stackRestore(stack); + }); + return; + } + Runtime.stackRestore(stack); + } + return ret; + } + + var sourceRegex = /^function\s*[a-zA-Z$_0-9]*\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/; + function parseJSFunc(jsfunc) { + // Match the body and the return value of a javascript function source + var parsed = jsfunc.toString().match(sourceRegex).slice(1); + return {arguments : parsed[0], body : parsed[1], returnValue: parsed[2]} + } + + // sources of useful functions. we create this lazily as it can trigger a source decompression on this entire file + var JSsource = null; + function ensureJSsource() { + if (!JSsource) { + JSsource = {}; + for (var fun in JSfuncs) { + if (JSfuncs.hasOwnProperty(fun)) { + // Elements of toCsource are arrays of three items: + // the code, and the return value + JSsource[fun] = parseJSFunc(JSfuncs[fun]); + } + } + } + } + + cwrap = function cwrap(ident, returnType, argTypes) { + argTypes = argTypes || []; + var cfunc = getCFunc(ident); + // When the function takes numbers and returns a number, we can just return + // the original function + var numericArgs = argTypes.every(function(type){ return type === 'number'}); + var numericRet = (returnType !== 'string'); + if ( numericRet && numericArgs) { + return cfunc; + } + // Creation of the arguments list (["$1","$2",...,"$nargs"]) + var argNames = argTypes.map(function(x,i){return '$'+i}); + var funcstr = "(function(" + argNames.join(',') + ") {"; + var nargs = argTypes.length; + if (!numericArgs) { + // Generate the code needed to convert the arguments from javascript + // values to pointers + ensureJSsource(); + funcstr += 'var stack = ' + JSsource['stackSave'].body + ';'; + for (var i = 0; i < nargs; i++) { + var arg = argNames[i], type = argTypes[i]; + if (type === 'number') continue; + var convertCode = JSsource[type + 'ToC']; // [code, return] + funcstr += 'var ' + convertCode.arguments + ' = ' + arg + ';'; + funcstr += convertCode.body + ';'; + funcstr += arg + '=(' + convertCode.returnValue + ');'; + } + } + + // When the code is compressed, the name of cfunc is not literally 'cfunc' anymore + var cfuncname = parseJSFunc(function(){return cfunc}).returnValue; + // Call the function + funcstr += 'var ret = ' + cfuncname + '(' + argNames.join(',') + ');'; + if (!numericRet) { // Return type can only by 'string' or 'number' + // Convert the result to a string + var strgfy = parseJSFunc(function(){return Pointer_stringify}).returnValue; + funcstr += 'ret = ' + strgfy + '(ret);'; + } + funcstr += "if (typeof EmterpreterAsync === 'object') { assert(!EmterpreterAsync.state, 'cannot start async op with normal JS calling cwrap') }"; + if (!numericArgs) { + // If we had a stack, restore it + ensureJSsource(); + funcstr += JSsource['stackRestore'].body.replace('()', '(stack)') + ';'; + } + funcstr += 'return ret})'; + return eval(funcstr); + }; +})(); +Module["ccall"] = ccall; +Module["cwrap"] = cwrap; + +/** @type {function(number, number, string, boolean=)} */ +function setValue(ptr, value, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit + switch(type) { + case 'i1': HEAP8[((ptr)>>0)]=value; break; + case 'i8': HEAP8[((ptr)>>0)]=value; break; + case 'i16': HEAP16[((ptr)>>1)]=value; break; + case 'i32': HEAP32[((ptr)>>2)]=value; break; + case 'i64': (tempI64 = [value>>>0,(tempDouble=value,(+(Math_abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math_min((+(Math_floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math_ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((ptr)>>2)]=tempI64[0],HEAP32[(((ptr)+(4))>>2)]=tempI64[1]); break; + case 'float': HEAPF32[((ptr)>>2)]=value; break; + case 'double': HEAPF64[((ptr)>>3)]=value; break; + default: abort('invalid type for setValue: ' + type); + } +} +Module["setValue"] = setValue; + +/** @type {function(number, string, boolean=)} */ +function getValue(ptr, type, noSafe) { + type = type || 'i8'; + if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit + switch(type) { + case 'i1': return HEAP8[((ptr)>>0)]; + case 'i8': return HEAP8[((ptr)>>0)]; + case 'i16': return HEAP16[((ptr)>>1)]; + case 'i32': return HEAP32[((ptr)>>2)]; + case 'i64': return HEAP32[((ptr)>>2)]; + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + default: abort('invalid type for setValue: ' + type); + } + return null; +} +Module["getValue"] = getValue; + +var ALLOC_NORMAL = 0; // Tries to use _malloc() +var ALLOC_STACK = 1; // Lives for the duration of the current function call +var ALLOC_STATIC = 2; // Cannot be freed +var ALLOC_DYNAMIC = 3; // Cannot be freed except through sbrk +var ALLOC_NONE = 4; // Do not allocate +Module["ALLOC_NORMAL"] = ALLOC_NORMAL; +Module["ALLOC_STACK"] = ALLOC_STACK; +Module["ALLOC_STATIC"] = ALLOC_STATIC; +Module["ALLOC_DYNAMIC"] = ALLOC_DYNAMIC; +Module["ALLOC_NONE"] = ALLOC_NONE; + +// allocate(): This is for internal use. You can use it yourself as well, but the interface +// is a little tricky (see docs right below). The reason is that it is optimized +// for multiple syntaxes to save space in generated code. So you should +// normally not use allocate(), and instead allocate memory using _malloc(), +// initialize it with setValue(), and so forth. +// @slab: An array of data, or a number. If a number, then the size of the block to allocate, +// in *bytes* (note that this is sometimes confusing: the next parameter does not +// affect this!) +// @types: Either an array of types, one for each byte (or 0 if no type at that position), +// or a single type which is used for the entire block. This only matters if there +// is initial data - if @slab is a number, then this does not matter at all and is +// ignored. +// @allocator: How to allocate memory, see ALLOC_* +/** @type {function((TypedArray|Array|number), string, number, number=)} */ +function allocate(slab, types, allocator, ptr) { + var zeroinit, size; + if (typeof slab === 'number') { + zeroinit = true; + size = slab; + } else { + zeroinit = false; + size = slab.length; + } + + var singleType = typeof types === 'string' ? types : null; + + var ret; + if (allocator == ALLOC_NONE) { + ret = ptr; + } else { + ret = [typeof _malloc === 'function' ? _malloc : Runtime.staticAlloc, Runtime.stackAlloc, Runtime.staticAlloc, Runtime.dynamicAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); + } + + if (zeroinit) { + var ptr = ret, stop; + assert((ret & 3) == 0); + stop = ret + (size & ~3); + for (; ptr < stop; ptr += 4) { + HEAP32[((ptr)>>2)]=0; + } + stop = ret + size; + while (ptr < stop) { + HEAP8[((ptr++)>>0)]=0; + } + return ret; + } + + if (singleType === 'i8') { + if (slab.subarray || slab.slice) { + HEAPU8.set(/** @type {!Uint8Array} */ (slab), ret); + } else { + HEAPU8.set(new Uint8Array(slab), ret); + } + return ret; + } + + var i = 0, type, typeSize, previousType; + while (i < size) { + var curr = slab[i]; + + if (typeof curr === 'function') { + curr = Runtime.getFunctionIndex(curr); + } + + type = singleType || types[i]; + if (type === 0) { + i++; + continue; + } + assert(type, 'Must know what type to store in allocate!'); + + if (type == 'i64') type = 'i32'; // special case: we have one i32 here, and one i32 later + + setValue(ret+i, curr, type); + + // no need to look up size unless type changes, so cache it + if (previousType !== type) { + typeSize = Runtime.getNativeTypeSize(type); + previousType = type; + } + i += typeSize; + } + + return ret; +} +Module["allocate"] = allocate; + +// Allocate memory during any stage of startup - static memory early on, dynamic memory later, malloc when ready +function getMemory(size) { + if (!staticSealed) return Runtime.staticAlloc(size); + if (!runtimeInitialized) return Runtime.dynamicAlloc(size); + return _malloc(size); +} +Module["getMemory"] = getMemory; + +/** @type {function(number, number=)} */ +function Pointer_stringify(ptr, length) { + if (length === 0 || !ptr) return ''; + // TODO: use TextDecoder + // Find the length, and check for UTF while doing so + var hasUtf = 0; + var t; + var i = 0; + while (1) { + assert(ptr + i < TOTAL_MEMORY); + t = HEAPU8[(((ptr)+(i))>>0)]; + hasUtf |= t; + if (t == 0 && !length) break; + i++; + if (length && i == length) break; + } + if (!length) length = i; + + var ret = ''; + + if (hasUtf < 128) { + var MAX_CHUNK = 1024; // split up into chunks, because .apply on a huge string can overflow the stack + var curr; + while (length > 0) { + curr = String.fromCharCode.apply(String, HEAPU8.subarray(ptr, ptr + Math.min(length, MAX_CHUNK))); + ret = ret ? ret + curr : curr; + ptr += MAX_CHUNK; + length -= MAX_CHUNK; + } + return ret; + } + return Module['UTF8ToString'](ptr); +} +Module["Pointer_stringify"] = Pointer_stringify; + +// Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +function AsciiToString(ptr) { + var str = ''; + while (1) { + var ch = HEAP8[((ptr++)>>0)]; + if (!ch) return str; + str += String.fromCharCode(ch); + } +} +Module["AsciiToString"] = AsciiToString; + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in ASCII form. The copy will require at most str.length+1 bytes of space in the HEAP. + +function stringToAscii(str, outPtr) { + return writeAsciiToMemory(str, outPtr, false); +} +Module["stringToAscii"] = stringToAscii; + +// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns +// a copy of that string as a Javascript String object. + +var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined; +function UTF8ArrayToString(u8Array, idx) { + var endPtr = idx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. + // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. + while (u8Array[endPtr]) ++endPtr; + + if (endPtr - idx > 16 && u8Array.subarray && UTF8Decoder) { + return UTF8Decoder.decode(u8Array.subarray(idx, endPtr)); + } else { + var u0, u1, u2, u3, u4, u5; + + var str = ''; + while (1) { + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 + u0 = u8Array[idx++]; + if (!u0) return str; + if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } + u1 = u8Array[idx++] & 63; + if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } + u2 = u8Array[idx++] & 63; + if ((u0 & 0xF0) == 0xE0) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + u3 = u8Array[idx++] & 63; + if ((u0 & 0xF8) == 0xF0) { + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | u3; + } else { + u4 = u8Array[idx++] & 63; + if ((u0 & 0xFC) == 0xF8) { + u0 = ((u0 & 3) << 24) | (u1 << 18) | (u2 << 12) | (u3 << 6) | u4; + } else { + u5 = u8Array[idx++] & 63; + u0 = ((u0 & 1) << 30) | (u1 << 24) | (u2 << 18) | (u3 << 12) | (u4 << 6) | u5; + } + } + } + if (u0 < 0x10000) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } + } + } +} +Module["UTF8ArrayToString"] = UTF8ArrayToString; + +// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +function UTF8ToString(ptr) { + return UTF8ArrayToString(HEAPU8,ptr); +} +Module["UTF8ToString"] = UTF8ToString; + +// Copies the given Javascript String object 'str' to the given byte array at address 'outIdx', +// encoded in UTF8 form and null-terminated. The copy will require at most str.length*4+1 bytes of space in the HEAP. +// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outU8Array: the array to copy to. Each index in this array is assumed to be one 8-byte element. +// outIdx: The starting offset in the array to begin the copying. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=1, only the null terminator will be written and nothing else. +// maxBytesToWrite=0 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF8Array(str, outU8Array, outIdx, maxBytesToWrite) { + if (!(maxBytesToWrite > 0)) // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes. + return 0; + + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); + if (u <= 0x7F) { + if (outIdx >= endIdx) break; + outU8Array[outIdx++] = u; + } else if (u <= 0x7FF) { + if (outIdx + 1 >= endIdx) break; + outU8Array[outIdx++] = 0xC0 | (u >> 6); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0xFFFF) { + if (outIdx + 2 >= endIdx) break; + outU8Array[outIdx++] = 0xE0 | (u >> 12); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0x1FFFFF) { + if (outIdx + 3 >= endIdx) break; + outU8Array[outIdx++] = 0xF0 | (u >> 18); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0x3FFFFFF) { + if (outIdx + 4 >= endIdx) break; + outU8Array[outIdx++] = 0xF8 | (u >> 24); + outU8Array[outIdx++] = 0x80 | ((u >> 18) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 5 >= endIdx) break; + outU8Array[outIdx++] = 0xFC | (u >> 30); + outU8Array[outIdx++] = 0x80 | ((u >> 24) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 18) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } + } + // Null-terminate the pointer to the buffer. + outU8Array[outIdx] = 0; + return outIdx - startIdx; +} +Module["stringToUTF8Array"] = stringToUTF8Array; + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF8 form. The copy will require at most str.length*4+1 bytes of space in the HEAP. +// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF8(str, outPtr, maxBytesToWrite) { + assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); + return stringToUTF8Array(str, HEAPU8,outPtr, maxBytesToWrite); +} +Module["stringToUTF8"] = stringToUTF8; + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF8(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); + if (u <= 0x7F) { + ++len; + } else if (u <= 0x7FF) { + len += 2; + } else if (u <= 0xFFFF) { + len += 3; + } else if (u <= 0x1FFFFF) { + len += 4; + } else if (u <= 0x3FFFFFF) { + len += 5; + } else { + len += 6; + } + } + return len; +} +Module["lengthBytesUTF8"] = lengthBytesUTF8; + +// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns +// a copy of that string as a Javascript String object. + +var UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined; +function UTF16ToString(ptr) { + assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!'); + var endPtr = ptr; + // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. + // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. + var idx = endPtr >> 1; + while (HEAP16[idx]) ++idx; + endPtr = idx << 1; + + if (endPtr - ptr > 32 && UTF16Decoder) { + return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr)); + } else { + var i = 0; + + var str = ''; + while (1) { + var codeUnit = HEAP16[(((ptr)+(i*2))>>1)]; + if (codeUnit == 0) return str; + ++i; + // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through. + str += String.fromCharCode(codeUnit); + } + } +} + + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF16 form. The copy will require at most str.length*4+2 bytes of space in the HEAP. +// Use the function lengthBytesUTF16() to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outPtr: Byte address in Emscripten HEAP where to write the string to. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=2, only the null terminator will be written and nothing else. +// maxBytesToWrite<2 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF16(str, outPtr, maxBytesToWrite) { + assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!'); + assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + if (maxBytesToWrite === undefined) { + maxBytesToWrite = 0x7FFFFFFF; + } + if (maxBytesToWrite < 2) return 0; + maxBytesToWrite -= 2; // Null terminator. + var startPtr = outPtr; + var numCharsToWrite = (maxBytesToWrite < str.length*2) ? (maxBytesToWrite / 2) : str.length; + for (var i = 0; i < numCharsToWrite; ++i) { + // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP. + var codeUnit = str.charCodeAt(i); // possibly a lead surrogate + HEAP16[((outPtr)>>1)]=codeUnit; + outPtr += 2; + } + // Null-terminate the pointer to the HEAP. + HEAP16[((outPtr)>>1)]=0; + return outPtr - startPtr; +} + + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF16(str) { + return str.length*2; +} + + +function UTF32ToString(ptr) { + assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!'); + var i = 0; + + var str = ''; + while (1) { + var utf32 = HEAP32[(((ptr)+(i*4))>>2)]; + if (utf32 == 0) + return str; + ++i; + // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + if (utf32 >= 0x10000) { + var ch = utf32 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } else { + str += String.fromCharCode(utf32); + } + } +} + + +// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', +// null-terminated and encoded in UTF32 form. The copy will require at most str.length*4+4 bytes of space in the HEAP. +// Use the function lengthBytesUTF32() to compute the exact number of bytes (excluding null terminator) that this function will write. +// Parameters: +// str: the Javascript string to copy. +// outPtr: Byte address in Emscripten HEAP where to write the string to. +// maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null +// terminator, i.e. if maxBytesToWrite=4, only the null terminator will be written and nothing else. +// maxBytesToWrite<4 does not write any bytes to the output, not even the null terminator. +// Returns the number of bytes written, EXCLUDING the null terminator. + +function stringToUTF32(str, outPtr, maxBytesToWrite) { + assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!'); + assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); + // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. + if (maxBytesToWrite === undefined) { + maxBytesToWrite = 0x7FFFFFFF; + } + if (maxBytesToWrite < 4) return 0; + var startPtr = outPtr; + var endPtr = startPtr + maxBytesToWrite - 4; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var codeUnit = str.charCodeAt(i); // possibly a lead surrogate + if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) { + var trailSurrogate = str.charCodeAt(++i); + codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF); + } + HEAP32[((outPtr)>>2)]=codeUnit; + outPtr += 4; + if (outPtr + 4 > endPtr) break; + } + // Null-terminate the pointer to the HEAP. + HEAP32[((outPtr)>>2)]=0; + return outPtr - startPtr; +} + + +// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. + +function lengthBytesUTF32(str) { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var codeUnit = str.charCodeAt(i); + if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate. + len += 4; + } + + return len; +} + + +function demangle(func) { + var __cxa_demangle_func = Module['___cxa_demangle'] || Module['__cxa_demangle']; + if (__cxa_demangle_func) { + try { + var s = + func.substr(1); + var len = lengthBytesUTF8(s)+1; + var buf = _malloc(len); + stringToUTF8(s, buf, len); + var status = _malloc(4); + var ret = __cxa_demangle_func(buf, 0, 0, status); + if (getValue(status, 'i32') === 0 && ret) { + return Pointer_stringify(ret); + } + // otherwise, libcxxabi failed + } catch(e) { + // ignore problems here + } finally { + if (buf) _free(buf); + if (status) _free(status); + if (ret) _free(ret); + } + // failure when using libcxxabi, don't demangle + return func; + } + Runtime.warnOnce('warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling'); + return func; +} + +function demangleAll(text) { + var regex = + /__Z[\w\d_]+/g; + return text.replace(regex, + function(x) { + var y = demangle(x); + return x === y ? x : (x + ' [' + y + ']'); + }); +} + +function jsStackTrace() { + var err = new Error(); + if (!err.stack) { + // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown, + // so try that as a special-case. + try { + throw new Error(0); + } catch(e) { + err = e; + } + if (!err.stack) { + return '(no stack trace available)'; + } + } + return err.stack.toString(); +} + +function stackTrace() { + var js = jsStackTrace(); + if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace'](); + return demangleAll(js); +} +Module["stackTrace"] = stackTrace; + +// Memory management + +var PAGE_SIZE = 16384; +var WASM_PAGE_SIZE = 65536; +var ASMJS_PAGE_SIZE = 16777216; +var MIN_TOTAL_MEMORY = 16777216; + +function alignUp(x, multiple) { + if (x % multiple > 0) { + x += multiple - (x % multiple); + } + return x; +} + +var HEAP, +/** @type {ArrayBuffer} */ + buffer, +/** @type {Int8Array} */ + HEAP8, +/** @type {Uint8Array} */ + HEAPU8, +/** @type {Int16Array} */ + HEAP16, +/** @type {Uint16Array} */ + HEAPU16, +/** @type {Int32Array} */ + HEAP32, +/** @type {Uint32Array} */ + HEAPU32, +/** @type {Float32Array} */ + HEAPF32, +/** @type {Float64Array} */ + HEAPF64; + +function updateGlobalBuffer(buf) { + Module['buffer'] = buffer = buf; +} + +function updateGlobalBufferViews() { + Module['HEAP8'] = HEAP8 = new Int8Array(buffer); + Module['HEAP16'] = HEAP16 = new Int16Array(buffer); + Module['HEAP32'] = HEAP32 = new Int32Array(buffer); + Module['HEAPU8'] = HEAPU8 = new Uint8Array(buffer); + Module['HEAPU16'] = HEAPU16 = new Uint16Array(buffer); + Module['HEAPU32'] = HEAPU32 = new Uint32Array(buffer); + Module['HEAPF32'] = HEAPF32 = new Float32Array(buffer); + Module['HEAPF64'] = HEAPF64 = new Float64Array(buffer); +} + +var STATIC_BASE, STATICTOP, staticSealed; // static area +var STACK_BASE, STACKTOP, STACK_MAX; // stack area +var DYNAMIC_BASE, DYNAMICTOP_PTR; // dynamic area handled by sbrk + + STATIC_BASE = STATICTOP = STACK_BASE = STACKTOP = STACK_MAX = DYNAMIC_BASE = DYNAMICTOP_PTR = 0; + staticSealed = false; + + +// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. +function writeStackCookie() { + assert((STACK_MAX & 3) == 0); + HEAPU32[(STACK_MAX >> 2)-1] = 0x02135467; + HEAPU32[(STACK_MAX >> 2)-2] = 0x89BACDFE; +} + +function checkStackCookie() { + if (HEAPU32[(STACK_MAX >> 2)-1] != 0x02135467 || HEAPU32[(STACK_MAX >> 2)-2] != 0x89BACDFE) { + abort('Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x02135467, but received 0x' + HEAPU32[(STACK_MAX >> 2)-2].toString(16) + ' ' + HEAPU32[(STACK_MAX >> 2)-1].toString(16)); + } + // Also test the global address 0 for integrity. This check is not compatible with SAFE_SPLIT_MEMORY though, since that mode already tests all address 0 accesses on its own. + if (HEAP32[0] !== 0x63736d65 /* 'emsc' */) throw 'Runtime error: The application has corrupted its heap memory area (address zero)!'; +} + +function abortStackOverflow(allocSize) { + abort('Stack overflow! Attempted to allocate ' + allocSize + ' bytes on the stack, but stack has only ' + (STACK_MAX - Module['asm'].stackSave() + allocSize) + ' bytes available!'); +} + +function abortOnCannotGrowMemory() { + abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or (4) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 '); +} + + +function enlargeMemory() { + abortOnCannotGrowMemory(); +} + + +var TOTAL_STACK = Module['TOTAL_STACK'] || 5242880; +var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 16777216; +if (TOTAL_MEMORY < TOTAL_STACK) Module.printErr('TOTAL_MEMORY should be larger than TOTAL_STACK, was ' + TOTAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); + +// Initialize the runtime's memory +// check for full engine support (use string 'subarray' to avoid closure compiler confusion) +assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray !== undefined && Int32Array.prototype.set !== undefined, + 'JS engine does not provide full typed array support'); + + + +// Use a provided buffer, if there is one, or else allocate a new one +if (Module['buffer']) { + buffer = Module['buffer']; + assert(buffer.byteLength === TOTAL_MEMORY, 'provided buffer should be ' + TOTAL_MEMORY + ' bytes, but it is ' + buffer.byteLength); +} else { + // Use a WebAssembly memory where available + { + buffer = new ArrayBuffer(TOTAL_MEMORY); + } + assert(buffer.byteLength === TOTAL_MEMORY); +} +updateGlobalBufferViews(); + + +function getTotalMemory() { + return TOTAL_MEMORY; +} + +// Endianness check (note: assumes compiler arch was little-endian) + HEAP32[0] = 0x63736d65; /* 'emsc' */ +HEAP16[1] = 0x6373; +if (HEAPU8[2] !== 0x73 || HEAPU8[3] !== 0x63) throw 'Runtime error: expected the system to be little-endian!'; + +Module['HEAP'] = HEAP; +Module['buffer'] = buffer; +Module['HEAP8'] = HEAP8; +Module['HEAP16'] = HEAP16; +Module['HEAP32'] = HEAP32; +Module['HEAPU8'] = HEAPU8; +Module['HEAPU16'] = HEAPU16; +Module['HEAPU32'] = HEAPU32; +Module['HEAPF32'] = HEAPF32; +Module['HEAPF64'] = HEAPF64; + +function callRuntimeCallbacks(callbacks) { + while(callbacks.length > 0) { + var callback = callbacks.shift(); + if (typeof callback == 'function') { + callback(); + continue; + } + var func = callback.func; + if (typeof func === 'number') { + if (callback.arg === undefined) { + func(); + } else { + func(arg); + } + } else { + func(callback.arg === undefined ? null : callback.arg); + } + } +} + +var __ATPRERUN__ = []; // functions called before the runtime is initialized +var __ATINIT__ = []; // functions called during startup +var __ATMAIN__ = []; // functions called when main() is to be run +var __ATEXIT__ = []; // functions called during shutdown +var __ATPOSTRUN__ = []; // functions called after the runtime has exited + +var runtimeInitialized = false; +var runtimeExited = false; + + +function preRun() { + // compatibility - merge in anything from Module['preRun'] at this time + if (Module['preRun']) { + if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; + while (Module['preRun'].length) { + addOnPreRun(Module['preRun'].shift()); + } + } + callRuntimeCallbacks(__ATPRERUN__); +} + +function ensureInitRuntime() { + checkStackCookie(); + if (runtimeInitialized) return; + runtimeInitialized = true; + callRuntimeCallbacks(__ATINIT__); +} + +function preMain() { + checkStackCookie(); + callRuntimeCallbacks(__ATMAIN__); +} + +function exitRuntime() { + checkStackCookie(); + callRuntimeCallbacks(__ATEXIT__); + runtimeExited = true; +} + +function postRun() { + checkStackCookie(); + // compatibility - merge in anything from Module['postRun'] at this time + if (Module['postRun']) { + if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; + while (Module['postRun'].length) { + addOnPostRun(Module['postRun'].shift()); + } + } + callRuntimeCallbacks(__ATPOSTRUN__); +} + +function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); +} +Module["addOnPreRun"] = addOnPreRun; + +function addOnInit(cb) { + __ATINIT__.unshift(cb); +} +Module["addOnInit"] = addOnInit; + +function addOnPreMain(cb) { + __ATMAIN__.unshift(cb); +} +Module["addOnPreMain"] = addOnPreMain; + +function addOnExit(cb) { + __ATEXIT__.unshift(cb); +} +Module["addOnExit"] = addOnExit; + +function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); +} +Module["addOnPostRun"] = addOnPostRun; + +// Tools + +/** @type {function(string, boolean=, number=)} */ +function intArrayFromString(stringy, dontAddNull, length) { + var len = length > 0 ? length : lengthBytesUTF8(stringy)+1; + var u8array = new Array(len); + var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); + if (dontAddNull) u8array.length = numBytesWritten; + return u8array; +} +Module["intArrayFromString"] = intArrayFromString; + +function intArrayToString(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + var chr = array[i]; + if (chr > 0xFF) { + assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ') at offset ' + i + ' not in 0x00-0xFF.'); + chr &= 0xFF; + } + ret.push(String.fromCharCode(chr)); + } + return ret.join(''); +} +Module["intArrayToString"] = intArrayToString; + +// Deprecated: This function should not be called because it is unsafe and does not provide +// a maximum length limit of how many bytes it is allowed to write. Prefer calling the +// function stringToUTF8Array() instead, which takes in a maximum length that can be used +// to be secure from out of bounds writes. +/** @deprecated */ +function writeStringToMemory(string, buffer, dontAddNull) { + Runtime.warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); + + var /** @type {number} */ lastChar, /** @type {number} */ end; + if (dontAddNull) { + // stringToUTF8Array always appends null. If we don't want to do that, remember the + // character that existed at the location where the null will be placed, and restore + // that after the write (below). + end = buffer + lengthBytesUTF8(string); + lastChar = HEAP8[end]; + } + stringToUTF8(string, buffer, Infinity); + if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. +} +Module["writeStringToMemory"] = writeStringToMemory; + +function writeArrayToMemory(array, buffer) { + assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') + HEAP8.set(array, buffer); +} +Module["writeArrayToMemory"] = writeArrayToMemory; + +function writeAsciiToMemory(str, buffer, dontAddNull) { + for (var i = 0; i < str.length; ++i) { + assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff); + HEAP8[((buffer++)>>0)]=str.charCodeAt(i); + } + // Null-terminate the pointer to the HEAP. + if (!dontAddNull) HEAP8[((buffer)>>0)]=0; +} +Module["writeAsciiToMemory"] = writeAsciiToMemory; + +function unSign(value, bits, ignore) { + if (value >= 0) { + return value; + } + return bits <= 32 ? 2*Math.abs(1 << (bits-1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts + : Math.pow(2, bits) + value; +} +function reSign(value, bits, ignore) { + if (value <= 0) { + return value; + } + var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32 + : Math.pow(2, bits-1); + if (value >= half && (bits <= 32 || value > half)) { // for huge values, we can hit the precision limit and always get true here. so don't do that + // but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors + // TODO: In i64 mode 1, resign the two parts separately and safely + value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts + } + return value; +} + +// check for imul support, and also for correctness ( https://bugs.webkit.org/show_bug.cgi?id=126345 ) +if (!Math['imul'] || Math['imul'](0xffffffff, 5) !== -5) Math['imul'] = function imul(a, b) { + var ah = a >>> 16; + var al = a & 0xffff; + var bh = b >>> 16; + var bl = b & 0xffff; + return (al*bl + ((ah*bl + al*bh) << 16))|0; +}; +Math.imul = Math['imul']; + + +if (!Math['clz32']) Math['clz32'] = function(x) { + x = x >>> 0; + for (var i = 0; i < 32; i++) { + if (x & (1 << (31 - i))) return i; + } + return 32; +}; +Math.clz32 = Math['clz32'] + +if (!Math['trunc']) Math['trunc'] = function(x) { + return x < 0 ? Math.ceil(x) : Math.floor(x); +}; +Math.trunc = Math['trunc']; + +var Math_abs = Math.abs; +var Math_cos = Math.cos; +var Math_sin = Math.sin; +var Math_tan = Math.tan; +var Math_acos = Math.acos; +var Math_asin = Math.asin; +var Math_atan = Math.atan; +var Math_atan2 = Math.atan2; +var Math_exp = Math.exp; +var Math_log = Math.log; +var Math_sqrt = Math.sqrt; +var Math_ceil = Math.ceil; +var Math_floor = Math.floor; +var Math_pow = Math.pow; +var Math_imul = Math.imul; +var Math_fround = Math.fround; +var Math_round = Math.round; +var Math_min = Math.min; +var Math_clz32 = Math.clz32; +var Math_trunc = Math.trunc; + +// A counter of dependencies for calling run(). If we need to +// do asynchronous work before running, increment this and +// decrement it. Incrementing must happen in a place like +// PRE_RUN_ADDITIONS (used by emcc to add file preloading). +// Note that you can add dependencies in preRun, even though +// it happens right before run - run will be postponed until +// the dependencies are met. +var runDependencies = 0; +var runDependencyWatcher = null; +var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled +var runDependencyTracking = {}; + +function getUniqueRunDependency(id) { + var orig = id; + while (1) { + if (!runDependencyTracking[id]) return id; + id = orig + Math.random(); + } + return id; +} + +function addRunDependency(id) { + runDependencies++; + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + if (id) { + assert(!runDependencyTracking[id]); + runDependencyTracking[id] = 1; + if (runDependencyWatcher === null && typeof setInterval !== 'undefined') { + // Check for missing dependencies every few seconds + runDependencyWatcher = setInterval(function() { + if (ABORT) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + return; + } + var shown = false; + for (var dep in runDependencyTracking) { + if (!shown) { + shown = true; + Module.printErr('still waiting on run dependencies:'); + } + Module.printErr('dependency: ' + dep); + } + if (shown) { + Module.printErr('(end of list)'); + } + }, 10000); + } + } else { + Module.printErr('warning: run dependency added without ID'); + } +} +Module["addRunDependency"] = addRunDependency; + +function removeRunDependency(id) { + runDependencies--; + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + if (id) { + assert(runDependencyTracking[id]); + delete runDependencyTracking[id]; + } else { + Module.printErr('warning: run dependency removed without ID'); + } + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); // can add another dependenciesFulfilled + } + } +} +Module["removeRunDependency"] = removeRunDependency; + +Module["preloadedImages"] = {}; // maps url to image data +Module["preloadedAudios"] = {}; // maps url to audio data + + + +var memoryInitializer = null; + + + + + + +// === Body === + +var ASM_CONSTS = [function($0, $1) { Module.printErr('bad name in getProcAddress: ' + [Pointer_stringify($0), Pointer_stringify($1)]) }]; + +function _emscripten_asm_const_iii(code, a0, a1) { + return ASM_CONSTS[code](a0, a1); +} + + + +STATIC_BASE = Runtime.GLOBAL_BASE; + +STATICTOP = STATIC_BASE + 85840; +/* global initializers */ __ATINIT__.push(); + + +/* memory initializer */ allocate([77,33,75,33,0,0,0,0,4,0,0,0,77,46,75,46,0,0,0,0,4,0,0,0,70,76,84,52,0,0,0,0,4,0,0,0,70,76,84,56,0,0,0,0,8,0,0,0,52,67,72,78,0,0,0,0,4,0,0,0,54,67,72,78,0,0,0,0,6,0,0,0,56,67,72,78,0,0,0,0,8,0,0,0,49,48,67,72,0,0,0,0,10,0,0,0,49,50,67,72,0,0,0,0,12,0,0,0,49,52,67,72,0,0,0,0,14,0,0,0,49,54,67,72,0,0,0,0,16,0,0,0,49,56,67,72,0,0,0,0,18,0,0,0,50,48,67,72,0,0,0,0,20,0,0,0,50,50,67,72,0,0,0,0,22,0,0,0,50,52,67,72,0,0,0,0,24,0,0,0,50,54,67,72,0,0,0,0,26,0,0,0,50,56,67,72,0,0,0,0,28,0,0,0,51,48,67,72,0,0,0,0,30,0,0,0,51,50,67,72,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,171,170,42,63,0,0,0,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,192,63,0,0,0,64,0,0,0,0,0,0,128,191,0,0,0,192,0,0,128,192,0,0,0,193,0,0,128,193,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,64,0,0,128,64,0,0,0,65,0,0,128,65,0,0,0,0,0,0,0,0,172,95,0,0,255,3,0,0,255,255,255,255,205,204,236,63,2,0,0,0,86,1,0,0,85,1,0,0,87,0,0,0,83,0,0,0,68,0,0,0,65,0,0,0,69,0,0,0,81,0,0,0,255,255,255,255,0,1,0,0,255,255,255,255,0,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,4,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0,7,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,63,0,0,128,63,0,0,128,63,0,0,128,63,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,63,0,0,128,63,0,0,128,63,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,32,0,0,176,1,0,0,0,0,0,0,0,0,0,32,37,249,142,0,10,2,0,0,128,190,125,95,244,125,31,160,242,43,74,30,9,82,8,0,64,34,65,80,20,4,16,32,32,41,46,18,8,34,8,0,32,34,65,80,20,4,16,32,32,249,16,76,8,250,62,60,16,34,125,222,247,125,16,32,32,161,232,50,8,34,8,0,8,34,5,16,4,69,16,0,240,163,164,50,8,82,8,0,4,34,5,16,4,69,16,32,32,249,226,94,8,2,0,129,2,62,125,31,244,125,16,0,0,32,0,0,176,1,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,190,15,0,192,15,224,247,251,125,126,191,95,232,190,80,0,162,8,8,68,232,47,20,10,133,2,129,80,72,160,80,0,162,40,228,73,40,40,20,10,132,2,129,64,72,160,72,0,190,15,2,16,175,235,247,9,132,62,159,216,79,160,71,0,34,136,228,9,161,42,20,10,132,2,129,80,72,160,72,0,34,40,8,4,160,47,20,10,133,2,129,80,72,162,80,0,190,143,0,0,33,32,244,251,125,126,129,95,232,156,208,7,0,128,0,0,224,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,1,12,0,130,66,191,223,239,247,251,11,5,5,133,66,191,4,72,0,198,66,161,80,40,20,64,8,5,37,133,66,160,8,168,0,170,70,161,80,40,20,64,8,5,37,133,66,144,16,8,0,146,74,161,95,232,247,67,8,5,37,121,126,136,32,8,0,130,82,161,64,40,1,66,8,137,36,133,64,132,64,8,0,130,98,161,64,42,2,66,8,81,36,133,64,130,128,8,0,130,66,191,192,47,244,67,248,33,252,133,126,191,0,9,62,0,0,0,0,4,0,0,0,0,0,0,0,128,1,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,0,4,0,32,72,65,0,0,0,0,0,8,0,0,4,4,0,4,60,32,0,65,0,0,0,0,0,8,0,0,240,125,223,247,133,239,75,81,190,239,251,190,239,59,81,4,0,69,65,20,133,40,74,73,170,40,138,162,32,8,81,4,240,69,65,244,157,40,74,71,170,40,138,162,224,11,81,4,16,69,65,20,132,40,74,73,170,40,138,162,0,10,145,2,240,125,223,247,133,47,74,209,170,232,251,190,224,123,31,1,0,0,0,0,4,8,64,0,0,0,8,32,0,0,0,0,0,0,0,0,132,15,96,0,0,0,8,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,172,1,15,0,0,0,0,0,0,0,0,0,0,0,0,0,36,1,15,0,0,0,0,0,0,0,0,0,6,0,0,0,36,1,15,0,0,0,0,0,0,0,128,16,9,162,40,250,36,1,15,0,0,0,0,0,0,0,0,62,1,42,37,66,34,82,15,0,0,0,0,0,0,0,128,138,3,42,34,34,36,41,15,0,0,0,0,0,0,0,128,10,1,42,37,18,36,1,15,0,0,0,0,0,0,0,128,10,1,190,232,251,36,1,15,0,0,0,0,0,0,0,128,190,14,0,0,2,172,1,15,0,0,0,0,0,0,0,128,4,0,0,224,3,0,0,15,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,0,0,0,14,184,67,132,3,58,32,0,128,160,190,2,32,0,0,240,138,32,82,196,2,43,32,4,34,145,2,248,59,0,240,7,142,56,75,228,2,58,32,2,28,138,30,8,42,233,17,4,224,11,66,244,2,130,36,1,20,4,20,232,186,4,209,5,128,184,195,231,10,58,137,0,28,14,60,40,2,9,80,4,128,0,64,196,2,128,68,0,34,132,32,232,2,0,80,4,0,0,64,128,2,0,32,5,0,142,62,8,2,0,16,4,224,3,64,128,66,0,0,7,0,132,0,248,3,0,240,7,0,0,64,128,34,0,0,4,0,0,0,0,0,0,0,0,0,0,64,128,2,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,7,128,0,194,160,72,24,0,0,1,132,33,9,146,2,66,38,4,1,33,81,0,0,127,63,2,66,2,16,41,0,34,20,192,239,247,251,253,126,9,161,223,239,247,187,187,3,18,15,68,40,20,10,133,66,9,129,64,32,16,16,17,1,8,4,68,40,20,10,133,66,127,129,64,32,16,16,17,1,4,130,199,239,247,251,253,126,9,129,207,231,243,17,17,1,50,169,80,40,20,10,133,66,9,161,64,32,16,16,17,1,64,184,80,40,20,10,133,66,121,191,223,239,247,187,187,3,32,160,31,0,0,0,0,0,0,16,0,0,0,0,0,0,112,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,2,8,131,34,1,0,2,8,67,2,1,0,1,1,124,20,4,132,68,1,0,32,4,132,4,128,8,63,130,0,132,66,191,223,239,247,3,126,161,80,40,20,10,33,0,0,132,70,161,80,40,20,138,82,161,80,40,20,122,161,239,3,158,74,161,80,40,20,82,82,161,80,40,20,74,31,8,2,132,82,161,80,40,20,34,74,161,80,40,244,75,161,239,3,132,98,161,80,40,20,82,74,161,80,40,4,122,161,40,2,124,66,191,223,239,247,139,126,191,223,239,247,11,189,239,3,0,0,0,0,0,0,0,4,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,5,32,0,0,4,132,0,34,129,69,17,16,66,1,0,148,66,81,0,0,8,66,81,148,42,162,32,8,165,80,0,0,0,32,0,0,0,0,0,0,0,5,0,0,0,0,8,190,239,251,254,251,190,239,251,20,145,235,251,190,239,251,0,32,8,130,32,10,162,40,138,20,145,40,138,162,40,138,62,190,239,251,254,11,190,239,251,20,145,40,138,162,40,138,0,162,40,138,34,8,130,32,8,20,145,40,138,162,40,138,8,190,239,251,254,251,190,239,251,20,145,47,250,190,239,251,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,33,0,4,0,0,0,0,0,0,0,0,0,0,0,0,130,80,20,2,20,0,0,0,0,0,0,0,0,0,0,16,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,190,40,138,162,40,34,0,0,0,0,0,0,0,0,0,0,170,40,138,162,232,34,0,0,0,0,0,0,0,0,0,0,170,40,138,162,168,34,0,0,0,0,0,0,0,0,0,0,170,40,138,162,232,34,0,0,0,0,0,0,0,0,0,0,190,239,251,190,47,62,0,0,0,0,0,0,0,0,0,0,4,0,0,0,40,32,0,0,0,0,0,0,0,0,0,0,0,0,0,128,15,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,4,0,0,0,6,0,0,0,5,0,0,0,7,0,0,0,6,0,0,0,2,0,0,0,3,0,0,0,3,0,0,0,5,0,0,0,5,0,0,0,2,0,0,0,4,0,0,0,1,0,0,0,7,0,0,0,5,0,0,0,2,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,4,0,0,0,3,0,0,0,6,0,0,0,7,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,3,0,0,0,5,0,0,0,6,0,0,0,5,0,0,0,7,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,7,0,0,0,6,0,0,0,7,0,0,0,7,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,2,0,0,0,7,0,0,0,2,0,0,0,3,0,0,0,5,0,0,0,2,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,4,0,0,0,5,0,0,0,5,0,0,0,1,0,0,0,2,0,0,0,5,0,0,0,2,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,4,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,3,0,0,0,1,0,0,0,3,0,0,0,4,0,0,0,4,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,7,0,0,0,1,0,0,0,5,0,0,0,3,0,0,0,7,0,0,0,3,0,0,0,5,0,0,0,4,0,0,0,1,0,0,0,7,0,0,0,4,0,0,0,3,0,0,0,5,0,0,0,3,0,0,0,3,0,0,0,2,0,0,0,5,0,0,0,6,0,0,0,1,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,5,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,7,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,7,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,5,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,4,0,0,0,6,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,9,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,3,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,3,0,0,0,5,0,0,0,20,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,191,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,191,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,191,0,0,0,0,0,0,128,191,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,191,0,0,128,63,0,0,0,0,0,0,0,0,0,0,128,63,0,0,128,191,0,0,0,0,0,0,0,0,0,0,128,191,0,0,128,191,0,0,0,0,0,0,128,63,46,186,232,62,0,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,8,0,0,0,8,0,0,0,4,0,0,0,4,0,0,0,2,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,0,0,0,8,0,0,0,8,0,0,0,8,0,0,0,4,0,0,0,4,0,0,0,2,0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,205,204,12,64,0,0,128,63,0,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0,7,0,0,0,8,0,0,0,9,0,0,0,10,0,0,0,11,0,0,0,10,0,0,0,12,0,0,0,10,0,0,0,0,0,0,0,48,98,159,57,21,31,31,58,45,208,110,58,21,31,159,58,161,247,198,58,159,174,238,58,150,67,11,59,21,31,31,59,91,11,51,59,218,230,70,59,88,89,91,59,211,249,112,59,38,228,131,59,97,226,143,59,97,136,156,59,196,205,169,59,38,170,183,59,177,54,198,59,158,98,213,59,80,54,229,59,44,186,245,59,231,114,3,60,204,96,12,60,198,166,21,60,212,68,31,60,41,63,41,60,146,145,51,60,66,64,62,60,56,75,73,60,166,182,84,60,90,126,96,60,135,166,108,60,43,47,121,60,11,10,131,60,213,174,137,60,245,133,144,60,80,141,151,60,0,199,158,60,30,53,166,60,120,211,173,60,64,166,181,60,92,171,189,60,255,230,197,60,248,84,206,60,94,247,214,60,74,208,223,60,165,221,232,60,134,33,242,60,212,153,251,60,97,165,2,61,28,153,7,61,26,168,12,61,91,210,17,61,236,24,23,61,204,123,28,61,253,250,33,61,125,150,39,61,77,78,45,61,121,35,51,61,245,20,57,61,205,35,63,61,1,80,69,61,145,153,75,61,113,255,81,61,199,132,88,61,108,38,95,61,121,230,101,61,240,196,108,61,206,193,115,61,22,221,122,61,99,11,129,61,111,183,132,61,54,115,136,61,50,62,140,61,231,24,144,61,88,3,148,61,130,253,151,61,226,6,156,61,129,32,160,61,98,74,164,61,253,131,168,61,83,205,172,61,233,38,177,61,71,145,181,61,95,11,186,61,184,149,190,61,81,48,195,61,177,219,199,61,217,151,204,61,65,100,209,61,234,64,214,61,224,46,219,61,157,45,224,61,155,60,229,61,230,92,234,61,126,142,239,61,87,208,244,61,3,36,250,61,118,136,255,61,27,127,2,62,95,66,5,62,141,14,8,62,97,227,10,62,30,193,13,62,63,167,16,62,74,150,19,62,63,142,22,62,29,143,25,62,162,152,28,62,17,171,31,62,172,198,34,62,238,234,37,62,93,24,41,62,181,78,44,62,59,142,47,62,170,214,50,62,70,40,54,62,14,131,57,62,4,231,60,62,38,84,64,62,117,202,67,62,241,73,71,62,154,210,74,62,178,100,78,62,59,0,82,62,240,164,85,62,21,83,89,62,170,10,93,62,108,203,96,62,225,149,100,62,198,105,104,62,27,71,108,62,35,46,112,62,155,30,116,62,131,24,120,62,29,28,124,62,182,20,128,62,54,32,130,62,144,48,132,62,195,69,134,62,208,95,136,62,183,126,138,62,119,162,140,62,50,203,142,62,232,248,144,62,119,43,147,62,2,99,149,62,102,159,151,62,231,224,153,62,66,39,156,62,185,114,158,62,43,195,160,62,152,24,163,62,0,115,165,62,133,210,167,62,38,55,170,62,195,160,172,62,90,15,175,62,48,131,177,62,34,252,179,62,16,122,182,62,59,253,184,62,131,133,187,62,232,18,190,62,106,165,192,62,41,61,195,62,39,218,197,62,66,124,200,62,121,35,203,62,15,208,205,62,195,129,208,62,214,56,211,62,6,245,213,62,149,182,216,62,99,125,219,62,111,73,222,62,185,26,225,62,99,241,227,62,108,205,230,62,180,174,233,62,91,149,236,62,98,129,239,62,201,114,242,62,110,105,245,62,149,101,248,62,27,103,251,62,1,110,254,62,52,189,0,63,6,70,2,63,171,209,3,63,254,95,5,63,2,241,6,63,199,132,8,63,92,27,10,63,162,180,11,63,152,80,13,63,95,239,14,63,247,144,16,63,63,53,18,63,89,220,19,63,35,134,21,63,207,50,23,63,59,226,24,63,104,148,26,63,119,73,28,63,71,1,30,63,216,187,31,63,74,121,33,63,143,57,35,63,164,252,36,63,139,194,38,63,68,139,40,63,205,86,42,63,57,37,44,63,136,246,45,63,151,202,47,63,152,161,49,63,108,123,51,63,33,88,53,63,185,55,55,63,34,26,57,63,126,255,58,63,188,231,60,63,204,210,62,63,207,192,64,63,196,177,66,63,139,165,68,63,69,156,70,63,242,149,72,63,129,146,74,63,4,146,76,63,104,148,78,63,208,153,80,63,26,162,82,63,88,173,84,63,136,187,86,63,171,204,88,63,210,224,90,63,219,247,92,63,232,17,95,63,232,46,97,63,236,78,99,63,227,113,101,63,221,151,103,63,219,192,105,63,204,236,107,63,193,27,110,63,186,77,112,63,182,130,114,63,182,186,116,63,169,245,118,63,177,51,121,63,205,116,123,63,220,184,125,63,0,0,128,63,13,0,115,0,13,0,122,0,13,0,128,0,13,0,135,0,13,0,141,0,13,0,148,0,13,0,154,0,13,0,161,0,26,0,167,0,26,0,180,0,26,0,193,0,26,0,206,0,26,0,218,0,26,0,231,0,26,0,244,0,26,0,1,1,51,0,14,1,51,0,40,1,51,0,65,1,51,0,91,1,51,0,117,1,51,0,143,1,51,0,168,1,51,0,194,1,103,0,220,1,103,0,15,2,103,0,67,2,103,0,118,2,103,0,170,2,103,0,221,2,103,0,17,3,103,0,68,3,206,0,120,3,206,0,223,3,206,0,70,4,206,0,173,4,206,0,20,5,197,0,123,5,188,0,221,5,181,0,59,6,88,1,151,6,66,1,66,7,48,1,227,7,32,1,123,8,18,1,11,9,6,1,148,9,252,0,23,10,242,0,149,10,203,1,15,11,174,1,244,11,149,1,203,12,128,1,149,13,110,1,86,14,94,1,13,15,80,1,188,15,67,1,99,16,100,2,7,17,62,2,56,18,29,2,87,19,1,2,102,20,233,1,102,21,211,1,90,22,192,1,68,23,175,1,36,24,49,3,254,24,254,2,150,26,210,2,21,28,173,2,126,29,141,2,212,30,112,2,26,32,86,2,82,33,64,2,125,34,67,4,159,35,254,3,192,37,196,3,191,39,146,3,161,41,103,3,106,43,65,3,29,45,31,3,190,46,0,3,77,48,176,5,209,49,85,5,168,52,7,5,82,55,197,4,213,57,139,4,55,60,88,4,124,62,42,4,168,64,1,4,189,66,152,7,194,68,30,7,142,72,182,6,28,76,93,6,118,79,16,6,165,82,204,5,172,85,143,5,146,88,89,5,89,91,35,10,12,94,128,9,28,99,246,8,219,103,127,8,85,108,24,8,148,112,189,7,160,116,108,7,125,120,35,7,51,124,1,1,0,0,1,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0,7,0,0,0,8,0,0,0,9,0,0,0,10,0,0,0,11,0,0,0,13,0,0,0,15,0,0,0,17,0,0,0,19,0,0,0,23,0,0,0,27,0,0,0,31,0,0,0,35,0,0,0,43,0,0,0,51,0,0,0,59,0,0,0,67,0,0,0,83,0,0,0,99,0,0,0,115,0,0,0,131,0,0,0,163,0,0,0,195,0,0,0,227,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,3,0,0,0,4,0,0,0,4,0,0,0,5,0,0,0,5,0,0,0,6,0,0,0,6,0,0,0,7,0,0,0,7,0,0,0,8,0,0,0,8,0,0,0,9,0,0,0,9,0,0,0,10,0,0,0,10,0,0,0,11,0,0,0,11,0,0,0,12,0,0,0,12,0,0,0,13,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,7,0,0,0,9,0,0,0,13,0,0,0,17,0,0,0,25,0,0,0,33,0,0,0,49,0,0,0,65,0,0,0,97,0,0,0,129,0,0,0,193,0,0,0,1,1,0,0,129,1,0,0,1,2,0,0,1,3,0,0,1,4,0,0,1,6,0,0,1,8,0,0,1,12,0,0,1,16,0,0,1,24,0,0,1,32,0,0,1,48,0,0,1,64,0,0,1,96,0,0,0,0,0,0,0,0,0,0,0,1,0,0,128,0,0,0,86,0,0,0,64,0,0,0,62,180,228,51,9,145,243,51,139,178,1,52,60,32,10,52,35,26,19,52,96,169,28,52,167,215,38,52,75,175,49,52,80,59,61,52,112,135,73,52,35,160,86,52,184,146,100,52,85,109,115,52,136,159,129,52,252,11,138,52,147,4,147,52,105,146,156,52,50,191,166,52,63,149,177,52,147,31,189,52,228,105,201,52,173,128,214,52,54,113,228,52,166,73,243,52,136,140,1,53,192,247,9,53,6,239,18,53,118,123,28,53,192,166,38,53,55,123,49,53,218,3,61,53,94,76,73,53,59,97,86,53,185,79,100,53,252,37,115,53,138,121,129,53,134,227,137,53,124,217,146,53,133,100,156,53,82,142,166,53,51,97,177,53,37,232,188,53,220,46,201,53,206,65,214,53,65,46,228,53,87,2,243,53,143,102,1,54,79,207,9,54,245,195,18,54,152,77,28,54,232,117,38,54,50,71,49,54,116,204,60,54,94,17,73,54,101,34,86,54,206,12,100,54,184,222,114,54,151,83,129,54,28,187,137,54,114,174,146,54,175,54,156,54,129,93,166,54,53,45,177,54,199,176,188,54,228,243,200,54,1,3,214,54,96,235,227,54,30,187,242,54,162,64,1,55,235,166,9,55,241,152,18,55,201,31,28,55,30,69,38,55,61,19,49,55,30,149,60,55,111,214,72,55,162,227,85,55,247,201,99,55,137,151,114,55,175,45,129,55,190,146,137,55,116,131,146,55,230,8,156,55,190,44,166,55,71,249,176,55,121,121,188,55,254,184,200,55,71,196,213,55,146,168,227,55,248,115,242,55,192,26,1,56,147,126,9,56,249,109,18,56,6,242,27,56,98,20,38,56,86,223,48,56,216,93,60,56,146,155,72,56,242,164,85,56,51,135,99,56,110,80,114,56,211,7,129,56,107,106,137,56,130,88,146,56,42,219,155,56,9,252,165,56,104,197,176,56,59,66,188,56,41,126,200,56,160,133,213,56,217,101,227,56,232,44,242,56,233,244,0,57,70,86,9,57,14,67,18,57,81,196,27,57,181,227,37,57,127,171,48,57,162,38,60,57,197,96,72,57,83,102,85,57,131,68,99,57,104,9,114,57,1,226,128,57,36,66,137,57,157,45,146,57,123,173,155,57,99,203,165,57,153,145,176,57,13,11,188,57,102,67,200,57,11,71,213,57,50,35,227,57,237,229,241,57,29,207,0,58,5,46,9,58,48,24,18,58,169,150,27,58,21,179,37,58,183,119,48,58,124,239,59,58,10,38,72,58,199,39,85,58,230,1,99,58,120,194,113,58,59,188,128,58,233,25,137,58,198,2,146,58,219,127,155,58,203,154,165,58,216,93,176,58,239,211,187,58,179,8,200,58,136,8,213,58,159,224,226,58,7,159,241,58,92,169,0,59,208,5,9,59,94,237,17,59,15,105,27,59,132,130,37,59,253,67,48,59,103,184,59,59,97,235,71,59,77,233,84,59,93,191,98,59,156,123,113,59,127,150,128,59,186,241,136,59,249,215,145,59,71,82,155,59,65,106,165,59,39,42,176,59,226,156,187,59,18,206,199,59,23,202,212,59,32,158,226,59,53,88,241,59,166,131,0,60,167,221,8,60,152,194,17,60,130,59,27,60,1,82,37,60,84,16,48,60,97,129,59,60,200,176,71,60,229,170,84,60,232,124,98,60,212,52,113,60,207,112,128,60,150,201,136,60,58,173,145,60,192,36,155,60,197,57,165,60,133,246,175,60,229,101,187,60,130,147,199,60,185,139,212,60,180,91,226,60,121,17,241,60,251,93,0,61,137,181,8,61,223,151,17,61,2,14,27,61,141,33,37,61,185,220,47,61,109,74,59,61,64,118,71,61,145,108,84,61,133,58,98,61,34,238,112,61,42,75,128,61,127,161,136,61,136,130,145,61,72,247,154,61,88,9,165,61,242,194,175,61,248,46,187,61,3,89,199,61,109,77,212,61,92,25,226,61,209,202,240,61,91,56,0,62,119,141,8,62,51,109,17,62,144,224,26,62,39,241,36,62,46,169,47,62,135,19,59,62,202,59,71,62,77,46,84,62,55,248,97,62,132,167,112,62,143,37,128,62,115,121,136,62,226,87,145,62,220,201,154,62,249,216,164,62,109,143,175,62,27,248,186,62,149,30,199,62,51,15,212,62,23,215,225,62,61,132,240,62,198,18,0,63,114,101,8,63,147,66,17,63,43,179,26,63,206,192,36,63,177,117,47,63,178,220,58,63,101,1,71,63,29,240,83,63,251,181,97,63,251,96,112,63,0,0,128,63,79,103,103,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,69,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,232,29,0,0,5,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,15,0,0,0,63,75,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,30,0,0,5,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,15,0,0,0,71,75,1,0,0,4,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,30,0,0,2,0,0,192,3,0,0,192,4,0,0,192,5,0,0,192,6,0,0,192,7,0,0,192,8,0,0,192,9,0,0,192,10,0,0,192,11,0,0,192,12,0,0,192,13,0,0,192,14,0,0,192,15,0,0,192,16,0,0,192,17,0,0,192,18,0,0,192,19,0,0,192,20,0,0,192,21,0,0,192,22,0,0,192,23,0,0,192,24,0,0,192,25,0,0,192,26,0,0,192,27,0,0,192,28,0,0,192,29,0,0,192,30,0,0,192,31,0,0,192,0,0,0,179,1,0,0,195,2,0,0,195,3,0,0,195,4,0,0,195,5,0,0,195,6,0,0,195,7,0,0,195,8,0,0,195,9,0,0,195,10,0,0,195,11,0,0,195,12,0,0,195,13,0,0,211,14,0,0,195,15,0,0,195,0,0,12,187,1,0,12,195,2,0,12,195,3,0,12,195,4,0,12,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,100,0,0,0,232,3,0,0,16,39,0,0,160,134,1,0,64,66,15,0,128,150,152,0,0,225,245,5,95,112,137,0,255,9,47,15,0,107,0,101,64,95,0,90,192,84,0,80,128,75,64,71,64,67,128,63,0,60,160,56,128,53,128,50,160,47,0,45,96,42,0,40,192,37,160,35,158,33,192,31,0,30,80,28,192,26,64,25,208,23,128,22,48,21,0,20,224,18,208,17,208,16,224,15,0,15,40,14,96,13,160,12,232,11,64,11,152,10,0,10,112,9,232,8,104,8,240,7,128,7,20,7,176,6,80,6,244,5,160,5,76,5,0,5,184,4,116,4,52,4,248,3,192,3,138,3,88,3,40,3,250,2,208,2,166,2,128,2,92,2,58,2,26,2,252,1,224,1,197,1,172,1,148,1,125,1,104,1,83,1,64,1,46,1,29,1,13,1,254,0,240,0,226,0,214,0,202,0,190,0,180,0,170,0,160,0,151,0,143,0,135,0,127,0,120,0,113,0,107,0,101,0,95,0,90,0,85,0,80,0,75,0,71,0,67,0,63,0,60,0,56,0,53,0,50,0,47,0,45,0,42,0,40,0,37,0,35,0,33,0,31,0,30,0,28,0,27,0,25,0,24,0,22,0,21,0,20,0,19,0,18,0,17,0,16,0,15,0,14,0,13,0,13,0,12,0,11,0,11,0,10,0,9,0,9,0,8,0,8,0,7,0,7,0,176,6,80,6,245,5,160,5,77,5,1,5,185,4,117,4,53,4,249,3,193,3,139,3,88,3,0,0,24,0,49,0,74,0,97,0,120,0,141,0,161,0,180,0,197,0,212,0,224,0,235,0,244,0,250,0,253,0,255,0,253,0,250,0,244,0,235,0,224,0,212,0,197,0,180,0,161,0,141,0,120,0,97,0,74,0,49,0,24,0,69,120,116,101,110,100,101,100,32,77,111,100,117,108,101,58,32,0,67,111,117,108,100,32,110,111,116,32,111,112,101,110,32,105,110,112,117,116,32,102,105,108,101,0,102,115,101,101,107,40,41,32,102,97,105,108,101,100,0,102,114,101,97,100,40,41,32,102,97,105,108,101,100,0,99,111,117,108,100,32,110,111,116,32,99,114,101,97,116,101,32,99,111,110,116,101,120,116,58,32,109,111,100,117,108,101,32,105,115,32,110,111,116,32,115,97,110,101,10,0,99,111,117,108,100,32,110,111,116,32,99,114,101,97,116,101,32,99,111,110,116,101,120,116,58,32,109,97,108,108,111,99,32,102,97,105,108,101,100,10,0,99,111,117,108,100,32,110,111,116,32,99,114,101,97,116,101,32,99,111,110,116,101,120,116,58,32,117,110,107,110,111,119,110,32,101,114,114,111,114,10,0,70,97,105,108,101,100,32,116,111,32,105,110,105,116,105,97,108,105,122,101,32,97,117,100,105,111,32,99,111,110,116,101,120,116,0,70,97,105,108,101,100,32,116,111,32,105,110,105,116,105,97,108,105,122,101,32,97,117,100,105,111,32,112,108,97,121,98,97,99,107,32,100,101,118,105,99,101,0,70,97,105,108,101,100,32,116,111,32,115,116,97,114,116,32,97,117,100,105,111,32,112,108,97,121,98,97,99,107,32,100,101,118,105,99,101,0,70,97,105,108,101,100,32,116,111,32,99,114,101,97,116,101,32,109,117,116,101,120,32,102,111,114,32,97,117,100,105,111,32,109,105,120,105,110,103,0,65,117,100,105,111,32,100,101,118,105,99,101,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,58,32,37,115,0,65,117,100,105,111,32,98,97,99,107,101,110,100,58,32,109,105,110,105,95,97,108,32,47,32,37,115,0,65,117,100,105,111,32,102,111,114,109,97,116,58,32,37,115,32,45,62,32,37,115,0,65,117,100,105,111,32,99,104,97,110,110,101,108,115,58,32,37,100,32,45,62,32,37,100,0,65,117,100,105,111,32,115,97,109,112,108,101,32,114,97,116,101,58,32,37,100,32,45,62,32,37,100,0,65,117,100,105,111,32,98,117,102,102,101,114,32,115,105,122,101,58,32,37,100,0,67,111,117,108,100,32,110,111,116,32,99,108,111,115,101,32,97,117,100,105,111,32,100,101,118,105,99,101,32,98,101,99,97,117,115,101,32,105,116,32,105,115,32,110,111,116,32,99,117,114,114,101,110,116,108,121,32,105,110,105,116,105,97,108,105,122,101,100,0,65,117,100,105,111,32,100,101,118,105,99,101,32,99,108,111,115,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,67,114,101,97,116,101,65,117,100,105,111,66,117,102,102,101,114,40,41,32,58,32,70,97,105,108,101,100,32,116,111,32,97,108,108,111,99,97,116,101,32,109,101,109,111,114,121,32,102,111,114,32,97,117,100,105,111,32,98,117,102,102,101,114,0,76,111,97,100,83,111,117,110,100,70,114,111,109,87,97,118,101,40,41,32,58,32,70,97,105,108,101,100,32,116,111,32,99,114,101,97,116,101,32,100,97,116,97,32,99,111,110,118,101,114,115,105,111,110,32,112,105,112,101,108,105,110,101,0,80,108,97,121,65,117,100,105,111,66,117,102,102,101,114,40,41,32,58,32,78,111,32,97,117,100,105,111,32,98,117,102,102,101,114,0,46,119,97,118,0,46,111,103,103,0,91,37,115,93,32,82,101,115,111,117,114,99,101,32,102,105,108,101,32,100,111,101,115,32,110,111,116,32,99,111,110,116,97,105,110,32,119,97,118,101,32,100,97,116,97,0,91,37,115,93,32,65,117,100,105,111,32,102,105,108,101,102,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,44,32,105,116,32,99,97,110,39,116,32,98,101,32,108,111,97,100,101,100,0,76,111,97,100,83,111,117,110,100,70,114,111,109,87,97,118,101,40,41,32,58,32,70,97,105,108,101,100,32,116,111,32,103,101,116,32,102,114,97,109,101,32,99,111,117,110,116,32,102,111,114,32,102,111,114,109,97,116,32,99,111,110,118,101,114,115,105,111,110,0,76,111,97,100,83,111,117,110,100,70,114,111,109,87,97,118,101,40,41,32,58,32,70,97,105,108,101,100,32,116,111,32,99,114,101,97,116,101,32,97,117,100,105,111,32,98,117,102,102,101,114,0,76,111,97,100,83,111,117,110,100,70,114,111,109,87,97,118,101,40,41,32,58,32,70,111,114,109,97,116,32,99,111,110,118,101,114,115,105,111,110,32,102,97,105,108,101,100,0,85,110,108,111,97,100,101,100,32,119,97,118,101,32,100,97,116,97,32,102,114,111,109,32,82,65,77,0,91,83,78,68,32,73,68,32,37,105,93,91,66,85,70,82,32,73,68,32,37,105,93,32,85,110,108,111,97,100,101,100,32,115,111,117,110,100,32,100,97,116,97,32,102,114,111,109,32,82,65,77,0,85,112,100,97,116,101,83,111,117,110,100,40,41,32,58,32,73,110,118,97,108,105,100,32,115,111,117,110,100,32,45,32,110,111,32,97,117,100,105,111,32,98,117,102,102,101,114,0,87,97,118,101,70,111,114,109,97,116,40,41,32,58,32,70,97,105,108,101,100,32,116,111,32,103,101,116,32,102,114,97,109,101,32,99,111,117,110,116,32,102,111,114,32,102,111,114,109,97,116,32,99,111,110,118,101,114,115,105,111,110,46,0,87,97,118,101,70,111,114,109,97,116,40,41,32,58,32,70,111,114,109,97,116,32,99,111,110,118,101,114,115,105,111,110,32,102,97,105,108,101,100,46,0,87,97,118,101,32,99,114,111,112,32,114,97,110,103,101,32,111,117,116,32,111,102,32,98,111,117,110,100,115,0,91,37,115,93,32,79,71,71,32,97,117,100,105,111,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,91,37,115,93,32,70,76,65,67,32,116,111,116,97,108,32,115,97,109,112,108,101,115,58,32,37,105,0,91,37,115,93,32,79,71,71,32,115,97,109,112,108,101,32,114,97,116,101,58,32,37,105,0,91,37,115,93,32,79,71,71,32,99,104,97,110,110,101,108,115,58,32,37,105,0,91,37,115,93,32,79,71,71,32,109,101,109,111,114,121,32,114,101,113,117,105,114,101,100,58,32,37,105,0,46,120,109,0,91,37,115,93,32,88,77,32,110,117,109,98,101,114,32,111,102,32,115,97,109,112,108,101,115,58,32,37,105,0,91,37,115,93,32,88,77,32,116,114,97,99,107,32,108,101,110,103,116,104,58,32,37,49,49,46,54,102,32,115,101,99,0,91,37,115,93,32,88,77,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,46,109,111,100,0,91,37,115], "i8", ALLOC_NONE, Runtime.GLOBAL_BASE); +/* memory initializer */ allocate([93,32,77,79,68,32,110,117,109,98,101,114,32,111,102,32,115,97,109,112,108,101,115,58,32,37,105,0,91,37,115,93,32,77,79,68,32,116,114,97,99,107,32,108,101,110,103,116,104,58,32,37,49,49,46,54,102,32,115,101,99,0,91,37,115,93,32,77,79,68,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,80,108,97,121,77,117,115,105,99,83,116,114,101,97,109,40,41,32,58,32,78,111,32,97,117,100,105,111,32,98,117,102,102,101,114,0,73,110,105,116,32,97,117,100,105,111,32,115,116,114,101,97,109,58,32,78,117,109,98,101,114,32,111,102,32,99,104,97,110,110,101,108,115,32,110,111,116,32,115,117,112,112,111,114,116,101,100,58,32,37,105,0,73,110,105,116,65,117,100,105,111,83,116,114,101,97,109,40,41,32,58,32,70,97,105,108,101,100,32,116,111,32,99,114,101,97,116,101,32,97,117,100,105,111,32,98,117,102,102,101,114,0,91,65,85,68,32,73,68,32,37,105,93,32,65,117,100,105,111,32,115,116,114,101,97,109,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,37,105,32,72,122,44,32,37,105,32,98,105,116,44,32,37,115,41,0,77,111,110,111,0,83,116,101,114,101,111,0,91,65,85,68,32,73,68,32,37,105,93,32,85,110,108,111,97,100,101,100,32,97,117,100,105,111,32,115,116,114,101,97,109,32,100,97,116,97,0,85,112,100,97,116,101,65,117,100,105,111,83,116,114,101,97,109,40,41,32,58,32,78,111,32,97,117,100,105,111,32,98,117,102,102,101,114,0,85,112,100,97,116,101,65,117,100,105,111,83,116,114,101,97,109,40,41,32,58,32,65,116,116,101,109,112,116,105,110,103,32,116,111,32,119,114,105,116,101,32,116,111,111,32,109,97,110,121,32,102,114,97,109,101,115,32,116,111,32,98,117,102,102,101,114,0,65,117,100,105,111,32,98,117,102,102,101,114,32,110,111,116,32,97,118,97,105,108,97,98,108,101,32,102,111,114,32,117,112,100,97,116,105,110,103,0,73,115,65,117,100,105,111,66,117,102,102,101,114,80,114,111,99,101,115,115,101,100,40,41,32,58,32,78,111,32,97,117,100,105,111,32,98,117,102,102,101,114,0,77,46,75,46,0,77,105,120,101,100,32,116,111,111,32,109,97,110,121,32,102,114,97,109,101,115,32,102,114,111,109,32,97,117,100,105,111,32,98,117,102,102,101,114,0,70,114,97,109,101,32,99,117,114,115,111,114,32,112,111,115,105,116,105,111,110,32,109,111,118,101,100,32,116,111,111,32,102,97,114,32,102,111,114,119,97,114,100,32,105,110,32,97,117,100,105,111,32,115,116,114,101,97,109,0,91,37,115,93,32,87,65,86,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,82,73,70,70,0,87,65,86,69,0,91,37,115,93,32,73,110,118,97,108,105,100,32,82,73,70,70,32,111,114,32,87,65,86,69,32,72,101,97,100,101,114,0,91,37,115,93,32,73,110,118,97,108,105,100,32,87,97,118,101,32,102,111,114,109,97,116,0,91,37,115,93,32,73,110,118,97,108,105,100,32,100,97,116,97,32,104,101,97,100,101,114,0,91,37,115,93,32,87,65,86,32,115,97,109,112,108,101,32,115,105,122,101,32,40,37,105,98,105,116,41,32,110,111,116,32,115,117,112,112,111,114,116,101,100,44,32,99,111,110,118,101,114,116,101,100,32,116,111,32,49,54,98,105,116,0,91,37,115,93,32,87,65,86,32,99,104,97,110,110,101,108,115,32,110,117,109,98,101,114,32,40,37,105,41,32,110,111,116,32,115,117,112,112,111,114,116,101,100,44,32,99,111,110,118,101,114,116,101,100,32,116,111,32,50,32,99,104,97,110,110,101,108,115,0,91,37,115,93,32,87,65,86,32,102,105,108,101,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,37,105,32,72,122,44,32,37,105,32,98,105,116,44,32,37,115,41,0,91,37,115,93,32,79,71,71,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,91,37,115,93,32,79,103,103,32,97,117,100,105,111,32,108,101,110,103,116,104,32,105,115,32,108,97,114,103,101,114,32,116,104,97,110,32,49,48,32,115,101,99,111,110,100,115,32,40,37,102,41,44,32,116,104,97,116,39,115,32,97,32,98,105,103,32,102,105,108,101,32,105,110,32,109,101,109,111,114,121,44,32,99,111,110,115,105,100,101,114,32,109,117,115,105,99,32,115,116,114,101,97,109,105,110,103,0,91,37,115,93,32,83,97,109,112,108,101,115,32,111,98,116,97,105,110,101,100,58,32,37,105,0,91,37,115,93,32,79,71,71,32,102,105,108,101,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,37,105,32,72,122,44,32,37,105,32,98,105,116,44,32,37,115,41,0,119,98,0,73,110,105,116,105,97,108,105,122,105,110,103,32,114,97,121,108,105,98,32,40,118,49,46,57,45,100,101,118,41,0,35,99,97,110,118,97,115,0,84,97,114,103,101,116,32,116,105,109,101,32,112,101,114,32,102,114,97,109,101,58,32,37,48,50,46,48,51,102,32,109,105,108,108,105,115,101,99,111,110,100,115,0,69,115,99,97,112,101,0,67,97,110,118,97,115,32,115,99,97,108,101,100,32,116,111,32,102,117,108,108,115,99,114,101,101,110,46,32,69,108,101,109,101,110,116,83,105,122,101,58,32,40,37,105,120,37,105,41,44,32,83,99,114,101,101,110,83,105,122,101,40,37,105,120,37,105,41,0,67,97,110,118,97,115,32,115,99,97,108,101,100,32,116,111,32,119,105,110,100,111,119,101,100,46,32,69,108,101,109,101,110,116,83,105,122,101,58,32,40,37,105,120,37,105,41,44,32,83,99,114,101,101,110,83,105,122,101,40,37,105,120,37,105,41,0,70,97,105,108,101,100,32,116,111,32,105,110,105,116,105,97,108,105,122,101,32,71,76,70,87,0,84,114,121,105,110,103,32,116,111,32,101,110,97,98,108,101,32,77,83,65,65,32,120,52,0,67,108,111,115,101,115,116,32,102,117,108,108,115,99,114,101,101,110,32,118,105,100,101,111,109,111,100,101,58,32,37,105,32,120,32,37,105,0,71,76,70,87,32,70,97,105,108,101,100,32,116,111,32,105,110,105,116,105,97,108,105,122,101,32,87,105,110,100,111,119,0,68,105,115,112,108,97,121,32,100,101,118,105,99,101,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,82,101,110,100,101,114,32,115,105,122,101,58,32,37,105,32,120,32,37,105,0,83,99,114,101,101,110,32,115,105,122,101,58,32,37,105,32,120,32,37,105,0,86,105,101,119,112,111,114,116,32,111,102,102,115,101,116,115,58,32,37,105,44,32,37,105,0,84,114,121,105,110,103,32,116,111,32,101,110,97,98,108,101,32,86,83,89,78,67,0,68,79,87,78,83,67,65,76,73,78,71,58,32,82,101,113,117,105,114,101,100,32,115,99,114,101,101,110,32,115,105,122,101,32,40,37,105,120,37,105,41,32,105,115,32,98,105,103,103,101,114,32,116,104,97,110,32,100,105,115,112,108,97,121,32,115,105,122,101,32,40,37,105,120,37,105,41,0,68,111,119,110,115,99,97,108,101,32,109,97,116,114,105,120,32,103,101,110,101,114,97,116,101,100,44,32,99,111,110,116,101,110,116,32,119,105,108,108,32,98,101,32,114,101,110,100,101,114,101,100,32,97,116,58,32,37,105,32,120,32,37,105,0,85,80,83,67,65,76,73,78,71,58,32,82,101,113,117,105,114,101,100,32,115,99,114,101,101,110,32,115,105,122,101,58,32,37,105,32,120,32,37,105,32,45,62,32,68,105,115,112,108,97,121,32,115,105,122,101,58,32,37,105,32,120,32,37,105,0,91,71,76,70,87,51,32,69,114,114,111,114,93,32,67,111,100,101,58,32,37,105,32,68,101,99,114,105,112,116,105,111,110,58,32,37,115,0,87,105,110,100,111,119,32,99,108,111,115,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,68,101,118,105,99,101,32,99,111,111,114,100,105,110,97,116,101,115,58,32,40,37,102,44,32,37,102,44,32,37,102,41,0,115,116,111,114,97,103,101,46,100,97,116,97,0,114,98,43,0,83,116,111,114,97,103,101,32,100,97,116,97,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,99,114,101,97,116,101,100,0,83,116,111,114,97,103,101,32,112,111,115,105,116,105,111,110,32,99,111,117,108,100,32,110,111,116,32,98,101,32,102,111,117,110,100,0,83,116,111,114,97,103,101,32,100,97,116,97,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,102,111,117,110,100,0,82,101,113,117,105,114,101,100,32,116,111,117,99,104,32,112,111,105,110,116,32,111,117,116,32,111,102,32,114,97,110,103,101,32,40,77,97,120,32,116,111,117,99,104,32,112,111,105,110,116,115,58,32,37,105,41,0,112,67,111,110,116,101,120,116,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,101,120,116,101,114,110,97,108,47,109,105,110,105,95,97,108,46,104,0,109,97,108,95,99,111,110,116,101,120,116,95,105,110,105,116,95,95,115,100,108,0,109,97,108,95,99,111,110,116,101,120,116,95,117,110,105,110,105,116,95,95,115,100,108,0,112,67,111,110,116,101,120,116,45,62,98,97,99,107,101,110,100,32,61,61,32,109,97,108,95,98,97,99,107,101,110,100,95,115,100,108,0,68,101,102,97,117,108,116,32,80,108,97,121,98,97,99,107,32,68,101,118,105,99,101,0,68,101,102,97,117,108,116,32,67,97,112,116,117,114,101,32,68,101,118,105,99,101,0,112,68,101,118,105,99,101,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,109,97,108,95,100,101,118,105,99,101,95,117,110,105,110,105,116,95,95,115,100,108,0,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,95,95,115,100,108,0,112,67,111,110,102,105,103,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,98,117,102,102,101,114,83,105,122,101,32,60,61,32,51,50,55,54,56,0,70,97,105,108,101,100,32,116,111,32,111,112,101,110,32,83,68,76,32,100,101,118,105,99,101,46,0,109,97,108,95,97,117,100,105,111,95,99,97,108,108,98,97,99,107,95,95,115,100,108,0,109,97,108,95,100,101,118,105,99,101,95,95,115,101,110,100,95,102,114,97,109,101,115,95,116,111,95,99,108,105,101,110,116,0,102,114,97,109,101,67,111,117,110,116,32,62,32,48,0,112,83,97,109,112,108,101,115,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,112,70,114,97,109,101,115,79,117,116,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,109,97,108,95,100,115,112,95,109,105,120,95,99,104,97,110,110,101,108,115,95,95,100,101,99,0,99,104,97,110,110,101,108,115,79,117,116,32,62,32,48,0,112,70,114,97,109,101,115,73,110,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,99,104,97,110,110,101,108,115,73,110,32,62,32,48,0,99,104,97,110,110,101,108,115,79,117,116,32,60,32,99,104,97,110,110,101,108,115,73,110,0,109,97,108,95,100,115,112,95,109,105,120,95,99,104,97,110,110,101,108,115,95,95,105,110,99,0,99,104,97,110,110,101,108,115,79,117,116,32,62,32,99,104,97,110,110,101,108,115,73,110,0,112,83,82,67,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,109,97,108,95,115,114,99,95,114,101,97,100,95,102,114,97,109,101,115,95,108,105,110,101,97,114,0,112,67,97,99,104,101,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,109,97,108,95,115,114,99,95,99,97,99,104,101,95,114,101,97,100,95,102,114,97,109,101,115,0,112,67,97,99,104,101,45,62,112,83,82,67,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,112,67,97,99,104,101,45,62,112,83,82,67,45,62,111,110,82,101,97,100,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,109,97,108,95,115,114,99,95,114,101,97,100,95,102,114,97,109,101,115,95,112,97,115,115,116,104,114,111,117,103,104,0,109,97,108,95,100,101,118,105,99,101,95,95,114,101,97,100,95,102,114,97,109,101,115,95,102,114,111,109,95,99,108,105,101,110,116,0,99,104,97,110,110,101,108,115,32,62,32,48,0,109,97,108,95,95,105,115,95,99,104,97,110,110,101,108,95,109,97,112,95,118,97,108,105,100,0,109,97,108,95,119,111,114,107,101,114,95,116,104,114,101,97,100,0,109,97,108,95,100,101,118,105,99,101,95,95,103,101,116,95,115,116,97,116,101,40,112,68,101,118,105,99,101,41,32,61,61,32,51,0,109,97,108,95,100,101,118,105,99,101,95,95,115,116,97,114,116,95,98,97,99,107,101,110,100,0,109,97,108,95,100,101,118,105,99,101,95,95,115,116,111,112,95,98,97,99,107,101,110,100,0,109,97,108,95,99,111,110,116,101,120,116,95,117,110,105,110,105,116,0,109,97,108,95,101,110,117,109,101,114,97,116,101,95,100,101,118,105,99,101,115,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,105,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,115,32,40,112,67,111,117,110,116,32,61,61,32,48,41,46,0,109,97,108,95,101,110,117,109,101,114,97,116,101,95,100,101,118,105,99,101,115,0,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,105,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,115,32,40,112,68,101,118,105,99,101,32,61,61,32,78,85,76,76,41,46,0,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,105,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,115,32,40,112,67,111,110,102,105,103,32,61,61,32,78,85,76,76,41,46,0,87,65,82,78,73,78,71,58,32,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,40,41,32,99,97,108,108,101,100,32,102,111,114,32,97,32,100,101,118,105,99,101,32,116,104,97,116,32,105,115,32,110,111,116,32,112,114,111,112,101,114,108,121,32,97,108,105,103,110,101,100,46,32,84,104,114,101,97,100,32,115,97,102,101,116,121,32,105,115,32,110,111,116,32,115,117,112,112,111,114,116,101,100,46,0,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,105,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,115,32,40,112,67,111,110,116,101,120,116,32,61,61,32,78,85,76,76,41,46,0,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,97,110,32,105,110,118,97,108,105,100,32,99,111,110,102,105,103,46,32,67,104,97,110,110,101,108,32,99,111,117,110,116,32,109,117,115,116,32,98,101,32,103,114,101,97,116,101,114,32,116,104,97,110,32,48,46,0,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,97,110,32,105,110,118,97,108,105,100,32,99,111,110,102,105,103,46,32,67,104,97,110,110,101,108,32,99,111,117,110,116,32,99,97,110,110,111,116,32,101,120,99,101,101,100,32,49,56,46,0,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,97,110,32,105,110,118,97,108,105,100,32,99,111,110,102,105,103,46,32,83,97,109,112,108,101,32,114,97,116,101,32,109,117,115,116,32,98,101,32,103,114,101,97,116,101,114,32,116,104,97,110,32,48,46,0,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,105,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,115,46,32,67,104,97,110,110,101,108,32,109,97,112,32,105,115,32,105,110,118,97,108,105,100,46,0,70,97,105,108,101,100,32,116,111,32,99,114,101,97,116,101,32,109,117,116,101,120,46,0,70,97,105,108,101,100,32,116,111,32,99,114,101,97,116,101,32,119,111,114,107,101,114,32,116,104,114,101,97,100,32,119,97,107,101,117,112,32,101,118,101,110,116,46,0,70,97,105,108,101,100,32,116,111,32,99,114,101,97,116,101,32,119,111,114,107,101,114,32,116,104,114,101,97,100,32,115,116,97,114,116,32,101,118,101,110,116,46,0,70,97,105,108,101,100,32,116,111,32,99,114,101,97,116,101,32,119,111,114,107,101,114,32,116,104,114,101,97,100,32,115,116,111,112,32,101,118,101,110,116,46,0,80,108,97,121,98,97,99,107,32,68,101,118,105,99,101,0,67,97,112,116,117,114,101,32,68,101,118,105,99,101,0,70,97,105,108,101,100,32,116,111,32,99,114,101,97,116,101,32,119,111,114,107,101,114,32,116,104,114,101,97,100,46,0,109,97,108,95,100,101,118,105,99,101,95,95,103,101,116,95,115,116,97,116,101,40,112,68,101,118,105,99,101,41,32,61,61,32,49,0,109,97,108,95,100,101,118,105,99,101,95,105,110,105,116,0,109,97,108,95,100,101,118,105,99,101,95,115,116,111,112,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,105,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,115,32,40,112,68,101,118,105,99,101,32,61,61,32,78,85,76,76,41,46,0,109,97,108,95,100,101,118,105,99,101,95,115,116,111,112,40,41,32,99,97,108,108,101,100,32,102,111,114,32,97,110,32,117,110,105,110,105,116,105,97,108,105,122,101,100,32,100,101,118,105,99,101,46,0,109,97,108,95,100,101,118,105,99,101,95,115,116,111,112,40,41,32,99,97,108,108,101,100,32,119,104,105,108,101,32,97,110,111,116,104,101,114,32,116,104,114,101,97,100,32,105,115,32,97,108,114,101,97,100,121,32,115,116,111,112,112,105,110,103,32,105,116,46,0,109,97,108,95,100,101,118,105,99,101,95,115,116,111,112,40,41,32,99,97,108,108,101,100,32,102,111,114,32,97,32,100,101,118,105,99,101,32,116,104,97,116,39,115,32,97,108,114,101,97,100,121,32,115,116,111,112,112,101,100,46,0,109,97,108,95,100,101,118,105,99,101,95,115,116,111,112,40,41,32,99,97,108,108,101,100,32,119,104,105,108,101,32,97,110,111,116,104,101,114,32,116,104,114,101,97,100,32,105,115,32,105,110,32,116,104,101,32,112,114,111,99,101,115,115,32,111,102,32,115,116,97,114,116,105,110,103,32,105,116,46,0,109,97,108,95,100,101,118,105,99,101,95,95,98,114,101,97,107,95,109,97,105,110,95,108,111,111,112,0,109,97,108,95,100,101,118,105,99,101,95,95,115,116,111,112,95,98,97,99,107,101,110,100,95,95,115,100,108,0,109,97,108,95,100,101,118,105,99,101,95,95,111,110,95,114,101,97,100,95,102,114,111,109,95,100,101,118,105,99,101,0,109,97,108,95,115,114,99,95,99,97,99,104,101,95,105,110,105,116,0,112,68,83,80,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,109,97,108,95,100,115,112,95,95,115,114,99,95,111,110,95,114,101,97,100,0,109,97,108,95,100,101,118,105,99,101,95,95,111,110,95,114,101,97,100,95,102,114,111,109,95,99,108,105,101,110,116,0,109,97,108,95,99,111,110,116,101,120,116,95,95,116,114,121,95,103,101,116,95,100,101,118,105,99,101,95,110,97,109,101,95,98,121,95,105,100,0,112,78,97,109,101,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,109,97,108,95,100,101,118,105,99,101,95,115,116,97,114,116,40,41,32,99,97,108,108,101,100,32,119,105,116,104,32,105,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,115,32,40,112,68,101,118,105,99,101,32,61,61,32,78,85,76,76,41,46,0,109,97,108,95,100,101,118,105,99,101,95,115,116,97,114,116,40,41,32,99,97,108,108,101,100,32,102,111,114,32,97,110,32,117,110,105,110,105,116,105,97,108,105,122,101,100,32,100,101,118,105,99,101,46,0,109,97,108,95,100,101,118,105,99,101,95,115,116,97,114,116,40,41,32,99,97,108,108,101,100,32,119,104,105,108,101,32,97,110,111,116,104,101,114,32,116,104,114,101,97,100,32,105,115,32,97,108,114,101,97,100,121,32,115,116,97,114,116,105,110,103,32,105,116,46,0,109,97,108,95,100,101,118,105,99,101,95,115,116,97,114,116,40,41,32,99,97,108,108,101,100,32,102,111,114,32,97,32,100,101,118,105,99,101,32,116,104,97,116,39,115,32,97,108,114,101,97,100,121,32,115,116,97,114,116,101,100,46,0,109,97,108,95,100,101,118,105,99,101,95,115,116,97,114,116,40,41,32,99,97,108,108,101,100,32,119,104,105,108,101,32,97,110,111,116,104,101,114,32,116,104,114,101,97,100,32,105,115,32,105,110,32,116,104,101,32,112,114,111,99,101,115,115,32,111,102,32,115,116,111,112,112,105,110,103,32,105,116,46,0,109,97,108,95,100,101,118,105,99,101,95,95,115,116,97,114,116,95,98,97,99,107,101,110,100,95,95,115,100,108,0,112,68,97,116,97,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,109,97,108,95,99,111,110,118,101,114,116,95,102,114,97,109,101,115,95,95,111,110,95,114,101,97,100,0,112,68,97,116,97,45,62,116,111,116,97,108,70,114,97,109,101,67,111,117,110,116,32,62,61,32,112,68,97,116,97,45,62,105,78,101,120,116,70,114,97,109,101,0,85,110,107,110,111,119,110,0,83,68,76,0,79,112,101,110,65,76,0,79,112,101,110,83,76,124,69,83,0,79,83,83,0,65,76,83,65,0,87,105,110,77,77,0,68,105,114,101,99,116,83,111,117,110,100,0,87,65,83,65,80,73,0,78,117,108,108,0,73,110,118,97,108,105,100,0,51,50,45,98,105,116,32,73,69,69,69,32,70,108,111,97,116,105,110,103,32,80,111,105,110,116,0,51,50,45,98,105,116,32,83,105,103,110,101,100,32,73,110,116,101,103,101,114,0,50,52,45,98,105,116,32,83,105,103,110,101,100,32,73,110,116,101,103,101,114,32,40,84,105,103,104,116,108,121,32,80,97,99,107,101,100,41,0,49,54,45,98,105,116,32,83,105,103,110,101,100,32,73,110,116,101,103,101,114,0,56,45,98,105,116,32,85,110,115,105,103,110,101,100,32,73,110,116,101,103,101,114,0,69,112,115,105,108,111,110,32,118,97,108,117,101,32,105,115,32,116,111,111,32,108,97,114,103,101,46,0,99,105,32,61,61,32,110,112,111,105,110,116,115,0,46,47,101,120,116,101,114,110,97,108,47,112,97,114,95,115,104,97,112,101,115,46,104,0,112,97,114,95,115,104,97,112,101,115,95,95,119,101,108,100,95,112,111,105,110,116,115,0,114,97,100,105,117,115,32,60,61,32,49,46,48,32,38,38,32,34,85,115,101,32,115,109,97,108,108,101,114,32,114,97,100,105,117,115,32,116,111,32,97,118,111,105,100,32,115,101,108,102,45,105,110,116,101,114,115,101,99,116,105,111,110,46,34,0,112,97,114,95,115,104,97,112,101,115,95,99,114,101,97,116,101,95,116,111,114,117,115,0,114,97,100,105,117,115,32,62,61,32,48,46,49,32,38,38,32,34,85,115,101,32,108,97,114,103,101,114,32,114,97,100,105,117,115,32,116,111,32,97,118,111,105,100,32,115,101,108,102,45,105,110,116,101,114,115,101,99,116,105,111,110,46,34,0,114,97,100,105,117,115,32,60,61,32,51,46,48,32,38,38,32,34,85,115,101,32,115,109,97,108,108,101,114,32,114,97,100,105,117,115,32,116,111,32,97,118,111,105,100,32,115,101,108,102,45,105,110,116,101,114,115,101,99,116,105,111,110,46,34,0,112,97,114,95,115,104,97,112,101,115,95,99,114,101,97,116,101,95,116,114,101,102,111,105,108,95,107,110,111,116,0,114,97,100,105,117,115,32,62,61,32,48,46,53,32,38,38,32,34,85,115,101,32,108,97,114,103,101,114,32,114,97,100,105,117,115,32,116,111,32,97,118,111,105,100,32,115,101,108,102,45,105,110,116,101,114,115,101,99,116,105,111,110,46,34,0,46,111,98,106,0,77,101,115,104,32,99,111,117,108,100,32,110,111,116,32,98,101,32,108,111,97,100,101,100,0,91,37,115,93,32,79,66,74,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,37,99,0,91,37,115,93,32,77,111,100,101,108,32,118,101,114,116,105,99,101,115,58,32,37,105,0,91,37,115,93,32,77,111,100,101,108,32,116,101,120,99,111,111,114,100,115,58,32,37,105,0,91,37,115,93,32,77,111,100,101,108,32,110,111,114,109,97,108,115,58,32,37,105,0,91,37,115,93,32,77,111,100,101,108,32,116,114,105,97,110,103,108,101,115,58,32,37,105,0,37,102,32,37,102,37,42,91,94,10,93,115,10,0,37,102,32,37,102,32,37,102,0,91,37,115,93,32,78,111,32,110,111,114,109,97,108,115,32,100,97,116,97,32,111,110,32,79,66,74,44,32,110,111,114,109,97,108,115,32,119,105,108,108,32,98,101,32,103,101,110,101,114,97,116,101,100,32,102,114,111,109,32,102,97,99,101,115,32,100,97,116,97,0,37,105,32,37,105,32,37,105,0,37,105,47,37,105,32,37,105,47,37,105,32,37,105,47,37,105,0,37,105,47,47,37,105,32,37,105,47,47,37,105,32,37,105,47,47,37,105,0,37,105,47,37,105,47,37,105,32,37,105,47,37,105,47,37,105,32,37,105,47,37,105,47,37,105,0,91,37,115,93,32,77,111,100,101,108,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,105,110,32,82,65,77,32,40,67,80,85,41,0,85,110,108,111,97,100,101,100,32,109,111,100,101,108,32,100,97,116,97,32,40,109,101,115,104,32,97,110,100,32,109,97,116,101,114,105,97,108,41,32,102,114,111,109,32,82,65,77,32,97,110,100,32,86,82,65,77,0,46,109,116,108,0,91,37,115,93,32,77,84,76,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,110,101,119,109,116,108,32,37,115,0,91,37,115,93,32,76,111,97,100,105,110,103,32,109,97,116,101,114,105,97,108,46,46,46,0,75,97,32,37,102,32,37,102,32,37,102,0,75,100,32,37,102,32,37,102,32,37,102,0,75,115,32,37,102,32,37,102,32,37,102,0,78,115,32,37,105,0,109,97,112,95,75,100,32,37,115,0,109,97,112,95,75,115,32,37,115,0,109,97,112,95,66,117,109,112,32,37,115,0,109,97,112,95,98,117,109,112,32,37,115,0,100,32,37,102,0,98,117,109,112,32,37,115,0,84,114,32,37,102,0,91,37,115,93,32,77,97,116,101,114,105,97,108,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,83,116,97,99,107,32,66,117,102,102,101,114,32,79,118,101,114,102,108,111,119,32,40,77,65,88,32,37,105,32,77,97,116,114,105,120,41,0,77,65,88,95,76,73,78,69,83,95,66,65,84,67,72,32,111,118,101,114,102,108,111,119,0,77,65,88,95,84,82,73,65,78,71,76,69,83,95,66,65,84,67,72,32,111,118,101,114,102,108,111,119,0,77,65,88,95,81,85,65,68,83,95,66,65,84,67,72,32,111,118,101,114,102,108,111,119,0,67,108,97,109,112,32,109,105,114,114,111,114,32,119,114,97,112,32,109,111,100,101,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,91,84,69,88,32,73,68,32,37,105,93,32,77,97,120,105,109,117,109,32,97,110,105,115,111,116,114,111,112,105,99,32,102,105,108,116,101,114,32,108,101,118,101,108,32,115,117,112,112,111,114,116,101,100,32,105,115,32,37,105,88,0,65,110,105,115,111,116,114,111,112,105,99,32,102,105,108,116,101,114,105,110,103,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,91,70,66,79,32,73,68,32,37,105,93,32,85,110,108,111,97,100,101,100,32,114,101,110,100,101,114,32,116,101,120,116,117,114,101,32,100,97,116,97,32,102,114,111,109,32,86,82,65,77,32,40,71,80,85,41,0,91,86,65,79,32,73,68,32,37,105,93,32,85,110,108,111,97,100,101,100,32,109,111,100,101,108,32,100,97,116,97,32,102,114,111,109,32,86,82,65,77,32,40,71,80,85,41,0,91,86,66,79,32,73,68,32,37,105,93,32,85,110,108,111,97,100,101,100,32,109,111,100,101,108,32,118,101,114,116,101,120,32,100,97,116,97,32,102,114,111,109,32,86,82,65,77,32,40,71,80,85,41,0,71,80,85,58,32,86,101,110,100,111,114,58,32,32,32,37,115,0,71,80,85,58,32,82,101,110,100,101,114,101,114,58,32,37,115,0,71,80,85,58,32,86,101,114,115,105,111,110,58,32,32,37,115,0,71,80,85,58,32,71,76,83,76,58,32,32,32,32,32,37,115,0,32,0,78,117,109,98,101,114,32,111,102,32,115,117,112,112,111,114,116,101,100,32,101,120,116,101,110,115,105,111,110,115,58,32,37,105,0,71,76,95,79,69,83,95,118,101,114,116,101,120,95,97,114,114,97,121,95,111,98,106,101,99,116,0,103,108,71,101,110,86,101,114,116,101,120,65,114,114,97,121,115,79,69,83,0,103,108,66,105,110,100,86,101,114,116,101,120,65,114,114,97,121,79,69,83,0,103,108,68,101,108,101,116,101,86,101,114,116,101,120,65,114,114,97,121,115,79,69,83,0,71,76,95,79,69,83,95,116,101,120,116,117,114,101,95,110,112,111,116,0,79,69,83,95,116,101,120,116,117,114,101,95,102,108,111,97,116,0,71,76,95,69,88,84,95,116,101,120,116,117,114,101,95,99,111,109,112,114,101,115,115,105,111,110,95,115,51,116,99,0,71,76,95,87,69,66,71,76,95,99,111,109,112,114,101,115,115,101,100,95,116,101,120,116,117,114,101,95,115,51,116,99,0,71,76,95,87,69,66,75,73,84,95,87,69,66,71,76,95,99,111,109,112,114,101,115,115,101,100,95,116,101,120,116,117,114,101,95,115,51,116,99,0,71,76,95,79,69,83,95,99,111,109,112,114,101,115,115,101,100,95,69,84,67,49,95,82,71,66,56,95,116,101,120,116,117,114,101,0,71,76,95,87,69,66,71,76,95,99,111,109,112,114,101,115,115,101,100,95,116,101,120,116,117,114,101,95,101,116,99,49,0,71,76,95,65,82,66,95,69,83,51,95,99,111,109,112,97,116,105,98,105,108,105,116,121,0,71,76,95,73,77,71,95,116,101,120,116,117,114,101,95,99,111,109,112,114,101,115,115,105,111,110,95,112,118,114,116,99,0,71,76,95,75,72,82,95,116,101,120,116,117,114,101,95,99,111,109,112,114,101,115,115,105,111,110,95,97,115,116,99,95,104,100,114,0,71,76,95,69,88,84,95,116,101,120,116,117,114,101,95,102,105,108,116,101,114,95,97,110,105,115,111,116,114,111,112,105,99,0,71,76,95,69,88,84,95,116,101,120,116,117,114,101,95,109,105,114,114,111,114,95,99,108,97,109,112,0,91,69,88,84,69,78,83,73,79,78,93,32,86,65,79,32,101,120,116,101,110,115,105,111,110,32,100,101,116,101,99,116,101,100,44,32,86,65,79,32,102,117,110,99,116,105,111,110,115,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,91,69,88,84,69,78,83,73,79,78,93,32,86,65,79,32,101,120,116,101,110,115,105,111,110,32,110,111,116,32,102,111,117,110,100,44,32,86,65,79,32,117,115,97,103,101,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,91,69,88,84,69,78,83,73,79,78,93,32,78,80,79,84,32,116,101,120,116,117,114,101,115,32,101,120,116,101,110,115,105,111,110,32,100,101,116,101,99,116,101,100,44,32,102,117,108,108,32,78,80,79,84,32,116,101,120,116,117,114,101,115,32,115,117,112,112,111,114,116,101,100,0,91,69,88,84,69,78,83,73,79,78,93,32,78,80,79,84,32,116,101,120,116,117,114,101,115,32,101,120,116,101,110,115,105,111,110,32,110,111,116,32,102,111,117,110,100,44,32,108,105,109,105,116,101,100,32,78,80,79,84,32,115,117,112,112,111,114,116,32,40,110,111,45,109,105,112,109,97,112,115,44,32,110,111,45,114,101,112,101,97,116,41,0,91,69,88,84,69,78,83,73,79,78,93,32,68,88,84,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,115,32,115,117,112,112,111,114,116,101,100,0,91,69,88,84,69,78,83,73,79,78,93,32,69,84,67,49,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,115,32,115,117,112,112,111,114,116,101,100,0,91,69,88,84,69,78,83,73,79,78,93,32,69,84,67,50,47,69,65,67,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,115,32,115,117,112,112,111,114,116,101,100,0,91,69,88,84,69,78,83,73,79,78,93,32,80,86,82,84,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,115,32,115,117,112,112,111,114,116,101,100,0,91,69,88,84,69,78,83,73,79,78,93,32,65,83,84,67,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,115,32,115,117,112,112,111,114,116,101,100,0,91,69,88,84,69,78,83,73,79,78,93,32,65,110,105,115,111,116,114,111,112,105,99,32,116,101,120,116,117,114,101,115,32,102,105,108,116,101,114,105,110,103,32,115,117,112,112,111,114,116,101,100,32,40,109,97,120,58,32,37,46,48,102,88,41,0,91,69,88,84,69,78,83,73,79,78,93,32,67,108,97,109,112,32,109,105,114,114,111,114,32,119,114,97,112,32,116,101,120,116,117,114,101,32,109,111,100,101,32,115,117,112,112,111,114,116,101,100,0,91,84,69,88,32,73,68,32,37,105,93,32,66,97,115,101,32,119,104,105,116,101,32,116,101,120,116,117,114,101,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,66,97,115,101,32,119,104,105,116,101,32,116,101,120,116,117,114,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,108,111,97,100,101,100,0,79,112,101,110,71,76,32,100,101,102,97,117,108,116,32,115,116,97,116,101,115,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,91,67,80,85,93,32,68,101,102,97,117,108,116,32,98,117,102,102,101,114,115,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,108,105,110,101,115,44,32,116,114,105,97,110,103,108,101,115,44,32,113,117,97,100,115,41,0,91,86,65,79,32,73,68,32,37,105,93,32,68,101,102,97,117,108,116,32,98,117,102,102,101,114,115,32,86,65,79,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,108,105,110,101,115,41,0,91,86,66,79,32,73,68,32,37,105,93,91,86,66,79,32,73,68,32,37,105,93,32,68,101,102,97,117,108,116,32,98,117,102,102,101,114,115,32,86,66,79,115,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,108,105,110,101,115,41,0,91,86,65,79,32,73,68,32,37,105,93,32,68,101,102,97,117,108,116,32,98,117,102,102,101,114,115,32,86,65,79,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,116,114,105,97,110,103,108,101,115,41,0,91,86,66,79,32,73,68,32,37,105,93,91,86,66,79,32,73,68,32,37,105,93,32,68,101,102,97,117,108,116,32,98,117,102,102,101,114,115,32,86,66,79,115,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,116,114,105,97,110,103,108,101,115,41,0,91,86,65,79,32,73,68,32,37,105,93,32,68,101,102,97,117,108,116,32,98,117,102,102,101,114,115,32,86,65,79,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,113,117,97,100,115,41,0,91,86,66,79,32,73,68,32,37,105,93,91,86,66,79,32,73,68,32,37,105,93,91,86,66,79,32,73,68,32,37,105,93,91,86,66,79,32,73,68,32,37,105,93,32,68,101,102,97,117,108,116,32,98,117,102,102,101,114,115,32,86,66,79,115,32,105,110,105,116,105,97,108,105,122,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,113,117,97,100,115,41,0,35,118,101,114,115,105,111,110,32,49,48,48,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,97,116,116,114,105,98,117,116,101,32,118,101,99,51,32,118,101,114,116,101,120,80,111,115,105,116,105,111,110,59,32,32,32,32,32,10,97,116,116,114,105,98,117,116,101,32,118,101,99,50,32,118,101,114,116,101,120,84,101,120,67,111,111,114,100,59,32,32,32,32,32,10,97,116,116,114,105,98,117,116,101,32,118,101,99,52,32,118,101,114,116,101,120,67,111,108,111,114,59,32,32,32,32,32,32,32,32,10,118,97,114,121,105,110,103,32,118,101,99,50,32,102,114,97,103,84,101,120,67,111,111,114,100,59,32,32,32,32,32,32,32,32,32,10,118,97,114,121,105,110,103,32,118,101,99,52,32,102,114,97,103,67,111,108,111,114,59,32,32,32,32,32,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,109,97,116,52,32,109,118,112,59,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,118,111,105,100,32,109,97,105,110,40,41,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,123,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,32,32,32,32,102,114,97,103,84,101,120,67,111,111,114,100,32,61,32,118,101,114,116,101,120,84,101,120,67,111,111,114,100,59,32,10,32,32,32,32,102,114,97,103,67,111,108,111,114,32,61,32,118,101,114,116,101,120,67,111,108,111,114,59,32,32,32,32,32,32,32,10,32,32,32,32,103,108,95,80,111,115,105,116,105,111,110,32,61,32,109,118,112,42,118,101,99,52,40,118,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,32,10,125,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,0,35,118,101,114,115,105,111,110,32,49,48,48,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,112,114,101,99,105,115,105,111,110,32,109,101,100,105,117,109,112,32,102,108,111,97,116,59,32,32,32,32,32,32,32,32,32,32,32,10,118,97,114,121,105,110,103,32,118,101,99,50,32,102,114,97,103,84,101,120,67,111,111,114,100,59,32,32,32,32,32,32,32,32,32,10,118,97,114,121,105,110,103,32,118,101,99,52,32,102,114,97,103,67,111,108,111,114,59,32,32,32,32,32,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,116,101,120,116,117,114,101,48,59,32,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,118,101,99,52,32,99,111,108,68,105,102,102,117,115,101,59,32,32,32,32,32,32,32,32,32,32,32,10,118,111,105,100,32,109,97,105,110,40,41,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,123,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,32,32,32,32,118,101,99,52,32,116,101,120,101,108,67,111,108,111,114,32,61,32,116,101,120,116,117,114,101,50,68,40,116,101,120,116,117,114,101,48,44,32,102,114,97,103,84,101,120,67,111,111,114,100,41,59,32,10,32,32,32,32,103,108,95,70,114,97,103,67,111,108,111,114,32,61,32,116,101,120,101,108,67,111,108,111,114,42,99,111,108,68,105,102,102,117,115,101,42,102,114,97,103,67,111,108,111,114,59,32,32,32,32,32,32,10,125,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,0,91,83,72,68,82,32,73,68,32,37,105,93,32,68,101,102,97,117,108,116,32,115,104,97,100,101,114,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,118,101,114,116,101,120,80,111,115,105,116,105,111,110,0,118,101,114,116,101,120,84,101,120,67,111,111,114,100,0,118,101,114,116,101,120,67,111,108,111,114,0,109,118,112,0,99,111,108,68,105,102,102,117,115,101,0,116,101,120,116,117,114,101,48,0,91,83,72,68,82,32,73,68,32,37,105,93,32,68,101,102,97,117,108,116,32,115,104,97,100,101,114,32,99,111,117,108,100,32,110,111,116,32,98,101,32,108,111,97,100,101,100,0,118,101,114,116,101,120,78,111,114,109,97,108,0,118,101,114,116,101,120,84,97,110,103,101,110,116,0,118,101,114,116,101,120,84,101,120,67,111,111,114,100,50,0,91,83,72,68,82,32,73,68,32,37,105,93,32,70,97,105,108,101,100,32,116,111,32,108,105,110,107,32,115,104,97,100,101,114,32,112,114,111,103,114,97,109,46,46,46,0,37,115,0,91,83,72,68,82,32,73,68,32,37,105,93,32,83,104,97,100,101,114,32,112,114,111,103,114,97,109,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,91,83,72,68,82,32,73,68,32,37,105,93,32,70,97,105,108,101,100,32,116,111,32,99,111,109,112,105,108,101,32,115,104,97,100,101,114,46,46,46,0,91,83,72,68,82,32,73,68,32,37,105,93,32,83,104,97,100,101,114,32,99,111,109,112,105,108,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,68,88,84,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,32,102,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,69,84,67,49,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,32,102,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,69,84,67,50,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,32,102,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,80,86,82,84,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,32,102,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,65,83,84,67,32,99,111,109,112,114,101,115,115,101,100,32,116,101,120], "i8", ALLOC_NONE, Runtime.GLOBAL_BASE+10240); +/* memory initializer */ allocate([116,117,114,101,32,102,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,84,101,120,116,117,114,101,32,102,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,91,84,69,88,32,73,68,32,37,105,93,32,84,101,120,116,117,114,101,32,99,114,101,97,116,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,37,105,120,37,105,41,0,91,84,69,88,32,73,68,32,37,105,93,32,85,110,108,111,97,100,101,100,32,116,101,120,116,117,114,101,32,100,97,116,97,32,40,98,97,115,101,32,119,104,105,116,101,32,116,101,120,116,117,114,101,41,32,102,114,111,109,32,86,82,65,77,0,84,101,120,116,117,114,101,32,102,111,114,109,97,116,32,117,112,100,97,116,105,110,103,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,70,114,97,109,101,98,117,102,102,101,114,32,111,98,106,101,99,116,32,99,111,117,108,100,32,110,111,116,32,98,101,32,99,114,101,97,116,101,100,46,46,46,0,70,114,97,109,101,98,117,102,102,101,114,32,105,115,32,117,110,115,117,112,112,111,114,116,101,100,0,70,114,97,109,101,98,117,102,102,101,114,32,105,110,99,111,109,112,108,101,116,101,32,97,116,116,97,99,104,109,101,110,116,0,70,114,97,109,101,98,117,102,102,101,114,32,105,110,99,111,109,112,108,101,116,101,32,100,105,109,101,110,115,105,111,110,115,0,70,114,97,109,101,98,117,102,102,101,114,32,105,110,99,111,109,112,108,101,116,101,32,109,105,115,115,105,110,103,32,97,116,116,97,99,104,109,101,110,116,0,91,70,66,79,32,73,68,32,37,105,93,32,70,114,97,109,101,98,117,102,102,101,114,32,111,98,106,101,99,116,32,99,114,101,97,116,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,91,84,69,88,32,73,68,32,37,105,93,32,77,105,112,109,97,112,115,32,103,101,110,101,114,97,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,0,91,84,69,88,32,73,68,32,37,105,93,32,77,105,112,109,97,112,115,32,99,97,110,32,110,111,116,32,98,101,32,103,101,110,101,114,97,116,101,100,0,91,86,65,79,32,73,68,32,37,105,93,32,77,101,115,104,32,117,112,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,116,111,32,86,82,65,77,32,40,71,80,85,41,0,77,101,115,104,32,99,111,117,108,100,32,110,111,116,32,98,101,32,117,112,108,111,97,100,101,100,32,116,111,32,86,82,65,77,32,40,71,80,85,41,0,91,86,66,79,115,93,32,77,101,115,104,32,117,112,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,116,111,32,86,82,65,77,32,40,71,80,85,41,0,91,37,115,93,32,84,101,120,116,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,67,117,115,116,111,109,32,115,104,97,100,101,114,32,99,111,117,108,100,32,110,111,116,32,98,101,32,108,111,97,100,101,100,0,91,83,72,68,82,32,73,68,32,37,105,93,32,65,99,116,105,118,101,32,117,110,105,102,111,114,109,32,91,37,115,93,32,115,101,116,32,97,116,32,108,111,99,97,116,105,111,110,58,32,37,105,0,112,114,111,106,101,99,116,105,111,110,0,118,105,101,119,0,116,101,120,116,117,114,101,49,0,116,101,120,116,117,114,101,50,0,91,83,72,68,82,32,73,68,32,37,105,93,32,85,110,108,111,97,100,101,100,32,115,104,97,100,101,114,32,112,114,111,103,114,97,109,32,100,97,116,97,0,91,83,72,68,82,32,73,68,32,37,105,93,32,83,104,97,100,101,114,32,117,110,105,102,111,114,109,32,91,37,115,93,32,67,79,85,76,68,32,78,79,84,32,66,69,32,70,79,85,78,68,0,91,83,72,68,82,32,73,68,32,37,105,93,32,83,104,97,100,101,114,32,117,110,105,102,111,114,109,32,91,37,115,93,32,115,101,116,32,97,116,32,108,111,99,97,116,105,111,110,58,32,37,105,0,83,104,97,100,101,114,32,118,97,108,117,101,32,102,108,111,97,116,32,97,114,114,97,121,32,115,105,122,101,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,83,104,97,100,101,114,32,118,97,108,117,101,32,105,110,116,32,97,114,114,97,121,32,115,105,122,101,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,73,110,105,116,105,97,108,105,122,105,110,103,32,86,82,32,83,105,109,117,108,97,116,111,114,32,40,79,99,117,108,117,115,32,82,105,102,116,32,67,86,49,41,0,73,110,105,116,105,97,108,105,122,105,110,103,32,86,82,32,83,105,109,117,108,97,116,111,114,32,40,79,99,117,108,117,115,32,82,105,102,116,32,68,75,50,41,0,35,118,101,114,115,105,111,110,32,49,48,48,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,97,116,116,114,105,98,117,116,101,32,118,101,99,51,32,118,101,114,116,101,120,80,111,115,105,116,105,111,110,59,32,32,32,32,32,10,97,116,116,114,105,98,117,116,101,32,118,101,99,50,32,118,101,114,116,101,120,84,101,120,67,111,111,114,100,59,32,32,32,32,32,10,97,116,116,114,105,98,117,116,101,32,118,101,99,52,32,118,101,114,116,101,120,67,111,108,111,114,59,32,32,32,32,32,32,32,32,10,118,97,114,121,105,110,103,32,118,101,99,50,32,102,114,97,103,84,101,120,67,111,111,114,100,59,32,32,32,32,32,32,32,32,32,10,118,97,114,121,105,110,103,32,118,101,99,52,32,102,114,97,103,67,111,108,111,114,59,32,32,32,32,32,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,109,97,116,52,32,109,118,112,59,32,32,32,32,32,32,32,32,32,32,32,32,10,118,111,105,100,32,109,97,105,110,40,41,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,123,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,32,32,32,32,102,114,97,103,84,101,120,67,111,111,114,100,32,61,32,118,101,114,116,101,120,84,101,120,67,111,111,114,100,59,32,10,32,32,32,32,102,114,97,103,67,111,108,111,114,32,61,32,118,101,114,116,101,120,67,111,108,111,114,59,32,32,32,32,32,32,32,10,32,32,32,32,103,108,95,80,111,115,105,116,105,111,110,32,61,32,109,118,112,42,118,101,99,52,40,118,101,114,116,101,120,80,111,115,105,116,105,111,110,44,32,49,46,48,41,59,32,10,125,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,0,35,118,101,114,115,105,111,110,32,49,48,48,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,112,114,101,99,105,115,105,111,110,32,109,101,100,105,117,109,112,32,102,108,111,97,116,59,32,32,32,32,32,32,32,32,32,32,32,10,118,97,114,121,105,110,103,32,118,101,99,50,32,102,114,97,103,84,101,120,67,111,111,114,100,59,32,32,32,32,32,32,32,32,32,10,118,97,114,121,105,110,103,32,118,101,99,52,32,102,114,97,103,67,111,108,111,114,59,32,32,32,32,32,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,115,97,109,112,108,101,114,50,68,32,116,101,120,116,117,114,101,48,59,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,118,101,99,50,32,108,101,102,116,76,101,110,115,67,101,110,116,101,114,59,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,118,101,99,50,32,114,105,103,104,116,76,101,110,115,67,101,110,116,101,114,59,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,118,101,99,50,32,108,101,102,116,83,99,114,101,101,110,67,101,110,116,101,114,59,32,32,32,32,32,10,117,110,105,102,111,114,109,32,118,101,99,50,32,114,105,103,104,116,83,99,114,101,101,110,67,101,110,116,101,114,59,32,32,32,32,10,117,110,105,102,111,114,109,32,118,101,99,50,32,115,99,97,108,101,59,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,118,101,99,50,32,115,99,97,108,101,73,110,59,32,32,32,32,32,32,32,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,118,101,99,52,32,104,109,100,87,97,114,112,80,97,114,97,109,59,32,32,32,32,32,32,32,32,32,10,117,110,105,102,111,114,109,32,118,101,99,52,32,99,104,114,111,109,97,65,98,80,97,114,97,109,59,32,32,32,32,32,32,32,32,10,118,111,105,100,32,109,97,105,110,40,41,32,10,123,32,10,32,32,32,118,101,99,50,32,108,101,110,115,67,101,110,116,101,114,32,61,32,102,114,97,103,84,101,120,67,111,111,114,100,46,120,32,60,32,48,46,53,32,63,32,108,101,102,116,76,101,110,115,67,101,110,116,101,114,32,58,32,114,105,103,104,116,76,101,110,115,67,101,110,116,101,114,59,32,10,32,32,32,118,101,99,50,32,115,99,114,101,101,110,67,101,110,116,101,114,32,61,32,102,114,97,103,84,101,120,67,111,111,114,100,46,120,32,60,32,48,46,53,32,63,32,108,101,102,116,83,99,114,101,101,110,67,101,110,116,101,114,32,58,32,114,105,103,104,116,83,99,114,101,101,110,67,101,110,116,101,114,59,32,10,32,32,32,118,101,99,50,32,116,104,101,116,97,32,61,32,40,102,114,97,103,84,101,120,67,111,111,114,100,32,45,32,108,101,110,115,67,101,110,116,101,114,41,42,115,99,97,108,101,73,110,59,32,10,32,32,32,102,108,111,97,116,32,114,83,113,32,61,32,116,104,101,116,97,46,120,42,116,104,101,116,97,46,120,32,43,32,116,104,101,116,97,46,121,42,116,104,101,116,97,46,121,59,32,10,32,32,32,118,101,99,50,32,116,104,101,116,97,49,32,61,32,116,104,101,116,97,42,40,104,109,100,87,97,114,112,80,97,114,97,109,46,120,32,43,32,104,109,100,87,97,114,112,80,97,114,97,109,46,121,42,114,83,113,32,43,32,104,109,100,87,97,114,112,80,97,114,97,109,46,122,42,114,83,113,42,114,83,113,32,43,32,104,109,100,87,97,114,112,80,97,114,97,109,46,119,42,114,83,113,42,114,83,113,42,114,83,113,41,59,32,10,32,32,32,118,101,99,50,32,116,104,101,116,97,66,108,117,101,32,61,32,116,104,101,116,97,49,42,40,99,104,114,111,109,97,65,98,80,97,114,97,109,46,122,32,43,32,99,104,114,111,109,97,65,98,80,97,114,97,109,46,119,42,114,83,113,41,59,32,10,32,32,32,118,101,99,50,32,116,99,66,108,117,101,32,61,32,108,101,110,115,67,101,110,116,101,114,32,43,32,115,99,97,108,101,42,116,104,101,116,97,66,108,117,101,59,32,10,32,32,32,105,102,32,40,97,110,121,40,98,118,101,99,50,40,99,108,97,109,112,40,116,99,66,108,117,101,44,32,115,99,114,101,101,110,67,101,110,116,101,114,32,45,32,118,101,99,50,40,48,46,50,53,44,32,48,46,53,41,44,32,115,99,114,101,101,110,67,101,110,116,101,114,32,43,32,118,101,99,50,40,48,46,50,53,44,32,48,46,53,41,41,32,45,32,116,99,66,108,117,101,41,41,41,32,10,32,32,32,123,32,10,32,32,32,32,32,32,32,103,108,95,70,114,97,103,67,111,108,111,114,32,61,32,118,101,99,52,40,48,46,48,44,32,48,46,48,44,32,48,46,48,44,32,49,46,48,41,59,32,10,32,32,32,125,32,10,32,32,32,101,108,115,101,32,10,32,32,32,123,32,10,32,32,32,32,32,32,32,102,108,111,97,116,32,98,108,117,101,32,61,32,116,101,120,116,117,114,101,50,68,40,116,101,120,116,117,114,101,48,44,32,116,99,66,108,117,101,41,46,98,59,32,10,32,32,32,32,32,32,32,118,101,99,50,32,116,99,71,114,101,101,110,32,61,32,108,101,110,115,67,101,110,116,101,114,32,43,32,115,99,97,108,101,42,116,104,101,116,97,49,59,32,10,32,32,32,32,32,32,32,102,108,111,97,116,32,103,114,101,101,110,32,61,32,116,101,120,116,117,114,101,50,68,40,116,101,120,116,117,114,101,48,44,32,116,99,71,114,101,101,110,41,46,103,59,32,10,32,32,32,32,32,32,32,118,101,99,50,32,116,104,101,116,97,82,101,100,32,61,32,116,104,101,116,97,49,42,40,99,104,114,111,109,97,65,98,80,97,114,97,109,46,120,32,43,32,99,104,114,111,109,97,65,98,80,97,114,97,109,46,121,42,114,83,113,41,59,32,10,32,32,32,32,32,32,32,118,101,99,50,32,116,99,82,101,100,32,61,32,108,101,110,115,67,101,110,116,101,114,32,43,32,115,99,97,108,101,42,116,104,101,116,97,82,101,100,59,32,10,32,32,32,32,32,32,32,102,108,111,97,116,32,114,101,100,32,61,32,116,101,120,116,117,114,101,50,68,40,116,101,120,116,117,114,101,48,44,32,116,99,82,101,100,41,46,114,59,32,10,32,32,32,32,32,32,32,103,108,95,70,114,97,103,67,111,108,111,114,32,61,32,118,101,99,52,40,114,101,100,44,32,103,114,101,101,110,44,32,98,108,117,101,44,32,49,46,48,41,59,32,10,32,32,32,32,125,32,10,125,32,10,0,86,82,58,32,68,105,115,116,111,114,116,105,111,110,32,83,99,97,108,101,58,32,37,102,0,86,82,58,32,68,105,115,116,111,114,116,105,111,110,32,83,104,97,100,101,114,58,32,76,101,102,116,76,101,110,115,67,101,110,116,101,114,32,61,32,123,32,37,102,44,32,37,102,32,125,0,86,82,58,32,68,105,115,116,111,114,116,105,111,110,32,83,104,97,100,101,114,58,32,82,105,103,104,116,76,101,110,115,67,101,110,116,101,114,32,61,32,123,32,37,102,44,32,37,102,32,125,0,86,82,58,32,68,105,115,116,111,114,116,105,111,110,32,83,104,97,100,101,114,58,32,83,99,97,108,101,32,61,32,123,32,37,102,44,32,37,102,32,125,0,86,82,58,32,68,105,115,116,111,114,116,105,111,110,32,83,104,97,100,101,114,58,32,83,99,97,108,101,73,110,32,61,32,123,32,37,102,44,32,37,102,32,125,0,108,101,102,116,76,101,110,115,67,101,110,116,101,114,0,114,105,103,104,116,76,101,110,115,67,101,110,116,101,114,0,108,101,102,116,83,99,114,101,101,110,67,101,110,116,101,114,0,114,105,103,104,116,83,99,114,101,101,110,67,101,110,116,101,114,0,115,99,97,108,101,0,115,99,97,108,101,73,110,0,104,109,100,87,97,114,112,80,97,114,97,109,0,99,104,114,111,109,97,65,98,80,97,114,97,109,0,91,84,69,88,32,73,68,32,37,105,93,32,68,101,102,97,117,108,116,32,102,111,110,116,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,46,116,116,102,0,46,102,110,116,0,91,37,115,93,32,83,112,114,105,116,101,70,111,110,116,32,99,111,117,108,100,32,110,111,116,32,98,101,32,108,111,97,100,101,100,44,32,117,115,105,110,103,32,100,101,102,97,117,108,116,32,102,111,110,116,0,83,112,114,105,116,101,70,111,110,116,32,100,97,116,97,32,112,97,114,115,101,100,32,99,111,114,114,101,99,116,108,121,32,102,114,111,109,32,105,109,97,103,101,0,73,109,97,103,101,32,102,105,108,101,32,108,111,97,100,101,100,32,99,111,114,114,101,99,116,108,121,32,97,115,32,83,112,114,105,116,101,70,111,110,116,0,114,116,0,91,37,115,93,32,70,78,84,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,108,105,110,101,72,101,105,103,104,116,0,108,105,110,101,72,101,105,103,104,116,61,37,105,32,98,97,115,101,61,37,105,32,115,99,97,108,101,87,61,37,105,32,115,99,97,108,101,72,61,37,105,0,91,37,115,93,32,70,111,110,116,32,115,105,122,101,58,32,37,105,0,91,37,115,93,32,70,111,110,116,32,116,101,120,116,117,114,101,32,115,99,97,108,101,58,32,37,105,120,37,105,0,102,105,108,101,0,102,105,108,101,61,34,37,49,50,56,91,94,34,93,34,0,91,37,115,93,32,70,111,110,116,32,116,101,120,116,117,114,101,32,102,105,108,101,110,97,109,101,58,32,37,115,0,99,111,117,110,116,0,99,111,117,110,116,61,37,105,0,91,37,115,93,32,70,111,110,116,32,110,117,109,32,99,104,97,114,115,58,32,37,105,0,91,37,115,93,32,70,111,110,116,32,116,101,120,116,117,114,101,32,108,111,97,100,105,110,103,32,112,97,116,104,58,32,37,115,0,99,104,97,114,32,105,100,61,37,105,32,120,61,37,105,32,121,61,37,105,32,119,105,100,116,104,61,37,105,32,104,101,105,103,104,116,61,37,105,32,120,111,102,102,115,101,116,61,37,105,32,121,111,102,102,115,101,116,61,37,105,32,120,97,100,118,97,110,99,101,61,37,105,0,91,37,115,93,32,83,112,114,105,116,101,70,111,110,116,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,85,110,108,111,97,100,101,100,32,115,112,114,105,116,101,32,102,111,110,116,32,100,97,116,97,0,91,37,115,93,32,83,112,114,105,116,101,70,111,110,116,32,99,111,117,108,100,32,110,111,116,32,98,101,32,103,101,110,101,114,97,116,101,100,44,32,117,115,105,110,103,32,100,101,102,97,117,108,116,32,102,111,110,116,0,84,84,70,32,115,112,114,105,116,101,102,111,110,116,32,108,111,97,100,105,110,103,58,32,80,114,101,100,105,99,116,101,100,32,116,101,120,116,117,114,101,32,115,105,122,101,58,32,37,105,120,37,105,0,91,37,115,93,32,84,84,70,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,84,84,70,32,115,112,114,105,116,101,102,111,110,116,32,108,111,97,100,105,110,103,58,32,102,105,114,115,116,32,99,104,97,114,97,99,116,101,114,32,105,115,32,110,111,116,32,83,80,65,67,69,40,51,50,41,32,99,104,97,114,97,99,116,101,114,0,84,84,70,32,115,112,114,105,116,101,102,111,110,116,32,108,111,97,100,105,110,103,58,32,78,111,116,32,97,108,108,32,116,104,101,32,99,104,97,114,97,99,116,101,114,115,32,102,105,116,32,105,110,32,116,104,101,32,102,111,110,116,0,120,43,103,119,32,60,32,112,119,0,46,47,101,120,116,101,114,110,97,108,47,115,116,98,95,116,114,117,101,116,121,112,101,46,104,0,115,116,98,116,116,95,66,97,107,101,70,111,110,116,66,105,116,109,97,112,95,105,110,116,101,114,110,97,108,0,122,45,62,100,105,114,101,99,116,105,111,110,0,115,116,98,116,116,95,95,114,97,115,116,101,114,105,122,101,95,115,111,114,116,101,100,95,101,100,103,101,115,0,122,45,62,101,121,32,62,61,32,115,99,97,110,95,121,95,116,111,112,0,101,45,62,101,121,32,62,61,32,121,95,116,111,112,0,115,116,98,116,116,95,95,102,105,108,108,95,97,99,116,105,118,101,95,101,100,103,101,115,95,110,101,119,0,101,45,62,115,121,32,60,61,32,121,95,98,111,116,116,111,109,32,38,38,32,101,45,62,101,121,32,62,61,32,121,95,116,111,112,0,120,32,62,61,32,48,32,38,38,32,120,32,60,32,108,101,110,0,102,97,98,115,40,97,114,101,97,41,32,60,61,32,49,46,48,49,102,0,121,48,32,60,32,121,49,0,115,116,98,116,116,95,95,104,97,110,100,108,101,95,99,108,105,112,112,101,100,95,101,100,103,101,0,101,45,62,115,121,32,60,61,32,101,45,62,101,121,0,120,49,32,60,61,32,120,43,49,0,120,49,32,62,61,32,120,0,120,49,32,60,61,32,120,0,120,49,32,62,61,32,120,43,49,0,120,49,32,62,61,32,120,32,38,38,32,120,49,32,60,61,32,120,43,49,0,120,48,32,62,61,32,120,32,38,38,32,120,48,32,60,61,32,120,43,49,32,38,38,32,120,49,32,62,61,32,120,32,38,38,32,120,49,32,60,61,32,120,43,49,0,122,32,33,61,32,40,40,118,111,105,100,42,41,48,41,0,115,116,98,116,116,95,95,110,101,119,95,97,99,116,105,118,101,0,33,105,110,102,111,45,62,99,102,102,46,115,105,122,101,0,115,116,98,116,116,95,95,71,101,116,71,108,121,102,79,102,102,115,101,116,0,115,116,98,116,116,95,95,99,102,102,95,105,110,116,0,110,32,62,61,32,49,32,38,38,32,110,32,60,61,32,52,0,115,116,98,116,116,95,95,98,117,102,95,103,101,116,0,115,105,122,101,32,60,32,48,120,52,48,48,48,48,48,48,48,0,115,116,98,116,116,95,95,110,101,119,95,98,117,102,0,33,40,111,32,62,32,98,45,62,115,105,122,101,32,124,124,32,111,32,60,32,48,41,0,115,116,98,116,116,95,95,98,117,102,95,115,101,101,107,0,111,102,102,115,105,122,101,32,62,61,32,49,32,38,38,32,111,102,102,115,105,122,101,32,60,61,32,52,0,115,116,98,116,116,95,95,99,102,102,95,103,101,116,95,105,110,100,101,120,0,98,48,32,62,61,32,50,56,0,115,116,98,116,116,95,95,99,102,102,95,115,107,105,112,95,111,112,101,114,97,110,100,0,105,32,62,61,32,48,32,38,38,32,105,32,60,32,99,111,117,110,116,0,115,116,98,116,116,95,95,99,102,102,95,105,110,100,101,120,95,103,101,116,0,111,117,116,112,117,116,95,99,116,120,46,110,117,109,95,118,101,114,116,105,99,101,115,32,61,61,32,99,111,117,110,116,95,99,116,120,46,110,117,109,95,118,101,114,116,105,99,101,115,0,115,116,98,116,116,95,95,71,101,116,71,108,121,112,104,83,104,97,112,101,84,50,0,115,116,98,116,116,95,95,71,101,116,71,108,121,112,104,83,104,97,112,101,84,84,0,115,116,98,116,116,95,70,105,110,100,71,108,121,112,104,73,110,100,101,120,0,117,110,105,99,111,100,101,95,99,111,100,101,112,111,105,110,116,32,60,61,32,116,116,85,83,72,79,82,84,40,100,97,116,97,32,43,32,101,110,100,67,111,117,110,116,32,43,32,50,42,105,116,101,109,41,0,99,109,97,112,0,108,111,99,97,0,104,101,97,100,0,103,108,121,102,0,104,104,101,97,0,104,109,116,120,0,107,101,114,110,0,67,70,70,32,0,109,97,120,112,0,37,50,105,32,70,80,83,0,23,125,161,52,103,117,70,37,247,101,203,169,124,126,44,123,152,238,145,45,171,114,253,10,192,136,4,157,249,30,35,72,175,63,77,90,181,16,96,111,133,104,75,162,93,56,66,240,8,50,84,229,49,210,173,239,141,1,87,18,2,198,143,57,225,160,58,217,168,206,245,204,199,6,73,60,20,230,211,233,94,200,88,9,74,155,33,15,219,130,226,202,83,236,42,172,165,218,55,222,46,107,98,154,109,67,196,178,127,158,13,243,65,79,166,248,25,224,115,80,68,51,184,128,232,208,151,122,26,212,105,43,179,213,235,148,146,89,14,195,28,78,112,76,250,47,24,251,140,108,186,190,228,170,183,139,39,188,244,246,132,48,119,144,180,138,134,193,82,182,120,121,86,220,209,3,91,241,149,85,205,150,113,216,31,100,41,164,177,214,153,231,38,71,185,174,97,201,29,95,7,92,54,254,191,118,34,221,131,11,163,99,234,81,227,147,156,176,17,142,69,12,110,62,27,255,0,194,59,116,242,252,19,21,187,53,207,129,64,135,61,40,167,237,102,223,106,159,197,189,215,137,36,32,22,5,23,125,161,52,103,117,70,37,247,101,203,169,124,126,44,123,152,238,145,45,171,114,253,10,192,136,4,157,249,30,35,72,175,63,77,90,181,16,96,111,133,104,75,162,93,56,66,240,8,50,84,229,49,210,173,239,141,1,87,18,2,198,143,57,225,160,58,217,168,206,245,204,199,6,73,60,20,230,211,233,94,200,88,9,74,155,33,15,219,130,226,202,83,236,42,172,165,218,55,222,46,107,98,154,109,67,196,178,127,158,13,243,65,79,166,248,25,224,115,80,68,51,184,128,232,208,151,122,26,212,105,43,179,213,235,148,146,89,14,195,28,78,112,76,250,47,24,251,140,108,186,190,228,170,183,139,39,188,244,246,132,48,119,144,180,138,134,193,82,182,120,121,86,220,209,3,91,241,149,85,205,150,113,216,31,100,41,164,177,214,153,231,38,71,185,174,97,201,29,95,7,92,54,254,191,118,34,221,131,11,163,99,234,81,227,147,156,176,17,142,69,12,110,62,27,255,0,194,59,116,242,252,19,21,187,53,207,129,64,135,61,40,167,237,102,223,106,159,197,189,215,137,36,32,22,5,0,1,2,3,4,5,6,7,8,9,10,11,0,9,1,11,0,1,2,3,4,5,6,7,8,9,10,11,0,1,2,3,4,5,6,7,8,9,10,11,0,1,2,3,4,5,6,7,8,9,10,11,0,1,2,3,4,5,6,7,8,9,10,11,114,105,46,98,105,116,115,95,112,101,114,95,99,104,97,110,110,101,108,32,61,61,32,49,54,0,46,47,101,120,116,101,114,110,97,108,47,115,116,98,95,105,109,97,103,101,46,104,0,115,116,98,105,95,95,108,111,97,100,95,97,110,100,95,112,111,115,116,112,114,111,99,101,115,115,95,56,98,105,116,0,111,117,116,111,102,109,101,109,0,117,110,107,110,111,119,110,32,105,109,97,103,101,32,116,121,112,101,0,35,63,82,65,68,73,65,78,67,69,0,35,63,82,71,66,69,0,110,111,116,32,72,68,82,0,70,79,82,77,65,84,61,51,50,45,98,105,116,95,114,108,101,95,114,103,98,101,0,117,110,115,117,112,112,111,114,116,101,100,32,102,111,114,109,97,116,0,45,89,32,0,117,110,115,117,112,112,111,114,116,101,100,32,100,97,116,97,32,108,97,121,111,117,116,0,43,88,32,0,116,111,111,32,108,97,114,103,101,0,105,110,118,97,108,105,100,32,100,101,99,111,100,101,100,32,115,99,97,110,108,105,110,101,32,108,101,110,103,116,104,0,99,111,114,114,117,112,116,0,35,63,82,65,68,73,65,78,67,69,10,0,35,63,82,71,66,69,10,0,98,97,100,32,114,101,113,95,99,111,109,112,0,114,101,113,95,99,111,109,112,32,62,61,32,49,32,38,38,32,114,101,113,95,99,111,109,112,32,60,61,32,52,0,115,116,98,105,95,95,99,111,110,118,101,114,116,95,102,111,114,109,97,116,49,54,0,115,116,98,105,95,95,99,111,110,118,101,114,116,95,102,111,114,109,97,116,0,109,117,108,116,105,112,108,101,32,73,72,68,82,0,98,97,100,32,73,72,68,82,32,108,101,110,0,49,47,50,47,52,47,56,47,49,54,45,98,105,116,32,111,110,108,121,0,98,97,100,32,99,116,121,112,101,0,98,97,100,32,99,111,109,112,32,109,101,116,104,111,100,0,98,97,100,32,102,105,108,116,101,114,32,109,101,116,104,111,100,0,98,97,100,32,105,110,116,101,114,108,97,99,101,32,109,101,116,104,111,100,0,48,45,112,105,120,101,108,32,105,109,97,103,101,0,102,105,114,115,116,32,110,111,116,32,73,72,68,82,0,105,110,118,97,108,105,100,32,80,76,84,69,0,116,82,78,83,32,97,102,116,101,114,32,73,68,65,84,0,116,82,78,83,32,98,101,102,111,114,101,32,80,76,84,69,0,98,97,100,32,116,82,78,83,32,108,101,110,0,116,82,78,83,32,119,105,116,104,32,97,108,112,104,97,0,0,255,85,0,17,0,0,0,1,110,111,32,80,76,84,69,0,111,117,116,111,102,100,97,116,97,0,110,111,32,73,68,65,84,0,88,88,88,88,32,80,78,71,32,99,104,117,110,107,32,110,111,116,32,107,110,111,119,110,0,115,45,62,105,109,103,95,111,117,116,95,110,32,61,61,32,52,0,115,116,98,105,95,95,100,101,95,105,112,104,111,110,101,0,111,117,116,95,110,32,61,61,32,50,32,124,124,32,111,117,116,95,110,32,61,61,32,52,0,115,116,98,105,95,95,99,111,109,112,117,116,101,95,116,114,97,110,115,112,97,114,101,110,99,121,0,115,116,98,105,95,95,99,111,109,112,117,116,101,95,116,114,97,110,115,112,97,114,101,110,99,121,49,54,0,111,117,116,95,110,32,61,61,32,115,45,62,105,109,103,95,110,32,124,124,32,111,117,116,95,110,32,61,61,32,115,45,62,105,109,103,95,110,43,49,0,115,116,98,105,95,95,99,114,101,97,116,101,95,112,110,103,95,105,109,97,103,101,95,114,97,119,0,110,111,116,32,101,110,111,117,103,104,32,112,105,120,101,108,115,0,105,109,103,95,119,105,100,116,104,95,98,121,116,101,115,32,60,61,32,120,0,0,1,0,5,6,105,109,103,95,110,43,49,32,61,61,32,111,117,116,95,110,0,105,110,118,97,108,105,100,32,102,105,108,116,101,114,0,105,109,103,95,110,32,61,61,32,51,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,98,97,100,32,104,117,102,102,109,97,110,32,99,111,100,101,0,98,97,100,32,100,105,115,116,0,111,117,116,112,117,116,32,98,117,102,102,101,114,32,108,105,109,105,116,0,122,45,62,115,105,122,101,91,98,93,32,61,61,32,115,0,115,116,98,105,95,95,122,104,117,102,102,109,97,110,95,100,101,99,111,100,101,95,115,108,111,119,112,97,116,104,0,98,105,116,115,32,60,61,32,49,54,0,115,116,98,105,95,95,98,105,116,95,114,101,118,101,114,115,101,0,122,45,62,99,111,100,101,95,98,117,102,102,101,114,32,60,32,40,49,85,32,60,60,32,122,45,62,110,117,109,95,98,105,116,115,41,0,115,116,98,105,95,95,102,105,108,108,95,98,105,116,115,0,98,97,100,32,99,111,100,101,108,101,110,103,116,104,115,0,99,32,61,61,32,49,56,0,115,116,98,105,95,95,99,111,109,112,117,116,101,95,104,117,102,102,109,97,110,95,99,111,100,101,115,0,98,97,100,32,115,105,122,101,115,0,97,45,62,110,117,109,95,98,105,116,115,32,61,61,32,48,0,115,116,98,105,95,95,112,97,114,115,101,95,117,110,99,111,109,112,114,101,115,115,101,100,95,98,108,111,99,107,0,122,108,105,98,32,99,111,114,114,117,112,116,0,114,101,97,100,32,112,97,115,116,32,98,117,102,102,101,114,0,98,97,100,32,122,108,105,98,32,104,101,97,100,101,114,0,110,111,32,112,114,101,115,101,116,32,100,105,99,116,0,98,97,100,32,99,111,109,112,114,101,115,115,105,111,110,0,98,97,100,32,112,110,103,32,115,105,103,0,1,2,4,4,105,110,102,111,45,62,99,104,97,110,110,101,108,115,32,62,61,32,48,0,46,47,101,120,116,101,114,110,97,108,47,115,116,98,95,105,109,97,103,101,95,114,101,115,105,122,101,46,104,0,115,116,98,105,114,95,95,114,101,115,105,122,101,95,97,108,108,111,99,97,116,101,100,0,105,110,102,111,45,62,99,104,97,110,110,101,108,115,32,60,61,32,54,52,0,105,110,102,111,45,62,104,111,114,105,122,111,110,116,97,108,95,102,105,108,116,101,114,32,60,32,40,115,105,122,101,111,102,40,40,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,41,41,47,115,105,122,101,111,102,40,40,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,41,91,48,93,41,41,0,105,110,102,111,45,62,118,101,114,116,105,99,97,108,95,102,105,108,116,101,114,32,60,32,40,115,105,122,101,111,102,40,40,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,41,41,47,115,105,122,101,111,102,40,40,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,41,91,48,93,41,41,0,97,108,112,104,97,95,99,104,97,110,110,101,108,32,62,61,32,48,32,38,38,32,97,108,112,104,97,95,99,104,97,110,110,101,108,32,60,32,105,110,102,111,45,62,99,104,97,110,110,101,108,115,0,116,101,109,112,109,101,109,0,116,101,109,112,109,101,109,95,115,105,122,101,95,105,110,95,98,121,116,101,115,32,62,61,32,109,101,109,111,114,121,95,114,101,113,117,105,114,101,100,0,40,115,105,122,101,95,116,41,40,117,110,115,105,103,110,101,100,32,99,104,97,114,42,41,40,40,40,117,110,115,105,103,110,101,100,32,99,104,97,114,42,41,105,110,102,111,45,62,101,110,99,111,100,101,95,98,117,102,102,101,114,41,32,43,32,105,110,102,111,45,62,101,110,99,111,100,101,95,98,117,102,102,101,114,95,115,105,122,101,41,32,61,61,32,40,115,105,122,101,95,116,41,116,101,109,112,109,101,109,32,43,32,116,101,109,112,109,101,109,95,115,105,122,101,95,105,110,95,98,121,116,101,115,0,40,115,105,122,101,95,116,41,40,117,110,115,105,103,110,101,100,32,99,104,97,114,42,41,40,40,40,117,110,115,105,103,110,101,100,32,99,104,97,114,42,41,105,110,102,111,45,62,114,105,110,103,95,98,117,102,102,101,114,41,32,43,32,105,110,102,111,45,62,114,105,110,103,95,98,117,102,102,101,114,95,115,105,122,101,41,32,61,61,32,40,115,105,122,101,95,116,41,116,101,109,112,109,101,109,32,43,32,116,101,109,112,109,101,109,95,115,105,122,101,95,105,110,95,98,121,116,101,115,0,33,115,116,98,105,114,95,95,117,115,101,95,104,101,105,103,104,116,95,117,112,115,97,109,112,108,105,110,103,40,115,116,98,105,114,95,105,110,102,111,41,0,115,116,98,105,114,95,95,98,117,102,102,101,114,95,108,111,111,112,95,100,111,119,110,115,97,109,112,108,101,0,111,117,116,95,108,97,115,116,95,115,99,97,110,108,105,110,101,32,45,32,111,117,116,95,102,105,114,115,116,95,115,99,97,110,108,105,110,101,32,43,32,49,32,60,61,32,115,116,98,105,114,95,105,110,102,111,45,62,114,105,110,103,95,98,117,102,102,101,114,95,110,117,109,95,101,110,116,114,105,101,115,0,115,116,98,105,114,95,95,114,101,115,97,109,112,108,101,95,118,101,114,116,105,99,97,108,95,100,111,119,110,115,97,109,112,108,101,0,114,105,110,103,95,98,117,102,102,101,114,95,105,110,100,101,120,32,33,61,32,115,116,98,105,114,95,105,110,102,111,45,62,114,105,110,103,95,98,117,102,102,101,114,95,98,101,103,105,110,95,105,110,100,101,120,0,115,116,98,105,114,95,95,97,100,100,95,101,109,112,116,121,95,114,105,110,103,95,98,117,102,102,101,114,95,101,110,116,114,121,0,33,115,116,98,105,114,95,95,117,115,101,95,119,105,100,116,104,95,117,112,115,97,109,112,108,105,110,103,40,115,116,98,105,114,95,105,110,102,111,41,0,115,116,98,105,114,95,95,114,101,115,97,109,112,108,101,95,104,111,114,105,122,111,110,116,97,108,95,100,111,119,110,115,97,109,112,108,101,0,99,111,101,102,102,105,99,105,101,110,116,32,33,61,32,48,0,110,49,32,62,61,32,110,48,0,115,116,98,105,114,95,95,114,101,115,97,109,112,108,101,95,104,111,114,105,122,111,110,116,97,108,95,117,112,115,97,109,112,108,101,0,110,48,32,62,61,32,45,115,116,98,105,114,95,105,110,102,111,45,62,104,111,114,105,122,111,110,116,97,108,95,102,105,108,116,101,114,95,112,105,120,101,108,95,109,97,114,103,105,110,0,110,49,32,62,61,32,45,115,116,98,105,114,95,105,110,102,111,45,62,104,111,114,105,122,111,110,116,97,108,95,102,105,108,116,101,114,95,112,105,120,101,108,95,109,97,114,103,105,110,0,110,48,32,60,32,115,116,98,105,114,95,105,110,102,111,45,62,105,110,112,117,116,95,119,32,43,32,115,116,98,105,114,95,105,110,102,111,45,62,104,111,114,105,122,111,110,116,97,108,95,102,105,108,116,101,114,95,112,105,120,101,108,95,109,97,114,103,105,110,0,110,49,32,60,32,115,116,98,105,114,95,105,110,102,111,45,62,105,110,112,117,116,95,119,32,43,32,115,116,98,105,114,95,105,110,102,111,45,62,104,111,114,105,122,111,110,116,97,108,95,102,105,108,116,101,114,95,112,105,120,101,108,95,109,97,114,103,105,110,0,33,34,85,110,107,110,111,119,110,32,116,121,112,101,47,99,111,108,111,114,115,112,97,99,101,47,99,104,97,110,110,101,108,115,32,99,111,109,98,105,110,97,116,105,111,110,46,34,0,115,116,98,105,114,95,95,100,101,99,111,100,101,95,115,99,97,110,108,105,110,101,0,33,34,85,110,105,109,112,108,101,109,101,110,116,101,100,32,101,100,103,101,32,116,121,112,101,34,0,115,116,98,105,114,95,95,101,100,103,101,95,119,114,97,112,95,115,108,111,119,0,115,116,98,105,114,95,95,101,110,99,111,100,101,95,115,99,97,110,108,105,110,101,0,115,99,97,108,101,32,60,61,32,49,0,115,116,98,105,114,95,95,115,117,112,112,111,114,116,95,116,114,97,112,101,122,111,105,100,0,115,116,98,105,114,95,95,102,105,108,116,101,114,95,116,114,97,112,101,122,111,105,100,0,115,116,98,105,114,95,95,117,115,101,95,104,101,105,103,104,116,95,117,112,115,97,109,112,108,105,110,103,40,115,116,98,105,114,95,105,110,102,111,41,0,115,116,98,105,114,95,95,98,117,102,102,101,114,95,108,111,111,112,95,117,112,115,97,109,112,108,101,0,105,110,95,108,97,115,116,95,115,99,97,110,108,105,110,101,32,45,32,105,110,95,102,105,114,115,116,95,115,99,97,110,108,105,110,101,32,43,32,49,32,60,61,32,115,116,98,105,114,95,105,110,102,111,45,62,114,105,110,103,95,98,117,102,102,101,114,95,110,117,109,95,101,110,116,114,105,101,115,0,115,116,98,105,114,95,95,114,101,115,97,109,112,108,101,95,118,101,114,116,105,99,97,108,95,117,112,115,97,109,112,108,101,0,116,111,116,97,108,32,62,32,48,46,57,102,0,115,116,98,105,114,95,95,110,111,114,109,97,108,105,122,101,95,100,111,119,110,115,97,109,112,108,101,95,99,111,101,102,102,105,99,105,101,110,116,115,0,116,111,116,97,108,32,60,32,49,46,49,102,0,111,117,116,95,108,97,115,116,95,112,105,120,101,108,32,45,32,111,117,116,95,102,105,114,115,116,95,112,105,120,101,108,32,60,61,32,40,105,110,116,41,99,101,105,108,40,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,91,102,105,108,116,101,114,93,46,115,117,112,112,111,114,116,40,115,99,97,108,101,95,114,97,116,105,111,41,32,42,32,50,41,0,115,116,98,105,114,95,95,99,97,108,99,117,108,97,116,101,95,99,111,101,102,102,105,99,105,101,110,116,115,95,100,111,119,110,115,97,109,112,108,101,0,99,111,110,116,114,105,98,117,116,111,114,45,62,110,49,32,62,61,32,99,111,110,116,114,105,98,117,116,111,114,45,62,110,48,0,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,91,102,105,108,116,101,114,93,46,107,101,114,110,101,108,40,40,102,108,111,97,116,41,40,111,117,116,95,108,97,115,116,95,112,105,120,101,108,32,43,32,49,41,32,43,32,48,46,53,102,32,45,32,111,117,116,95,99,101,110,116,101,114,95,111,102,95,105,110,44,32,115,99,97,108,101,95,114,97,116,105,111,41,32,61,61,32,48,0,105,110,95,108,97,115,116,95,112,105,120,101,108,32,45,32,105,110,95,102,105,114,115,116,95,112,105,120,101,108,32,60,61,32,40,105,110,116,41,99,101,105,108,40,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,91,102,105,108,116,101,114,93,46,115,117,112,112,111,114,116,40,49,47,115,99,97,108,101,41,32,42,32,50,41,0,115,116,98,105,114,95,95,99,97,108,99,117,108,97,116,101,95,99,111,101,102,102,105,99,105,101,110,116,115,95,117,112,115,97,109,112,108,101,0,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,91,102,105,108,116,101,114,93,46,107,101,114,110,101,108,40,40,102,108,111,97,116,41,40,105,110,95,108,97,115,116,95,112,105,120,101,108,32,43,32,49,41,32,43,32,48,46,53,102,32,45,32,105,110,95,99,101,110,116,101,114,95,111,102,95,111,117,116,44,32,49,47,115,99,97,108,101,41,32,61,61,32,48,0,116,111,116,97,108,95,102,105,108,116,101,114,32,62,32,48,46,57,0,116,111,116,97,108,95,102,105,108,116,101,114,32,60,32,49,46,49,102,0,102,105,108,116,101,114,32,33,61,32,48,0,115,116,98,105,114,95,95,103,101,116,95,102,105,108,116,101,114,95,112,105,120,101,108,95,119,105,100,116,104,0,102,105,108,116,101,114,32,60,32,40,115,105,122,101,111,102,40,40,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,41,41,47,115,105,122,101,111,102,40,40,115,116,98,105,114,95,95,102,105,108,116,101,114,95,105,110,102,111,95,116,97,98,108,101,41,91,48,93,41,41,0,105,110,102,111,45,62,104,111,114,105,122,111,110,116,97,108,95,102,105,108,116,101,114,32,33,61,32,48,0,115,116,98,105,114,95,95,99,97,108,99,117,108,97,116,101,95,109,101,109,111,114,121,0,105,110,102,111,45,62,118,101,114,116,105,99,97,108,95,102,105,108,116,101,114,32,33,61,32,48,0,46,114,114,101,115,0,91,37,115,93,32,82,101,115,111,117,114,99,101,32,102,105,108,101,32,100,111,101,115,32,110,111,116,32,99,111,110,116,97,105,110,32,105,109,97,103,101,32,100,97,116,97,0,46,112,110,103,0,46,103,105,102,0,46,104,100,114,0,91,37,115,93,32,73,109,97,103,101,32,102,105,108,101,102,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,32,40,111,110,108,121,32,51,32,99,104,97,110,110,101,108,32,51,50,32,98,105,116,32,102,108,111,97,116,115,41,0,46,100,100,115,0,46,107], "i8", ALLOC_NONE, Runtime.GLOBAL_BASE+20480); +/* memory initializer */ allocate([116,120,0,46,97,115,116,99,0,91,37,115,93,32,73,109,97,103,101,32,102,105,108,101,102,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,91,37,115,93,32,73,109,97,103,101,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,40,37,105,120,37,105,41,0,91,37,115,93,32,73,109,97,103,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,108,111,97,100,101,100,0,91,37,115,93,32,65,83,84,67,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,91,37,115,93,32,65,83,84,67,32,102,105,108,101,32,100,111,101,115,32,110,111,116,32,115,101,101,109,32,116,111,32,98,101,32,97,32,118,97,108,105,100,32,105,109,97,103,101,0,65,83,84,67,32,105,109,97,103,101,32,119,105,100,116,104,58,32,37,105,0,65,83,84,67,32,105,109,97,103,101,32,104,101,105,103,104,116,58,32,37,105,0,65,83,84,67,32,105,109,97,103,101,32,98,108,111,99,107,115,58,32,37,105,120,37,105,0,91,37,115,93,32,65,83,84,67,32,98,108,111,99,107,32,115,105,122,101,32,99,111,110,102,105,103,117,114,97,116,105,111,110,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,91,37,115,93,32,75,84,88,32,105,109,97,103,101,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,91,37,115,93,32,75,84,88,32,102,105,108,101,32,100,111,101,115,32,110,111,116,32,115,101,101,109,32,116,111,32,98,101,32,97,32,118,97,108,105,100,32,102,105,108,101,0,75,84,88,32,40,69,84,67,41,32,105,109,97,103,101,32,119,105,100,116,104,58,32,37,105,0,75,84,88,32,40,69,84,67,41,32,105,109,97,103,101,32,104,101,105,103,104,116,58,32,37,105,0,75,84,88,32,40,69,84,67,41,32,105,109,97,103,101,32,102,111,114,109,97,116,58,32,48,120,37,120,0,91,37,115,93,32,68,68,83,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,68,68,83,32,0,91,37,115,93,32,68,68,83,32,102,105,108,101,32,100,111,101,115,32,110,111,116,32,115,101,101,109,32,116,111,32,98,101,32,97,32,118,97,108,105,100,32,105,109,97,103,101,0,91,37,115,93,32,68,68,83,32,102,105,108,101,32,104,101,97,100,101,114,32,115,105,122,101,58,32,37,105,0,91,37,115,93,32,68,68,83,32,102,105,108,101,32,112,105,120,101,108,32,102,111,114,109,97,116,32,115,105,122,101,58,32,37,105,0,91,37,115,93,32,68,68,83,32,102,105,108,101,32,112,105,120,101,108,32,102,111,114,109,97,116,32,102,108,97,103,115,58,32,48,120,37,120,0,91,37,115,93,32,68,68,83,32,102,105,108,101,32,102,111,114,109,97,116,58,32,48,120,37,120,0,91,37,115,93,32,68,68,83,32,102,105,108,101,32,98,105,116,32,99,111,117,110,116,58,32,48,120,37,120,0,80,105,116,99,104,32,111,114,32,108,105,110,101,97,114,32,115,105,122,101,58,32,37,105,0,73,109,97,103,101,32,102,111,114,109,97,116,32,110,111,116,32,114,101,99,111,103,110,105,122,101,100,0,91,37,115,93,32,82,65,87,32,105,109,97,103,101,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,73,109,97,103,101,32,102,111,114,109,97,116,32,110,111,116,32,115,117,112,111,114,116,101,100,0,91,37,115,93,32,82,65,87,32,105,109,97,103,101,32,100,97,116,97,32,99,97,110,32,110,111,116,32,98,101,32,114,101,97,100,44,32,119,114,111,110,103,32,114,101,113,117,101,115,116,101,100,32,102,111,114,109,97,116,32,111,114,32,115,105,122,101,0,84,101,120,116,117,114,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,99,114,101,97,116,101,100,0,91,84,69,88,32,73,68,32,37,105,93,32,80,97,114,97,109,101,116,101,114,115,58,32,37,105,120,37,105,44,32,37,105,32,109,105,112,115,44,32,102,111,114,109,97,116,32,37,105,0,91,84,69,88,32,73,68,32,37,105,93,32,85,110,108,111,97,100,101,100,32,116,101,120,116,117,114,101,32,100,97,116,97,32,102,114,111,109,32,86,82,65,77,32,40,71,80,85,41,0,70,111,114,109,97,116,32,110,111,116,32,115,117,112,112,111,114,116,101,100,32,102,111,114,32,112,105,120,101,108,32,100,97,116,97,32,114,101,116,114,105,101,118,97,108,0,84,101,120,116,117,114,101,32,112,105,120,101,108,32,100,97,116,97,32,111,98,116,97,105,110,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,84,101,120,116,117,114,101,32,112,105,120,101,108,32,100,97,116,97,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,98,116,97,105,110,101,100,0,67,111,109,112,114,101,115,115,101,100,32,116,101,120,116,117,114,101,32,100,97,116,97,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,98,116,97,105,110,101,100,0,73,109,97,103,101,32,100,97,116,97,32,102,111,114,109,97,116,32,105,115,32,99,111,109,112,114,101,115,115,101,100,44,32,99,97,110,32,110,111,116,32,98,101,32,99,111,110,118,101,114,116,101,100,0,65,108,112,104,97,32,109,97,115,107,32,109,117,115,116,32,98,101,32,115,97,109,101,32,115,105,122,101,32,97,115,32,105,109,97,103,101,0,65,108,112,104,97,32,109,97,115,107,32,99,97,110,32,110,111,116,32,98,101,32,97,112,112,108,105,101,100,32,116,111,32,99,111,109,112,114,101,115,115,101,100,32,100,97,116,97,32,102,111,114,109,97,116,115,0,73,109,97,103,101,32,99,111,110,118,101,114,116,101,100,32,116,111,32,80,79,84,58,32,40,37,105,120,37,105,41,32,45,62,32,40,37,105,120,37,105,41,0,67,114,111,112,32,114,101,99,116,97,110,103,108,101,32,119,105,100,116,104,32,111,117,116,32,111,102,32,98,111,117,110,100,115,44,32,114,101,115,99,97,108,101,100,32,99,114,111,112,32,119,105,100,116,104,58,32,37,105,0,67,114,111,112,32,114,101,99,116,97,110,103,108,101,32,104,101,105,103,104,116,32,111,117,116,32,111,102,32,98,111,117,110,100,115,44,32,114,101,115,99,97,108,101,100,32,99,114,111,112,32,104,101,105,103,104,116,58,32,37,105,0,73,109,97,103,101,32,99,97,110,32,110,111,116,32,98,101,32,99,114,111,112,112,101,100,44,32,99,114,111,112,32,114,101,99,116,97,110,103,108,101,32,111,117,116,32,111,102,32,98,111,117,110,100,115,0,83,111,117,114,99,101,32,114,101,99,116,97,110,103,108,101,32,119,105,100,116,104,32,111,117,116,32,111,102,32,98,111,117,110,100,115,44,32,114,101,115,99,97,108,101,100,32,119,105,100,116,104,58,32,37,105,0,83,111,117,114,99,101,32,114,101,99,116,97,110,103,108,101,32,104,101,105,103,104,116,32,111,117,116,32,111,102,32,98,111,117,110,100,115,44,32,114,101,115,99,97,108,101,100,32,104,101,105,103,104,116,58,32,37,105,0,68,101,115,116,105,110,97,116,105,111,110,32,114,101,99,116,97,110,103,108,101,32,119,105,100,116,104,32,111,117,116,32,111,102,32,98,111,117,110,100,115,44,32,114,101,115,99,97,108,101,100,32,119,105,100,116,104,58,32,37,105,0,68,101,115,116,105,110,97,116,105,111,110,32,114,101,99,116,97,110,103,108,101,32,104,101,105,103,104,116,32,111,117,116,32,111,102,32,98,111,117,110,100,115,44,32,114,101,115,99,97,108,101,100,32,104,101,105,103,104,116,58,32,37,105,0,84,101,120,116,32,73,109,97,103,101,32,115,105,122,101,58,32,37,102,44,32,37,102,0,83,99,97,108,101,102,97,99,116,111,114,58,32,37,102,0,67,111,109,112,114,101,115,115,101,100,32,100,97,116,97,32,102,111,114,109,97,116,115,32,99,97,110,32,110,111,116,32,98,101,32,100,105,116,104,101,114,101,100,0,85,110,115,117,112,112,111,114,116,101,100,32,100,105,116,104,101,114,105,110,103,32,98,112,112,115,32,40,37,105,98,112,112,41,44,32,111,110,108,121,32,49,54,98,112,112,32,111,114,32,108,111,119,101,114,32,109,111,100,101,115,32,115,117,112,112,111,114,116,101,100,0,73,109,97,103,101,32,102,111,114,109,97,116,32,105,115,32,97,108,114,101,97,100,121,32,49,54,98,112,112,32,111,114,32,108,111,119,101,114,44,32,100,105,116,104,101,114,105,110,103,32,99,111,117,108,100,32,104,97,118,101,32,110,111,32,101,102,102,101,99,116,0,85,110,115,117,112,112,111,114,116,101,100,32,100,105,116,104,101,114,101,100,32,79,112,101,110,71,76,32,105,110,116,101,114,110,97,108,32,102,111,114,109,97,116,58,32,37,105,98,112,112,32,40,82,37,105,71,37,105,66,37,105,65,37,105,41,0,73,110,102,0,76,105,109,105,116,101,100,32,78,80,79,84,32,115,117,112,112,111,114,116,44,32,110,111,32,109,105,112,109,97,112,115,32,97,118,97,105,108,97,98,108,101,32,102,111,114,32,78,80,79,84,32,116,101,120,116,117,114,101,115,0,91,84,69,88,32,73,68,32,37,105,93,32,78,111,32,109,105,112,109,97,112,115,32,97,118,97,105,108,97,98,108,101,32,102,111,114,32,84,82,73,76,73,78,69,65,82,32,116,101,120,116,117,114,101,32,102,105,108,116,101,114,105,110,103,0,5,5,4,0,16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15,2,3,7,0,3,3,11,0,91,37,115,93,32,114,82,69,83,32,114,97,121,108,105,98,32,114,101,115,111,117,114,99,101,32,102,105,108,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,111,112,101,110,101,100,0,91,37,115,93,32,84,104,105,115,32,105,115,32,110,111,116,32,97,32,118,97,108,105,100,32,114,97,121,108,105,98,32,114,101,115,111,117,114,99,101,32,102,105,108,101,0,91,37,115,93,91,73,68,32,37,105,93,32,82,101,115,111,117,114,99,101,32,100,97,116,97,32,108,111,97,100,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,0,91,37,115,93,91,73,68,32,37,105,93,32,82,101,113,117,101,115,116,101,100,32,114,101,115,111,117,114,99,101,32,99,111,117,108,100,32,110,111,116,32,98,101,32,102,111,117,110,100,0,79,117,116,32,111,102,32,109,101,109,111,114,121,32,119,104,105,108,101,32,100,101,99,111,109,112,114,101,115,115,105,110,103,32,100,97,116,97,0,68,97,116,97,32,100,101,99,111,109,112,114,101,115,115,105,111,110,32,102,97,105,108,101,100,0,69,120,112,101,99,116,101,100,32,117,110,99,111,109,112,114,101,115,115,101,100,32,115,105,122,101,32,100,111,32,110,111,116,32,109,97,116,99,104,44,32,100,97,116,97,32,109,97,121,32,98,101,32,99,111,114,114,117,112,116,101,100,0,32,45,45,32,69,120,112,101,99,116,101,100,32,117,110,99,111,109,112,114,101,115,115,101,100,32,115,105,122,101,58,32,37,105,0,32,45,45,32,82,101,116,117,114,110,101,100,32,117,110,99,111,109,112,114,101,115,115,101,100,32,115,105,122,101,58,32,37,105,0,68,97,116,97,32,100,101,99,111,109,112,114,101,115,115,101,100,32,115,117,99,99,101,115,115,102,117,108,108,121,32,102,114,111,109,32,37,117,32,98,121,116,101,115,32,116,111,32,37,117,32,98,121,116,101,115,0,73,78,70,79,58,32,0,87,65,82,78,73,78,71,58,32,0,48,0,101,120,116,101,114,110,97,108,47,115,116,98,95,118,111,114,98,105,115,46,99,0,103,101,116,95,119,105,110,100,111,119,0,102,45,62,98,121,116,101,115,95,105,110,95,115,101,103,32,62,32,48,0,103,101,116,56,95,112,97,99,107,101,116,95,114,97,119,0,102,45,62,98,121,116,101,115,95,105,110,95,115,101,103,32,61,61,32,48,0,110,101,120,116,95,115,101,103,109,101,110,116,0,102,45,62,97,108,108,111,99,46,97,108,108,111,99,95,98,117,102,102,101,114,95,108,101,110,103,116,104,95,105,110,95,98,121,116,101,115,32,61,61,32,102,45,62,116,101,109,112,95,111,102,102,115,101,116,0,118,111,114,98,105,115,95,100,101,99,111,100,101,95,112,97,99,107,101,116,95,114,101,115,116,0,40,110,32,38,32,51,41,32,61,61,32,48,0,105,109,100,99,116,95,115,116,101,112,51,95,105,116,101,114,48,95,108,111,111,112,0,122,32,60,32,99,45,62,115,111,114,116,101,100,95,101,110,116,114,105,101,115,0,99,111,100,101,98,111,111,107,95,100,101,99,111,100,101,95,115,116,97,114,116,0,33,99,45,62,115,112,97,114,115,101,32,124,124,32,122,32,60,32,99,45,62,115,111,114,116,101,100,95,101,110,116,114,105,101,115,0,99,111,100,101,98,111,111,107,95,100,101,99,111,100,101,95,100,101,105,110,116,101,114,108,101,97,118,101,95,114,101,112,101,97,116,0,33,99,45,62,115,112,97,114,115,101,0,99,111,100,101,98,111,111,107,95,100,101,99,111,100,101,95,115,99,97,108,97,114,95,114,97,119,0,0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,118,111,114,98,105,115,95,100,101,99,111,100,101,95,105,110,105,116,105,97,108,0,102,45,62,116,101,109,112,95,111,102,102,115,101,116,32,61,61,32,102,45,62,97,108,108,111,99,46,97,108,108,111,99,95,98,117,102,102,101,114,95,108,101,110,103,116,104,95,105,110,95,98,121,116,101,115,0,115,116,97,114,116,95,100,101,99,111,100,101,114,0,112,111,119,40,40,102,108,111,97,116,41,32,114,43,49,44,32,100,105,109,41,32,62,32,101,110,116,114,105,101,115,0,108,111,111,107,117,112,49,95,118,97,108,117,101,115,0,40,105,110,116,41,32,102,108,111,111,114,40,112,111,119,40,40,102,108,111,97,116,41,32,114,44,32,100,105,109,41,41,32,60,61,32,101,110,116,114,105,101,115,0,107,32,61,61,32,99,45,62,115,111,114,116,101,100,95,101,110,116,114,105,101,115,0,99,111,109,112,117,116,101,95,115,111,114,116,101,100,95,104,117,102,102,109,97,110,0,99,45,62,115,111,114,116,101,100,95,99,111,100,101,119,111,114,100,115,91,120,93,32,61,61,32,99,111,100,101,0,108,101,110,32,33,61,32,78,79,95,67,79,68,69,0,105,110,99,108,117,100,101,95,105,110,95,115,111,114,116,0,99,45,62,115,111,114,116,101,100,95,101,110,116,114,105,101,115,32,61,61,32,48,0,99,111,109,112,117,116,101,95,99,111,100,101,119,111,114,100,115,0,122,32,62,61,32,48,32,38,38,32,122,32,60,32,51,50,0,108,101,110,91,105,93,32,62,61,32,48,32,38,38,32,108,101,110,91,105,93,32,60,32,51,50,0,97,118,97,105,108,97,98,108,101,91,121,93,32,61,61,32,48,0,118,111,114,98,105,115,114,98,0,0,0,0,0,0,0,7,0,0,0,0,0,3,5,0,0,0,0,3,7,5,0,0,0,3,5,3,5,0,0,3,7,5,3,5,0,3,7,5,3,5,7,98,117,102,95,99,32,61,61,32,50,0,99,111,110,118,101,114,116,95,99,104,97,110,110,101,108,115,95,115,104,111,114,116,95,105,110,116,101,114,108,101,97,118,101,100,0,69,88,84,0,65,82,66,0,79,69,83,0,65,78,71,76,69,0,103,108,67,114,101,97,116,101,80,114,111,103,114,97,109,79,98,106,101,99,116,0,103,108,67,114,101,97,116,101,80,114,111,103,114,97,109,0,103,108,85,115,101,80,114,111,103,114,97,109,79,98,106,101,99,116,0,103,108,85,115,101,80,114,111,103,114,97,109,0,103,108,67,114,101,97,116,101,83,104,97,100,101,114,79,98,106,101,99,116,0,103,108,67,114,101,97,116,101,83,104,97,100,101,114,0,103,108,65,116,116,97,99,104,79,98,106,101,99,116,0,103,108,65,116,116,97,99,104,83,104,97,100,101,114,0,103,108,68,101,116,97,99,104,79,98,106,101,99,116,0,103,108,68,101,116,97,99,104,83,104,97,100,101,114,0,103,108,80,105,120,101,108,83,116,111,114,101,105,0,103,108,71,101,116,83,116,114,105,110,103,0,103,108,71,101,116,73,110,116,101,103,101,114,118,0,103,108,71,101,116,70,108,111,97,116,118,0,103,108,71,101,116,66,111,111,108,101,97,110,118,0,103,108,71,101,110,84,101,120,116,117,114,101,115,0,103,108,68,101,108,101,116,101,84,101,120,116,117,114,101,115,0,103,108,67,111,109,112,114,101,115,115,101,100,84,101,120,73,109,97,103,101,50,68,0,103,108,67,111,109,112,114,101,115,115,101,100,84,101,120,83,117,98,73,109,97,103,101,50,68,0,103,108,84,101,120,73,109,97,103,101,50,68,0,103,108,84,101,120,83,117,98,73,109,97,103,101,50,68,0,103,108,82,101,97,100,80,105,120,101,108,115,0,103,108,66,105,110,100,84,101,120,116,117,114,101,0,103,108,71,101,116,84,101,120,80,97,114,97,109,101,116,101,114,102,118,0,103,108,71,101,116,84,101,120,80,97,114,97,109,101,116,101,114,105,118,0,103,108,84,101,120,80,97,114,97,109,101,116,101,114,102,118,0,103,108,84,101,120,80,97,114,97,109,101,116,101,114,105,118,0,103,108,73,115,84,101,120,116,117,114,101,0,103,108,71,101,110,66,117,102,102,101,114,115,0,103,108,68,101,108,101,116,101,66,117,102,102,101,114,115,0,103,108,71,101,116,66,117,102,102,101,114,80,97,114,97,109,101,116,101,114,105,118,0,103,108,66,117,102,102,101,114,68,97,116,97,0,103,108,66,117,102,102,101,114,83,117,98,68,97,116,97,0,103,108,73,115,66,117,102,102,101,114,0,103,108,71,101,110,82,101,110,100,101,114,98,117,102,102,101,114,115,0,103,108,68,101,108,101,116,101,82,101,110,100,101,114,98,117,102,102,101,114,115,0,103,108,66,105,110,100,82,101,110,100,101,114,98,117,102,102,101,114,0,103,108,71,101,116,82,101,110,100,101,114,98,117,102,102,101,114,80,97,114,97,109,101,116,101,114,105,118,0,103,108,73,115,82,101,110,100,101,114,98,117,102,102,101,114,0,103,108,71,101,116,85,110,105,102,111,114,109,102,118,0,103,108,71,101,116,85,110,105,102,111,114,109,105,118,0,103,108,71,101,116,85,110,105,102,111,114,109,76,111,99,97,116,105,111,110,0,103,108,71,101,116,86,101,114,116,101,120,65,116,116,114,105,98,102,118,0,103,108,71,101,116,86,101,114,116,101,120,65,116,116,114,105,98,105,118,0,103,108,71,101,116,86,101,114,116,101,120,65,116,116,114,105,98,80,111,105,110,116,101,114,118,0,103,108,71,101,116,65,99,116,105,118,101,85,110,105,102,111,114,109,0,103,108,85,110,105,102,111,114,109,49,102,0,103,108,85,110,105,102,111,114,109,50,102,0,103,108,85,110,105,102,111,114,109,51,102,0,103,108,85,110,105,102,111,114,109,52,102,0,103,108,85,110,105,102,111,114,109,49,105,0,103,108,85,110,105,102,111,114,109,50,105,0,103,108,85,110,105,102,111,114,109,51,105,0,103,108,85,110,105,102,111,114,109,52,105,0,103,108,85,110,105,102,111,114,109,49,105,118,0,103,108,85,110,105,102,111,114,109,50,105,118,0,103,108,85,110,105,102,111,114,109,51,105,118,0,103,108,85,110,105,102,111,114,109,52,105,118,0,103,108,85,110,105,102,111,114,109,49,102,118,0,103,108,85,110,105,102,111,114,109,50,102,118,0,103,108,85,110,105,102,111,114,109,51,102,118,0,103,108,85,110,105,102,111,114,109,52,102,118,0,103,108,85,110,105,102,111,114,109,77,97,116,114,105,120,50,102,118,0,103,108,85,110,105,102,111,114,109,77,97,116,114,105,120,51,102,118,0,103,108,85,110,105,102,111,114,109,77,97,116,114,105,120,52,102,118,0,103,108,66,105,110,100,66,117,102,102,101,114,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,49,102,118,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,50,102,118,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,51,102,118,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,52,102,118,0,103,108,71,101,116,65,116,116,114,105,98,76,111,99,97,116,105,111,110,0,103,108,71,101,116,65,99,116,105,118,101,65,116,116,114,105,98,0,103,108,68,101,108,101,116,101,83,104,97,100,101,114,0,103,108,71,101,116,65,116,116,97,99,104,101,100,83,104,97,100,101,114,115,0,103,108,83,104,97,100,101,114,83,111,117,114,99,101,0,103,108,71,101,116,83,104,97,100,101,114,83,111,117,114,99,101,0,103,108,67,111,109,112,105,108,101,83,104,97,100,101,114,0,103,108,71,101,116,83,104,97,100,101,114,73,110,102,111,76,111,103,0,103,108,71,101,116,83,104,97,100,101,114,105,118,0,103,108,71,101,116,80,114,111,103,114,97,109,105,118,0,103,108,73,115,83,104,97,100,101,114,0,103,108,68,101,108,101,116,101,80,114,111,103,114,97,109,0,103,108,71,101,116,83,104,97,100,101,114,80,114,101,99,105,115,105,111,110,70,111,114,109,97,116,0,103,108,76,105,110,107,80,114,111,103,114,97,109,0,103,108,71,101,116,80,114,111,103,114,97,109,73,110,102,111,76,111,103,0,103,108,86,97,108,105,100,97,116,101,80,114,111,103,114,97,109,0,103,108,73,115,80,114,111,103,114,97,109,0,103,108,66,105,110,100,65,116,116,114,105,98,76,111,99,97,116,105,111,110,0,103,108,66,105,110,100,70,114,97,109,101,98,117,102,102,101,114,0,103,108,71,101,110,70,114,97,109,101,98,117,102,102,101,114,115,0,103,108,68,101,108,101,116,101,70,114,97,109,101,98,117,102,102,101,114,115,0,103,108,70,114,97,109,101,98,117,102,102,101,114,82,101,110,100,101,114,98,117,102,102,101,114,0,103,108,70,114,97,109,101,98,117,102,102,101,114,84,101,120,116,117,114,101,50,68,0,103,108,71,101,116,70,114,97,109,101,98,117,102,102,101,114,65,116,116,97,99,104,109,101,110,116,80,97,114,97,109,101,116,101,114,105,118,0,103,108,73,115,70,114,97,109,101,98,117,102,102,101,114,0,103,108,68,101,108,101,116,101,79,98,106,101,99,116,0,103,108,71,101,116,79,98,106,101,99,116,80,97,114,97,109,101,116,101,114,105,118,0,103,108,71,101,116,73,110,102,111,76,111,103,0,103,108,66,105,110,100,80,114,111,103,114,97,109,0,103,108,71,101,116,80,111,105,110,116,101,114,118,0,103,108,68,114,97,119,82,97,110,103,101,69,108,101,109,101,110,116,115,0,103,108,69,110,97,98,108,101,67,108,105,101,110,116,83,116,97,116,101,0,103,108,86,101,114,116,101,120,80,111,105,110,116,101,114,0,103,108,84,101,120,67,111,111,114,100,80,111,105,110,116,101,114,0,103,108,78,111,114,109,97,108,80,111,105,110,116,101,114,0,103,108,67,111,108,111,114,80,111,105,110,116,101,114,0,103,108,67,108,105,101,110,116,65,99,116,105,118,101,84,101,120,116,117,114,101,0,103,108,73,115,86,101,114,116,101,120,65,114,114,97,121,0,103,108,71,101,110,86,101,114,116,101,120,65,114,114,97,121,115,0,103,108,68,101,108,101,116,101,86,101,114,116,101,120,65,114,114,97,121,115,0,103,108,66,105,110,100,86,101,114,116,101,120,65,114,114,97,121,0,103,108,77,97,116,114,105,120,77,111,100,101,0,103,108,76,111,97,100,73,100,101,110,116,105,116,121,0,103,108,76,111,97,100,77,97,116,114,105,120,102,0,103,108,70,114,117,115,116,117,109,0,103,108,82,111,116,97,116,101,102,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,80,111,105,110,116,101,114,0,103,108,69,110,97,98,108,101,86,101,114,116,101,120,65,116,116,114,105,98,65,114,114,97,121,0,103,108,68,105,115,97,98,108,101,86,101,114,116,101,120,65,116,116,114,105,98,65,114,114,97,121,0,103,108,68,114,97,119,65,114,114,97,121,115,0,103,108,68,114,97,119,69,108,101,109,101,110,116,115,0,103,108,83,104,97,100,101,114,66,105,110,97,114,121,0,103,108,82,101,108,101,97,115,101,83,104,97,100,101,114,67,111,109,112,105,108,101,114,0,103,108,71,101,116,69,114,114,111,114,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,68,105,118,105,115,111,114,0,103,108,68,114,97,119,65,114,114,97,121,115,73,110,115,116,97,110,99,101,100,0,103,108,68,114,97,119,69,108,101,109,101,110,116,115,73,110,115,116,97,110,99,101,100,0,103,108,70,105,110,105,115,104,0,103,108,70,108,117,115,104,0,103,108,67,108,101,97,114,68,101,112,116,104,0,103,108,67,108,101,97,114,68,101,112,116,104,102,0,103,108,68,101,112,116,104,70,117,110,99,0,103,108,69,110,97,98,108,101,0,103,108,68,105,115,97,98,108,101,0,103,108,70,114,111,110,116,70,97,99,101,0,103,108,67,117,108,108,70,97,99,101,0,103,108,67,108,101,97,114,0,103,108,76,105,110,101,87,105,100,116,104,0,103,108,67,108,101,97,114,83,116,101,110,99,105,108,0,103,108,68,101,112,116,104,77,97,115,107,0,103,108,83,116,101,110,99,105,108,77,97,115,107,0,103,108,67,104,101,99,107,70,114,97,109,101,98,117,102,102,101,114,83,116,97,116,117,115,0,103,108,71,101,110,101,114,97,116,101,77,105,112,109,97,112,0,103,108,65,99,116,105,118,101,84,101,120,116,117,114,101,0,103,108,66,108,101,110,100,69,113,117,97,116,105,111,110,0,103,108,73,115,69,110,97,98,108,101,100,0,103,108,66,108,101,110,100,70,117,110,99,0,103,108,66,108,101,110,100,69,113,117,97,116,105,111,110,83,101,112,97,114,97,116,101,0,103,108,68,101,112,116,104,82,97,110,103,101,0,103,108,68,101,112,116,104,82,97,110,103,101,102,0,103,108,83,116,101,110,99,105,108,77,97,115,107,83,101,112,97,114,97,116,101,0,103,108,72,105,110,116,0,103,108,80,111,108,121,103,111,110,79,102,102,115,101,116,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,49,102,0,103,108,83,97,109,112,108,101,67,111,118,101,114,97,103,101,0,103,108,84,101,120,80,97,114,97,109,101,116,101,114,105,0,103,108,84,101,120,80,97,114,97,109,101,116,101,114,102,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,50,102,0,103,108,83,116,101,110,99,105,108,70,117,110,99,0,103,108,83,116,101,110,99,105,108,79,112,0,103,108,86,105,101,119,112,111,114,116,0,103,108,67,108,101,97,114,67,111,108,111,114,0,103,108,83,99,105,115,115,111,114,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,51,102,0,103,108,67,111,108,111,114,77,97,115,107,0,103,108,82,101,110,100,101,114,98,117,102,102,101,114,83,116,111,114,97,103,101,0,103,108,66,108,101,110,100,70,117,110,99,83,101,112,97,114,97,116,101,0,103,108,66,108,101,110,100,67,111,108,111,114,0,103,108,83,116,101,110,99,105,108,70,117,110,99,83,101,112,97,114,97,116,101,0,103,108,83,116,101,110,99,105,108,79,112,83,101,112,97,114,97,116,101,0,103,108,86,101,114,116,101,120,65,116,116,114,105,98,52,102,0,103,108,67,111,112,121,84,101,120,73,109,97,103,101,50,68,0,103,108,67,111,112,121,84,101,120,83,117,98,73,109,97,103,101,50,68,0,103,108,68,114,97,119,66,117,102,102,101,114,115,0,77,111,100,117,108,101,46,112,114,105,110,116,69,114,114,40,39,98,97,100,32,110,97,109,101,32,105,110,32,103,101,116,80,114,111,99,65,100,100,114,101,115,115,58,32,39,32,43,32,91,80,111,105,110,116,101,114,95,115,116,114,105,110,103,105,102,121,40,36,48,41,44,32,80,111,105,110,116,101,114,95,115,116,114,105,110,103,105,102,121,40,36,49,41,93,41,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,1,2,3,4,5,6,7,8,9,255,255,255,255,255,255,255,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,255,255,255,255,255,255,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,1,2,4,7,3,6,5,0,17,0,10,0,17,17,17,0,0,0,0,5,0,0,0,0,0,0,9,0,0,0,0,11,0,0,0,0,0,0,0,0,17,0,15,10,17,17,17,3,10,7,0,1,19,9,11,11,0,0,9,6,11,0,0,11,0,6,17,0,0,0,17,17,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,17,0,10,10,17,17,17,0,10,0,0,2,0,9,11,0,0,0,9,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,9,12,0,0,0,0,0,12,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,4,13,0,0,0,0,9,14,0,0,0,0,0,14,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,15,0,0,0,0,9,16,0,0,0,0,0,16,0,0,16,0,0,18,0,0,0,18,18,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,18,18,18,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,10,0,0,0,0,9,11,0,0,0,0,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,9,12,0,0,0,0,0,12,0,0,12,0,0,45,43,32,32,32,48,88,48,120,0,40,110,117,108,108,41,0,45,48,88,43,48,88,32,48,88,45,48,120,43,48,120,32,48,120,0,105,110,102,0,73,78,70,0,78,65,78,0,48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,46,0,84,33,34,25,13,1,2,3,17,75,28,12,16,4,11,29,18,30,39,104,110,111,112,113,98,32,5,6,15,19,20,21,26,8,22,7,40,36,23,24,9,10,14,27,31,37,35,131,130,125,38,42,43,60,61,62,63,67,71,74,77,88,89,90,91,92,93,94,95,96,97,99,100,101,102,103,105,106,107,108,114,115,116,121,122,123,124,0,73,108,108,101,103,97,108,32,98,121,116,101,32,115,101,113,117,101,110,99,101,0,68,111,109,97,105,110,32,101,114,114,111,114,0,82,101,115,117,108,116,32,110,111,116,32,114,101,112,114,101,115,101,110,116,97,98,108,101,0,78,111,116,32,97,32,116,116,121,0,80,101,114,109,105,115,115,105,111,110,32,100,101,110,105,101,100,0,79,112,101,114,97,116,105,111,110,32,110,111,116,32,112,101,114,109,105,116,116,101,100,0,78,111,32,115,117,99,104,32,102,105,108,101,32,111,114,32,100,105,114,101,99,116,111,114,121,0,78,111,32,115,117,99,104,32,112,114,111,99,101,115,115,0,70,105,108,101,32,101,120,105,115,116,115,0,86,97,108,117,101,32,116,111,111,32,108,97,114,103,101,32,102,111,114,32,100,97,116,97,32,116,121,112,101,0,78,111,32,115,112,97,99,101,32,108,101,102,116,32,111,110,32,100,101,118,105,99,101,0,79,117,116,32,111,102,32,109,101,109,111,114,121,0,82,101,115,111,117,114,99,101,32,98,117,115,121,0,73,110,116,101,114,114,117,112,116,101,100,32,115,121,115,116,101,109,32,99,97,108,108,0,82,101,115,111,117,114,99,101,32,116,101,109,112,111,114,97,114,105,108,121,32,117,110,97,118,97,105,108,97,98,108,101,0,73,110,118,97,108,105,100,32,115,101,101,107,0,67,114,111,115,115,45,100,101,118,105,99,101,32,108,105,110,107,0,82,101,97,100,45,111,110,108,121,32,102,105,108,101,32,115,121,115,116,101,109,0,68,105,114,101,99,116,111,114,121,32,110,111,116,32,101,109,112,116,121,0,67,111,110,110,101,99,116,105,111,110,32,114,101,115,101,116,32,98,121,32,112,101,101,114,0,79,112,101,114,97,116,105,111,110,32,116,105,109,101,100,32,111,117,116,0,67,111,110,110,101,99,116,105,111,110,32,114,101,102,117,115,101,100,0,72,111,115,116,32,105,115,32,100,111,119,110,0,72,111,115,116,32,105,115,32,117,110,114,101,97,99,104,97,98,108,101,0,65,100,100,114,101,115,115,32,105,110,32,117,115,101,0,66,114,111,107,101,110,32,112,105,112,101,0,73,47,79,32,101,114,114,111,114,0,78,111,32,115,117,99,104,32,100,101,118,105,99,101,32,111,114,32,97,100,100,114,101,115,115,0,66,108,111,99,107,32,100,101,118,105,99,101,32,114,101,113,117,105,114,101,100,0,78,111,32,115,117,99,104,32,100,101,118,105,99,101,0,78,111,116,32,97,32,100,105,114,101,99,116,111,114,121,0,73,115,32,97,32,100,105,114,101,99,116,111,114,121,0,84,101,120,116,32,102,105,108,101,32,98,117,115,121,0,69,120,101,99,32,102,111,114,109,97,116,32,101,114,114,111,114,0,73,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,0,65,114,103,117,109,101,110,116,32,108,105,115,116,32,116,111,111,32,108,111,110,103,0,83,121,109,98,111,108,105,99,32,108,105,110,107,32,108,111,111,112,0,70,105,108,101,110,97,109,101,32,116,111,111,32,108,111,110,103,0,84,111,111,32,109,97,110,121,32,111,112,101,110,32,102,105,108,101,115,32,105,110,32,115,121,115,116,101,109,0,78,111,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,115,32,97,118,97,105,108,97,98,108,101,0,66,97,100,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,0,78,111,32,99,104,105,108,100,32,112,114,111,99,101,115,115,0,66,97,100,32,97,100,100,114,101,115,115,0,70,105,108,101,32,116,111,111,32,108,97,114,103,101,0,84,111,111,32,109,97,110,121,32,108,105,110,107,115,0,78,111,32,108,111,99,107,115,32,97,118,97,105,108,97,98,108,101,0,82,101,115,111,117,114,99,101,32,100,101,97,100,108,111,99,107,32,119,111,117,108,100,32,111,99,99,117,114,0,83,116,97,116,101,32,110,111,116,32,114,101,99,111,118,101,114,97,98,108,101,0,80,114,101,118,105,111,117,115,32,111,119,110,101,114,32,100,105,101,100,0,79,112,101,114,97,116,105,111,110,32,99,97,110,99,101,108,101,100,0,70,117,110,99,116,105,111,110,32,110,111,116,32,105,109,112,108,101,109,101,110,116,101,100,0,78,111,32,109,101,115,115,97,103,101,32,111,102,32,100,101,115,105,114,101,100,32,116,121,112,101,0,73,100,101,110,116,105,102,105,101,114,32,114,101,109,111,118,101,100,0,68,101,118,105,99,101,32,110,111,116,32,97,32,115,116,114,101,97,109,0,78,111,32,100,97,116,97,32,97,118,97,105,108,97,98,108,101,0,68,101,118,105,99,101,32,116,105,109,101,111,117,116,0,79,117,116,32,111,102,32,115,116,114,101,97,109,115,32,114,101,115,111,117,114,99,101,115,0,76,105,110,107,32,104,97,115,32,98,101,101,110,32,115,101,118,101,114,101,100,0,80,114,111,116,111,99,111,108,32,101,114,114,111,114,0,66,97,100,32,109,101,115,115,97,103,101,0,70,105,108,101,32,100,101,115,99,114,105,112,116,111,114,32,105,110,32,98,97,100,32,115,116,97,116,101,0,78,111,116,32,97,32,115,111,99,107,101,116,0,68,101,115,116,105,110,97,116,105,111,110,32,97,100,100,114,101,115,115,32,114,101,113,117,105,114,101,100,0,77,101,115,115,97,103,101,32,116,111,111,32,108,97,114,103,101,0,80,114,111,116,111,99,111,108,32,119,114,111,110,103,32,116,121,112,101,32,102,111,114,32,115,111,99,107,101,116,0,80,114,111,116,111,99,111,108,32,110,111,116,32,97,118,97,105,108,97,98,108,101,0,80,114,111,116,111,99,111,108,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,83,111,99,107,101,116,32,116,121,112,101,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,78,111,116,32,115,117,112,112,111,114,116,101,100,0,80,114,111,116,111,99,111,108,32,102,97,109,105,108,121,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,65,100,100,114,101,115,115,32,102,97,109,105,108,121,32,110,111,116,32,115,117,112,112,111,114,116,101,100,32,98,121,32,112,114,111,116,111,99,111,108,0,65,100,100,114,101,115,115,32,110,111,116,32,97,118,97,105,108,97,98,108,101,0,78,101,116,119,111,114,107,32,105,115,32,100,111,119,110,0,78,101,116,119,111,114,107,32,117,110,114,101,97,99,104,97,98,108,101,0,67,111,110,110,101,99,116,105,111,110,32,114,101,115,101,116,32,98,121,32,110,101,116,119,111,114,107,0,67,111,110,110,101,99,116,105,111,110,32,97,98,111,114,116,101,100,0,78,111,32,98,117,102,102,101,114,32,115,112,97,99,101,32,97,118,97,105,108,97,98,108,101,0,83,111,99,107,101,116,32,105,115,32,99,111,110,110,101,99,116,101,100,0,83,111,99,107,101,116,32,110,111,116,32,99,111,110,110,101,99,116,101,100,0,67,97,110,110,111,116,32,115,101,110,100,32,97,102,116,101,114,32,115,111,99,107,101,116,32,115,104,117,116,100,111,119,110,0,79,112,101,114,97,116,105,111,110,32,97,108,114,101,97,100,121,32,105,110,32,112,114,111,103,114,101,115,115,0,79,112,101,114,97,116,105,111,110,32,105,110,32,112,114,111,103,114,101,115,115,0,83,116,97,108,101,32,102,105,108,101,32,104,97,110,100,108,101,0,82,101,109,111,116,101,32,73,47,79,32,101,114,114,111,114,0,81,117,111,116,97,32,101,120,99,101,101,100,101,100,0,78,111,32,109,101,100,105,117,109,32,102,111,117,110,100,0,87,114,111,110,103,32,109,101,100,105,117,109,32,116,121,112,101,0,78,111,32,101,114,114,111,114,32,105,110,102,111,114,109,97,116,105,111,110,0,0,105,110,102,105,110,105,116,121,0,110,97,110,0,114,119,97,0], "i8", ALLOC_NONE, Runtime.GLOBAL_BASE+30720); + + + + + +/* no memory initializer */ +var tempDoublePtr = STATICTOP; STATICTOP += 16; + +assert(tempDoublePtr % 8 == 0); + +function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much + + HEAP8[tempDoublePtr] = HEAP8[ptr]; + + HEAP8[tempDoublePtr+1] = HEAP8[ptr+1]; + + HEAP8[tempDoublePtr+2] = HEAP8[ptr+2]; + + HEAP8[tempDoublePtr+3] = HEAP8[ptr+3]; + +} + +function copyTempDouble(ptr) { + + HEAP8[tempDoublePtr] = HEAP8[ptr]; + + HEAP8[tempDoublePtr+1] = HEAP8[ptr+1]; + + HEAP8[tempDoublePtr+2] = HEAP8[ptr+2]; + + HEAP8[tempDoublePtr+3] = HEAP8[ptr+3]; + + HEAP8[tempDoublePtr+4] = HEAP8[ptr+4]; + + HEAP8[tempDoublePtr+5] = HEAP8[ptr+5]; + + HEAP8[tempDoublePtr+6] = HEAP8[ptr+6]; + + HEAP8[tempDoublePtr+7] = HEAP8[ptr+7]; + +} + +// {{PRE_LIBRARY}} + + + + var GL={counter:1,lastError:0,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],contexts:[],currentContext:null,offscreenCanvases:{},timerQueriesEXT:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],programInfos:{},stringCache:{},tempFixedLengthArray:[],packAlignment:4,unpackAlignment:4,init:function () { + GL.miniTempBuffer = new Float32Array(GL.MINI_TEMP_BUFFER_SIZE); + for (var i = 0; i < GL.MINI_TEMP_BUFFER_SIZE; i++) { + GL.miniTempBufferViews[i] = GL.miniTempBuffer.subarray(0, i+1); + } + + // For functions such as glDrawBuffers, glInvalidateFramebuffer and glInvalidateSubFramebuffer that need to pass a short array to the WebGL API, + // create a set of short fixed-length arrays to avoid having to generate any garbage when calling those functions. + for (var i = 0; i < 32; i++) { + GL.tempFixedLengthArray.push(new Array(i)); + } + },recordError:function recordError(errorCode) { + if (!GL.lastError) { + GL.lastError = errorCode; + } + },getNewId:function (table) { + var ret = GL.counter++; + for (var i = table.length; i < ret; i++) { + table[i] = null; + } + return ret; + },MINI_TEMP_BUFFER_SIZE:256,miniTempBuffer:null,miniTempBufferViews:[0],getSource:function (shader, count, string, length) { + var source = ''; + for (var i = 0; i < count; ++i) { + var frag; + if (length) { + var len = HEAP32[(((length)+(i*4))>>2)]; + if (len < 0) { + frag = Pointer_stringify(HEAP32[(((string)+(i*4))>>2)]); + } else { + frag = Pointer_stringify(HEAP32[(((string)+(i*4))>>2)], len); + } + } else { + frag = Pointer_stringify(HEAP32[(((string)+(i*4))>>2)]); + } + source += frag; + } + return source; + },createContext:function (canvas, webGLContextAttributes) { + if (typeof webGLContextAttributes['majorVersion'] === 'undefined' && typeof webGLContextAttributes['minorVersion'] === 'undefined') { + webGLContextAttributes['majorVersion'] = 1; + webGLContextAttributes['minorVersion'] = 0; + } + + + var ctx; + var errorInfo = '?'; + function onContextCreationError(event) { + errorInfo = event.statusMessage || errorInfo; + } + try { + canvas.addEventListener('webglcontextcreationerror', onContextCreationError, false); + try { + if (webGLContextAttributes['majorVersion'] == 1 && webGLContextAttributes['minorVersion'] == 0) { + ctx = canvas.getContext("webgl", webGLContextAttributes) || canvas.getContext("experimental-webgl", webGLContextAttributes); + } else if (webGLContextAttributes['majorVersion'] == 2 && webGLContextAttributes['minorVersion'] == 0) { + ctx = canvas.getContext("webgl2", webGLContextAttributes); + } else { + throw 'Unsupported WebGL context version ' + majorVersion + '.' + minorVersion + '!' + } + } finally { + canvas.removeEventListener('webglcontextcreationerror', onContextCreationError, false); + } + if (!ctx) throw ':('; + } catch (e) { + Module.print('Could not create canvas: ' + [errorInfo, e, JSON.stringify(webGLContextAttributes)]); + return 0; + } + + if (!ctx) return 0; + var context = GL.registerContext(ctx, webGLContextAttributes); + return context; + },registerContext:function (ctx, webGLContextAttributes) { + var handle = GL.getNewId(GL.contexts); + var context = { + handle: handle, + attributes: webGLContextAttributes, + version: webGLContextAttributes['majorVersion'], + GLctx: ctx + }; + + + // Store the created context object so that we can access the context given a canvas without having to pass the parameters again. + if (ctx.canvas) ctx.canvas.GLctxObject = context; + GL.contexts[handle] = context; + if (typeof webGLContextAttributes['enableExtensionsByDefault'] === 'undefined' || webGLContextAttributes['enableExtensionsByDefault']) { + GL.initExtensions(context); + } + return handle; + },makeContextCurrent:function (contextHandle) { + var context = GL.contexts[contextHandle]; + if (!context) return false; + GLctx = Module.ctx = context.GLctx; // Active WebGL context object. + GL.currentContext = context; // Active Emscripten GL layer context object. + return true; + },getContext:function (contextHandle) { + return GL.contexts[contextHandle]; + },deleteContext:function (contextHandle) { + if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = null; + if (typeof JSEvents === 'object') JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); // Release all JS event handlers on the DOM element that the GL context is associated with since the context is now deleted. + if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises. + GL.contexts[contextHandle] = null; + },initExtensions:function (context) { + // If this function is called without a specific context object, init the extensions of the currently active context. + if (!context) context = GL.currentContext; + + if (context.initExtensionsDone) return; + context.initExtensionsDone = true; + + var GLctx = context.GLctx; + + context.maxVertexAttribs = GLctx.getParameter(GLctx.MAX_VERTEX_ATTRIBS); + + // Detect the presence of a few extensions manually, this GL interop layer itself will need to know if they exist. + + if (context.version < 2) { + // Extension available from Firefox 26 and Google Chrome 30 + var instancedArraysExt = GLctx.getExtension('ANGLE_instanced_arrays'); + if (instancedArraysExt) { + GLctx['vertexAttribDivisor'] = function(index, divisor) { instancedArraysExt['vertexAttribDivisorANGLE'](index, divisor); }; + GLctx['drawArraysInstanced'] = function(mode, first, count, primcount) { instancedArraysExt['drawArraysInstancedANGLE'](mode, first, count, primcount); }; + GLctx['drawElementsInstanced'] = function(mode, count, type, indices, primcount) { instancedArraysExt['drawElementsInstancedANGLE'](mode, count, type, indices, primcount); }; + } + + // Extension available from Firefox 25 and WebKit + var vaoExt = GLctx.getExtension('OES_vertex_array_object'); + if (vaoExt) { + GLctx['createVertexArray'] = function() { return vaoExt['createVertexArrayOES'](); }; + GLctx['deleteVertexArray'] = function(vao) { vaoExt['deleteVertexArrayOES'](vao); }; + GLctx['bindVertexArray'] = function(vao) { vaoExt['bindVertexArrayOES'](vao); }; + GLctx['isVertexArray'] = function(vao) { return vaoExt['isVertexArrayOES'](vao); }; + } + + var drawBuffersExt = GLctx.getExtension('WEBGL_draw_buffers'); + if (drawBuffersExt) { + GLctx['drawBuffers'] = function(n, bufs) { drawBuffersExt['drawBuffersWEBGL'](n, bufs); }; + } + } + + GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); + + // These are the 'safe' feature-enabling extensions that don't add any performance impact related to e.g. debugging, and + // should be enabled by default so that client GLES2/GL code will not need to go through extra hoops to get its stuff working. + // As new extensions are ratified at http://www.khronos.org/registry/webgl/extensions/ , feel free to add your new extensions + // here, as long as they don't produce a performance impact for users that might not be using those extensions. + // E.g. debugging-related extensions should probably be off by default. + var automaticallyEnabledExtensions = [ "OES_texture_float", "OES_texture_half_float", "OES_standard_derivatives", + "OES_vertex_array_object", "WEBGL_compressed_texture_s3tc", "WEBGL_depth_texture", + "OES_element_index_uint", "EXT_texture_filter_anisotropic", "ANGLE_instanced_arrays", + "OES_texture_float_linear", "OES_texture_half_float_linear", "WEBGL_compressed_texture_atc", + "WEBGL_compressed_texture_pvrtc", "EXT_color_buffer_half_float", "WEBGL_color_buffer_float", + "EXT_frag_depth", "EXT_sRGB", "WEBGL_draw_buffers", "WEBGL_shared_resources", + "EXT_shader_texture_lod", "EXT_color_buffer_float"]; + + function shouldEnableAutomatically(extension) { + var ret = false; + automaticallyEnabledExtensions.forEach(function(include) { + if (ext.indexOf(include) != -1) { + ret = true; + } + }); + return ret; + } + + var exts = GLctx.getSupportedExtensions(); + if (exts && exts.length > 0) { + GLctx.getSupportedExtensions().forEach(function(ext) { + if (automaticallyEnabledExtensions.indexOf(ext) != -1) { + GLctx.getExtension(ext); // Calling .getExtension enables that extension permanently, no need to store the return value to be enabled. + } + }); + } + },populateUniformTable:function (program) { + var p = GL.programs[program]; + GL.programInfos[program] = { + uniforms: {}, + maxUniformLength: 0, // This is eagerly computed below, since we already enumerate all uniforms anyway. + maxAttributeLength: -1, // This is lazily computed and cached, computed when/if first asked, "-1" meaning not computed yet. + maxUniformBlockNameLength: -1 // Lazily computed as well + }; + + var ptable = GL.programInfos[program]; + var utable = ptable.uniforms; + // A program's uniform table maps the string name of an uniform to an integer location of that uniform. + // The global GL.uniforms map maps integer locations to WebGLUniformLocations. + var numUniforms = GLctx.getProgramParameter(p, GLctx.ACTIVE_UNIFORMS); + for (var i = 0; i < numUniforms; ++i) { + var u = GLctx.getActiveUniform(p, i); + + var name = u.name; + ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length+1); + + // Strip off any trailing array specifier we might have got, e.g. "[0]". + if (name.indexOf(']', name.length-1) !== -1) { + var ls = name.lastIndexOf('['); + name = name.slice(0, ls); + } + + // Optimize memory usage slightly: If we have an array of uniforms, e.g. 'vec3 colors[3];', then + // only store the string 'colors' in utable, and 'colors[0]', 'colors[1]' and 'colors[2]' will be parsed as 'colors'+i. + // Note that for the GL.uniforms table, we still need to fetch the all WebGLUniformLocations for all the indices. + var loc = GLctx.getUniformLocation(p, name); + if (loc != null) + { + var id = GL.getNewId(GL.uniforms); + utable[name] = [u.size, id]; + GL.uniforms[id] = loc; + + for (var j = 1; j < u.size; ++j) { + var n = name + '['+j+']'; + loc = GLctx.getUniformLocation(p, n); + id = GL.getNewId(GL.uniforms); + + GL.uniforms[id] = loc; + } + } + } + }};function _emscripten_glIsRenderbuffer(renderbuffer) { + var rb = GL.renderbuffers[renderbuffer]; + if (!rb) return 0; + return GLctx.isRenderbuffer(rb); + } + + function _emscripten_glStencilMaskSeparate(x0, x1) { GLctx['stencilMaskSeparate'](x0, x1) } + + + + + + function _emscripten_get_now() { abort() } + + + + function _emscripten_set_main_loop_timing(mode, value) { + Browser.mainLoop.timingMode = mode; + Browser.mainLoop.timingValue = value; + + if (!Browser.mainLoop.func) { + console.error('emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.'); + return 1; // Return non-zero on failure, can't set timing mode when there is no main loop. + } + + if (mode == 0 /*EM_TIMING_SETTIMEOUT*/) { + Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setTimeout() { + var timeUntilNextTick = Math.max(0, Browser.mainLoop.tickStartTime + value - _emscripten_get_now())|0; + setTimeout(Browser.mainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop + }; + Browser.mainLoop.method = 'timeout'; + } else if (mode == 1 /*EM_TIMING_RAF*/) { + Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_rAF() { + Browser.requestAnimationFrame(Browser.mainLoop.runner); + }; + Browser.mainLoop.method = 'rAF'; + } else if (mode == 2 /*EM_TIMING_SETIMMEDIATE*/) { + if (!window['setImmediate']) { + // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed) + var setImmediates = []; + var emscriptenMainLoopMessageId = 'setimmediate'; + function Browser_setImmediate_messageHandler(event) { + if (event.source === window && event.data === emscriptenMainLoopMessageId) { + event.stopPropagation(); + setImmediates.shift()(); + } + } + window.addEventListener("message", Browser_setImmediate_messageHandler, true); + window['setImmediate'] = function Browser_emulated_setImmediate(func) { + setImmediates.push(func); + if (ENVIRONMENT_IS_WORKER) { + if (Module['setImmediates'] === undefined) Module['setImmediates'] = []; + Module['setImmediates'].push(func); + window.postMessage({target: emscriptenMainLoopMessageId}); // In --proxy-to-worker, route the message via proxyClient.js + } else window.postMessage(emscriptenMainLoopMessageId, "*"); // On the main thread, can just send the message to itself. + } + } + Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setImmediate() { + window['setImmediate'](Browser.mainLoop.runner); + }; + Browser.mainLoop.method = 'immediate'; + } + return 0; + }function _emscripten_set_main_loop(func, fps, simulateInfiniteLoop, arg, noSetTiming) { + Module['noExitRuntime'] = true; + + assert(!Browser.mainLoop.func, 'emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.'); + + Browser.mainLoop.func = func; + Browser.mainLoop.arg = arg; + + var browserIterationFunc; + if (typeof arg !== 'undefined') { + browserIterationFunc = function() { + func(arg); + }; + } else { + browserIterationFunc = function() { + func(); + }; + } + + var thisMainLoopId = Browser.mainLoop.currentlyRunningMainloop; + + Browser.mainLoop.runner = function Browser_mainLoop_runner() { + if (ABORT) return; + if (Browser.mainLoop.queue.length > 0) { + var start = Date.now(); + var blocker = Browser.mainLoop.queue.shift(); + blocker.func(blocker.arg); + if (Browser.mainLoop.remainingBlockers) { + var remaining = Browser.mainLoop.remainingBlockers; + var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining); + if (blocker.counted) { + Browser.mainLoop.remainingBlockers = next; + } else { + // not counted, but move the progress along a tiny bit + next = next + 0.5; // do not steal all the next one's progress + Browser.mainLoop.remainingBlockers = (8*remaining + next)/9; + } + } + console.log('main loop blocker "' + blocker.name + '" took ' + (Date.now() - start) + ' ms'); //, left: ' + Browser.mainLoop.remainingBlockers); + Browser.mainLoop.updateStatus(); + + // catches pause/resume main loop from blocker execution + if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; + + setTimeout(Browser.mainLoop.runner, 0); + return; + } + + // catch pauses from non-main loop sources + if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; + + // Implement very basic swap interval control + Browser.mainLoop.currentFrameNumber = Browser.mainLoop.currentFrameNumber + 1 | 0; + if (Browser.mainLoop.timingMode == 1/*EM_TIMING_RAF*/ && Browser.mainLoop.timingValue > 1 && Browser.mainLoop.currentFrameNumber % Browser.mainLoop.timingValue != 0) { + // Not the scheduled time to render this frame - skip. + Browser.mainLoop.scheduler(); + return; + } else if (Browser.mainLoop.timingMode == 0/*EM_TIMING_SETTIMEOUT*/) { + Browser.mainLoop.tickStartTime = _emscripten_get_now(); + } + + // Signal GL rendering layer that processing of a new frame is about to start. This helps it optimize + // VBO double-buffering and reduce GPU stalls. + + + if (Browser.mainLoop.method === 'timeout' && Module.ctx) { + Module.printErr('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!'); + Browser.mainLoop.method = ''; // just warn once per call to set main loop + } + + Browser.mainLoop.runIter(browserIterationFunc); + + checkStackCookie(); + + // catch pauses from the main loop itself + if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; + + // Queue new audio data. This is important to be right after the main loop invocation, so that we will immediately be able + // to queue the newest produced audio samples. + // TODO: Consider adding pre- and post- rAF callbacks so that GL.newRenderingFrameStarted() and SDL.audio.queueNewAudioData() + // do not need to be hardcoded into this function, but can be more generic. + if (typeof SDL === 'object' && SDL.audio && SDL.audio.queueNewAudioData) SDL.audio.queueNewAudioData(); + + Browser.mainLoop.scheduler(); + } + + if (!noSetTiming) { + if (fps && fps > 0) _emscripten_set_main_loop_timing(0/*EM_TIMING_SETTIMEOUT*/, 1000.0 / fps); + else _emscripten_set_main_loop_timing(1/*EM_TIMING_RAF*/, 1); // Do rAF by rendering each frame (no decimating) + + Browser.mainLoop.scheduler(); + } + + if (simulateInfiniteLoop) { + throw 'SimulateInfiniteLoop'; + } + } + Module["_emscripten_set_main_loop"] = _emscripten_set_main_loop;var Browser={mainLoop:{scheduler:null,method:"",currentlyRunningMainloop:0,func:null,arg:0,timingMode:0,timingValue:0,currentFrameNumber:0,queue:[],pause:function () { + Browser.mainLoop.scheduler = null; + Browser.mainLoop.currentlyRunningMainloop++; // Incrementing this signals the previous main loop that it's now become old, and it must return. + },resume:function () { + Browser.mainLoop.currentlyRunningMainloop++; + var timingMode = Browser.mainLoop.timingMode; + var timingValue = Browser.mainLoop.timingValue; + var func = Browser.mainLoop.func; + Browser.mainLoop.func = null; + _emscripten_set_main_loop(func, 0, false, Browser.mainLoop.arg, true /* do not set timing and call scheduler, we will do it on the next lines */); + _emscripten_set_main_loop_timing(timingMode, timingValue); + Browser.mainLoop.scheduler(); + },updateStatus:function () { + if (Module['setStatus']) { + var message = Module['statusMessage'] || 'Please wait...'; + var remaining = Browser.mainLoop.remainingBlockers; + var expected = Browser.mainLoop.expectedBlockers; + if (remaining) { + if (remaining < expected) { + Module['setStatus'](message + ' (' + (expected - remaining) + '/' + expected + ')'); + } else { + Module['setStatus'](message); + } + } else { + Module['setStatus'](''); + } + } + },runIter:function (func) { + if (ABORT) return; + if (Module['preMainLoop']) { + var preRet = Module['preMainLoop'](); + if (preRet === false) { + return; // |return false| skips a frame + } + } + try { + func(); + } catch (e) { + if (e instanceof ExitStatus) { + return; + } else { + if (e && typeof e === 'object' && e.stack) Module.printErr('exception thrown: ' + [e, e.stack]); + throw e; + } + } + if (Module['postMainLoop']) Module['postMainLoop'](); + }},isFullscreen:false,pointerLock:false,moduleContextCreatedCallbacks:[],workers:[],init:function () { + if (!Module["preloadPlugins"]) Module["preloadPlugins"] = []; // needs to exist even in workers + + if (Browser.initted) return; + Browser.initted = true; + + try { + new Blob(); + Browser.hasBlobConstructor = true; + } catch(e) { + Browser.hasBlobConstructor = false; + console.log("warning: no blob constructor, cannot create blobs with mimetypes"); + } + Browser.BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : (!Browser.hasBlobConstructor ? console.log("warning: no BlobBuilder") : null)); + Browser.URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : undefined; + if (!Module.noImageDecoding && typeof Browser.URLObject === 'undefined') { + console.log("warning: Browser does not support creating object URLs. Built-in browser image decoding will not be available."); + Module.noImageDecoding = true; + } + + // Support for plugins that can process preloaded files. You can add more of these to + // your app by creating and appending to Module.preloadPlugins. + // + // Each plugin is asked if it can handle a file based on the file's name. If it can, + // it is given the file's raw data. When it is done, it calls a callback with the file's + // (possibly modified) data. For example, a plugin might decompress a file, or it + // might create some side data structure for use later (like an Image element, etc.). + + var imagePlugin = {}; + imagePlugin['canHandle'] = function imagePlugin_canHandle(name) { + return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/i.test(name); + }; + imagePlugin['handle'] = function imagePlugin_handle(byteArray, name, onload, onerror) { + var b = null; + if (Browser.hasBlobConstructor) { + try { + b = new Blob([byteArray], { type: Browser.getMimetype(name) }); + if (b.size !== byteArray.length) { // Safari bug #118630 + // Safari's Blob can only take an ArrayBuffer + b = new Blob([(new Uint8Array(byteArray)).buffer], { type: Browser.getMimetype(name) }); + } + } catch(e) { + Runtime.warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder'); + } + } + if (!b) { + var bb = new Browser.BlobBuilder(); + bb.append((new Uint8Array(byteArray)).buffer); // we need to pass a buffer, and must copy the array to get the right data range + b = bb.getBlob(); + } + var url = Browser.URLObject.createObjectURL(b); + assert(typeof url == 'string', 'createObjectURL must return a url as a string'); + var img = new Image(); + img.onload = function img_onload() { + assert(img.complete, 'Image ' + name + ' could not be decoded'); + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + Module["preloadedImages"][name] = canvas; + Browser.URLObject.revokeObjectURL(url); + if (onload) onload(byteArray); + }; + img.onerror = function img_onerror(event) { + console.log('Image ' + url + ' could not be decoded'); + if (onerror) onerror(); + }; + img.src = url; + }; + Module['preloadPlugins'].push(imagePlugin); + + var audioPlugin = {}; + audioPlugin['canHandle'] = function audioPlugin_canHandle(name) { + return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 }; + }; + audioPlugin['handle'] = function audioPlugin_handle(byteArray, name, onload, onerror) { + var done = false; + function finish(audio) { + if (done) return; + done = true; + Module["preloadedAudios"][name] = audio; + if (onload) onload(byteArray); + } + function fail() { + if (done) return; + done = true; + Module["preloadedAudios"][name] = new Audio(); // empty shim + if (onerror) onerror(); + } + if (Browser.hasBlobConstructor) { + try { + var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); + } catch(e) { + return fail(); + } + var url = Browser.URLObject.createObjectURL(b); // XXX we never revoke this! + assert(typeof url == 'string', 'createObjectURL must return a url as a string'); + var audio = new Audio(); + audio.addEventListener('canplaythrough', function() { finish(audio) }, false); // use addEventListener due to chromium bug 124926 + audio.onerror = function audio_onerror(event) { + if (done) return; + console.log('warning: browser could not fully decode audio ' + name + ', trying slower base64 approach'); + function encode64(data) { + var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + var PAD = '='; + var ret = ''; + var leftchar = 0; + var leftbits = 0; + for (var i = 0; i < data.length; i++) { + leftchar = (leftchar << 8) | data[i]; + leftbits += 8; + while (leftbits >= 6) { + var curr = (leftchar >> (leftbits-6)) & 0x3f; + leftbits -= 6; + ret += BASE[curr]; + } + } + if (leftbits == 2) { + ret += BASE[(leftchar&3) << 4]; + ret += PAD + PAD; + } else if (leftbits == 4) { + ret += BASE[(leftchar&0xf) << 2]; + ret += PAD; + } + return ret; + } + audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray); + finish(audio); // we don't wait for confirmation this worked - but it's worth trying + }; + audio.src = url; + // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror + Browser.safeSetTimeout(function() { + finish(audio); // try to use it even though it is not necessarily ready to play + }, 10000); + } else { + return fail(); + } + }; + Module['preloadPlugins'].push(audioPlugin); + + // Canvas event setup + + function pointerLockChange() { + Browser.pointerLock = document['pointerLockElement'] === Module['canvas'] || + document['mozPointerLockElement'] === Module['canvas'] || + document['webkitPointerLockElement'] === Module['canvas'] || + document['msPointerLockElement'] === Module['canvas']; + } + var canvas = Module['canvas']; + if (canvas) { + // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module + // Module['forcedAspectRatio'] = 4 / 3; + + canvas.requestPointerLock = canvas['requestPointerLock'] || + canvas['mozRequestPointerLock'] || + canvas['webkitRequestPointerLock'] || + canvas['msRequestPointerLock'] || + function(){}; + canvas.exitPointerLock = document['exitPointerLock'] || + document['mozExitPointerLock'] || + document['webkitExitPointerLock'] || + document['msExitPointerLock'] || + function(){}; // no-op if function does not exist + canvas.exitPointerLock = canvas.exitPointerLock.bind(document); + + document.addEventListener('pointerlockchange', pointerLockChange, false); + document.addEventListener('mozpointerlockchange', pointerLockChange, false); + document.addEventListener('webkitpointerlockchange', pointerLockChange, false); + document.addEventListener('mspointerlockchange', pointerLockChange, false); + + if (Module['elementPointerLock']) { + canvas.addEventListener("click", function(ev) { + if (!Browser.pointerLock && Module['canvas'].requestPointerLock) { + Module['canvas'].requestPointerLock(); + ev.preventDefault(); + } + }, false); + } + } + },createContext:function (canvas, useWebGL, setInModule, webGLContextAttributes) { + if (useWebGL && Module.ctx && canvas == Module.canvas) return Module.ctx; // no need to recreate GL context if it's already been created for this canvas. + + var ctx; + var contextHandle; + if (useWebGL) { + // For GLES2/desktop GL compatibility, adjust a few defaults to be different to WebGL defaults, so that they align better with the desktop defaults. + var contextAttributes = { + antialias: false, + alpha: false + }; + + if (webGLContextAttributes) { + for (var attribute in webGLContextAttributes) { + contextAttributes[attribute] = webGLContextAttributes[attribute]; + } + } + + contextHandle = GL.createContext(canvas, contextAttributes); + if (contextHandle) { + ctx = GL.getContext(contextHandle).GLctx; + } + } else { + ctx = canvas.getContext('2d'); + } + + if (!ctx) return null; + + if (setInModule) { + if (!useWebGL) assert(typeof GLctx === 'undefined', 'cannot set in module if GLctx is used, but we are a non-GL context that would replace it'); + + Module.ctx = ctx; + if (useWebGL) GL.makeContextCurrent(contextHandle); + Module.useWebGL = useWebGL; + Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() }); + Browser.init(); + } + return ctx; + },destroyContext:function (canvas, useWebGL, setInModule) {},fullscreenHandlersInstalled:false,lockPointer:undefined,resizeCanvas:undefined,requestFullscreen:function (lockPointer, resizeCanvas, vrDevice) { + Browser.lockPointer = lockPointer; + Browser.resizeCanvas = resizeCanvas; + Browser.vrDevice = vrDevice; + if (typeof Browser.lockPointer === 'undefined') Browser.lockPointer = true; + if (typeof Browser.resizeCanvas === 'undefined') Browser.resizeCanvas = false; + if (typeof Browser.vrDevice === 'undefined') Browser.vrDevice = null; + + var canvas = Module['canvas']; + function fullscreenChange() { + Browser.isFullscreen = false; + var canvasContainer = canvas.parentNode; + if ((document['fullscreenElement'] || document['mozFullScreenElement'] || + document['msFullscreenElement'] || document['webkitFullscreenElement'] || + document['webkitCurrentFullScreenElement']) === canvasContainer) { + canvas.exitFullscreen = document['exitFullscreen'] || + document['cancelFullScreen'] || + document['mozCancelFullScreen'] || + document['msExitFullscreen'] || + document['webkitCancelFullScreen'] || + function() {}; + canvas.exitFullscreen = canvas.exitFullscreen.bind(document); + if (Browser.lockPointer) canvas.requestPointerLock(); + Browser.isFullscreen = true; + if (Browser.resizeCanvas) Browser.setFullscreenCanvasSize(); + } else { + + // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen + canvasContainer.parentNode.insertBefore(canvas, canvasContainer); + canvasContainer.parentNode.removeChild(canvasContainer); + + if (Browser.resizeCanvas) Browser.setWindowedCanvasSize(); + } + if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullscreen); + if (Module['onFullscreen']) Module['onFullscreen'](Browser.isFullscreen); + Browser.updateCanvasDimensions(canvas); + } + + if (!Browser.fullscreenHandlersInstalled) { + Browser.fullscreenHandlersInstalled = true; + document.addEventListener('fullscreenchange', fullscreenChange, false); + document.addEventListener('mozfullscreenchange', fullscreenChange, false); + document.addEventListener('webkitfullscreenchange', fullscreenChange, false); + document.addEventListener('MSFullscreenChange', fullscreenChange, false); + } + + // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root + var canvasContainer = document.createElement("div"); + canvas.parentNode.insertBefore(canvasContainer, canvas); + canvasContainer.appendChild(canvas); + + // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size) + canvasContainer.requestFullscreen = canvasContainer['requestFullscreen'] || + canvasContainer['mozRequestFullScreen'] || + canvasContainer['msRequestFullscreen'] || + (canvasContainer['webkitRequestFullscreen'] ? function() { canvasContainer['webkitRequestFullscreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null) || + (canvasContainer['webkitRequestFullScreen'] ? function() { canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null); + + if (vrDevice) { + canvasContainer.requestFullscreen({ vrDisplay: vrDevice }); + } else { + canvasContainer.requestFullscreen(); + } + },requestFullScreen:function (lockPointer, resizeCanvas, vrDevice) { + Module.printErr('Browser.requestFullScreen() is deprecated. Please call Browser.requestFullscreen instead.'); + Browser.requestFullScreen = function(lockPointer, resizeCanvas, vrDevice) { + return Browser.requestFullscreen(lockPointer, resizeCanvas, vrDevice); + } + return Browser.requestFullscreen(lockPointer, resizeCanvas, vrDevice); + },nextRAF:0,fakeRequestAnimationFrame:function (func) { + // try to keep 60fps between calls to here + var now = Date.now(); + if (Browser.nextRAF === 0) { + Browser.nextRAF = now + 1000/60; + } else { + while (now + 2 >= Browser.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0 + Browser.nextRAF += 1000/60; + } + } + var delay = Math.max(Browser.nextRAF - now, 0); + setTimeout(func, delay); + },requestAnimationFrame:function requestAnimationFrame(func) { + if (typeof window === 'undefined') { // Provide fallback to setTimeout if window is undefined (e.g. in Node.js) + Browser.fakeRequestAnimationFrame(func); + } else { + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = window['requestAnimationFrame'] || + window['mozRequestAnimationFrame'] || + window['webkitRequestAnimationFrame'] || + window['msRequestAnimationFrame'] || + window['oRequestAnimationFrame'] || + Browser.fakeRequestAnimationFrame; + } + window.requestAnimationFrame(func); + } + },safeCallback:function (func) { + return function() { + if (!ABORT) return func.apply(null, arguments); + }; + },allowAsyncCallbacks:true,queuedAsyncCallbacks:[],pauseAsyncCallbacks:function () { + Browser.allowAsyncCallbacks = false; + },resumeAsyncCallbacks:function () { // marks future callbacks as ok to execute, and synchronously runs any remaining ones right now + Browser.allowAsyncCallbacks = true; + if (Browser.queuedAsyncCallbacks.length > 0) { + var callbacks = Browser.queuedAsyncCallbacks; + Browser.queuedAsyncCallbacks = []; + callbacks.forEach(function(func) { + func(); + }); + } + },safeRequestAnimationFrame:function (func) { + return Browser.requestAnimationFrame(function() { + if (ABORT) return; + if (Browser.allowAsyncCallbacks) { + func(); + } else { + Browser.queuedAsyncCallbacks.push(func); + } + }); + },safeSetTimeout:function (func, timeout) { + Module['noExitRuntime'] = true; + return setTimeout(function() { + if (ABORT) return; + if (Browser.allowAsyncCallbacks) { + func(); + } else { + Browser.queuedAsyncCallbacks.push(func); + } + }, timeout); + },safeSetInterval:function (func, timeout) { + Module['noExitRuntime'] = true; + return setInterval(function() { + if (ABORT) return; + if (Browser.allowAsyncCallbacks) { + func(); + } // drop it on the floor otherwise, next interval will kick in + }, timeout); + },getMimetype:function (name) { + return { + 'jpg': 'image/jpeg', + 'jpeg': 'image/jpeg', + 'png': 'image/png', + 'bmp': 'image/bmp', + 'ogg': 'audio/ogg', + 'wav': 'audio/wav', + 'mp3': 'audio/mpeg' + }[name.substr(name.lastIndexOf('.')+1)]; + },getUserMedia:function (func) { + if(!window.getUserMedia) { + window.getUserMedia = navigator['getUserMedia'] || + navigator['mozGetUserMedia']; + } + window.getUserMedia(func); + },getMovementX:function (event) { + return event['movementX'] || + event['mozMovementX'] || + event['webkitMovementX'] || + 0; + },getMovementY:function (event) { + return event['movementY'] || + event['mozMovementY'] || + event['webkitMovementY'] || + 0; + },getMouseWheelDelta:function (event) { + var delta = 0; + switch (event.type) { + case 'DOMMouseScroll': + delta = event.detail; + break; + case 'mousewheel': + delta = event.wheelDelta; + break; + case 'wheel': + delta = event['deltaY']; + break; + default: + throw 'unrecognized mouse wheel event: ' + event.type; + } + return delta; + },mouseX:0,mouseY:0,mouseMovementX:0,mouseMovementY:0,touches:{},lastTouches:{},calculateMouseEvent:function (event) { // event should be mousemove, mousedown or mouseup + if (Browser.pointerLock) { + // When the pointer is locked, calculate the coordinates + // based on the movement of the mouse. + // Workaround for Firefox bug 764498 + if (event.type != 'mousemove' && + ('mozMovementX' in event)) { + Browser.mouseMovementX = Browser.mouseMovementY = 0; + } else { + Browser.mouseMovementX = Browser.getMovementX(event); + Browser.mouseMovementY = Browser.getMovementY(event); + } + + // check if SDL is available + if (typeof SDL != "undefined") { + Browser.mouseX = SDL.mouseX + Browser.mouseMovementX; + Browser.mouseY = SDL.mouseY + Browser.mouseMovementY; + } else { + // just add the mouse delta to the current absolut mouse position + // FIXME: ideally this should be clamped against the canvas size and zero + Browser.mouseX += Browser.mouseMovementX; + Browser.mouseY += Browser.mouseMovementY; + } + } else { + // Otherwise, calculate the movement based on the changes + // in the coordinates. + var rect = Module["canvas"].getBoundingClientRect(); + var cw = Module["canvas"].width; + var ch = Module["canvas"].height; + + // Neither .scrollX or .pageXOffset are defined in a spec, but + // we prefer .scrollX because it is currently in a spec draft. + // (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/) + var scrollX = ((typeof window.scrollX !== 'undefined') ? window.scrollX : window.pageXOffset); + var scrollY = ((typeof window.scrollY !== 'undefined') ? window.scrollY : window.pageYOffset); + // If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset + // and we have no viable fallback. + assert((typeof scrollX !== 'undefined') && (typeof scrollY !== 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.'); + + if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') { + var touch = event.touch; + if (touch === undefined) { + return; // the "touch" property is only defined in SDL + + } + var adjustedX = touch.pageX - (scrollX + rect.left); + var adjustedY = touch.pageY - (scrollY + rect.top); + + adjustedX = adjustedX * (cw / rect.width); + adjustedY = adjustedY * (ch / rect.height); + + var coords = { x: adjustedX, y: adjustedY }; + + if (event.type === 'touchstart') { + Browser.lastTouches[touch.identifier] = coords; + Browser.touches[touch.identifier] = coords; + } else if (event.type === 'touchend' || event.type === 'touchmove') { + var last = Browser.touches[touch.identifier]; + if (!last) last = coords; + Browser.lastTouches[touch.identifier] = last; + Browser.touches[touch.identifier] = coords; + } + return; + } + + var x = event.pageX - (scrollX + rect.left); + var y = event.pageY - (scrollY + rect.top); + + // the canvas might be CSS-scaled compared to its backbuffer; + // SDL-using content will want mouse coordinates in terms + // of backbuffer units. + x = x * (cw / rect.width); + y = y * (ch / rect.height); + + Browser.mouseMovementX = x - Browser.mouseX; + Browser.mouseMovementY = y - Browser.mouseY; + Browser.mouseX = x; + Browser.mouseY = y; + } + },asyncLoad:function (url, onload, onerror, noRunDep) { + var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : ''; + Module['readAsync'](url, function(arrayBuffer) { + assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).'); + onload(new Uint8Array(arrayBuffer)); + if (dep) removeRunDependency(dep); + }, function(event) { + if (onerror) { + onerror(); + } else { + throw 'Loading data file "' + url + '" failed.'; + } + }); + if (dep) addRunDependency(dep); + },resizeListeners:[],updateResizeListeners:function () { + var canvas = Module['canvas']; + Browser.resizeListeners.forEach(function(listener) { + listener(canvas.width, canvas.height); + }); + },setCanvasSize:function (width, height, noUpdates) { + var canvas = Module['canvas']; + Browser.updateCanvasDimensions(canvas, width, height); + if (!noUpdates) Browser.updateResizeListeners(); + },windowedWidth:0,windowedHeight:0,setFullscreenCanvasSize:function () { + // check if SDL is available + if (typeof SDL != "undefined") { + var flags = HEAPU32[((SDL.screen+Runtime.QUANTUM_SIZE*0)>>2)]; + flags = flags | 0x00800000; // set SDL_FULLSCREEN flag + HEAP32[((SDL.screen+Runtime.QUANTUM_SIZE*0)>>2)]=flags + } + Browser.updateResizeListeners(); + },setWindowedCanvasSize:function () { + // check if SDL is available + if (typeof SDL != "undefined") { + var flags = HEAPU32[((SDL.screen+Runtime.QUANTUM_SIZE*0)>>2)]; + flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag + HEAP32[((SDL.screen+Runtime.QUANTUM_SIZE*0)>>2)]=flags + } + Browser.updateResizeListeners(); + },updateCanvasDimensions:function (canvas, wNative, hNative) { + if (wNative && hNative) { + canvas.widthNative = wNative; + canvas.heightNative = hNative; + } else { + wNative = canvas.widthNative; + hNative = canvas.heightNative; + } + var w = wNative; + var h = hNative; + if (Module['forcedAspectRatio'] && Module['forcedAspectRatio'] > 0) { + if (w/h < Module['forcedAspectRatio']) { + w = Math.round(h * Module['forcedAspectRatio']); + } else { + h = Math.round(w / Module['forcedAspectRatio']); + } + } + if (((document['fullscreenElement'] || document['mozFullScreenElement'] || + document['msFullscreenElement'] || document['webkitFullscreenElement'] || + document['webkitCurrentFullScreenElement']) === canvas.parentNode) && (typeof screen != 'undefined')) { + var factor = Math.min(screen.width / w, screen.height / h); + w = Math.round(w * factor); + h = Math.round(h * factor); + } + if (Browser.resizeCanvas) { + if (canvas.width != w) canvas.width = w; + if (canvas.height != h) canvas.height = h; + if (typeof canvas.style != 'undefined') { + canvas.style.removeProperty( "width"); + canvas.style.removeProperty("height"); + } + } else { + if (canvas.width != wNative) canvas.width = wNative; + if (canvas.height != hNative) canvas.height = hNative; + if (typeof canvas.style != 'undefined') { + if (w != wNative || h != hNative) { + canvas.style.setProperty( "width", w + "px", "important"); + canvas.style.setProperty("height", h + "px", "important"); + } else { + canvas.style.removeProperty( "width"); + canvas.style.removeProperty("height"); + } + } + } + },wgetRequests:{},nextWgetRequestHandle:0,getNextWgetRequestHandle:function () { + var handle = Browser.nextWgetRequestHandle; + Browser.nextWgetRequestHandle++; + return handle; + }};var GLFW={Window:function (id, width, height, title, monitor, share) { + this.id = id; + this.x = 0; + this.y = 0; + this.fullscreen = false; // Used to determine if app in fullscreen mode + this.storedX = 0; // Used to store X before fullscreen + this.storedY = 0; // Used to store Y before fullscreen + this.width = width; + this.height = height; + this.storedWidth = width; // Used to store width before fullscreen + this.storedHeight = height; // Used to store height before fullscreen + this.title = title; + this.monitor = monitor; + this.share = share; + this.attributes = GLFW.hints; + this.inputModes = { + 0x00033001:0x00034001, // GLFW_CURSOR (GLFW_CURSOR_NORMAL) + 0x00033002:0, // GLFW_STICKY_KEYS + 0x00033003:0, // GLFW_STICKY_MOUSE_BUTTONS + }; + this.buttons = 0; + this.keys = new Array(); + this.domKeys = new Array(); + this.shouldClose = 0; + this.title = null; + this.windowPosFunc = null; // GLFWwindowposfun + this.windowSizeFunc = null; // GLFWwindowsizefun + this.windowCloseFunc = null; // GLFWwindowclosefun + this.windowRefreshFunc = null; // GLFWwindowrefreshfun + this.windowFocusFunc = null; // GLFWwindowfocusfun + this.windowIconifyFunc = null; // GLFWwindowiconifyfun + this.framebufferSizeFunc = null; // GLFWframebuffersizefun + this.mouseButtonFunc = null; // GLFWmousebuttonfun + this.cursorPosFunc = null; // GLFWcursorposfun + this.cursorEnterFunc = null; // GLFWcursorenterfun + this.scrollFunc = null; // GLFWscrollfun + this.dropFunc = null; // GLFWdropfun + this.keyFunc = null; // GLFWkeyfun + this.charFunc = null; // GLFWcharfun + this.userptr = null; + },WindowFromId:function (id) { + if (id <= 0 || !GLFW.windows) return null; + return GLFW.windows[id - 1]; + },joystickFunc:null,errorFunc:null,monitorFunc:null,active:null,windows:null,monitors:null,monitorString:null,versionString:null,initialTime:null,extensions:null,hints:null,defaultHints:{131073:0,131074:0,131075:1,131076:1,131077:1,135169:8,135170:8,135171:8,135172:8,135173:24,135174:8,135175:0,135176:0,135177:0,135178:0,135179:0,135180:0,135181:0,135182:0,135183:0,139265:196609,139266:1,139267:0,139268:0,139269:0,139270:0,139271:0,139272:0},DOMToGLFWKeyCode:function (keycode) { + switch (keycode) { + // these keycodes are only defined for GLFW3, assume they are the same for GLFW2 + case 0x20:return 32; // DOM_VK_SPACE -> GLFW_KEY_SPACE + case 0xDE:return 39; // DOM_VK_QUOTE -> GLFW_KEY_APOSTROPHE + case 0xBC:return 44; // DOM_VK_COMMA -> GLFW_KEY_COMMA + case 0xAD:return 45; // DOM_VK_HYPHEN_MINUS -> GLFW_KEY_MINUS + case 0xBD:return 45; // DOM_VK_MINUS -> GLFW_KEY_MINUS + case 0xBE:return 46; // DOM_VK_PERIOD -> GLFW_KEY_PERIOD + case 0xBF:return 47; // DOM_VK_SLASH -> GLFW_KEY_SLASH + case 0x30:return 48; // DOM_VK_0 -> GLFW_KEY_0 + case 0x31:return 49; // DOM_VK_1 -> GLFW_KEY_1 + case 0x32:return 50; // DOM_VK_2 -> GLFW_KEY_2 + case 0x33:return 51; // DOM_VK_3 -> GLFW_KEY_3 + case 0x34:return 52; // DOM_VK_4 -> GLFW_KEY_4 + case 0x35:return 53; // DOM_VK_5 -> GLFW_KEY_5 + case 0x36:return 54; // DOM_VK_6 -> GLFW_KEY_6 + case 0x37:return 55; // DOM_VK_7 -> GLFW_KEY_7 + case 0x38:return 56; // DOM_VK_8 -> GLFW_KEY_8 + case 0x39:return 57; // DOM_VK_9 -> GLFW_KEY_9 + case 0x3B:return 59; // DOM_VK_SEMICOLON -> GLFW_KEY_SEMICOLON + case 0x3D:return 61; // DOM_VK_EQUALS -> GLFW_KEY_EQUAL + case 0xBB:return 61; // DOM_VK_EQUALS -> GLFW_KEY_EQUAL + case 0x41:return 65; // DOM_VK_A -> GLFW_KEY_A + case 0x42:return 66; // DOM_VK_B -> GLFW_KEY_B + case 0x43:return 67; // DOM_VK_C -> GLFW_KEY_C + case 0x44:return 68; // DOM_VK_D -> GLFW_KEY_D + case 0x45:return 69; // DOM_VK_E -> GLFW_KEY_E + case 0x46:return 70; // DOM_VK_F -> GLFW_KEY_F + case 0x47:return 71; // DOM_VK_G -> GLFW_KEY_G + case 0x48:return 72; // DOM_VK_H -> GLFW_KEY_H + case 0x49:return 73; // DOM_VK_I -> GLFW_KEY_I + case 0x4A:return 74; // DOM_VK_J -> GLFW_KEY_J + case 0x4B:return 75; // DOM_VK_K -> GLFW_KEY_K + case 0x4C:return 76; // DOM_VK_L -> GLFW_KEY_L + case 0x4D:return 77; // DOM_VK_M -> GLFW_KEY_M + case 0x4E:return 78; // DOM_VK_N -> GLFW_KEY_N + case 0x4F:return 79; // DOM_VK_O -> GLFW_KEY_O + case 0x50:return 80; // DOM_VK_P -> GLFW_KEY_P + case 0x51:return 81; // DOM_VK_Q -> GLFW_KEY_Q + case 0x52:return 82; // DOM_VK_R -> GLFW_KEY_R + case 0x53:return 83; // DOM_VK_S -> GLFW_KEY_S + case 0x54:return 84; // DOM_VK_T -> GLFW_KEY_T + case 0x55:return 85; // DOM_VK_U -> GLFW_KEY_U + case 0x56:return 86; // DOM_VK_V -> GLFW_KEY_V + case 0x57:return 87; // DOM_VK_W -> GLFW_KEY_W + case 0x58:return 88; // DOM_VK_X -> GLFW_KEY_X + case 0x59:return 89; // DOM_VK_Y -> GLFW_KEY_Y + case 0x5a:return 90; // DOM_VK_Z -> GLFW_KEY_Z + case 0xDB:return 91; // DOM_VK_OPEN_BRACKET -> GLFW_KEY_LEFT_BRACKET + case 0xDC:return 92; // DOM_VK_BACKSLASH -> GLFW_KEY_BACKSLASH + case 0xDD:return 93; // DOM_VK_CLOSE_BRACKET -> GLFW_KEY_RIGHT_BRACKET + case 0xC0:return 94; // DOM_VK_BACK_QUOTE -> GLFW_KEY_GRAVE_ACCENT + + + case 0x1B:return 256; // DOM_VK_ESCAPE -> GLFW_KEY_ESCAPE + case 0x0D:return 257; // DOM_VK_RETURN -> GLFW_KEY_ENTER + case 0x09:return 258; // DOM_VK_TAB -> GLFW_KEY_TAB + case 0x08:return 259; // DOM_VK_BACK -> GLFW_KEY_BACKSPACE + case 0x2D:return 260; // DOM_VK_INSERT -> GLFW_KEY_INSERT + case 0x2E:return 261; // DOM_VK_DELETE -> GLFW_KEY_DELETE + case 0x27:return 262; // DOM_VK_RIGHT -> GLFW_KEY_RIGHT + case 0x25:return 263; // DOM_VK_LEFT -> GLFW_KEY_LEFT + case 0x28:return 264; // DOM_VK_DOWN -> GLFW_KEY_DOWN + case 0x26:return 265; // DOM_VK_UP -> GLFW_KEY_UP + case 0x21:return 266; // DOM_VK_PAGE_UP -> GLFW_KEY_PAGE_UP + case 0x22:return 267; // DOM_VK_PAGE_DOWN -> GLFW_KEY_PAGE_DOWN + case 0x24:return 268; // DOM_VK_HOME -> GLFW_KEY_HOME + case 0x23:return 269; // DOM_VK_END -> GLFW_KEY_END + case 0x14:return 280; // DOM_VK_CAPS_LOCK -> GLFW_KEY_CAPS_LOCK + case 0x91:return 281; // DOM_VK_SCROLL_LOCK -> GLFW_KEY_SCROLL_LOCK + case 0x90:return 282; // DOM_VK_NUM_LOCK -> GLFW_KEY_NUM_LOCK + case 0x2C:return 283; // DOM_VK_SNAPSHOT -> GLFW_KEY_PRINT_SCREEN + case 0x13:return 284; // DOM_VK_PAUSE -> GLFW_KEY_PAUSE + case 0x70:return 290; // DOM_VK_F1 -> GLFW_KEY_F1 + case 0x71:return 291; // DOM_VK_F2 -> GLFW_KEY_F2 + case 0x72:return 292; // DOM_VK_F3 -> GLFW_KEY_F3 + case 0x73:return 293; // DOM_VK_F4 -> GLFW_KEY_F4 + case 0x74:return 294; // DOM_VK_F5 -> GLFW_KEY_F5 + case 0x75:return 295; // DOM_VK_F6 -> GLFW_KEY_F6 + case 0x76:return 296; // DOM_VK_F7 -> GLFW_KEY_F7 + case 0x77:return 297; // DOM_VK_F8 -> GLFW_KEY_F8 + case 0x78:return 298; // DOM_VK_F9 -> GLFW_KEY_F9 + case 0x79:return 299; // DOM_VK_F10 -> GLFW_KEY_F10 + case 0x7A:return 300; // DOM_VK_F11 -> GLFW_KEY_F11 + case 0x7B:return 301; // DOM_VK_F12 -> GLFW_KEY_F12 + case 0x7C:return 302; // DOM_VK_F13 -> GLFW_KEY_F13 + case 0x7D:return 303; // DOM_VK_F14 -> GLFW_KEY_F14 + case 0x7E:return 304; // DOM_VK_F15 -> GLFW_KEY_F15 + case 0x7F:return 305; // DOM_VK_F16 -> GLFW_KEY_F16 + case 0x80:return 306; // DOM_VK_F17 -> GLFW_KEY_F17 + case 0x81:return 307; // DOM_VK_F18 -> GLFW_KEY_F18 + case 0x82:return 308; // DOM_VK_F19 -> GLFW_KEY_F19 + case 0x83:return 309; // DOM_VK_F20 -> GLFW_KEY_F20 + case 0x84:return 310; // DOM_VK_F21 -> GLFW_KEY_F21 + case 0x85:return 311; // DOM_VK_F22 -> GLFW_KEY_F22 + case 0x86:return 312; // DOM_VK_F23 -> GLFW_KEY_F23 + case 0x87:return 313; // DOM_VK_F24 -> GLFW_KEY_F24 + case 0x88:return 314; // 0x88 (not used?) -> GLFW_KEY_F25 + case 0x60:return 320; // DOM_VK_NUMPAD0 -> GLFW_KEY_KP_0 + case 0x61:return 321; // DOM_VK_NUMPAD1 -> GLFW_KEY_KP_1 + case 0x62:return 322; // DOM_VK_NUMPAD2 -> GLFW_KEY_KP_2 + case 0x63:return 323; // DOM_VK_NUMPAD3 -> GLFW_KEY_KP_3 + case 0x64:return 324; // DOM_VK_NUMPAD4 -> GLFW_KEY_KP_4 + case 0x65:return 325; // DOM_VK_NUMPAD5 -> GLFW_KEY_KP_5 + case 0x66:return 326; // DOM_VK_NUMPAD6 -> GLFW_KEY_KP_6 + case 0x67:return 327; // DOM_VK_NUMPAD7 -> GLFW_KEY_KP_7 + case 0x68:return 328; // DOM_VK_NUMPAD8 -> GLFW_KEY_KP_8 + case 0x69:return 329; // DOM_VK_NUMPAD9 -> GLFW_KEY_KP_9 + case 0x6E:return 330; // DOM_VK_DECIMAL -> GLFW_KEY_KP_DECIMAL + case 0x6F:return 331; // DOM_VK_DIVIDE -> GLFW_KEY_KP_DIVIDE + case 0x6A:return 332; // DOM_VK_MULTIPLY -> GLFW_KEY_KP_MULTIPLY + case 0x6D:return 333; // DOM_VK_SUBTRACT -> GLFW_KEY_KP_SUBTRACT + case 0x6B:return 334; // DOM_VK_ADD -> GLFW_KEY_KP_ADD + // case 0x0D:return 335; // DOM_VK_RETURN -> GLFW_KEY_KP_ENTER (DOM_KEY_LOCATION_RIGHT) + // case 0x61:return 336; // DOM_VK_EQUALS -> GLFW_KEY_KP_EQUAL (DOM_KEY_LOCATION_RIGHT) + case 0x10:return 340; // DOM_VK_SHIFT -> GLFW_KEY_LEFT_SHIFT + case 0x11:return 341; // DOM_VK_CONTROL -> GLFW_KEY_LEFT_CONTROL + case 0x12:return 342; // DOM_VK_ALT -> GLFW_KEY_LEFT_ALT + case 0x5B:return 343; // DOM_VK_WIN -> GLFW_KEY_LEFT_SUPER + // case 0x10:return 344; // DOM_VK_SHIFT -> GLFW_KEY_RIGHT_SHIFT (DOM_KEY_LOCATION_RIGHT) + // case 0x11:return 345; // DOM_VK_CONTROL -> GLFW_KEY_RIGHT_CONTROL (DOM_KEY_LOCATION_RIGHT) + // case 0x12:return 346; // DOM_VK_ALT -> GLFW_KEY_RIGHT_ALT (DOM_KEY_LOCATION_RIGHT) + // case 0x5B:return 347; // DOM_VK_WIN -> GLFW_KEY_RIGHT_SUPER (DOM_KEY_LOCATION_RIGHT) + case 0x5D:return 348; // DOM_VK_CONTEXT_MENU -> GLFW_KEY_MENU + // XXX: GLFW_KEY_WORLD_1, GLFW_KEY_WORLD_2 what are these? + default:return -1; // GLFW_KEY_UNKNOWN + }; + },getModBits:function (win) { + var mod = 0; + if (win.keys[340]) mod |= 0x0001; // GLFW_MOD_SHIFT + if (win.keys[341]) mod |= 0x0002; // GLFW_MOD_CONTROL + if (win.keys[342]) mod |= 0x0004; // GLFW_MOD_ALT + if (win.keys[343]) mod |= 0x0008; // GLFW_MOD_SUPER + return mod; + },onKeyPress:function (event) { + if (!GLFW.active || !GLFW.active.charFunc) return; + if (event.ctrlKey || event.metaKey) return; + + // correct unicode charCode is only available with onKeyPress event + var charCode = event.charCode; + if (charCode == 0 || (charCode >= 0x00 && charCode <= 0x1F)) return; + + + Module['dynCall_vii'](GLFW.active.charFunc, GLFW.active.id, charCode); + },onKeyChanged:function (keyCode, status) { + if (!GLFW.active) return; + + var key = GLFW.DOMToGLFWKeyCode(keyCode); + if (key == -1) return; + + var repeat = status && GLFW.active.keys[key]; + GLFW.active.keys[key] = status; + GLFW.active.domKeys[keyCode] = status; + if (!GLFW.active.keyFunc) return; + + + if (repeat) status = 2; // GLFW_REPEAT + Module['dynCall_viiiii'](GLFW.active.keyFunc, GLFW.active.id, key, keyCode, status, GLFW.getModBits(GLFW.active)); + },onGamepadConnected:function (event) { + GLFW.refreshJoysticks(); + },onGamepadDisconnected:function (event) { + GLFW.refreshJoysticks(); + },onKeydown:function (event) { + GLFW.onKeyChanged(event.keyCode, 1); // GLFW_PRESS or GLFW_REPEAT + + // This logic comes directly from the sdl implementation. We cannot + // call preventDefault on all keydown events otherwise onKeyPress will + // not get called + if (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */) { + event.preventDefault(); + } + },onKeyup:function (event) { + GLFW.onKeyChanged(event.keyCode, 0); // GLFW_RELEASE + },onBlur:function (event) { + if (!GLFW.active) return; + + for (var i = 0; i < GLFW.active.domKeys.length; ++i) { + if (GLFW.active.domKeys[i]) { + GLFW.onKeyChanged(i, 0); // GLFW_RELEASE + } + } + },onMousemove:function (event) { + if (!GLFW.active) return; + + Browser.calculateMouseEvent(event); + + if (event.target != Module["canvas"] || !GLFW.active.cursorPosFunc) return; + + + Module['dynCall_vidd'](GLFW.active.cursorPosFunc, GLFW.active.id, Browser.mouseX, Browser.mouseY); + },DOMToGLFWMouseButton:function (event) { + // DOM and glfw have different button codes. + // See http://www.w3schools.com/jsref/event_button.asp. + var eventButton = event['button']; + if (eventButton > 0) { + if (eventButton == 1) { + eventButton = 2; + } else { + eventButton = 1; + } + } + return eventButton; + },onMouseenter:function (event) { + if (!GLFW.active) return; + + if (event.target != Module["canvas"] || !GLFW.active.cursorEnterFunc) return; + + Module['dynCall_vii'](GLFW.active.cursorEnterFunc, GLFW.active.id, 1); + },onMouseleave:function (event) { + if (!GLFW.active) return; + + if (event.target != Module["canvas"] || !GLFW.active.cursorEnterFunc) return; + + Module['dynCall_vii'](GLFW.active.cursorEnterFunc, GLFW.active.id, 0); + },onMouseButtonChanged:function (event, status) { + if (!GLFW.active) return; + + Browser.calculateMouseEvent(event); + + if (event.target != Module["canvas"]) return; + + eventButton = GLFW.DOMToGLFWMouseButton(event); + + if (status == 1) { // GLFW_PRESS + GLFW.active.buttons |= (1 << eventButton); + try { + event.target.setCapture(); + } catch (e) {} + } else { // GLFW_RELEASE + GLFW.active.buttons &= ~(1 << eventButton); + } + + if (!GLFW.active.mouseButtonFunc) return; + + + Module['dynCall_viiii'](GLFW.active.mouseButtonFunc, GLFW.active.id, eventButton, status, GLFW.getModBits(GLFW.active)); + },onMouseButtonDown:function (event) { + if (!GLFW.active) return; + GLFW.onMouseButtonChanged(event, 1); // GLFW_PRESS + },onMouseButtonUp:function (event) { + if (!GLFW.active) return; + GLFW.onMouseButtonChanged(event, 0); // GLFW_RELEASE + },onMouseWheel:function (event) { + // Note the minus sign that flips browser wheel direction (positive direction scrolls page down) to native wheel direction (positive direction is mouse wheel up) + var delta = -Browser.getMouseWheelDelta(event); + delta = (delta == 0) ? 0 : (delta > 0 ? Math.max(delta, 1) : Math.min(delta, -1)); // Quantize to integer so that minimum scroll is at least +/- 1. + GLFW.wheelPos += delta; + + if (!GLFW.active || !GLFW.active.scrollFunc || event.target != Module['canvas']) return; + + + var sx = 0; + var sy = 0; + if (event.type == 'mousewheel') { + sx = event.wheelDeltaX; + sy = event.wheelDeltaY; + } else { + sx = event.deltaX; + sy = event.deltaY; + } + + Module['dynCall_vidd'](GLFW.active.scrollFunc, GLFW.active.id, sx, sy); + + event.preventDefault(); + },onCanvasResize:function (width, height) { + if (!GLFW.active) return; + + var resizeNeeded = true; + + // If the client is requestiong fullscreen mode + if (document["fullscreen"] || document["fullScreen"] || document["mozFullScreen"] || document["webkitIsFullScreen"]) { + GLFW.active.storedX = GLFW.active.x; + GLFW.active.storedY = GLFW.active.y; + GLFW.active.storedWidth = GLFW.active.width; + GLFW.active.storedHeight = GLFW.active.height; + GLFW.active.x = GLFW.active.y = 0; + GLFW.active.width = screen.width; + GLFW.active.height = screen.height; + GLFW.active.fullscreen = true; + + // If the client is reverting from fullscreen mode + } else if (GLFW.active.fullscreen == true) { + GLFW.active.x = GLFW.active.storedX; + GLFW.active.y = GLFW.active.storedY; + GLFW.active.width = GLFW.active.storedWidth; + GLFW.active.height = GLFW.active.storedHeight; + GLFW.active.fullscreen = false; + + // If the width/height values do not match current active window sizes + } else if (GLFW.active.width != width || GLFW.active.height != height) { + GLFW.active.width = width; + GLFW.active.height = height; + } else { + resizeNeeded = false; + } + + // If any of the above conditions were true, we need to resize the canvas + if (resizeNeeded) { + // resets the canvas size to counter the aspect preservation of Browser.updateCanvasDimensions + Browser.setCanvasSize(GLFW.active.width, GLFW.active.height, true); + // TODO: Client dimensions (clientWidth/clientHeight) vs pixel dimensions (width/height) of + // the canvas should drive window and framebuffer size respectfully. + GLFW.onWindowSizeChanged(); + GLFW.onFramebufferSizeChanged(); + } + },onWindowSizeChanged:function () { + if (!GLFW.active) return; + + if (!GLFW.active.windowSizeFunc) return; + + + Module['dynCall_viii'](GLFW.active.windowSizeFunc, GLFW.active.id, GLFW.active.width, GLFW.active.height); + },onFramebufferSizeChanged:function () { + if (!GLFW.active) return; + + if (!GLFW.active.framebufferSizeFunc) return; + + Module['dynCall_viii'](GLFW.active.framebufferSizeFunc, GLFW.active.id, GLFW.active.width, GLFW.active.height); + },requestFullscreen:function () { + var RFS = Module["canvas"]['requestFullscreen'] || + Module["canvas"]['mozRequestFullScreen'] || + Module["canvas"]['webkitRequestFullScreen'] || + (function() {}); + RFS.apply(Module["canvas"], []); + },requestFullScreen:function () { + Module.printErr('GLFW.requestFullScreen() is deprecated. Please call GLFW.requestFullscreen instead.'); + GLFW.requestFullScreen = function() { + return GLFW.requestFullscreen(); + } + return GLFW.requestFullscreen(); + },exitFullscreen:function () { + var CFS = document['exitFullscreen'] || + document['cancelFullScreen'] || + document['mozCancelFullScreen'] || + document['webkitCancelFullScreen'] || + (function() {}); + CFS.apply(document, []); + },cancelFullScreen:function () { + Module.printErr('GLFW.cancelFullScreen() is deprecated. Please call GLFW.exitFullscreen instead.'); + GLFW.cancelFullScreen = function() { + return GLFW.exitFullscreen(); + } + return GLFW.exitFullscreen(); + },getTime:function () { + return _emscripten_get_now() / 1000; + },setWindowTitle:function (winid, title) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + + win.title = Pointer_stringify(title); + if (GLFW.active.id == win.id) { + document.title = win.title; + } + },setJoystickCallback:function (cbfun) { + GLFW.joystickFunc = cbfun; + GLFW.refreshJoysticks(); + },joys:{},lastGamepadState:null,lastGamepadStateFrame:null,refreshJoysticks:function () { + // Produce a new Gamepad API sample if we are ticking a new game frame, or if not using emscripten_set_main_loop() at all to drive animation. + if (Browser.mainLoop.currentFrameNumber !== GLFW.lastGamepadStateFrame || !Browser.mainLoop.currentFrameNumber) { + GLFW.lastGamepadState = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : null); + GLFW.lastGamepadStateFrame = Browser.mainLoop.currentFrameNumber; + + for (var joy = 0; joy < GLFW.lastGamepadState.length; ++joy) { + var gamepad = GLFW.lastGamepadState[joy]; + + if (gamepad) { + if (!GLFW.joys[joy]) { + console.log('glfw joystick connected:',joy); + GLFW.joys[joy] = { + id: allocate(intArrayFromString(gamepad.id), 'i8', ALLOC_NORMAL), + buttonsCount: gamepad.buttons.length, + axesCount: gamepad.axes.length, + buttons: allocate(new Array(gamepad.buttons.length), 'i8', ALLOC_NORMAL), + axes: allocate(new Array(gamepad.axes.length*4), 'float', ALLOC_NORMAL) + }; + + if (GLFW.joystickFunc) { + Module['dynCall_vii'](GLFW.joystickFunc, joy, 0x00040001); // GLFW_CONNECTED + } + } + + var data = GLFW.joys[joy]; + + for (var i = 0; i < gamepad.buttons.length; ++i) { + setValue(data.buttons + i, gamepad.buttons[i].pressed, 'i8'); + } + + for (var i = 0; i < gamepad.axes.length; ++i) { + setValue(data.axes + i*4, gamepad.axes[i], 'float'); + } + } else { + if (GLFW.joys[joy]) { + console.log('glfw joystick disconnected',joy); + + if (GLFW.joystickFunc) { + Module['dynCall_vii'](GLFW.joystickFunc, joy, 0x00040002); // GLFW_DISCONNECTED + } + + _free(GLFW.joys[joy].id); + _free(GLFW.joys[joy].buttons); + _free(GLFW.joys[joy].axes); + + delete GLFW.joys[joy]; + } + } + } + } + },setKeyCallback:function (winid, cbfun) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.keyFunc = cbfun; + },setCharCallback:function (winid, cbfun) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.charFunc = cbfun; + },setMouseButtonCallback:function (winid, cbfun) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.mouseButtonFunc = cbfun; + },setCursorPosCallback:function (winid, cbfun) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.cursorPosFunc = cbfun; + },setScrollCallback:function (winid, cbfun) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.scrollFunc = cbfun; + },setDropCallback:function (winid, cbfun) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.dropFunc = cbfun; + },onDrop:function (event) { + if (!GLFW.active || !GLFW.active.dropFunc) return; + if (!event.dataTransfer || !event.dataTransfer.files || event.dataTransfer.files.length == 0) return; + + event.preventDefault(); + + var filenames = allocate(new Array(event.dataTransfer.files.length*4), 'i8*', ALLOC_NORMAL); + var filenamesArray = []; + var count = event.dataTransfer.files.length; + + // Read and save the files to emscripten's FS + var written = 0; + var drop_dir = '.glfw_dropped_files'; + FS.createPath('/', drop_dir); + + function save(file) { + var path = '/' + drop_dir + '/' + file.name.replace(/\//g, '_'); + var reader = new FileReader(); + reader.onloadend = function(e) { + if (reader.readyState != 2) { // not DONE + ++written; + console.log('failed to read dropped file: '+file.name+': '+reader.error); + return; + } + + var data = e.target.result; + FS.writeFile(path, new Uint8Array(data), { encoding: 'binary' }); + if (++written === count) { + Module['dynCall_viii'](GLFW.active.dropFunc, GLFW.active.id, count, filenames); + + for (var i = 0; i < filenamesArray.length; ++i) { + _free(filenamesArray[i]); + } + _free(filenames); + } + }; + reader.readAsArrayBuffer(file); + + var filename = allocate(intArrayFromString(path), 'i8', ALLOC_NORMAL); + filenamesArray.push(filename); + setValue(filenames + i*4, filename, 'i8*'); + } + + for (var i = 0; i < count; ++i) { + save(event.dataTransfer.files[i]); + } + + return false; + },onDragover:function (event) { + if (!GLFW.active || !GLFW.active.dropFunc) return; + + event.preventDefault(); + return false; + },setWindowSizeCallback:function (winid, cbfun) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.windowSizeFunc = cbfun; + + },setWindowCloseCallback:function (winid, cbfun) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.windowCloseFunc = cbfun; + },setWindowRefreshCallback:function (winid, cbfun) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.windowRefreshFunc = cbfun; + },onClickRequestPointerLock:function (e) { + if (!Browser.pointerLock && Module['canvas'].requestPointerLock) { + Module['canvas'].requestPointerLock(); + e.preventDefault(); + } + },setInputMode:function (winid, mode, value) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + + switch(mode) { + case 0x00033001: { // GLFW_CURSOR + switch(value) { + case 0x00034001: { // GLFW_CURSOR_NORMAL + win.inputModes[mode] = value; + Module['canvas'].removeEventListener('click', GLFW.onClickRequestPointerLock, true); + Module['canvas'].exitPointerLock(); + break; + } + case 0x00034002: { // GLFW_CURSOR_HIDDEN + console.log("glfwSetInputMode called with GLFW_CURSOR_HIDDEN value not implemented."); + break; + } + case 0x00034003: { // GLFW_CURSOR_DISABLED + win.inputModes[mode] = value; + Module['canvas'].addEventListener('click', GLFW.onClickRequestPointerLock, true); + Module['canvas'].requestPointerLock(); + break; + } + default: { + console.log("glfwSetInputMode called with unknown value parameter value: " + value + "."); + break; + } + } + break; + } + case 0x00033002: { // GLFW_STICKY_KEYS + console.log("glfwSetInputMode called with GLFW_STICKY_KEYS mode not implemented."); + break; + } + case 0x00033003: { // GLFW_STICKY_MOUSE_BUTTONS + console.log("glfwSetInputMode called with GLFW_STICKY_MOUSE_BUTTONS mode not implemented."); + break; + } + default: { + console.log("glfwSetInputMode called with unknown mode parameter value: " + mode + "."); + break; + } + } + },getKey:function (winid, key) { + var win = GLFW.WindowFromId(winid); + if (!win) return 0; + return win.keys[key]; + },getMouseButton:function (winid, button) { + var win = GLFW.WindowFromId(winid); + if (!win) return 0; + return (win.buttons & (1 << button)) > 0; + },getCursorPos:function (winid, x, y) { + setValue(x, Browser.mouseX, 'double'); + setValue(y, Browser.mouseY, 'double'); + },getMousePos:function (winid, x, y) { + setValue(x, Browser.mouseX, 'i32'); + setValue(y, Browser.mouseY, 'i32'); + },setCursorPos:function (winid, x, y) { + },getWindowPos:function (winid, x, y) { + var wx = 0; + var wy = 0; + + var win = GLFW.WindowFromId(winid); + if (win) { + wx = win.x; + wy = win.y; + } + + setValue(x, wx, 'i32'); + setValue(y, wy, 'i32'); + },setWindowPos:function (winid, x, y) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + win.x = x; + win.y = y; + },getWindowSize:function (winid, width, height) { + var ww = 0; + var wh = 0; + + var win = GLFW.WindowFromId(winid); + if (win) { + ww = win.width; + wh = win.height; + } + + setValue(width, ww, 'i32'); + setValue(height, wh, 'i32'); + },setWindowSize:function (winid, width, height) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + + if (GLFW.active.id == win.id) { + if (width == screen.width && height == screen.height) { + GLFW.requestFullscreen(); + } else { + GLFW.exitFullscreen(); + Browser.setCanvasSize(width, height); + win.width = width; + win.height = height; + } + } + + if (!win.windowSizeFunc) return; + + + Module['dynCall_viii'](win.windowSizeFunc, win.id, width, height); + },createWindow:function (width, height, title, monitor, share) { + var i, id; + for (i = 0; i < GLFW.windows.length && GLFW.windows[i] !== null; i++); + if (i > 0) throw "glfwCreateWindow only supports one window at time currently"; + + // id for window + id = i + 1; + + // not valid + if (width <= 0 || height <= 0) return 0; + + if (monitor) { + GLFW.requestFullscreen(); + } else { + Browser.setCanvasSize(width, height); + } + + // Create context when there are no existing alive windows + for (i = 0; i < GLFW.windows.length && GLFW.windows[i] == null; i++); + if (i == GLFW.windows.length) { + var contextAttributes = { + antialias: (GLFW.hints[0x0002100D] > 1), // GLFW_SAMPLES + depth: (GLFW.hints[0x00021005] > 0), // GLFW_DEPTH_BITS + stencil: (GLFW.hints[0x00021006] > 0), // GLFW_STENCIL_BITS + alpha: (GLFW.hints[0x00021004] > 0) // GLFW_ALPHA_BITS + } + Module.ctx = Browser.createContext(Module['canvas'], true, true, contextAttributes); + } + + // If context creation failed, do not return a valid window + if (!Module.ctx) return 0; + + // Get non alive id + var win = new GLFW.Window(id, width, height, title, monitor, share); + + // Set window to array + if (id - 1 == GLFW.windows.length) { + GLFW.windows.push(win); + } else { + GLFW.windows[id - 1] = win; + } + + GLFW.active = win; + return win.id; + },destroyWindow:function (winid) { + var win = GLFW.WindowFromId(winid); + if (!win) return; + + if (win.windowCloseFunc) + func(arg); + + GLFW.windows[win.id - 1] = null; + if (GLFW.active.id == win.id) + GLFW.active = null; + + // Destroy context when no alive windows + for (var i = 0; i < GLFW.windows.length; i++) + if (GLFW.windows[i] !== null) return; + + Module.ctx = Browser.destroyContext(Module['canvas'], true, true); + },swapBuffers:function (winid) { + },GLFW2ParamToGLFW3Param:function (param) { + table = { + 0x00030001:0, // GLFW_MOUSE_CURSOR + 0x00030002:0, // GLFW_STICKY_KEYS + 0x00030003:0, // GLFW_STICKY_MOUSE_BUTTONS + 0x00030004:0, // GLFW_SYSTEM_KEYS + 0x00030005:0, // GLFW_KEY_REPEAT + 0x00030006:0, // GLFW_AUTO_POLL_EVENTS + 0x00020001:0, // GLFW_OPENED + 0x00020002:0, // GLFW_ACTIVE + 0x00020003:0, // GLFW_ICONIFIED + 0x00020004:0, // GLFW_ACCELERATED + 0x00020005:0x00021001, // GLFW_RED_BITS + 0x00020006:0x00021002, // GLFW_GREEN_BITS + 0x00020007:0x00021003, // GLFW_BLUE_BITS + 0x00020008:0x00021004, // GLFW_ALPHA_BITS + 0x00020009:0x00021005, // GLFW_DEPTH_BITS + 0x0002000A:0x00021006, // GLFW_STENCIL_BITS + 0x0002000B:0x0002100F, // GLFW_REFRESH_RATE + 0x0002000C:0x00021007, // GLFW_ACCUM_RED_BITS + 0x0002000D:0x00021008, // GLFW_ACCUM_GREEN_BITS + 0x0002000E:0x00021009, // GLFW_ACCUM_BLUE_BITS + 0x0002000F:0x0002100A, // GLFW_ACCUM_ALPHA_BITS + 0x00020010:0x0002100B, // GLFW_AUX_BUFFERS + 0x00020011:0x0002100C, // GLFW_STEREO + 0x00020012:0, // GLFW_WINDOW_NO_RESIZE + 0x00020013:0x0002100D, // GLFW_FSAA_SAMPLES + 0x00020014:0x00022002, // GLFW_OPENGL_VERSION_MAJOR + 0x00020015:0x00022003, // GLFW_OPENGL_VERSION_MINOR + 0x00020016:0x00022006, // GLFW_OPENGL_FORWARD_COMPAT + 0x00020017:0x00022007, // GLFW_OPENGL_DEBUG_CONTEXT + 0x00020018:0x00022008, // GLFW_OPENGL_PROFILE + }; + return table[param]; + }};function _glfwGetVideoModes(monitor, count) { + setValue(count, 0, 'i32'); + return 0; + } + + function _glLinkProgram(program) { + GLctx.linkProgram(GL.programs[program]); + GL.programInfos[program] = null; // uniforms no longer keep the same names after linking + GL.populateUniformTable(program); + } + + function _glBindTexture(target, texture) { + GLctx.bindTexture(target, texture ? GL.textures[texture] : null); + } + + function _emscripten_glStencilFunc(x0, x1, x2) { GLctx['stencilFunc'](x0, x1, x2) } + + function _glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer) { + GLctx.framebufferRenderbuffer(target, attachment, renderbuffertarget, + GL.renderbuffers[renderbuffer]); + } + + function _glGetString(name_) { + if (GL.stringCache[name_]) return GL.stringCache[name_]; + var ret; + switch(name_) { + case 0x1F00 /* GL_VENDOR */: + case 0x1F01 /* GL_RENDERER */: + case 0x9245 /* UNMASKED_VENDOR_WEBGL */: + case 0x9246 /* UNMASKED_RENDERER_WEBGL */: + ret = allocate(intArrayFromString(GLctx.getParameter(name_)), 'i8', ALLOC_NORMAL); + break; + case 0x1F02 /* GL_VERSION */: + var glVersion = GLctx.getParameter(GLctx.VERSION); + // return GLES version string corresponding to the version of the WebGL context + { + glVersion = 'OpenGL ES 2.0 (' + glVersion + ')'; + } + ret = allocate(intArrayFromString(glVersion), 'i8', ALLOC_NORMAL); + break; + case 0x1F03 /* GL_EXTENSIONS */: + var exts = GLctx.getSupportedExtensions(); + var gl_exts = []; + for (var i = 0; i < exts.length; ++i) { + gl_exts.push(exts[i]); + gl_exts.push("GL_" + exts[i]); + } + ret = allocate(intArrayFromString(gl_exts.join(' ')), 'i8', ALLOC_NORMAL); + break; + case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */: + var glslVersion = GLctx.getParameter(GLctx.SHADING_LANGUAGE_VERSION); + // extract the version number 'N.M' from the string 'WebGL GLSL ES N.M ...' + var ver_re = /^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/; + var ver_num = glslVersion.match(ver_re); + if (ver_num !== null) { + if (ver_num[1].length == 3) ver_num[1] = ver_num[1] + '0'; // ensure minor version has 2 digits + glslVersion = 'OpenGL ES GLSL ES ' + ver_num[1] + ' (' + glslVersion + ')'; + } + ret = allocate(intArrayFromString(glslVersion), 'i8', ALLOC_NORMAL); + break; + default: + GL.recordError(0x0500/*GL_INVALID_ENUM*/); + return 0; + } + GL.stringCache[name_] = ret; + return ret; + } + + function _emscripten_glUniform3iv(location, count, value) { + + + GLctx.uniform3iv(GL.uniforms[location], HEAP32.subarray((value)>>2,(value+count*12)>>2)); + } + + function _glDetachShader(program, shader) { + GLctx.detachShader(GL.programs[program], + GL.shaders[shader]); + } + + function _pthread_mutex_init() {} + + function _emscripten_glReleaseShaderCompiler() { + // NOP (as allowed by GLES 2.0 spec) + } + + function _glfwSetScrollCallback(winid, cbfun) { + GLFW.setScrollCallback(winid, cbfun); + } + + function _emscripten_glTexParameterf(x0, x1, x2) { GLctx['texParameterf'](x0, x1, x2) } + + function _emscripten_glTexParameteri(x0, x1, x2) { GLctx['texParameteri'](x0, x1, x2) } + + function _glCompileShader(shader) { + GLctx.compileShader(GL.shaders[shader]); + } + + + + + var ERRNO_CODES={EPERM:1,ENOENT:2,ESRCH:3,EINTR:4,EIO:5,ENXIO:6,E2BIG:7,ENOEXEC:8,EBADF:9,ECHILD:10,EAGAIN:11,EWOULDBLOCK:11,ENOMEM:12,EACCES:13,EFAULT:14,ENOTBLK:15,EBUSY:16,EEXIST:17,EXDEV:18,ENODEV:19,ENOTDIR:20,EISDIR:21,EINVAL:22,ENFILE:23,EMFILE:24,ENOTTY:25,ETXTBSY:26,EFBIG:27,ENOSPC:28,ESPIPE:29,EROFS:30,EMLINK:31,EPIPE:32,EDOM:33,ERANGE:34,ENOMSG:42,EIDRM:43,ECHRNG:44,EL2NSYNC:45,EL3HLT:46,EL3RST:47,ELNRNG:48,EUNATCH:49,ENOCSI:50,EL2HLT:51,EDEADLK:35,ENOLCK:37,EBADE:52,EBADR:53,EXFULL:54,ENOANO:55,EBADRQC:56,EBADSLT:57,EDEADLOCK:35,EBFONT:59,ENOSTR:60,ENODATA:61,ETIME:62,ENOSR:63,ENONET:64,ENOPKG:65,EREMOTE:66,ENOLINK:67,EADV:68,ESRMNT:69,ECOMM:70,EPROTO:71,EMULTIHOP:72,EDOTDOT:73,EBADMSG:74,ENOTUNIQ:76,EBADFD:77,EREMCHG:78,ELIBACC:79,ELIBBAD:80,ELIBSCN:81,ELIBMAX:82,ELIBEXEC:83,ENOSYS:38,ENOTEMPTY:39,ENAMETOOLONG:36,ELOOP:40,EOPNOTSUPP:95,EPFNOSUPPORT:96,ECONNRESET:104,ENOBUFS:105,EAFNOSUPPORT:97,EPROTOTYPE:91,ENOTSOCK:88,ENOPROTOOPT:92,ESHUTDOWN:108,ECONNREFUSED:111,EADDRINUSE:98,ECONNABORTED:103,ENETUNREACH:101,ENETDOWN:100,ETIMEDOUT:110,EHOSTDOWN:112,EHOSTUNREACH:113,EINPROGRESS:115,EALREADY:114,EDESTADDRREQ:89,EMSGSIZE:90,EPROTONOSUPPORT:93,ESOCKTNOSUPPORT:94,EADDRNOTAVAIL:99,ENETRESET:102,EISCONN:106,ENOTCONN:107,ETOOMANYREFS:109,EUSERS:87,EDQUOT:122,ESTALE:116,ENOTSUP:95,ENOMEDIUM:123,EILSEQ:84,EOVERFLOW:75,ECANCELED:125,ENOTRECOVERABLE:131,EOWNERDEAD:130,ESTRPIPE:86}; + + var ERRNO_MESSAGES={0:"Success",1:"Not super-user",2:"No such file or directory",3:"No such process",4:"Interrupted system call",5:"I/O error",6:"No such device or address",7:"Arg list too long",8:"Exec format error",9:"Bad file number",10:"No children",11:"No more processes",12:"Not enough core",13:"Permission denied",14:"Bad address",15:"Block device required",16:"Mount device busy",17:"File exists",18:"Cross-device link",19:"No such device",20:"Not a directory",21:"Is a directory",22:"Invalid argument",23:"Too many open files in system",24:"Too many open files",25:"Not a typewriter",26:"Text file busy",27:"File too large",28:"No space left on device",29:"Illegal seek",30:"Read only file system",31:"Too many links",32:"Broken pipe",33:"Math arg out of domain of func",34:"Math result not representable",35:"File locking deadlock error",36:"File or path name too long",37:"No record locks available",38:"Function not implemented",39:"Directory not empty",40:"Too many symbolic links",42:"No message of desired type",43:"Identifier removed",44:"Channel number out of range",45:"Level 2 not synchronized",46:"Level 3 halted",47:"Level 3 reset",48:"Link number out of range",49:"Protocol driver not attached",50:"No CSI structure available",51:"Level 2 halted",52:"Invalid exchange",53:"Invalid request descriptor",54:"Exchange full",55:"No anode",56:"Invalid request code",57:"Invalid slot",59:"Bad font file fmt",60:"Device not a stream",61:"No data (for no delay io)",62:"Timer expired",63:"Out of streams resources",64:"Machine is not on the network",65:"Package not installed",66:"The object is remote",67:"The link has been severed",68:"Advertise error",69:"Srmount error",70:"Communication error on send",71:"Protocol error",72:"Multihop attempted",73:"Cross mount point (not really error)",74:"Trying to read unreadable message",75:"Value too large for defined data type",76:"Given log. name not unique",77:"f.d. invalid for this operation",78:"Remote address changed",79:"Can access a needed shared lib",80:"Accessing a corrupted shared lib",81:".lib section in a.out corrupted",82:"Attempting to link in too many libs",83:"Attempting to exec a shared library",84:"Illegal byte sequence",86:"Streams pipe error",87:"Too many users",88:"Socket operation on non-socket",89:"Destination address required",90:"Message too long",91:"Protocol wrong type for socket",92:"Protocol not available",93:"Unknown protocol",94:"Socket type not supported",95:"Not supported",96:"Protocol family not supported",97:"Address family not supported by protocol family",98:"Address already in use",99:"Address not available",100:"Network interface is not configured",101:"Network is unreachable",102:"Connection reset by network",103:"Connection aborted",104:"Connection reset by peer",105:"No buffer space available",106:"Socket is already connected",107:"Socket is not connected",108:"Can't send after socket shutdown",109:"Too many references",110:"Connection timed out",111:"Connection refused",112:"Host is down",113:"Host is unreachable",114:"Socket already connected",115:"Connection already in progress",116:"Stale file handle",122:"Quota exceeded",123:"No medium (in tape drive)",125:"Operation canceled",130:"Previous owner died",131:"State not recoverable"}; + + function ___setErrNo(value) { + if (Module['___errno_location']) HEAP32[((Module['___errno_location']())>>2)]=value; + else Module.printErr('failed to set errno from JS'); + return value; + } + + var PATH={splitPath:function (filename) { + var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; + return splitPathRe.exec(filename).slice(1); + },normalizeArray:function (parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up; up--) { + parts.unshift('..'); + } + } + return parts; + },normalize:function (path) { + var isAbsolute = path.charAt(0) === '/', + trailingSlash = path.substr(-1) === '/'; + // Normalize the path + path = PATH.normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), !isAbsolute).join('/'); + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + return (isAbsolute ? '/' : '') + path; + },dirname:function (path) { + var result = PATH.splitPath(path), + root = result[0], + dir = result[1]; + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + return root + dir; + },basename:function (path) { + // EMSCRIPTEN return '/'' for '/', not an empty string + if (path === '/') return '/'; + var lastSlash = path.lastIndexOf('/'); + if (lastSlash === -1) return path; + return path.substr(lastSlash+1); + },extname:function (path) { + return PATH.splitPath(path)[3]; + },join:function () { + var paths = Array.prototype.slice.call(arguments, 0); + return PATH.normalize(paths.join('/')); + },join2:function (l, r) { + return PATH.normalize(l + '/' + r); + },resolve:function () { + var resolvedPath = '', + resolvedAbsolute = false; + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : FS.cwd(); + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + return ''; // an invalid portion invalidates the whole thing + } + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + resolvedPath = PATH.normalizeArray(resolvedPath.split('/').filter(function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; + },relative:function (from, to) { + from = PATH.resolve(from).substr(1); + to = PATH.resolve(to).substr(1); + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + return outputParts.join('/'); + }}; + + var TTY={ttys:[],init:function () { + // https://github.com/kripken/emscripten/pull/1555 + // if (ENVIRONMENT_IS_NODE) { + // // currently, FS.init does not distinguish if process.stdin is a file or TTY + // // device, it always assumes it's a TTY device. because of this, we're forcing + // // process.stdin to UTF8 encoding to at least make stdin reading compatible + // // with text files until FS.init can be refactored. + // process['stdin']['setEncoding']('utf8'); + // } + },shutdown:function () { + // https://github.com/kripken/emscripten/pull/1555 + // if (ENVIRONMENT_IS_NODE) { + // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)? + // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation + // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists? + // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle + // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call + // process['stdin']['pause'](); + // } + },register:function (dev, ops) { + TTY.ttys[dev] = { input: [], output: [], ops: ops }; + FS.registerDevice(dev, TTY.stream_ops); + },stream_ops:{open:function (stream) { + var tty = TTY.ttys[stream.node.rdev]; + if (!tty) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + stream.tty = tty; + stream.seekable = false; + },close:function (stream) { + // flush any pending line data + stream.tty.ops.flush(stream.tty); + },flush:function (stream) { + stream.tty.ops.flush(stream.tty); + },read:function (stream, buffer, offset, length, pos /* ignored */) { + if (!stream.tty || !stream.tty.ops.get_char) { + throw new FS.ErrnoError(ERRNO_CODES.ENXIO); + } + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = stream.tty.ops.get_char(stream.tty); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(ERRNO_CODES.EAGAIN); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset+i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + },write:function (stream, buffer, offset, length, pos) { + if (!stream.tty || !stream.tty.ops.put_char) { + throw new FS.ErrnoError(ERRNO_CODES.ENXIO); + } + for (var i = 0; i < length; i++) { + try { + stream.tty.ops.put_char(stream.tty, buffer[offset+i]); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + }},default_tty_ops:{get_char:function (tty) { + if (!tty.input.length) { + var result = null; + if (ENVIRONMENT_IS_NODE) { + // we will read data by chunks of BUFSIZE + var BUFSIZE = 256; + var buf = new Buffer(BUFSIZE); + var bytesRead = 0; + + var isPosixPlatform = (process.platform != 'win32'); // Node doesn't offer a direct check, so test by exclusion + + var fd = process.stdin.fd; + if (isPosixPlatform) { + // Linux and Mac cannot use process.stdin.fd (which isn't set up as sync) + var usingDevice = false; + try { + fd = fs.openSync('/dev/stdin', 'r'); + usingDevice = true; + } catch (e) {} + } + + try { + bytesRead = fs.readSync(fd, buf, 0, BUFSIZE, null); + } catch(e) { + // Cross-platform differences: on Windows, reading EOF throws an exception, but on other OSes, + // reading EOF returns 0. Uniformize behavior by treating the EOF exception to return 0. + if (e.toString().indexOf('EOF') != -1) bytesRead = 0; + else throw e; + } + + if (usingDevice) { fs.closeSync(fd); } + if (bytesRead > 0) { + result = buf.slice(0, bytesRead).toString('utf-8'); + } else { + result = null; + } + + } else if (typeof window != 'undefined' && + typeof window.prompt == 'function') { + // Browser. + result = window.prompt('Input: '); // returns null on cancel + if (result !== null) { + result += '\n'; + } + } else if (typeof readline == 'function') { + // Command line. + result = readline(); + if (result !== null) { + result += '\n'; + } + } + if (!result) { + return null; + } + tty.input = intArrayFromString(result, true); + } + return tty.input.shift(); + },put_char:function (tty, val) { + if (val === null || val === 10) { + Module['print'](UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } else { + if (val != 0) tty.output.push(val); // val == 0 would cut text output off in the middle. + } + },flush:function (tty) { + if (tty.output && tty.output.length > 0) { + Module['print'](UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } + }},default_tty1_ops:{put_char:function (tty, val) { + if (val === null || val === 10) { + Module['printErr'](UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } else { + if (val != 0) tty.output.push(val); + } + },flush:function (tty) { + if (tty.output && tty.output.length > 0) { + Module['printErr'](UTF8ArrayToString(tty.output, 0)); + tty.output = []; + } + }}}; + + var MEMFS={ops_table:null,mount:function (mount) { + return MEMFS.createNode(null, '/', 16384 | 511 /* 0777 */, 0); + },createNode:function (parent, name, mode, dev) { + if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { + // no supported + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (!MEMFS.ops_table) { + MEMFS.ops_table = { + dir: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + lookup: MEMFS.node_ops.lookup, + mknod: MEMFS.node_ops.mknod, + rename: MEMFS.node_ops.rename, + unlink: MEMFS.node_ops.unlink, + rmdir: MEMFS.node_ops.rmdir, + readdir: MEMFS.node_ops.readdir, + symlink: MEMFS.node_ops.symlink + }, + stream: { + llseek: MEMFS.stream_ops.llseek + } + }, + file: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: { + llseek: MEMFS.stream_ops.llseek, + read: MEMFS.stream_ops.read, + write: MEMFS.stream_ops.write, + allocate: MEMFS.stream_ops.allocate, + mmap: MEMFS.stream_ops.mmap, + msync: MEMFS.stream_ops.msync + } + }, + link: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + readlink: MEMFS.node_ops.readlink + }, + stream: {} + }, + chrdev: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: FS.chrdev_stream_ops + } + }; + } + var node = FS.createNode(parent, name, mode, dev); + if (FS.isDir(node.mode)) { + node.node_ops = MEMFS.ops_table.dir.node; + node.stream_ops = MEMFS.ops_table.dir.stream; + node.contents = {}; + } else if (FS.isFile(node.mode)) { + node.node_ops = MEMFS.ops_table.file.node; + node.stream_ops = MEMFS.ops_table.file.stream; + node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. + // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred + // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size + // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme. + node.contents = null; + } else if (FS.isLink(node.mode)) { + node.node_ops = MEMFS.ops_table.link.node; + node.stream_ops = MEMFS.ops_table.link.stream; + } else if (FS.isChrdev(node.mode)) { + node.node_ops = MEMFS.ops_table.chrdev.node; + node.stream_ops = MEMFS.ops_table.chrdev.stream; + } + node.timestamp = Date.now(); + // add the new node to the parent + if (parent) { + parent.contents[name] = node; + } + return node; + },getFileDataAsRegularArray:function (node) { + if (node.contents && node.contents.subarray) { + var arr = []; + for (var i = 0; i < node.usedBytes; ++i) arr.push(node.contents[i]); + return arr; // Returns a copy of the original data. + } + return node.contents; // No-op, the file contents are already in a JS array. Return as-is. + },getFileDataAsTypedArray:function (node) { + if (!node.contents) return new Uint8Array; + if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. + return new Uint8Array(node.contents); + },expandFileStorage:function (node, newCapacity) { + // If we are asked to expand the size of a file that already exists, revert to using a standard JS array to store the file + // instead of a typed array. This makes resizing the array more flexible because we can just .push() elements at the back to + // increase the size. + if (node.contents && node.contents.subarray && newCapacity > node.contents.length) { + node.contents = MEMFS.getFileDataAsRegularArray(node); + node.usedBytes = node.contents.length; // We might be writing to a lazy-loaded file which had overridden this property, so force-reset it. + } + + if (!node.contents || node.contents.subarray) { // Keep using a typed array if creating a new storage, or if old one was a typed array as well. + var prevCapacity = node.contents ? node.contents.length : 0; + if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough. + // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity. + // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to + // avoid overshooting the allocation cap by a very large margin. + var CAPACITY_DOUBLING_MAX = 1024 * 1024; + newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) | 0); + if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. + var oldContents = node.contents; + node.contents = new Uint8Array(newCapacity); // Allocate new storage. + if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage. + return; + } + // Not using a typed array to back the file storage. Use a standard JS array instead. + if (!node.contents && newCapacity > 0) node.contents = []; + while (node.contents.length < newCapacity) node.contents.push(0); + },resizeFileStorage:function (node, newSize) { + if (node.usedBytes == newSize) return; + if (newSize == 0) { + node.contents = null; // Fully decommit when requesting a resize to zero. + node.usedBytes = 0; + return; + } + if (!node.contents || node.contents.subarray) { // Resize a typed array if that is being used as the backing store. + var oldContents = node.contents; + node.contents = new Uint8Array(new ArrayBuffer(newSize)); // Allocate new storage. + if (oldContents) { + node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. + } + node.usedBytes = newSize; + return; + } + // Backing with a JS array. + if (!node.contents) node.contents = []; + if (node.contents.length > newSize) node.contents.length = newSize; + else while (node.contents.length < newSize) node.contents.push(0); + node.usedBytes = newSize; + },node_ops:{getattr:function (node) { + var attr = {}; + // device numbers reuse inode numbers. + attr.dev = FS.isChrdev(node.mode) ? node.id : 1; + attr.ino = node.id; + attr.mode = node.mode; + attr.nlink = 1; + attr.uid = 0; + attr.gid = 0; + attr.rdev = node.rdev; + if (FS.isDir(node.mode)) { + attr.size = 4096; + } else if (FS.isFile(node.mode)) { + attr.size = node.usedBytes; + } else if (FS.isLink(node.mode)) { + attr.size = node.link.length; + } else { + attr.size = 0; + } + attr.atime = new Date(node.timestamp); + attr.mtime = new Date(node.timestamp); + attr.ctime = new Date(node.timestamp); + // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), + // but this is not required by the standard. + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + },setattr:function (node, attr) { + if (attr.mode !== undefined) { + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + node.timestamp = attr.timestamp; + } + if (attr.size !== undefined) { + MEMFS.resizeFileStorage(node, attr.size); + } + },lookup:function (parent, name) { + throw FS.genericErrors[ERRNO_CODES.ENOENT]; + },mknod:function (parent, name, mode, dev) { + return MEMFS.createNode(parent, name, mode, dev); + },rename:function (old_node, new_dir, new_name) { + // if we're overwriting a directory at new_name, make sure it's empty. + if (FS.isDir(old_node.mode)) { + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) { + } + if (new_node) { + for (var i in new_node.contents) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); + } + } + } + // do the internal rewiring + delete old_node.parent.contents[old_node.name]; + old_node.name = new_name; + new_dir.contents[new_name] = old_node; + old_node.parent = new_dir; + },unlink:function (parent, name) { + delete parent.contents[name]; + },rmdir:function (parent, name) { + var node = FS.lookupNode(parent, name); + for (var i in node.contents) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); + } + delete parent.contents[name]; + },readdir:function (node) { + var entries = ['.', '..'] + for (var key in node.contents) { + if (!node.contents.hasOwnProperty(key)) { + continue; + } + entries.push(key); + } + return entries; + },symlink:function (parent, newname, oldpath) { + var node = MEMFS.createNode(parent, newname, 511 /* 0777 */ | 40960, 0); + node.link = oldpath; + return node; + },readlink:function (node) { + if (!FS.isLink(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + return node.link; + }},stream_ops:{read:function (stream, buffer, offset, length, position) { + var contents = stream.node.contents; + if (position >= stream.node.usedBytes) return 0; + var size = Math.min(stream.node.usedBytes - position, length); + assert(size >= 0); + if (size > 8 && contents.subarray) { // non-trivial, and typed array + buffer.set(contents.subarray(position, position + size), offset); + } else { + for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i]; + } + return size; + },write:function (stream, buffer, offset, length, position, canOwn) { + if (!length) return 0; + var node = stream.node; + node.timestamp = Date.now(); + + if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array? + if (canOwn) { + assert(position === 0, 'canOwn must imply no weird position inside the file'); + node.contents = buffer.subarray(offset, offset + length); + node.usedBytes = length; + return length; + } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. + node.contents = new Uint8Array(buffer.subarray(offset, offset + length)); + node.usedBytes = length; + return length; + } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file? + node.contents.set(buffer.subarray(offset, offset + length), position); + return length; + } + } + + // Appending to an existing file and we need to reallocate, or source data did not come as a typed array. + MEMFS.expandFileStorage(node, position+length); + if (node.contents.subarray && buffer.subarray) node.contents.set(buffer.subarray(offset, offset + length), position); // Use typed array write if available. + else { + for (var i = 0; i < length; i++) { + node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not. + } + } + node.usedBytes = Math.max(node.usedBytes, position+length); + return length; + },llseek:function (stream, offset, whence) { + var position = offset; + if (whence === 1) { // SEEK_CUR. + position += stream.position; + } else if (whence === 2) { // SEEK_END. + if (FS.isFile(stream.node.mode)) { + position += stream.node.usedBytes; + } + } + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + return position; + },allocate:function (stream, offset, length) { + MEMFS.expandFileStorage(stream.node, offset + length); + stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length); + },mmap:function (stream, buffer, offset, length, position, prot, flags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + var ptr; + var allocated; + var contents = stream.node.contents; + // Only make a new copy when MAP_PRIVATE is specified. + if ( !(flags & 2) && + (contents.buffer === buffer || contents.buffer === buffer.buffer) ) { + // We can't emulate MAP_SHARED when the file is not backed by the buffer + // we're mapping to (e.g. the HEAP buffer). + allocated = false; + ptr = contents.byteOffset; + } else { + // Try to avoid unnecessary slices. + if (position > 0 || position + length < stream.node.usedBytes) { + if (contents.subarray) { + contents = contents.subarray(position, position + length); + } else { + contents = Array.prototype.slice.call(contents, position, position + length); + } + } + allocated = true; + ptr = _malloc(length); + if (!ptr) { + throw new FS.ErrnoError(ERRNO_CODES.ENOMEM); + } + buffer.set(contents, ptr); + } + return { ptr: ptr, allocated: allocated }; + },msync:function (stream, buffer, offset, length, mmapFlags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + if (mmapFlags & 2) { + // MAP_PRIVATE calls need not to be synced back to underlying fs + return 0; + } + + var bytesWritten = MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); + // should we check if bytesWritten and length are the same? + return 0; + }}}; + + var IDBFS={dbs:{},indexedDB:function () { + if (typeof indexedDB !== 'undefined') return indexedDB; + var ret = null; + if (typeof window === 'object') ret = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + assert(ret, 'IDBFS used, but indexedDB not supported'); + return ret; + },DB_VERSION:21,DB_STORE_NAME:"FILE_DATA",mount:function (mount) { + // reuse all of the core MEMFS functionality + return MEMFS.mount.apply(null, arguments); + },syncfs:function (mount, populate, callback) { + IDBFS.getLocalSet(mount, function(err, local) { + if (err) return callback(err); + + IDBFS.getRemoteSet(mount, function(err, remote) { + if (err) return callback(err); + + var src = populate ? remote : local; + var dst = populate ? local : remote; + + IDBFS.reconcile(src, dst, callback); + }); + }); + },getDB:function (name, callback) { + // check the cache first + var db = IDBFS.dbs[name]; + if (db) { + return callback(null, db); + } + + var req; + try { + req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION); + } catch (e) { + return callback(e); + } + if (!req) { + return callback("Unable to connect to IndexedDB"); + } + req.onupgradeneeded = function(e) { + var db = e.target.result; + var transaction = e.target.transaction; + + var fileStore; + + if (db.objectStoreNames.contains(IDBFS.DB_STORE_NAME)) { + fileStore = transaction.objectStore(IDBFS.DB_STORE_NAME); + } else { + fileStore = db.createObjectStore(IDBFS.DB_STORE_NAME); + } + + if (!fileStore.indexNames.contains('timestamp')) { + fileStore.createIndex('timestamp', 'timestamp', { unique: false }); + } + }; + req.onsuccess = function() { + db = req.result; + + // add to the cache + IDBFS.dbs[name] = db; + callback(null, db); + }; + req.onerror = function(e) { + callback(this.error); + e.preventDefault(); + }; + },getLocalSet:function (mount, callback) { + var entries = {}; + + function isRealDir(p) { + return p !== '.' && p !== '..'; + }; + function toAbsolute(root) { + return function(p) { + return PATH.join2(root, p); + } + }; + + var check = FS.readdir(mount.mountpoint).filter(isRealDir).map(toAbsolute(mount.mountpoint)); + + while (check.length) { + var path = check.pop(); + var stat; + + try { + stat = FS.stat(path); + } catch (e) { + return callback(e); + } + + if (FS.isDir(stat.mode)) { + check.push.apply(check, FS.readdir(path).filter(isRealDir).map(toAbsolute(path))); + } + + entries[path] = { timestamp: stat.mtime }; + } + + return callback(null, { type: 'local', entries: entries }); + },getRemoteSet:function (mount, callback) { + var entries = {}; + + IDBFS.getDB(mount.mountpoint, function(err, db) { + if (err) return callback(err); + + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly'); + transaction.onerror = function(e) { + callback(this.error); + e.preventDefault(); + }; + + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + var index = store.index('timestamp'); + + index.openKeyCursor().onsuccess = function(event) { + var cursor = event.target.result; + + if (!cursor) { + return callback(null, { type: 'remote', db: db, entries: entries }); + } + + entries[cursor.primaryKey] = { timestamp: cursor.key }; + + cursor.continue(); + }; + }); + },loadLocalEntry:function (path, callback) { + var stat, node; + + try { + var lookup = FS.lookupPath(path); + node = lookup.node; + stat = FS.stat(path); + } catch (e) { + return callback(e); + } + + if (FS.isDir(stat.mode)) { + return callback(null, { timestamp: stat.mtime, mode: stat.mode }); + } else if (FS.isFile(stat.mode)) { + // Performance consideration: storing a normal JavaScript array to a IndexedDB is much slower than storing a typed array. + // Therefore always convert the file contents to a typed array first before writing the data to IndexedDB. + node.contents = MEMFS.getFileDataAsTypedArray(node); + return callback(null, { timestamp: stat.mtime, mode: stat.mode, contents: node.contents }); + } else { + return callback(new Error('node type not supported')); + } + },storeLocalEntry:function (path, entry, callback) { + try { + if (FS.isDir(entry.mode)) { + FS.mkdir(path, entry.mode); + } else if (FS.isFile(entry.mode)) { + FS.writeFile(path, entry.contents, { encoding: 'binary', canOwn: true }); + } else { + return callback(new Error('node type not supported')); + } + + FS.chmod(path, entry.mode); + FS.utime(path, entry.timestamp, entry.timestamp); + } catch (e) { + return callback(e); + } + + callback(null); + },removeLocalEntry:function (path, callback) { + try { + var lookup = FS.lookupPath(path); + var stat = FS.stat(path); + + if (FS.isDir(stat.mode)) { + FS.rmdir(path); + } else if (FS.isFile(stat.mode)) { + FS.unlink(path); + } + } catch (e) { + return callback(e); + } + + callback(null); + },loadRemoteEntry:function (store, path, callback) { + var req = store.get(path); + req.onsuccess = function(event) { callback(null, event.target.result); }; + req.onerror = function(e) { + callback(this.error); + e.preventDefault(); + }; + },storeRemoteEntry:function (store, path, entry, callback) { + var req = store.put(entry, path); + req.onsuccess = function() { callback(null); }; + req.onerror = function(e) { + callback(this.error); + e.preventDefault(); + }; + },removeRemoteEntry:function (store, path, callback) { + var req = store.delete(path); + req.onsuccess = function() { callback(null); }; + req.onerror = function(e) { + callback(this.error); + e.preventDefault(); + }; + },reconcile:function (src, dst, callback) { + var total = 0; + + var create = []; + Object.keys(src.entries).forEach(function (key) { + var e = src.entries[key]; + var e2 = dst.entries[key]; + if (!e2 || e.timestamp > e2.timestamp) { + create.push(key); + total++; + } + }); + + var remove = []; + Object.keys(dst.entries).forEach(function (key) { + var e = dst.entries[key]; + var e2 = src.entries[key]; + if (!e2) { + remove.push(key); + total++; + } + }); + + if (!total) { + return callback(null); + } + + var errored = false; + var completed = 0; + var db = src.type === 'remote' ? src.db : dst.db; + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite'); + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + + function done(err) { + if (err) { + if (!done.errored) { + done.errored = true; + return callback(err); + } + return; + } + if (++completed >= total) { + return callback(null); + } + }; + + transaction.onerror = function(e) { + done(this.error); + e.preventDefault(); + }; + + // sort paths in ascending order so directory entries are created + // before the files inside them + create.sort().forEach(function (path) { + if (dst.type === 'local') { + IDBFS.loadRemoteEntry(store, path, function (err, entry) { + if (err) return done(err); + IDBFS.storeLocalEntry(path, entry, done); + }); + } else { + IDBFS.loadLocalEntry(path, function (err, entry) { + if (err) return done(err); + IDBFS.storeRemoteEntry(store, path, entry, done); + }); + } + }); + + // sort paths in descending order so files are deleted before their + // parent directories + remove.sort().reverse().forEach(function(path) { + if (dst.type === 'local') { + IDBFS.removeLocalEntry(path, done); + } else { + IDBFS.removeRemoteEntry(store, path, done); + } + }); + }}; + + var NODEFS={isWindows:false,staticInit:function () { + NODEFS.isWindows = !!process.platform.match(/^win/); + },mount:function (mount) { + assert(ENVIRONMENT_IS_NODE); + return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0); + },createNode:function (parent, name, mode, dev) { + if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var node = FS.createNode(parent, name, mode); + node.node_ops = NODEFS.node_ops; + node.stream_ops = NODEFS.stream_ops; + return node; + },getMode:function (path) { + var stat; + try { + stat = fs.lstatSync(path); + if (NODEFS.isWindows) { + // On Windows, directories return permission bits 'rw-rw-rw-', even though they have 'rwxrwxrwx', so + // propagate write bits to execute bits. + stat.mode = stat.mode | ((stat.mode & 146) >> 1); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return stat.mode; + },realPath:function (node) { + var parts = []; + while (node.parent !== node) { + parts.push(node.name); + node = node.parent; + } + parts.push(node.mount.opts.root); + parts.reverse(); + return PATH.join.apply(null, parts); + },flagsToPermissionStringMap:{0:"r",1:"r+",2:"r+",64:"r",65:"r+",66:"r+",129:"rx+",193:"rx+",514:"w+",577:"w",578:"w+",705:"wx",706:"wx+",1024:"a",1025:"a",1026:"a+",1089:"a",1090:"a+",1153:"ax",1154:"ax+",1217:"ax",1218:"ax+",4096:"rs",4098:"rs+"},flagsToPermissionString:function (flags) { + flags &= ~0x200000 /*O_PATH*/; // Ignore this flag from musl, otherwise node.js fails to open the file. + flags &= ~0x800 /*O_NONBLOCK*/; // Ignore this flag from musl, otherwise node.js fails to open the file. + flags &= ~0x8000 /*O_LARGEFILE*/; // Ignore this flag from musl, otherwise node.js fails to open the file. + flags &= ~0x80000 /*O_CLOEXEC*/; // Some applications may pass it; it makes no sense for a single process. + if (flags in NODEFS.flagsToPermissionStringMap) { + return NODEFS.flagsToPermissionStringMap[flags]; + } else { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + },node_ops:{getattr:function (node) { + var path = NODEFS.realPath(node); + var stat; + try { + stat = fs.lstatSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + // node.js v0.10.20 doesn't report blksize and blocks on Windows. Fake them with default blksize of 4096. + // See http://support.microsoft.com/kb/140365 + if (NODEFS.isWindows && !stat.blksize) { + stat.blksize = 4096; + } + if (NODEFS.isWindows && !stat.blocks) { + stat.blocks = (stat.size+stat.blksize-1)/stat.blksize|0; + } + return { + dev: stat.dev, + ino: stat.ino, + mode: stat.mode, + nlink: stat.nlink, + uid: stat.uid, + gid: stat.gid, + rdev: stat.rdev, + size: stat.size, + atime: stat.atime, + mtime: stat.mtime, + ctime: stat.ctime, + blksize: stat.blksize, + blocks: stat.blocks + }; + },setattr:function (node, attr) { + var path = NODEFS.realPath(node); + try { + if (attr.mode !== undefined) { + fs.chmodSync(path, attr.mode); + // update the common node structure mode as well + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + var date = new Date(attr.timestamp); + fs.utimesSync(path, date, date); + } + if (attr.size !== undefined) { + fs.truncateSync(path, attr.size); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + },lookup:function (parent, name) { + var path = PATH.join2(NODEFS.realPath(parent), name); + var mode = NODEFS.getMode(path); + return NODEFS.createNode(parent, name, mode); + },mknod:function (parent, name, mode, dev) { + var node = NODEFS.createNode(parent, name, mode, dev); + // create the backing node for this in the fs root as well + var path = NODEFS.realPath(node); + try { + if (FS.isDir(node.mode)) { + fs.mkdirSync(path, node.mode); + } else { + fs.writeFileSync(path, '', { mode: node.mode }); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return node; + },rename:function (oldNode, newDir, newName) { + var oldPath = NODEFS.realPath(oldNode); + var newPath = PATH.join2(NODEFS.realPath(newDir), newName); + try { + fs.renameSync(oldPath, newPath); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + },unlink:function (parent, name) { + var path = PATH.join2(NODEFS.realPath(parent), name); + try { + fs.unlinkSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + },rmdir:function (parent, name) { + var path = PATH.join2(NODEFS.realPath(parent), name); + try { + fs.rmdirSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + },readdir:function (node) { + var path = NODEFS.realPath(node); + try { + return fs.readdirSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + },symlink:function (parent, newName, oldPath) { + var newPath = PATH.join2(NODEFS.realPath(parent), newName); + try { + fs.symlinkSync(oldPath, newPath); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + },readlink:function (node) { + var path = NODEFS.realPath(node); + try { + path = fs.readlinkSync(path); + path = NODEJS_PATH.relative(NODEJS_PATH.resolve(node.mount.opts.root), path); + return path; + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }},stream_ops:{open:function (stream) { + var path = NODEFS.realPath(stream.node); + try { + if (FS.isFile(stream.node.mode)) { + stream.nfd = fs.openSync(path, NODEFS.flagsToPermissionString(stream.flags)); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + },close:function (stream) { + try { + if (FS.isFile(stream.node.mode) && stream.nfd) { + fs.closeSync(stream.nfd); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + },read:function (stream, buffer, offset, length, position) { + if (length === 0) return 0; // node errors on 0 length reads + // FIXME this is terrible. + var nbuffer = new Buffer(length); + var res; + try { + res = fs.readSync(stream.nfd, nbuffer, 0, length, position); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + if (res > 0) { + for (var i = 0; i < res; i++) { + buffer[offset + i] = nbuffer[i]; + } + } + return res; + },write:function (stream, buffer, offset, length, position) { + // FIXME this is terrible. + var nbuffer = new Buffer(buffer.subarray(offset, offset + length)); + var res; + try { + res = fs.writeSync(stream.nfd, nbuffer, 0, length, position); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return res; + },llseek:function (stream, offset, whence) { + var position = offset; + if (whence === 1) { // SEEK_CUR. + position += stream.position; + } else if (whence === 2) { // SEEK_END. + if (FS.isFile(stream.node.mode)) { + try { + var stat = fs.fstatSync(stream.nfd); + position += stat.size; + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + } + } + + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + + return position; + }}}; + + var WORKERFS={DIR_MODE:16895,FILE_MODE:33279,reader:null,mount:function (mount) { + assert(ENVIRONMENT_IS_WORKER); + if (!WORKERFS.reader) WORKERFS.reader = new FileReaderSync(); + var root = WORKERFS.createNode(null, '/', WORKERFS.DIR_MODE, 0); + var createdParents = {}; + function ensureParent(path) { + // return the parent node, creating subdirs as necessary + var parts = path.split('/'); + var parent = root; + for (var i = 0; i < parts.length-1; i++) { + var curr = parts.slice(0, i+1).join('/'); + // Issue 4254: Using curr as a node name will prevent the node + // from being found in FS.nameTable when FS.open is called on + // a path which holds a child of this node, + // given that all FS functions assume node names + // are just their corresponding parts within their given path, + // rather than incremental aggregates which include their parent's + // directories. + if (!createdParents[curr]) { + createdParents[curr] = WORKERFS.createNode(parent, parts[i], WORKERFS.DIR_MODE, 0); + } + parent = createdParents[curr]; + } + return parent; + } + function base(path) { + var parts = path.split('/'); + return parts[parts.length-1]; + } + // We also accept FileList here, by using Array.prototype + Array.prototype.forEach.call(mount.opts["files"] || [], function(file) { + WORKERFS.createNode(ensureParent(file.name), base(file.name), WORKERFS.FILE_MODE, 0, file, file.lastModifiedDate); + }); + (mount.opts["blobs"] || []).forEach(function(obj) { + WORKERFS.createNode(ensureParent(obj["name"]), base(obj["name"]), WORKERFS.FILE_MODE, 0, obj["data"]); + }); + (mount.opts["packages"] || []).forEach(function(pack) { + pack['metadata'].files.forEach(function(file) { + var name = file.filename.substr(1); // remove initial slash + WORKERFS.createNode(ensureParent(name), base(name), WORKERFS.FILE_MODE, 0, pack['blob'].slice(file.start, file.end)); + }); + }); + return root; + },createNode:function (parent, name, mode, dev, contents, mtime) { + var node = FS.createNode(parent, name, mode); + node.mode = mode; + node.node_ops = WORKERFS.node_ops; + node.stream_ops = WORKERFS.stream_ops; + node.timestamp = (mtime || new Date).getTime(); + assert(WORKERFS.FILE_MODE !== WORKERFS.DIR_MODE); + if (mode === WORKERFS.FILE_MODE) { + node.size = contents.size; + node.contents = contents; + } else { + node.size = 4096; + node.contents = {}; + } + if (parent) { + parent.contents[name] = node; + } + return node; + },node_ops:{getattr:function (node) { + return { + dev: 1, + ino: undefined, + mode: node.mode, + nlink: 1, + uid: 0, + gid: 0, + rdev: undefined, + size: node.size, + atime: new Date(node.timestamp), + mtime: new Date(node.timestamp), + ctime: new Date(node.timestamp), + blksize: 4096, + blocks: Math.ceil(node.size / 4096), + }; + },setattr:function (node, attr) { + if (attr.mode !== undefined) { + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + node.timestamp = attr.timestamp; + } + },lookup:function (parent, name) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + },mknod:function (parent, name, mode, dev) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + },rename:function (oldNode, newDir, newName) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + },unlink:function (parent, name) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + },rmdir:function (parent, name) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + },readdir:function (node) { + var entries = ['.', '..']; + for (var key in node.contents) { + if (!node.contents.hasOwnProperty(key)) { + continue; + } + entries.push(key); + } + return entries; + },symlink:function (parent, newName, oldPath) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + },readlink:function (node) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + }},stream_ops:{read:function (stream, buffer, offset, length, position) { + if (position >= stream.node.size) return 0; + var chunk = stream.node.contents.slice(position, position + length); + var ab = WORKERFS.reader.readAsArrayBuffer(chunk); + buffer.set(new Uint8Array(ab), offset); + return chunk.size; + },write:function (stream, buffer, offset, length, position) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + },llseek:function (stream, offset, whence) { + var position = offset; + if (whence === 1) { // SEEK_CUR. + position += stream.position; + } else if (whence === 2) { // SEEK_END. + if (FS.isFile(stream.node.mode)) { + position += stream.node.size; + } + } + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + return position; + }}}; + + var _stdin=STATICTOP; STATICTOP += 16;; + + var _stdout=STATICTOP; STATICTOP += 16;; + + var _stderr=STATICTOP; STATICTOP += 16;;var FS={root:null,mounts:[],devices:[null],streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,trackingDelegate:{},tracking:{openFlags:{READ:1,WRITE:2}},ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,handleFSError:function (e) { + if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + stackTrace(); + return ___setErrNo(e.errno); + },lookupPath:function (path, opts) { + path = PATH.resolve(FS.cwd(), path); + opts = opts || {}; + + if (!path) return { path: '', node: null }; + + var defaults = { + follow_mount: true, + recurse_count: 0 + }; + for (var key in defaults) { + if (opts[key] === undefined) { + opts[key] = defaults[key]; + } + } + + if (opts.recurse_count > 8) { // max recursive lookup of 8 + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); + } + + // split the path + var parts = PATH.normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), false); + + // start at the root + var current = FS.root; + var current_path = '/'; + + for (var i = 0; i < parts.length; i++) { + var islast = (i === parts.length-1); + if (islast && opts.parent) { + // stop resolving + break; + } + + current = FS.lookupNode(current, parts[i]); + current_path = PATH.join2(current_path, parts[i]); + + // jump to the mount's root node if this is a mountpoint + if (FS.isMountpoint(current)) { + if (!islast || (islast && opts.follow_mount)) { + current = current.mounted.root; + } + } + + // by default, lookupPath will not follow a symlink if it is the final path component. + // setting opts.follow = true will override this behavior. + if (!islast || opts.follow) { + var count = 0; + while (FS.isLink(current.mode)) { + var link = FS.readlink(current_path); + current_path = PATH.resolve(PATH.dirname(current_path), link); + + var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count }); + current = lookup.node; + + if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); + } + } + } + } + + return { path: current_path, node: current }; + },getPath:function (node) { + var path; + while (true) { + if (FS.isRoot(node)) { + var mount = node.mount.mountpoint; + if (!path) return mount; + return mount[mount.length-1] !== '/' ? mount + '/' + path : mount + path; + } + path = path ? node.name + '/' + path : node.name; + node = node.parent; + } + },hashName:function (parentid, name) { + var hash = 0; + + + for (var i = 0; i < name.length; i++) { + hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; + } + return ((parentid + hash) >>> 0) % FS.nameTable.length; + },hashAddNode:function (node) { + var hash = FS.hashName(node.parent.id, node.name); + node.name_next = FS.nameTable[hash]; + FS.nameTable[hash] = node; + },hashRemoveNode:function (node) { + var hash = FS.hashName(node.parent.id, node.name); + if (FS.nameTable[hash] === node) { + FS.nameTable[hash] = node.name_next; + } else { + var current = FS.nameTable[hash]; + while (current) { + if (current.name_next === node) { + current.name_next = node.name_next; + break; + } + current = current.name_next; + } + } + },lookupNode:function (parent, name) { + var err = FS.mayLookup(parent); + if (err) { + throw new FS.ErrnoError(err, parent); + } + var hash = FS.hashName(parent.id, name); + for (var node = FS.nameTable[hash]; node; node = node.name_next) { + var nodeName = node.name; + if (node.parent.id === parent.id && nodeName === name) { + return node; + } + } + // if we failed to find it in the cache, call into the VFS + return FS.lookup(parent, name); + },createNode:function (parent, name, mode, rdev) { + if (!FS.FSNode) { + FS.FSNode = function(parent, name, mode, rdev) { + if (!parent) { + parent = this; // root node sets parent to itself + } + this.parent = parent; + this.mount = parent.mount; + this.mounted = null; + this.id = FS.nextInode++; + this.name = name; + this.mode = mode; + this.node_ops = {}; + this.stream_ops = {}; + this.rdev = rdev; + }; + + FS.FSNode.prototype = {}; + + // compatibility + var readMode = 292 | 73; + var writeMode = 146; + + // NOTE we must use Object.defineProperties instead of individual calls to + // Object.defineProperty in order to make closure compiler happy + Object.defineProperties(FS.FSNode.prototype, { + read: { + get: function() { return (this.mode & readMode) === readMode; }, + set: function(val) { val ? this.mode |= readMode : this.mode &= ~readMode; } + }, + write: { + get: function() { return (this.mode & writeMode) === writeMode; }, + set: function(val) { val ? this.mode |= writeMode : this.mode &= ~writeMode; } + }, + isFolder: { + get: function() { return FS.isDir(this.mode); } + }, + isDevice: { + get: function() { return FS.isChrdev(this.mode); } + } + }); + } + + var node = new FS.FSNode(parent, name, mode, rdev); + + FS.hashAddNode(node); + + return node; + },destroyNode:function (node) { + FS.hashRemoveNode(node); + },isRoot:function (node) { + return node === node.parent; + },isMountpoint:function (node) { + return !!node.mounted; + },isFile:function (mode) { + return (mode & 61440) === 32768; + },isDir:function (mode) { + return (mode & 61440) === 16384; + },isLink:function (mode) { + return (mode & 61440) === 40960; + },isChrdev:function (mode) { + return (mode & 61440) === 8192; + },isBlkdev:function (mode) { + return (mode & 61440) === 24576; + },isFIFO:function (mode) { + return (mode & 61440) === 4096; + },isSocket:function (mode) { + return (mode & 49152) === 49152; + },flagModes:{"r":0,"rs":1052672,"r+":2,"w":577,"wx":705,"xw":705,"w+":578,"wx+":706,"xw+":706,"a":1089,"ax":1217,"xa":1217,"a+":1090,"ax+":1218,"xa+":1218},modeStringToFlags:function (str) { + var flags = FS.flagModes[str]; + if (typeof flags === 'undefined') { + throw new Error('Unknown file open mode: ' + str); + } + return flags; + },flagsToPermissionString:function (flag) { + var perms = ['r', 'w', 'rw'][flag & 3]; + if ((flag & 512)) { + perms += 'w'; + } + return perms; + },nodePermissions:function (node, perms) { + if (FS.ignorePermissions) { + return 0; + } + // return 0 if any user, group or owner bits are set. + if (perms.indexOf('r') !== -1 && !(node.mode & 292)) { + return ERRNO_CODES.EACCES; + } else if (perms.indexOf('w') !== -1 && !(node.mode & 146)) { + return ERRNO_CODES.EACCES; + } else if (perms.indexOf('x') !== -1 && !(node.mode & 73)) { + return ERRNO_CODES.EACCES; + } + return 0; + },mayLookup:function (dir) { + var err = FS.nodePermissions(dir, 'x'); + if (err) return err; + if (!dir.node_ops.lookup) return ERRNO_CODES.EACCES; + return 0; + },mayCreate:function (dir, name) { + try { + var node = FS.lookupNode(dir, name); + return ERRNO_CODES.EEXIST; + } catch (e) { + } + return FS.nodePermissions(dir, 'wx'); + },mayDelete:function (dir, name, isdir) { + var node; + try { + node = FS.lookupNode(dir, name); + } catch (e) { + return e.errno; + } + var err = FS.nodePermissions(dir, 'wx'); + if (err) { + return err; + } + if (isdir) { + if (!FS.isDir(node.mode)) { + return ERRNO_CODES.ENOTDIR; + } + if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { + return ERRNO_CODES.EBUSY; + } + } else { + if (FS.isDir(node.mode)) { + return ERRNO_CODES.EISDIR; + } + } + return 0; + },mayOpen:function (node, flags) { + if (!node) { + return ERRNO_CODES.ENOENT; + } + if (FS.isLink(node.mode)) { + return ERRNO_CODES.ELOOP; + } else if (FS.isDir(node.mode)) { + if (FS.flagsToPermissionString(flags) !== 'r' || // opening for write + (flags & 512)) { // TODO: check for O_SEARCH? (== search for dir only) + return ERRNO_CODES.EISDIR; + } + } + return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); + },MAX_OPEN_FDS:4096,nextfd:function (fd_start, fd_end) { + fd_start = fd_start || 0; + fd_end = fd_end || FS.MAX_OPEN_FDS; + for (var fd = fd_start; fd <= fd_end; fd++) { + if (!FS.streams[fd]) { + return fd; + } + } + throw new FS.ErrnoError(ERRNO_CODES.EMFILE); + },getStream:function (fd) { + return FS.streams[fd]; + },createStream:function (stream, fd_start, fd_end) { + if (!FS.FSStream) { + FS.FSStream = function(){}; + FS.FSStream.prototype = {}; + // compatibility + Object.defineProperties(FS.FSStream.prototype, { + object: { + get: function() { return this.node; }, + set: function(val) { this.node = val; } + }, + isRead: { + get: function() { return (this.flags & 2097155) !== 1; } + }, + isWrite: { + get: function() { return (this.flags & 2097155) !== 0; } + }, + isAppend: { + get: function() { return (this.flags & 1024); } + } + }); + } + // clone it, so we can return an instance of FSStream + var newStream = new FS.FSStream(); + for (var p in stream) { + newStream[p] = stream[p]; + } + stream = newStream; + var fd = FS.nextfd(fd_start, fd_end); + stream.fd = fd; + FS.streams[fd] = stream; + return stream; + },closeStream:function (fd) { + FS.streams[fd] = null; + },chrdev_stream_ops:{open:function (stream) { + var device = FS.getDevice(stream.node.rdev); + // override node's stream ops with the device's + stream.stream_ops = device.stream_ops; + // forward the open call + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + },llseek:function () { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + }},major:function (dev) { + return ((dev) >> 8); + },minor:function (dev) { + return ((dev) & 0xff); + },makedev:function (ma, mi) { + return ((ma) << 8 | (mi)); + },registerDevice:function (dev, ops) { + FS.devices[dev] = { stream_ops: ops }; + },getDevice:function (dev) { + return FS.devices[dev]; + },getMounts:function (mount) { + var mounts = []; + var check = [mount]; + + while (check.length) { + var m = check.pop(); + + mounts.push(m); + + check.push.apply(check, m.mounts); + } + + return mounts; + },syncfs:function (populate, callback) { + if (typeof(populate) === 'function') { + callback = populate; + populate = false; + } + + FS.syncFSRequests++; + + if (FS.syncFSRequests > 1) { + console.log('warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work'); + } + + var mounts = FS.getMounts(FS.root.mount); + var completed = 0; + + function doCallback(err) { + assert(FS.syncFSRequests > 0); + FS.syncFSRequests--; + return callback(err); + } + + function done(err) { + if (err) { + if (!done.errored) { + done.errored = true; + return doCallback(err); + } + return; + } + if (++completed >= mounts.length) { + doCallback(null); + } + }; + + // sync all mounts + mounts.forEach(function (mount) { + if (!mount.type.syncfs) { + return done(null); + } + mount.type.syncfs(mount, populate, done); + }); + },mount:function (type, opts, mountpoint) { + var root = mountpoint === '/'; + var pseudo = !mountpoint; + var node; + + if (root && FS.root) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } else if (!root && !pseudo) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + + mountpoint = lookup.path; // use the absolute path + node = lookup.node; + + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + + if (!FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + } + + var mount = { + type: type, + opts: opts, + mountpoint: mountpoint, + mounts: [] + }; + + // create a root node for the fs + var mountRoot = type.mount(mount); + mountRoot.mount = mount; + mount.root = mountRoot; + + if (root) { + FS.root = mountRoot; + } else if (node) { + // set as a mountpoint + node.mounted = mount; + + // add the new mount to the current mount's children + if (node.mount) { + node.mount.mounts.push(mount); + } + } + + return mountRoot; + },unmount:function (mountpoint) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + + if (!FS.isMountpoint(lookup.node)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + + // destroy the nodes for this mount, and all its child mounts + var node = lookup.node; + var mount = node.mounted; + var mounts = FS.getMounts(mount); + + Object.keys(FS.nameTable).forEach(function (hash) { + var current = FS.nameTable[hash]; + + while (current) { + var next = current.name_next; + + if (mounts.indexOf(current.mount) !== -1) { + FS.destroyNode(current); + } + + current = next; + } + }); + + // no longer a mountpoint + node.mounted = null; + + // remove this mount from the child mounts + var idx = node.mount.mounts.indexOf(mount); + assert(idx !== -1); + node.mount.mounts.splice(idx, 1); + },lookup:function (parent, name) { + return parent.node_ops.lookup(parent, name); + },mknod:function (path, mode, dev) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + if (!name || name === '.' || name === '..') { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var err = FS.mayCreate(parent, name); + if (err) { + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.mknod) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + return parent.node_ops.mknod(parent, name, mode, dev); + },create:function (path, mode) { + mode = mode !== undefined ? mode : 438 /* 0666 */; + mode &= 4095; + mode |= 32768; + return FS.mknod(path, mode, 0); + },mkdir:function (path, mode) { + mode = mode !== undefined ? mode : 511 /* 0777 */; + mode &= 511 | 512; + mode |= 16384; + return FS.mknod(path, mode, 0); + },mkdirTree:function (path, mode) { + var dirs = path.split('/'); + var d = ''; + for (var i = 0; i < dirs.length; ++i) { + if (!dirs[i]) continue; + d += '/' + dirs[i]; + try { + FS.mkdir(d, mode); + } catch(e) { + if (e.errno != ERRNO_CODES.EEXIST) throw e; + } + } + },mkdev:function (path, mode, dev) { + if (typeof(dev) === 'undefined') { + dev = mode; + mode = 438 /* 0666 */; + } + mode |= 8192; + return FS.mknod(path, mode, dev); + },symlink:function (oldpath, newpath) { + if (!PATH.resolve(oldpath)) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + } + var lookup = FS.lookupPath(newpath, { parent: true }); + var parent = lookup.node; + if (!parent) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + } + var newname = PATH.basename(newpath); + var err = FS.mayCreate(parent, newname); + if (err) { + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.symlink) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + return parent.node_ops.symlink(parent, newname, oldpath); + },rename:function (old_path, new_path) { + var old_dirname = PATH.dirname(old_path); + var new_dirname = PATH.dirname(new_path); + var old_name = PATH.basename(old_path); + var new_name = PATH.basename(new_path); + // parents must exist + var lookup, old_dir, new_dir; + try { + lookup = FS.lookupPath(old_path, { parent: true }); + old_dir = lookup.node; + lookup = FS.lookupPath(new_path, { parent: true }); + new_dir = lookup.node; + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + if (!old_dir || !new_dir) throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + // need to be part of the same mount + if (old_dir.mount !== new_dir.mount) { + throw new FS.ErrnoError(ERRNO_CODES.EXDEV); + } + // source must exist + var old_node = FS.lookupNode(old_dir, old_name); + // old path should not be an ancestor of the new path + var relative = PATH.relative(old_path, new_dirname); + if (relative.charAt(0) !== '.') { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + // new path should not be an ancestor of the old path + relative = PATH.relative(new_path, old_dirname); + if (relative.charAt(0) !== '.') { + throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); + } + // see if the new path already exists + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) { + // not fatal + } + // early out if nothing needs to change + if (old_node === new_node) { + return; + } + // we'll need to delete the old entry + var isdir = FS.isDir(old_node.mode); + var err = FS.mayDelete(old_dir, old_name, isdir); + if (err) { + throw new FS.ErrnoError(err); + } + // need delete permissions if we'll be overwriting. + // need create permissions if new doesn't already exist. + err = new_node ? + FS.mayDelete(new_dir, new_name, isdir) : + FS.mayCreate(new_dir, new_name); + if (err) { + throw new FS.ErrnoError(err); + } + if (!old_dir.node_ops.rename) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + // if we are going to change the parent, check write permissions + if (new_dir !== old_dir) { + err = FS.nodePermissions(old_dir, 'w'); + if (err) { + throw new FS.ErrnoError(err); + } + } + try { + if (FS.trackingDelegate['willMovePath']) { + FS.trackingDelegate['willMovePath'](old_path, new_path); + } + } catch(e) { + console.log("FS.trackingDelegate['willMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); + } + // remove the node from the lookup hash + FS.hashRemoveNode(old_node); + // do the underlying fs rename + try { + old_dir.node_ops.rename(old_node, new_dir, new_name); + } catch (e) { + throw e; + } finally { + // add the node back to the hash (in case node_ops.rename + // changed its name) + FS.hashAddNode(old_node); + } + try { + if (FS.trackingDelegate['onMovePath']) FS.trackingDelegate['onMovePath'](old_path, new_path); + } catch(e) { + console.log("FS.trackingDelegate['onMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); + } + },rmdir:function (path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var err = FS.mayDelete(parent, name, true); + if (err) { + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.rmdir) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + try { + if (FS.trackingDelegate['willDeletePath']) { + FS.trackingDelegate['willDeletePath'](path); + } + } catch(e) { + console.log("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); + } + parent.node_ops.rmdir(parent, name); + FS.destroyNode(node); + try { + if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); + } catch(e) { + console.log("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); + } + },readdir:function (path) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + if (!node.node_ops.readdir) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + return node.node_ops.readdir(node); + },unlink:function (path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var err = FS.mayDelete(parent, name, false); + if (err) { + // According to POSIX, we should map EISDIR to EPERM, but + // we instead do what Linux does (and we must, as we use + // the musl linux libc). + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.unlink) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + try { + if (FS.trackingDelegate['willDeletePath']) { + FS.trackingDelegate['willDeletePath'](path); + } + } catch(e) { + console.log("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); + } + parent.node_ops.unlink(parent, name); + FS.destroyNode(node); + try { + if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); + } catch(e) { + console.log("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); + } + },readlink:function (path) { + var lookup = FS.lookupPath(path); + var link = lookup.node; + if (!link) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + } + if (!link.node_ops.readlink) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + return PATH.resolve(FS.getPath(link.parent), link.node_ops.readlink(link)); + },stat:function (path, dontFollow) { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + var node = lookup.node; + if (!node) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + } + if (!node.node_ops.getattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + return node.node_ops.getattr(node); + },lstat:function (path) { + return FS.stat(path, true); + },chmod:function (path, mode, dontFollow) { + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + node.node_ops.setattr(node, { + mode: (mode & 4095) | (node.mode & ~4095), + timestamp: Date.now() + }); + },lchmod:function (path, mode) { + FS.chmod(path, mode, true); + },fchmod:function (fd, mode) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + FS.chmod(stream.node, mode); + },chown:function (path, uid, gid, dontFollow) { + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + node.node_ops.setattr(node, { + timestamp: Date.now() + // we ignore the uid / gid for now + }); + },lchown:function (path, uid, gid) { + FS.chown(path, uid, gid, true); + },fchown:function (fd, uid, gid) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + FS.chown(stream.node, uid, gid); + },truncate:function (path, len) { + if (len < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EISDIR); + } + if (!FS.isFile(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var err = FS.nodePermissions(node, 'w'); + if (err) { + throw new FS.ErrnoError(err); + } + node.node_ops.setattr(node, { + size: len, + timestamp: Date.now() + }); + },ftruncate:function (fd, len) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + FS.truncate(stream.node, len); + },utime:function (path, atime, mtime) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + node.node_ops.setattr(node, { + timestamp: Math.max(atime, mtime) + }); + },open:function (path, flags, mode, fd_start, fd_end) { + if (path === "") { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + } + flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; + mode = typeof mode === 'undefined' ? 438 /* 0666 */ : mode; + if ((flags & 64)) { + mode = (mode & 4095) | 32768; + } else { + mode = 0; + } + var node; + if (typeof path === 'object') { + node = path; + } else { + path = PATH.normalize(path); + try { + var lookup = FS.lookupPath(path, { + follow: !(flags & 131072) + }); + node = lookup.node; + } catch (e) { + // ignore + } + } + // perhaps we need to create the node + var created = false; + if ((flags & 64)) { + if (node) { + // if O_CREAT and O_EXCL are set, error out if the node already exists + if ((flags & 128)) { + throw new FS.ErrnoError(ERRNO_CODES.EEXIST); + } + } else { + // node doesn't exist, try to create it + node = FS.mknod(path, mode, 0); + created = true; + } + } + if (!node) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + } + // can't truncate a device + if (FS.isChrdev(node.mode)) { + flags &= ~512; + } + // if asked only for a directory, then this must be one + if ((flags & 65536) && !FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + // check permissions, if this is not a file we just created now (it is ok to + // create and write to a file with read-only permissions; it is read-only + // for later use) + if (!created) { + var err = FS.mayOpen(node, flags); + if (err) { + throw new FS.ErrnoError(err); + } + } + // do truncation if necessary + if ((flags & 512)) { + FS.truncate(node, 0); + } + // we've already handled these, don't pass down to the underlying vfs + flags &= ~(128 | 512); + + // register the stream with the filesystem + var stream = FS.createStream({ + node: node, + path: FS.getPath(node), // we want the absolute path to the node + flags: flags, + seekable: true, + position: 0, + stream_ops: node.stream_ops, + // used by the file family libc calls (fopen, fwrite, ferror, etc.) + ungotten: [], + error: false + }, fd_start, fd_end); + // call the new stream's open function + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + if (Module['logReadFiles'] && !(flags & 1)) { + if (!FS.readFiles) FS.readFiles = {}; + if (!(path in FS.readFiles)) { + FS.readFiles[path] = 1; + Module['printErr']('read file: ' + path); + } + } + try { + if (FS.trackingDelegate['onOpenFile']) { + var trackingFlags = 0; + if ((flags & 2097155) !== 1) { + trackingFlags |= FS.tracking.openFlags.READ; + } + if ((flags & 2097155) !== 0) { + trackingFlags |= FS.tracking.openFlags.WRITE; + } + FS.trackingDelegate['onOpenFile'](path, trackingFlags); + } + } catch(e) { + console.log("FS.trackingDelegate['onOpenFile']('"+path+"', flags) threw an exception: " + e.message); + } + return stream; + },close:function (stream) { + if (stream.getdents) stream.getdents = null; // free readdir state + try { + if (stream.stream_ops.close) { + stream.stream_ops.close(stream); + } + } catch (e) { + throw e; + } finally { + FS.closeStream(stream.fd); + } + },llseek:function (stream, offset, whence) { + if (!stream.seekable || !stream.stream_ops.llseek) { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + stream.position = stream.stream_ops.llseek(stream, offset, whence); + stream.ungotten = []; + return stream.position; + },read:function (stream, buffer, offset, length, position) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if ((stream.flags & 2097155) === 1) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EISDIR); + } + if (!stream.stream_ops.read) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var seeking = true; + if (typeof position === 'undefined') { + position = stream.position; + seeking = false; + } else if (!stream.seekable) { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesRead; + return bytesRead; + },write:function (stream, buffer, offset, length, position, canOwn) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EISDIR); + } + if (!stream.stream_ops.write) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if (stream.flags & 1024) { + // seek to the end before writing in append mode + FS.llseek(stream, 0, 2); + } + var seeking = true; + if (typeof position === 'undefined') { + position = stream.position; + seeking = false; + } else if (!stream.seekable) { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); + if (!seeking) stream.position += bytesWritten; + try { + if (stream.path && FS.trackingDelegate['onWriteToFile']) FS.trackingDelegate['onWriteToFile'](stream.path); + } catch(e) { + console.log("FS.trackingDelegate['onWriteToFile']('"+path+"') threw an exception: " + e.message); + } + return bytesWritten; + },allocate:function (stream, offset, length) { + if (offset < 0 || length <= 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if ((stream.flags & 2097155) === 0) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + if (!stream.stream_ops.allocate) { + throw new FS.ErrnoError(ERRNO_CODES.EOPNOTSUPP); + } + stream.stream_ops.allocate(stream, offset, length); + },mmap:function (stream, buffer, offset, length, position, prot, flags) { + // TODO if PROT is PROT_WRITE, make sure we have write access + if ((stream.flags & 2097155) === 1) { + throw new FS.ErrnoError(ERRNO_CODES.EACCES); + } + if (!stream.stream_ops.mmap) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags); + },msync:function (stream, buffer, offset, length, mmapFlags) { + if (!stream || !stream.stream_ops.msync) { + return 0; + } + return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); + },munmap:function (stream) { + return 0; + },ioctl:function (stream, cmd, arg) { + if (!stream.stream_ops.ioctl) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTTY); + } + return stream.stream_ops.ioctl(stream, cmd, arg); + },readFile:function (path, opts) { + opts = opts || {}; + opts.flags = opts.flags || 'r'; + opts.encoding = opts.encoding || 'binary'; + if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } + var ret; + var stream = FS.open(path, opts.flags); + var stat = FS.stat(path); + var length = stat.size; + var buf = new Uint8Array(length); + FS.read(stream, buf, 0, length, 0); + if (opts.encoding === 'utf8') { + ret = UTF8ArrayToString(buf, 0); + } else if (opts.encoding === 'binary') { + ret = buf; + } + FS.close(stream); + return ret; + },writeFile:function (path, data, opts) { + opts = opts || {}; + opts.flags = opts.flags || 'w'; + opts.encoding = opts.encoding || 'utf8'; + if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } + var stream = FS.open(path, opts.flags, opts.mode); + if (opts.encoding === 'utf8') { + var buf = new Uint8Array(lengthBytesUTF8(data)+1); + var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length); + FS.write(stream, buf, 0, actualNumBytes, 0, opts.canOwn); + } else if (opts.encoding === 'binary') { + FS.write(stream, data, 0, data.length, 0, opts.canOwn); + } + FS.close(stream); + },cwd:function () { + return FS.currentPath; + },chdir:function (path) { + var lookup = FS.lookupPath(path, { follow: true }); + if (lookup.node === null) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + } + if (!FS.isDir(lookup.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + var err = FS.nodePermissions(lookup.node, 'x'); + if (err) { + throw new FS.ErrnoError(err); + } + FS.currentPath = lookup.path; + },createDefaultDirectories:function () { + FS.mkdir('/tmp'); + FS.mkdir('/home'); + FS.mkdir('/home/web_user'); + },createDefaultDevices:function () { + // create /dev + FS.mkdir('/dev'); + // setup /dev/null + FS.registerDevice(FS.makedev(1, 3), { + read: function() { return 0; }, + write: function(stream, buffer, offset, length, pos) { return length; } + }); + FS.mkdev('/dev/null', FS.makedev(1, 3)); + // setup /dev/tty and /dev/tty1 + // stderr needs to print output using Module['printErr'] + // so we register a second tty just for it. + TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); + TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); + FS.mkdev('/dev/tty', FS.makedev(5, 0)); + FS.mkdev('/dev/tty1', FS.makedev(6, 0)); + // setup /dev/[u]random + var random_device; + if (typeof crypto !== 'undefined') { + // for modern web browsers + var randomBuffer = new Uint8Array(1); + random_device = function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; + } else if (ENVIRONMENT_IS_NODE) { + // for nodejs + random_device = function() { return require('crypto').randomBytes(1)[0]; }; + } else { + // default for ES5 platforms + random_device = function() { return (Math.random()*256)|0; }; + } + FS.createDevice('/dev', 'random', random_device); + FS.createDevice('/dev', 'urandom', random_device); + // we're not going to emulate the actual shm device, + // just create the tmp dirs that reside in it commonly + FS.mkdir('/dev/shm'); + FS.mkdir('/dev/shm/tmp'); + },createSpecialDirectories:function () { + // create /proc/self/fd which allows /proc/self/fd/6 => readlink gives the name of the stream for fd 6 (see test_unistd_ttyname) + FS.mkdir('/proc'); + FS.mkdir('/proc/self'); + FS.mkdir('/proc/self/fd'); + FS.mount({ + mount: function() { + var node = FS.createNode('/proc/self', 'fd', 16384 | 511 /* 0777 */, 73); + node.node_ops = { + lookup: function(parent, name) { + var fd = +name; + var stream = FS.getStream(fd); + if (!stream) throw new FS.ErrnoError(ERRNO_CODES.EBADF); + var ret = { + parent: null, + mount: { mountpoint: 'fake' }, + node_ops: { readlink: function() { return stream.path } } + }; + ret.parent = ret; // make it look like a simple root node + return ret; + } + }; + return node; + } + }, {}, '/proc/self/fd'); + },createStandardStreams:function () { + // TODO deprecate the old functionality of a single + // input / output callback and that utilizes FS.createDevice + // and instead require a unique set of stream ops + + // by default, we symlink the standard streams to the + // default tty devices. however, if the standard streams + // have been overwritten we create a unique device for + // them instead. + if (Module['stdin']) { + FS.createDevice('/dev', 'stdin', Module['stdin']); + } else { + FS.symlink('/dev/tty', '/dev/stdin'); + } + if (Module['stdout']) { + FS.createDevice('/dev', 'stdout', null, Module['stdout']); + } else { + FS.symlink('/dev/tty', '/dev/stdout'); + } + if (Module['stderr']) { + FS.createDevice('/dev', 'stderr', null, Module['stderr']); + } else { + FS.symlink('/dev/tty1', '/dev/stderr'); + } + + // open default streams for the stdin, stdout and stderr devices + var stdin = FS.open('/dev/stdin', 'r'); + assert(stdin.fd === 0, 'invalid handle for stdin (' + stdin.fd + ')'); + + var stdout = FS.open('/dev/stdout', 'w'); + assert(stdout.fd === 1, 'invalid handle for stdout (' + stdout.fd + ')'); + + var stderr = FS.open('/dev/stderr', 'w'); + assert(stderr.fd === 2, 'invalid handle for stderr (' + stderr.fd + ')'); + },ensureErrnoError:function () { + if (FS.ErrnoError) return; + FS.ErrnoError = function ErrnoError(errno, node) { + //Module.printErr(stackTrace()); // useful for debugging + this.node = node; + this.setErrno = function(errno) { + this.errno = errno; + for (var key in ERRNO_CODES) { + if (ERRNO_CODES[key] === errno) { + this.code = key; + break; + } + } + }; + this.setErrno(errno); + this.message = ERRNO_MESSAGES[errno]; + if (this.stack) this.stack = demangleAll(this.stack); + }; + FS.ErrnoError.prototype = new Error(); + FS.ErrnoError.prototype.constructor = FS.ErrnoError; + // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info) + [ERRNO_CODES.ENOENT].forEach(function(code) { + FS.genericErrors[code] = new FS.ErrnoError(code); + FS.genericErrors[code].stack = ''; + }); + },staticInit:function () { + FS.ensureErrnoError(); + + FS.nameTable = new Array(4096); + + FS.mount(MEMFS, {}, '/'); + + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + FS.createSpecialDirectories(); + + FS.filesystems = { + 'MEMFS': MEMFS, + 'IDBFS': IDBFS, + 'NODEFS': NODEFS, + 'WORKERFS': WORKERFS, + }; + },init:function (input, output, error) { + assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); + FS.init.initialized = true; + + FS.ensureErrnoError(); + + // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here + Module['stdin'] = input || Module['stdin']; + Module['stdout'] = output || Module['stdout']; + Module['stderr'] = error || Module['stderr']; + + FS.createStandardStreams(); + },quit:function () { + FS.init.initialized = false; + // force-flush all streams, so we get musl std streams printed out + var fflush = Module['_fflush']; + if (fflush) fflush(0); + // close all of our streams + for (var i = 0; i < FS.streams.length; i++) { + var stream = FS.streams[i]; + if (!stream) { + continue; + } + FS.close(stream); + } + },getMode:function (canRead, canWrite) { + var mode = 0; + if (canRead) mode |= 292 | 73; + if (canWrite) mode |= 146; + return mode; + },joinPath:function (parts, forceRelative) { + var path = PATH.join.apply(null, parts); + if (forceRelative && path[0] == '/') path = path.substr(1); + return path; + },absolutePath:function (relative, base) { + return PATH.resolve(base, relative); + },standardizePath:function (path) { + return PATH.normalize(path); + },findObject:function (path, dontResolveLastLink) { + var ret = FS.analyzePath(path, dontResolveLastLink); + if (ret.exists) { + return ret.object; + } else { + ___setErrNo(ret.error); + return null; + } + },analyzePath:function (path, dontResolveLastLink) { + // operate from within the context of the symlink's target + try { + var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + path = lookup.path; + } catch (e) { + } + var ret = { + isRoot: false, exists: false, error: 0, name: null, path: null, object: null, + parentExists: false, parentPath: null, parentObject: null + }; + try { + var lookup = FS.lookupPath(path, { parent: true }); + ret.parentExists = true; + ret.parentPath = lookup.path; + ret.parentObject = lookup.node; + ret.name = PATH.basename(path); + lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + ret.exists = true; + ret.path = lookup.path; + ret.object = lookup.node; + ret.name = lookup.node.name; + ret.isRoot = lookup.path === '/'; + } catch (e) { + ret.error = e.errno; + }; + return ret; + },createFolder:function (parent, name, canRead, canWrite) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(canRead, canWrite); + return FS.mkdir(path, mode); + },createPath:function (parent, path, canRead, canWrite) { + parent = typeof parent === 'string' ? parent : FS.getPath(parent); + var parts = path.split('/').reverse(); + while (parts.length) { + var part = parts.pop(); + if (!part) continue; + var current = PATH.join2(parent, part); + try { + FS.mkdir(current); + } catch (e) { + // ignore EEXIST + } + parent = current; + } + return current; + },createFile:function (parent, name, properties, canRead, canWrite) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(canRead, canWrite); + return FS.create(path, mode); + },createDataFile:function (parent, name, data, canRead, canWrite, canOwn) { + var path = name ? PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent; + var mode = FS.getMode(canRead, canWrite); + var node = FS.create(path, mode); + if (data) { + if (typeof data === 'string') { + var arr = new Array(data.length); + for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); + data = arr; + } + // make sure we can write to the file + FS.chmod(node, mode | 146); + var stream = FS.open(node, 'w'); + FS.write(stream, data, 0, data.length, 0, canOwn); + FS.close(stream); + FS.chmod(node, mode); + } + return node; + },createDevice:function (parent, name, input, output) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(!!input, !!output); + if (!FS.createDevice.major) FS.createDevice.major = 64; + var dev = FS.makedev(FS.createDevice.major++, 0); + // Create a fake device that a set of stream ops to emulate + // the old behavior. + FS.registerDevice(dev, { + open: function(stream) { + stream.seekable = false; + }, + close: function(stream) { + // flush any pending line data + if (output && output.buffer && output.buffer.length) { + output(10); + } + }, + read: function(stream, buffer, offset, length, pos /* ignored */) { + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = input(); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(ERRNO_CODES.EAGAIN); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset+i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write: function(stream, buffer, offset, length, pos) { + for (var i = 0; i < length; i++) { + try { + output(buffer[offset+i]); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }); + return FS.mkdev(path, mode, dev); + },createLink:function (parent, name, target, canRead, canWrite) { + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); + return FS.symlink(target, path); + },forceLoadFile:function (obj) { + if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; + var success = true; + if (typeof XMLHttpRequest !== 'undefined') { + throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + } else if (Module['read']) { + // Command-line. + try { + // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as + // read() will try to parse UTF8. + obj.contents = intArrayFromString(Module['read'](obj.url), true); + obj.usedBytes = obj.contents.length; + } catch (e) { + success = false; + } + } else { + throw new Error('Cannot load without read() or XMLHttpRequest.'); + } + if (!success) ___setErrNo(ERRNO_CODES.EIO); + return success; + },createLazyFile:function (parent, name, url, canRead, canWrite) { + // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. + function LazyUint8Array() { + this.lengthKnown = false; + this.chunks = []; // Loaded chunks. Index is the chunk number + } + LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { + if (idx > this.length-1 || idx < 0) { + return undefined; + } + var chunkOffset = idx % this.chunkSize; + var chunkNum = (idx / this.chunkSize)|0; + return this.getter(chunkNum)[chunkOffset]; + } + LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) { + this.getter = getter; + } + LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { + // Find length + var xhr = new XMLHttpRequest(); + xhr.open('HEAD', url, false); + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + var datalength = Number(xhr.getResponseHeader("Content-length")); + var header; + var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; + var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip"; + + var chunkSize = 1024*1024; // Chunk size in bytes + + if (!hasByteServing) chunkSize = datalength; + + // Function to get a range from the remote URL. + var doXHR = (function(from, to) { + if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); + if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); + + // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + + // Some hints to the browser that we want binary data. + if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; + if (xhr.overrideMimeType) { + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + } + + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== undefined) { + return new Uint8Array(xhr.response || []); + } else { + return intArrayFromString(xhr.responseText || '', true); + } + }); + var lazyArray = this; + lazyArray.setDataGetter(function(chunkNum) { + var start = chunkNum * chunkSize; + var end = (chunkNum+1) * chunkSize - 1; // including this byte + end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { + lazyArray.chunks[chunkNum] = doXHR(start, end); + } + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); + return lazyArray.chunks[chunkNum]; + }); + + if (usesGzip || !datalength) { + // if the server uses gzip or doesn't supply the length, we have to download the whole file to get the (uncompressed) length + chunkSize = datalength = 1; // this will force getter(0)/doXHR do download the whole file + datalength = this.getter(0).length; + chunkSize = datalength; + console.log("LazyFiles on gzip forces download of the whole file when length is accessed"); + } + + this._length = datalength; + this._chunkSize = chunkSize; + this.lengthKnown = true; + } + if (typeof XMLHttpRequest !== 'undefined') { + if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; + var lazyArray = new LazyUint8Array(); + Object.defineProperties(lazyArray, { + length: { + get: function() { + if(!this.lengthKnown) { + this.cacheLength(); + } + return this._length; + } + }, + chunkSize: { + get: function() { + if(!this.lengthKnown) { + this.cacheLength(); + } + return this._chunkSize; + } + } + }); + + var properties = { isDevice: false, contents: lazyArray }; + } else { + var properties = { isDevice: false, url: url }; + } + + var node = FS.createFile(parent, name, properties, canRead, canWrite); + // This is a total hack, but I want to get this lazy file code out of the + // core of MEMFS. If we want to keep this lazy file concept I feel it should + // be its own thin LAZYFS proxying calls to MEMFS. + if (properties.contents) { + node.contents = properties.contents; + } else if (properties.url) { + node.contents = null; + node.url = properties.url; + } + // Add a function that defers querying the file size until it is asked the first time. + Object.defineProperties(node, { + usedBytes: { + get: function() { return this.contents.length; } + } + }); + // override each stream op with one that tries to force load the lazy file first + var stream_ops = {}; + var keys = Object.keys(node.stream_ops); + keys.forEach(function(key) { + var fn = node.stream_ops[key]; + stream_ops[key] = function forceLoadLazyFile() { + if (!FS.forceLoadFile(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + return fn.apply(null, arguments); + }; + }); + // use a custom read function + stream_ops.read = function stream_ops_read(stream, buffer, offset, length, position) { + if (!FS.forceLoadFile(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + var contents = stream.node.contents; + if (position >= contents.length) + return 0; + var size = Math.min(contents.length - position, length); + assert(size >= 0); + if (contents.slice) { // normal array + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents[position + i]; + } + } else { + for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR + buffer[offset + i] = contents.get(position + i); + } + } + return size; + }; + node.stream_ops = stream_ops; + return node; + },createPreloadedFile:function (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) { + Browser.init(); // XXX perhaps this method should move onto Browser? + // TODO we should allow people to just pass in a complete filename instead + // of parent and name being that we just join them anyways + var fullname = name ? PATH.resolve(PATH.join2(parent, name)) : parent; + var dep = getUniqueRunDependency('cp ' + fullname); // might have several active requests for the same fullname + function processData(byteArray) { + function finish(byteArray) { + if (preFinish) preFinish(); + if (!dontCreateFile) { + FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); + } + if (onload) onload(); + removeRunDependency(dep); + } + var handled = false; + Module['preloadPlugins'].forEach(function(plugin) { + if (handled) return; + if (plugin['canHandle'](fullname)) { + plugin['handle'](byteArray, fullname, finish, function() { + if (onerror) onerror(); + removeRunDependency(dep); + }); + handled = true; + } + }); + if (!handled) finish(byteArray); + } + addRunDependency(dep); + if (typeof url == 'string') { + Browser.asyncLoad(url, function(byteArray) { + processData(byteArray); + }, onerror); + } else { + processData(url); + } + },indexedDB:function () { + return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + },DB_NAME:function () { + return 'EM_FS_' + window.location.pathname; + },DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:function (paths, onload, onerror) { + onload = onload || function(){}; + onerror = onerror || function(){}; + var indexedDB = FS.indexedDB(); + try { + var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); + } catch (e) { + return onerror(e); + } + openRequest.onupgradeneeded = function openRequest_onupgradeneeded() { + console.log('creating db'); + var db = openRequest.result; + db.createObjectStore(FS.DB_STORE_NAME); + }; + openRequest.onsuccess = function openRequest_onsuccess() { + var db = openRequest.result; + var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite'); + var files = transaction.objectStore(FS.DB_STORE_NAME); + var ok = 0, fail = 0, total = paths.length; + function finish() { + if (fail == 0) onload(); else onerror(); + } + paths.forEach(function(path) { + var putRequest = files.put(FS.analyzePath(path).object.contents, path); + putRequest.onsuccess = function putRequest_onsuccess() { ok++; if (ok + fail == total) finish() }; + putRequest.onerror = function putRequest_onerror() { fail++; if (ok + fail == total) finish() }; + }); + transaction.onerror = onerror; + }; + openRequest.onerror = onerror; + },loadFilesFromDB:function (paths, onload, onerror) { + onload = onload || function(){}; + onerror = onerror || function(){}; + var indexedDB = FS.indexedDB(); + try { + var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); + } catch (e) { + return onerror(e); + } + openRequest.onupgradeneeded = onerror; // no database to load from + openRequest.onsuccess = function openRequest_onsuccess() { + var db = openRequest.result; + try { + var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly'); + } catch(e) { + onerror(e); + return; + } + var files = transaction.objectStore(FS.DB_STORE_NAME); + var ok = 0, fail = 0, total = paths.length; + function finish() { + if (fail == 0) onload(); else onerror(); + } + paths.forEach(function(path) { + var getRequest = files.get(path); + getRequest.onsuccess = function getRequest_onsuccess() { + if (FS.analyzePath(path).exists) { + FS.unlink(path); + } + FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true); + ok++; + if (ok + fail == total) finish(); + }; + getRequest.onerror = function getRequest_onerror() { fail++; if (ok + fail == total) finish() }; + }); + transaction.onerror = onerror; + }; + openRequest.onerror = onerror; + }};var SYSCALLS={DEFAULT_POLLMASK:5,mappings:{},umask:511,calculateAt:function (dirfd, path) { + if (path[0] !== '/') { + // relative path + var dir; + if (dirfd === -100) { + dir = FS.cwd(); + } else { + var dirstream = FS.getStream(dirfd); + if (!dirstream) throw new FS.ErrnoError(ERRNO_CODES.EBADF); + dir = dirstream.path; + } + path = PATH.join2(dir, path); + } + return path; + },doStat:function (func, path, buf) { + try { + var stat = func(path); + } catch (e) { + if (e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node))) { + // an error occurred while trying to look up the path; we should just report ENOTDIR + return -ERRNO_CODES.ENOTDIR; + } + throw e; + } + HEAP32[((buf)>>2)]=stat.dev; + HEAP32[(((buf)+(4))>>2)]=0; + HEAP32[(((buf)+(8))>>2)]=stat.ino; + HEAP32[(((buf)+(12))>>2)]=stat.mode; + HEAP32[(((buf)+(16))>>2)]=stat.nlink; + HEAP32[(((buf)+(20))>>2)]=stat.uid; + HEAP32[(((buf)+(24))>>2)]=stat.gid; + HEAP32[(((buf)+(28))>>2)]=stat.rdev; + HEAP32[(((buf)+(32))>>2)]=0; + HEAP32[(((buf)+(36))>>2)]=stat.size; + HEAP32[(((buf)+(40))>>2)]=4096; + HEAP32[(((buf)+(44))>>2)]=stat.blocks; + HEAP32[(((buf)+(48))>>2)]=(stat.atime.getTime() / 1000)|0; + HEAP32[(((buf)+(52))>>2)]=0; + HEAP32[(((buf)+(56))>>2)]=(stat.mtime.getTime() / 1000)|0; + HEAP32[(((buf)+(60))>>2)]=0; + HEAP32[(((buf)+(64))>>2)]=(stat.ctime.getTime() / 1000)|0; + HEAP32[(((buf)+(68))>>2)]=0; + HEAP32[(((buf)+(72))>>2)]=stat.ino; + return 0; + },doMsync:function (addr, stream, len, flags) { + var buffer = new Uint8Array(HEAPU8.subarray(addr, addr + len)); + FS.msync(stream, buffer, 0, len, flags); + },doMkdir:function (path, mode) { + // remove a trailing slash, if one - /a/b/ has basename of '', but + // we want to create b in the context of this function + path = PATH.normalize(path); + if (path[path.length-1] === '/') path = path.substr(0, path.length-1); + FS.mkdir(path, mode, 0); + return 0; + },doMknod:function (path, mode, dev) { + // we don't want this in the JS API as it uses mknod to create all nodes. + switch (mode & 61440) { + case 32768: + case 8192: + case 24576: + case 4096: + case 49152: + break; + default: return -ERRNO_CODES.EINVAL; + } + FS.mknod(path, mode, dev); + return 0; + },doReadlink:function (path, buf, bufsize) { + if (bufsize <= 0) return -ERRNO_CODES.EINVAL; + var ret = FS.readlink(path); + + var len = Math.min(bufsize, lengthBytesUTF8(ret)); + var endChar = HEAP8[buf+len]; + stringToUTF8(ret, buf, bufsize+1); + // readlink is one of the rare functions that write out a C string, but does never append a null to the output buffer(!) + // stringToUTF8() always appends a null byte, so restore the character under the null byte after the write. + HEAP8[buf+len] = endChar; + + return len; + },doAccess:function (path, amode) { + if (amode & ~7) { + // need a valid mode + return -ERRNO_CODES.EINVAL; + } + var node; + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + var perms = ''; + if (amode & 4) perms += 'r'; + if (amode & 2) perms += 'w'; + if (amode & 1) perms += 'x'; + if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) { + return -ERRNO_CODES.EACCES; + } + return 0; + },doDup:function (path, flags, suggestFD) { + var suggest = FS.getStream(suggestFD); + if (suggest) FS.close(suggest); + return FS.open(path, flags, 0, suggestFD, suggestFD).fd; + },doReadv:function (stream, iov, iovcnt, offset) { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[(((iov)+(i*8))>>2)]; + var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; + var curr = FS.read(stream, HEAP8,ptr, len, offset); + if (curr < 0) return -1; + ret += curr; + if (curr < len) break; // nothing more to read + } + return ret; + },doWritev:function (stream, iov, iovcnt, offset) { + var ret = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[(((iov)+(i*8))>>2)]; + var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; + var curr = FS.write(stream, HEAP8,ptr, len, offset); + if (curr < 0) return -1; + ret += curr; + } + return ret; + },varargs:0,get:function (varargs) { + SYSCALLS.varargs += 4; + var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)]; + return ret; + },getStr:function () { + var ret = Pointer_stringify(SYSCALLS.get()); + return ret; + },getStreamFromFD:function () { + var stream = FS.getStream(SYSCALLS.get()); + if (!stream) throw new FS.ErrnoError(ERRNO_CODES.EBADF); + return stream; + },getSocketFromFD:function () { + var socket = SOCKFS.getSocket(SYSCALLS.get()); + if (!socket) throw new FS.ErrnoError(ERRNO_CODES.EBADF); + return socket; + },getSocketAddress:function (allowNull) { + var addrp = SYSCALLS.get(), addrlen = SYSCALLS.get(); + if (allowNull && addrp === 0) return null; + var info = __read_sockaddr(addrp, addrlen); + if (info.errno) throw new FS.ErrnoError(info.errno); + info.addr = DNS.lookup_addr(info.addr) || info.addr; + return info; + },get64:function () { + var low = SYSCALLS.get(), high = SYSCALLS.get(); + if (low >= 0) assert(high === 0); + else assert(high === -1); + return low; + },getZero:function () { + assert(SYSCALLS.get() === 0); + }};function ___syscall54(which, varargs) {SYSCALLS.varargs = varargs; + try { + // ioctl + var stream = SYSCALLS.getStreamFromFD(), op = SYSCALLS.get(); + switch (op) { + case 21505: { + if (!stream.tty) return -ERRNO_CODES.ENOTTY; + return 0; + } + case 21506: { + if (!stream.tty) return -ERRNO_CODES.ENOTTY; + return 0; // no-op, not actually adjusting terminal settings + } + case 21519: { + if (!stream.tty) return -ERRNO_CODES.ENOTTY; + var argp = SYSCALLS.get(); + HEAP32[((argp)>>2)]=0; + return 0; + } + case 21520: { + if (!stream.tty) return -ERRNO_CODES.ENOTTY; + return -ERRNO_CODES.EINVAL; // not supported + } + case 21531: { + var argp = SYSCALLS.get(); + return FS.ioctl(stream, op, argp); + } + case 21523: { + // TODO: in theory we should write to the winsize struct that gets + // passed in, but for now musl doesn't read anything on it + if (!stream.tty) return -ERRNO_CODES.ENOTTY; + return 0; + } + default: abort('bad ioctl syscall ' + op); + } + } catch (e) { + if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); + return -e.errno; + } + } + + function _emscripten_glSampleCoverage(value, invert) { + GLctx.sampleCoverage(value, !!invert); + } + + function _emscripten_glUniform4f(location, v0, v1, v2, v3) { + GLctx.uniform4f(GL.uniforms[location], v0, v1, v2, v3); + } + + function _emscripten_glFrustum() { + Module['printErr']('missing function: emscripten_glFrustum'); abort(-1); + } + + function _glVertexAttrib4f(x0, x1, x2, x3, x4) { GLctx['vertexAttrib4f'](x0, x1, x2, x3, x4) } + + function _glfwSetWindowSizeCallback(winid, cbfun) { + GLFW.setWindowSizeCallback(winid, cbfun); + } + + function _emscripten_glGetTexParameterfv(target, pname, params) { + if (!params) { + // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense + // if p == null, issue a GL error to notify user about it. + GL.recordError(0x0501 /* GL_INVALID_VALUE */); + return; + } + HEAPF32[((params)>>2)]=GLctx.getTexParameter(target, pname); + } + + function _emscripten_glUniform4i(location, v0, v1, v2, v3) { + GLctx.uniform4i(GL.uniforms[location], v0, v1, v2, v3); + } + + function _emscripten_glBindRenderbuffer(target, renderbuffer) { + GLctx.bindRenderbuffer(target, renderbuffer ? GL.renderbuffers[renderbuffer] : null); + } + + function _emscripten_glViewport(x0, x1, x2, x3) { GLctx['viewport'](x0, x1, x2, x3) } + + + var DLFCN={error:null,errorMsg:null,loadedLibs:{},loadedLibNames:{}};function _dlclose(handle) { + // int dlclose(void *handle); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlclose.html + if (!DLFCN.loadedLibs[handle]) { + DLFCN.errorMsg = 'Tried to dlclose() unopened handle: ' + handle; + return 1; + } else { + var lib_record = DLFCN.loadedLibs[handle]; + if (--lib_record.refcount == 0) { + if (lib_record.module.cleanups) { + lib_record.module.cleanups.forEach(function(cleanup) { cleanup() }); + } + delete DLFCN.loadedLibNames[lib_record.name]; + delete DLFCN.loadedLibs[handle]; + } + return 0; + } + } + + + + var JSEvents={keyEvent:0,mouseEvent:0,wheelEvent:0,uiEvent:0,focusEvent:0,deviceOrientationEvent:0,deviceMotionEvent:0,fullscreenChangeEvent:0,pointerlockChangeEvent:0,visibilityChangeEvent:0,touchEvent:0,lastGamepadState:null,lastGamepadStateFrame:null,numGamepadsConnected:0,previousFullscreenElement:null,previousScreenX:null,previousScreenY:null,removeEventListenersRegistered:false,staticInit:function () { + if (typeof window !== 'undefined') { + window.addEventListener("gamepadconnected", function() { ++JSEvents.numGamepadsConnected; }); + window.addEventListener("gamepaddisconnected", function() { --JSEvents.numGamepadsConnected; }); + + // Chromium does not fire the gamepadconnected event on reload, so we need to get the number of gamepads here as a workaround. + // See https://bugs.chromium.org/p/chromium/issues/detail?id=502824 + var firstState = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : null); + if (firstState) { + JSEvents.numGamepadsConnected = firstState.length; + } + } + },registerRemoveEventListeners:function () { + if (!JSEvents.removeEventListenersRegistered) { + __ATEXIT__.push(function() { + for(var i = JSEvents.eventHandlers.length-1; i >= 0; --i) { + JSEvents._removeHandler(i); + } + }); + JSEvents.removeEventListenersRegistered = true; + } + },findEventTarget:function (target) { + if (target) { + if (typeof target == "number") { + target = Pointer_stringify(target); + } + if (target == '#window') return window; + else if (target == '#document') return document; + else if (target == '#screen') return window.screen; + else if (target == '#canvas') return Module['canvas']; + + if (typeof target == 'string') return document.getElementById(target); + else return target; + } else { + // The sensible target varies between events, but use window as the default + // since DOM events mostly can default to that. Specific callback registrations + // override their own defaults. + return window; + } + },deferredCalls:[],deferCall:function (targetFunction, precedence, argsList) { + function arraysHaveEqualContent(arrA, arrB) { + if (arrA.length != arrB.length) return false; + + for(var i in arrA) { + if (arrA[i] != arrB[i]) return false; + } + return true; + } + // Test if the given call was already queued, and if so, don't add it again. + for(var i in JSEvents.deferredCalls) { + var call = JSEvents.deferredCalls[i]; + if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { + return; + } + } + JSEvents.deferredCalls.push({ + targetFunction: targetFunction, + precedence: precedence, + argsList: argsList + }); + + JSEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; }); + },removeDeferredCalls:function (targetFunction) { + for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { + if (JSEvents.deferredCalls[i].targetFunction == targetFunction) { + JSEvents.deferredCalls.splice(i, 1); + --i; + } + } + },canPerformEventHandlerRequests:function () { + return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls; + },runDeferredCalls:function () { + if (!JSEvents.canPerformEventHandlerRequests()) { + return; + } + for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { + var call = JSEvents.deferredCalls[i]; + JSEvents.deferredCalls.splice(i, 1); + --i; + call.targetFunction.apply(this, call.argsList); + } + },inEventHandler:0,currentEventHandler:null,eventHandlers:[],isInternetExplorer:function () { return navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0; },removeAllHandlersOnTarget:function (target, eventTypeString) { + for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if (JSEvents.eventHandlers[i].target == target && + (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) { + JSEvents._removeHandler(i--); + } + } + },_removeHandler:function (i) { + var h = JSEvents.eventHandlers[i]; + h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture); + JSEvents.eventHandlers.splice(i, 1); + },registerOrRemoveHandler:function (eventHandler) { + var jsEventHandler = function jsEventHandler(event) { + // Increment nesting count for the event handler. + ++JSEvents.inEventHandler; + JSEvents.currentEventHandler = eventHandler; + // Process any old deferred calls the user has placed. + JSEvents.runDeferredCalls(); + // Process the actual event, calls back to user C code handler. + eventHandler.handlerFunc(event); + // Process any new deferred calls that were placed right now from this event handler. + JSEvents.runDeferredCalls(); + // Out of event handler - restore nesting count. + --JSEvents.inEventHandler; + } + + if (eventHandler.callbackfunc) { + eventHandler.eventListenerFunc = jsEventHandler; + eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture); + JSEvents.eventHandlers.push(eventHandler); + JSEvents.registerRemoveEventListeners(); + } else { + for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if (JSEvents.eventHandlers[i].target == eventHandler.target + && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { + JSEvents._removeHandler(i--); + } + } + } + },registerKeyEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.keyEvent) { + JSEvents.keyEvent = _malloc( 164 ); + } + var handlerFunc = function(event) { + var e = event || window.event; + stringToUTF8(e.key ? e.key : "", JSEvents.keyEvent + 0, 32); + stringToUTF8(e.code ? e.code : "", JSEvents.keyEvent + 32, 32); + HEAP32[(((JSEvents.keyEvent)+(64))>>2)]=e.location; + HEAP32[(((JSEvents.keyEvent)+(68))>>2)]=e.ctrlKey; + HEAP32[(((JSEvents.keyEvent)+(72))>>2)]=e.shiftKey; + HEAP32[(((JSEvents.keyEvent)+(76))>>2)]=e.altKey; + HEAP32[(((JSEvents.keyEvent)+(80))>>2)]=e.metaKey; + HEAP32[(((JSEvents.keyEvent)+(84))>>2)]=e.repeat; + stringToUTF8(e.locale ? e.locale : "", JSEvents.keyEvent + 88, 32); + stringToUTF8(e.char ? e.char : "", JSEvents.keyEvent + 120, 32); + HEAP32[(((JSEvents.keyEvent)+(152))>>2)]=e.charCode; + HEAP32[(((JSEvents.keyEvent)+(156))>>2)]=e.keyCode; + HEAP32[(((JSEvents.keyEvent)+(160))>>2)]=e.which; + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.keyEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: JSEvents.isInternetExplorer() ? false : true, // MSIE doesn't allow fullscreen and pointerlock requests from key handlers, others do. + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },getBoundingClientRectOrZeros:function (target) { + return target.getBoundingClientRect ? target.getBoundingClientRect() : { left: 0, top: 0 }; + },fillMouseEventData:function (eventStruct, e, target) { + HEAPF64[((eventStruct)>>3)]=JSEvents.tick(); + HEAP32[(((eventStruct)+(8))>>2)]=e.screenX; + HEAP32[(((eventStruct)+(12))>>2)]=e.screenY; + HEAP32[(((eventStruct)+(16))>>2)]=e.clientX; + HEAP32[(((eventStruct)+(20))>>2)]=e.clientY; + HEAP32[(((eventStruct)+(24))>>2)]=e.ctrlKey; + HEAP32[(((eventStruct)+(28))>>2)]=e.shiftKey; + HEAP32[(((eventStruct)+(32))>>2)]=e.altKey; + HEAP32[(((eventStruct)+(36))>>2)]=e.metaKey; + HEAP16[(((eventStruct)+(40))>>1)]=e.button; + HEAP16[(((eventStruct)+(42))>>1)]=e.buttons; + HEAP32[(((eventStruct)+(44))>>2)]=e["movementX"] || e["mozMovementX"] || e["webkitMovementX"] || (e.screenX-JSEvents.previousScreenX); + HEAP32[(((eventStruct)+(48))>>2)]=e["movementY"] || e["mozMovementY"] || e["webkitMovementY"] || (e.screenY-JSEvents.previousScreenY); + + if (Module['canvas']) { + var rect = Module['canvas'].getBoundingClientRect(); + HEAP32[(((eventStruct)+(60))>>2)]=e.clientX - rect.left; + HEAP32[(((eventStruct)+(64))>>2)]=e.clientY - rect.top; + } else { // Canvas is not initialized, return 0. + HEAP32[(((eventStruct)+(60))>>2)]=0; + HEAP32[(((eventStruct)+(64))>>2)]=0; + } + if (target) { + var rect = JSEvents.getBoundingClientRectOrZeros(target); + HEAP32[(((eventStruct)+(52))>>2)]=e.clientX - rect.left; + HEAP32[(((eventStruct)+(56))>>2)]=e.clientY - rect.top; + } else { // No specific target passed, return 0. + HEAP32[(((eventStruct)+(52))>>2)]=0; + HEAP32[(((eventStruct)+(56))>>2)]=0; + } + // wheel and mousewheel events contain wrong screenX/screenY on chrome/opera + // https://github.com/kripken/emscripten/pull/4997 + // https://bugs.chromium.org/p/chromium/issues/detail?id=699956 + if (e.type !== 'wheel' && e.type !== 'mousewheel') { + JSEvents.previousScreenX = e.screenX; + JSEvents.previousScreenY = e.screenY; + } + },registerMouseEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.mouseEvent) { + JSEvents.mouseEvent = _malloc( 72 ); + } + target = JSEvents.findEventTarget(target); + var handlerFunc = function(event) { + var e = event || window.event; + JSEvents.fillMouseEventData(JSEvents.mouseEvent, e, target); + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.mouseEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: eventTypeString != 'mousemove' && eventTypeString != 'mouseenter' && eventTypeString != 'mouseleave', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + // In IE, mousedown events don't either allow deferred calls to be run! + if (JSEvents.isInternetExplorer() && eventTypeString == 'mousedown') eventHandler.allowsDeferredCalls = false; + JSEvents.registerOrRemoveHandler(eventHandler); + },registerWheelEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.wheelEvent) { + JSEvents.wheelEvent = _malloc( 104 ); + } + target = JSEvents.findEventTarget(target); + // The DOM Level 3 events spec event 'wheel' + var wheelHandlerFunc = function(event) { + var e = event || window.event; + JSEvents.fillMouseEventData(JSEvents.wheelEvent, e, target); + HEAPF64[(((JSEvents.wheelEvent)+(72))>>3)]=e["deltaX"]; + HEAPF64[(((JSEvents.wheelEvent)+(80))>>3)]=e["deltaY"]; + HEAPF64[(((JSEvents.wheelEvent)+(88))>>3)]=e["deltaZ"]; + HEAP32[(((JSEvents.wheelEvent)+(96))>>2)]=e["deltaMode"]; + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.wheelEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + // The 'mousewheel' event as implemented in Safari 6.0.5 + var mouseWheelHandlerFunc = function(event) { + var e = event || window.event; + JSEvents.fillMouseEventData(JSEvents.wheelEvent, e, target); + HEAPF64[(((JSEvents.wheelEvent)+(72))>>3)]=e["wheelDeltaX"] || 0; + HEAPF64[(((JSEvents.wheelEvent)+(80))>>3)]=-(e["wheelDeltaY"] ? e["wheelDeltaY"] : e["wheelDelta"]) /* 1. Invert to unify direction with the DOM Level 3 wheel event. 2. MSIE does not provide wheelDeltaY, so wheelDelta is used as a fallback. */; + HEAPF64[(((JSEvents.wheelEvent)+(88))>>3)]=0 /* Not available */; + HEAP32[(((JSEvents.wheelEvent)+(96))>>2)]=0 /* DOM_DELTA_PIXEL */; + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.wheelEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: true, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: (eventTypeString == 'wheel') ? wheelHandlerFunc : mouseWheelHandlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },pageScrollPos:function () { + if (window.pageXOffset > 0 || window.pageYOffset > 0) { + return [window.pageXOffset, window.pageYOffset]; + } + if (typeof document.documentElement.scrollLeft !== 'undefined' || typeof document.documentElement.scrollTop !== 'undefined') { + return [document.documentElement.scrollLeft, document.documentElement.scrollTop]; + } + return [document.body.scrollLeft|0, document.body.scrollTop|0]; + },registerUiEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.uiEvent) { + JSEvents.uiEvent = _malloc( 36 ); + } + + if (eventTypeString == "scroll" && !target) { + target = document; // By default read scroll events on document rather than window. + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + if (e.target != target) { + // Never take ui events such as scroll via a 'bubbled' route, but always from the direct element that + // was targeted. Otherwise e.g. if app logs a message in response to a page scroll, the Emscripten log + // message box could cause to scroll, generating a new (bubbled) scroll message, causing a new log print, + // causing a new scroll, etc.. + return; + } + var scrollPos = JSEvents.pageScrollPos(); + HEAP32[((JSEvents.uiEvent)>>2)]=e.detail; + HEAP32[(((JSEvents.uiEvent)+(4))>>2)]=document.body.clientWidth; + HEAP32[(((JSEvents.uiEvent)+(8))>>2)]=document.body.clientHeight; + HEAP32[(((JSEvents.uiEvent)+(12))>>2)]=window.innerWidth; + HEAP32[(((JSEvents.uiEvent)+(16))>>2)]=window.innerHeight; + HEAP32[(((JSEvents.uiEvent)+(20))>>2)]=window.outerWidth; + HEAP32[(((JSEvents.uiEvent)+(24))>>2)]=window.outerHeight; + HEAP32[(((JSEvents.uiEvent)+(28))>>2)]=scrollPos[0]; + HEAP32[(((JSEvents.uiEvent)+(32))>>2)]=scrollPos[1]; + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.uiEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, // Neither scroll or resize events allow running requests inside them. + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },getNodeNameForTarget:function (target) { + if (!target) return ''; + if (target == window) return '#window'; + if (target == window.screen) return '#screen'; + return (target && target.nodeName) ? target.nodeName : ''; + },registerFocusEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.focusEvent) { + JSEvents.focusEvent = _malloc( 256 ); + } + var handlerFunc = function(event) { + var e = event || window.event; + + var nodeName = JSEvents.getNodeNameForTarget(e.target); + var id = e.target.id ? e.target.id : ''; + stringToUTF8(nodeName, JSEvents.focusEvent + 0, 128); + stringToUTF8(id, JSEvents.focusEvent + 128, 128); + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.focusEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },tick:function () { + if (window['performance'] && window['performance']['now']) return window['performance']['now'](); + else return Date.now(); + },registerDeviceOrientationEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.deviceOrientationEvent) { + JSEvents.deviceOrientationEvent = _malloc( 40 ); + } + var handlerFunc = function(event) { + var e = event || window.event; + + HEAPF64[((JSEvents.deviceOrientationEvent)>>3)]=JSEvents.tick(); + HEAPF64[(((JSEvents.deviceOrientationEvent)+(8))>>3)]=e.alpha; + HEAPF64[(((JSEvents.deviceOrientationEvent)+(16))>>3)]=e.beta; + HEAPF64[(((JSEvents.deviceOrientationEvent)+(24))>>3)]=e.gamma; + HEAP32[(((JSEvents.deviceOrientationEvent)+(32))>>2)]=e.absolute; + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.deviceOrientationEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },registerDeviceMotionEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.deviceMotionEvent) { + JSEvents.deviceMotionEvent = _malloc( 80 ); + } + var handlerFunc = function(event) { + var e = event || window.event; + + HEAPF64[((JSEvents.deviceMotionEvent)>>3)]=JSEvents.tick(); + HEAPF64[(((JSEvents.deviceMotionEvent)+(8))>>3)]=e.acceleration.x; + HEAPF64[(((JSEvents.deviceMotionEvent)+(16))>>3)]=e.acceleration.y; + HEAPF64[(((JSEvents.deviceMotionEvent)+(24))>>3)]=e.acceleration.z; + HEAPF64[(((JSEvents.deviceMotionEvent)+(32))>>3)]=e.accelerationIncludingGravity.x; + HEAPF64[(((JSEvents.deviceMotionEvent)+(40))>>3)]=e.accelerationIncludingGravity.y; + HEAPF64[(((JSEvents.deviceMotionEvent)+(48))>>3)]=e.accelerationIncludingGravity.z; + HEAPF64[(((JSEvents.deviceMotionEvent)+(56))>>3)]=e.rotationRate.alpha; + HEAPF64[(((JSEvents.deviceMotionEvent)+(64))>>3)]=e.rotationRate.beta; + HEAPF64[(((JSEvents.deviceMotionEvent)+(72))>>3)]=e.rotationRate.gamma; + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.deviceMotionEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },screenOrientation:function () { + if (!window.screen) return undefined; + return window.screen.orientation || window.screen.mozOrientation || window.screen.webkitOrientation || window.screen.msOrientation; + },fillOrientationChangeEventData:function (eventStruct, e) { + var orientations = ["portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary"]; + var orientations2 = ["portrait", "portrait", "landscape", "landscape"]; + + var orientationString = JSEvents.screenOrientation(); + var orientation = orientations.indexOf(orientationString); + if (orientation == -1) { + orientation = orientations2.indexOf(orientationString); + } + + HEAP32[((eventStruct)>>2)]=1 << orientation; + HEAP32[(((eventStruct)+(4))>>2)]=window.orientation; + },registerOrientationChangeEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.orientationChangeEvent) { + JSEvents.orientationChangeEvent = _malloc( 8 ); + } + + if (!target) { + target = window.screen; // Orientation events need to be captured from 'window.screen' instead of 'window' + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillOrientationChangeEventData(JSEvents.orientationChangeEvent, e); + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.orientationChangeEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + if (eventTypeString == "orientationchange" && window.screen.mozOrientation !== undefined) { + eventTypeString = "mozorientationchange"; + } + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },fullscreenEnabled:function () { + return document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled; + },fillFullscreenChangeEventData:function (eventStruct, e) { + var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; + var isFullscreen = !!fullscreenElement; + HEAP32[((eventStruct)>>2)]=isFullscreen; + HEAP32[(((eventStruct)+(4))>>2)]=JSEvents.fullscreenEnabled(); + // If transitioning to fullscreen, report info about the element that is now fullscreen. + // If transitioning to windowed mode, report info about the element that just was fullscreen. + var reportedElement = isFullscreen ? fullscreenElement : JSEvents.previousFullscreenElement; + var nodeName = JSEvents.getNodeNameForTarget(reportedElement); + var id = (reportedElement && reportedElement.id) ? reportedElement.id : ''; + stringToUTF8(nodeName, eventStruct + 8, 128); + stringToUTF8(id, eventStruct + 136, 128); + HEAP32[(((eventStruct)+(264))>>2)]=reportedElement ? reportedElement.clientWidth : 0; + HEAP32[(((eventStruct)+(268))>>2)]=reportedElement ? reportedElement.clientHeight : 0; + HEAP32[(((eventStruct)+(272))>>2)]=screen.width; + HEAP32[(((eventStruct)+(276))>>2)]=screen.height; + if (isFullscreen) { + JSEvents.previousFullscreenElement = fullscreenElement; + } + },registerFullscreenChangeEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.fullscreenChangeEvent) { + JSEvents.fullscreenChangeEvent = _malloc( 280 ); + } + + if (!target) { + target = document; // Fullscreen change events need to be captured from 'document' by default instead of 'window' + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillFullscreenChangeEventData(JSEvents.fullscreenChangeEvent, e); + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.fullscreenChangeEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },resizeCanvasForFullscreen:function (target, strategy) { + var restoreOldStyle = __registerRestoreOldStyle(target); + var cssWidth = strategy.softFullscreen ? window.innerWidth : screen.width; + var cssHeight = strategy.softFullscreen ? window.innerHeight : screen.height; + var rect = target.getBoundingClientRect(); + var windowedCssWidth = rect.right - rect.left; + var windowedCssHeight = rect.bottom - rect.top; + var windowedRttWidth = target.width; + var windowedRttHeight = target.height; + + if (strategy.scaleMode == 3) { + __setLetterbox(target, (cssHeight - windowedCssHeight) / 2, (cssWidth - windowedCssWidth) / 2); + cssWidth = windowedCssWidth; + cssHeight = windowedCssHeight; + } else if (strategy.scaleMode == 2) { + if (cssWidth*windowedRttHeight < windowedRttWidth*cssHeight) { + var desiredCssHeight = windowedRttHeight * cssWidth / windowedRttWidth; + __setLetterbox(target, (cssHeight - desiredCssHeight) / 2, 0); + cssHeight = desiredCssHeight; + } else { + var desiredCssWidth = windowedRttWidth * cssHeight / windowedRttHeight; + __setLetterbox(target, 0, (cssWidth - desiredCssWidth) / 2); + cssWidth = desiredCssWidth; + } + } + + // If we are adding padding, must choose a background color or otherwise Chrome will give the + // padding a default white color. Do it only if user has not customized their own background color. + if (!target.style.backgroundColor) target.style.backgroundColor = 'black'; + // IE11 does the same, but requires the color to be set in the document body. + if (!document.body.style.backgroundColor) document.body.style.backgroundColor = 'black'; // IE11 + // Firefox always shows black letterboxes independent of style color. + + target.style.width = cssWidth + 'px'; + target.style.height = cssHeight + 'px'; + + if (strategy.filteringMode == 1) { + target.style.imageRendering = 'optimizeSpeed'; + target.style.imageRendering = '-moz-crisp-edges'; + target.style.imageRendering = '-o-crisp-edges'; + target.style.imageRendering = '-webkit-optimize-contrast'; + target.style.imageRendering = 'optimize-contrast'; + target.style.imageRendering = 'crisp-edges'; + target.style.imageRendering = 'pixelated'; + } + + var dpiScale = (strategy.canvasResolutionScaleMode == 2) ? window.devicePixelRatio : 1; + if (strategy.canvasResolutionScaleMode != 0) { + target.width = cssWidth * dpiScale; + target.height = cssHeight * dpiScale; + if (target.GLctxObject) target.GLctxObject.GLctx.viewport(0, 0, target.width, target.height); + } + return restoreOldStyle; + },requestFullscreen:function (target, strategy) { + // EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT + EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE is a mode where no extra logic is performed to the DOM elements. + if (strategy.scaleMode != 0 || strategy.canvasResolutionScaleMode != 0) { + JSEvents.resizeCanvasForFullscreen(target, strategy); + } + + if (target.requestFullscreen) { + target.requestFullscreen(); + } else if (target.msRequestFullscreen) { + target.msRequestFullscreen(); + } else if (target.mozRequestFullScreen) { + target.mozRequestFullScreen(); + } else if (target.mozRequestFullscreen) { + target.mozRequestFullscreen(); + } else if (target.webkitRequestFullscreen) { + target.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } else { + if (typeof JSEvents.fullscreenEnabled() === 'undefined') { + return -1; + } else { + return -3; + } + } + + if (strategy.canvasResizedCallback) { + Module['dynCall_iiii'](strategy.canvasResizedCallback, 37, 0, strategy.canvasResizedCallbackUserData); + } + + return 0; + },fillPointerlockChangeEventData:function (eventStruct, e) { + var pointerLockElement = document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement || document.msPointerLockElement; + var isPointerlocked = !!pointerLockElement; + HEAP32[((eventStruct)>>2)]=isPointerlocked; + var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement); + var id = (pointerLockElement && pointerLockElement.id) ? pointerLockElement.id : ''; + stringToUTF8(nodeName, eventStruct + 4, 128); + stringToUTF8(id, eventStruct + 132, 128); + },registerPointerlockChangeEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.pointerlockChangeEvent) { + JSEvents.pointerlockChangeEvent = _malloc( 260 ); + } + + if (!target) { + target = document; // Pointer lock change events need to be captured from 'document' by default instead of 'window' + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillPointerlockChangeEventData(JSEvents.pointerlockChangeEvent, e); + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.pointerlockChangeEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },registerPointerlockErrorEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!target) { + target = document; // Pointer lock events need to be captured from 'document' by default instead of 'window' + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, 0, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },requestPointerLock:function (target) { + if (target.requestPointerLock) { + target.requestPointerLock(); + } else if (target.mozRequestPointerLock) { + target.mozRequestPointerLock(); + } else if (target.webkitRequestPointerLock) { + target.webkitRequestPointerLock(); + } else if (target.msRequestPointerLock) { + target.msRequestPointerLock(); + } else { + // document.body is known to accept pointer lock, so use that to differentiate if the user passed a bad element, + // or if the whole browser just doesn't support the feature. + if (document.body.requestPointerLock || document.body.mozRequestPointerLock || document.body.webkitRequestPointerLock || document.body.msRequestPointerLock) { + return -3; + } else { + return -1; + } + } + return 0; + },fillVisibilityChangeEventData:function (eventStruct, e) { + var visibilityStates = [ "hidden", "visible", "prerender", "unloaded" ]; + var visibilityState = visibilityStates.indexOf(document.visibilityState); + + HEAP32[((eventStruct)>>2)]=document.hidden; + HEAP32[(((eventStruct)+(4))>>2)]=visibilityState; + },registerVisibilityChangeEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.visibilityChangeEvent) { + JSEvents.visibilityChangeEvent = _malloc( 8 ); + } + + if (!target) { + target = document; // Visibility change events need to be captured from 'document' by default instead of 'window' + } else { + target = JSEvents.findEventTarget(target); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillVisibilityChangeEventData(JSEvents.visibilityChangeEvent, e); + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.visibilityChangeEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },registerTouchEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.touchEvent) { + JSEvents.touchEvent = _malloc( 1684 ); + } + + target = JSEvents.findEventTarget(target); + + var handlerFunc = function(event) { + var e = event || window.event; + + var touches = {}; + for(var i = 0; i < e.touches.length; ++i) { + var touch = e.touches[i]; + touches[touch.identifier] = touch; + } + for(var i = 0; i < e.changedTouches.length; ++i) { + var touch = e.changedTouches[i]; + touches[touch.identifier] = touch; + touch.changed = true; + } + for(var i = 0; i < e.targetTouches.length; ++i) { + var touch = e.targetTouches[i]; + touches[touch.identifier].onTarget = true; + } + + var ptr = JSEvents.touchEvent; + HEAP32[(((ptr)+(4))>>2)]=e.ctrlKey; + HEAP32[(((ptr)+(8))>>2)]=e.shiftKey; + HEAP32[(((ptr)+(12))>>2)]=e.altKey; + HEAP32[(((ptr)+(16))>>2)]=e.metaKey; + ptr += 20; // Advance to the start of the touch array. + var canvasRect = Module['canvas'] ? Module['canvas'].getBoundingClientRect() : undefined; + var targetRect = JSEvents.getBoundingClientRectOrZeros(target); + var numTouches = 0; + for(var i in touches) { + var t = touches[i]; + HEAP32[((ptr)>>2)]=t.identifier; + HEAP32[(((ptr)+(4))>>2)]=t.screenX; + HEAP32[(((ptr)+(8))>>2)]=t.screenY; + HEAP32[(((ptr)+(12))>>2)]=t.clientX; + HEAP32[(((ptr)+(16))>>2)]=t.clientY; + HEAP32[(((ptr)+(20))>>2)]=t.pageX; + HEAP32[(((ptr)+(24))>>2)]=t.pageY; + HEAP32[(((ptr)+(28))>>2)]=t.changed; + HEAP32[(((ptr)+(32))>>2)]=t.onTarget; + if (canvasRect) { + HEAP32[(((ptr)+(44))>>2)]=t.clientX - canvasRect.left; + HEAP32[(((ptr)+(48))>>2)]=t.clientY - canvasRect.top; + } else { + HEAP32[(((ptr)+(44))>>2)]=0; + HEAP32[(((ptr)+(48))>>2)]=0; + } + HEAP32[(((ptr)+(36))>>2)]=t.clientX - targetRect.left; + HEAP32[(((ptr)+(40))>>2)]=t.clientY - targetRect.top; + + ptr += 52; + + if (++numTouches >= 32) { + break; + } + } + HEAP32[((JSEvents.touchEvent)>>2)]=numTouches; + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.touchEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: target, + allowsDeferredCalls: eventTypeString == 'touchstart' || eventTypeString == 'touchend', + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },fillGamepadEventData:function (eventStruct, e) { + HEAPF64[((eventStruct)>>3)]=e.timestamp; + for(var i = 0; i < e.axes.length; ++i) { + HEAPF64[(((eventStruct+i*8)+(16))>>3)]=e.axes[i]; + } + for(var i = 0; i < e.buttons.length; ++i) { + if (typeof(e.buttons[i]) === 'object') { + HEAPF64[(((eventStruct+i*8)+(528))>>3)]=e.buttons[i].value; + } else { + HEAPF64[(((eventStruct+i*8)+(528))>>3)]=e.buttons[i]; + } + } + for(var i = 0; i < e.buttons.length; ++i) { + if (typeof(e.buttons[i]) === 'object') { + HEAP32[(((eventStruct+i*4)+(1040))>>2)]=e.buttons[i].pressed; + } else { + HEAP32[(((eventStruct+i*4)+(1040))>>2)]=e.buttons[i] == 1.0; + } + } + HEAP32[(((eventStruct)+(1296))>>2)]=e.connected; + HEAP32[(((eventStruct)+(1300))>>2)]=e.index; + HEAP32[(((eventStruct)+(8))>>2)]=e.axes.length; + HEAP32[(((eventStruct)+(12))>>2)]=e.buttons.length; + stringToUTF8(e.id, eventStruct + 1304, 64); + stringToUTF8(e.mapping, eventStruct + 1368, 64); + },registerGamepadEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.gamepadEvent) { + JSEvents.gamepadEvent = _malloc( 1432 ); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillGamepadEventData(JSEvents.gamepadEvent, e.gamepad); + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.gamepadEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: true, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },registerBeforeUnloadEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + var handlerFunc = function(event) { + var e = event || window.event; + + var confirmationMessage = Module['dynCall_iiii'](callbackfunc, eventTypeId, 0, userData); + + if (confirmationMessage) { + confirmationMessage = Pointer_stringify(confirmationMessage); + } + if (confirmationMessage) { + e.preventDefault(); + e.returnValue = confirmationMessage; + return confirmationMessage; + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },battery:function () { return navigator.battery || navigator.mozBattery || navigator.webkitBattery; },fillBatteryEventData:function (eventStruct, e) { + HEAPF64[((eventStruct)>>3)]=e.chargingTime; + HEAPF64[(((eventStruct)+(8))>>3)]=e.dischargingTime; + HEAPF64[(((eventStruct)+(16))>>3)]=e.level; + HEAP32[(((eventStruct)+(24))>>2)]=e.charging; + },registerBatteryEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!JSEvents.batteryEvent) { + JSEvents.batteryEvent = _malloc( 32 ); + } + + var handlerFunc = function(event) { + var e = event || window.event; + + JSEvents.fillBatteryEventData(JSEvents.batteryEvent, JSEvents.battery()); + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, JSEvents.batteryEvent, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + },registerWebGlEventCallback:function (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { + if (!target) { + target = Module['canvas']; + } + var handlerFunc = function(event) { + var e = event || window.event; + + var shouldCancel = Module['dynCall_iiii'](callbackfunc, eventTypeId, 0, userData); + if (shouldCancel) { + e.preventDefault(); + } + }; + + var eventHandler = { + target: JSEvents.findEventTarget(target), + allowsDeferredCalls: false, + eventTypeString: eventTypeString, + callbackfunc: callbackfunc, + handlerFunc: handlerFunc, + useCapture: useCapture + }; + JSEvents.registerOrRemoveHandler(eventHandler); + }};function __emscripten_sample_gamepad_data() { + // Polling gamepads generates garbage, so don't do it when we know there are no gamepads connected. + if (!JSEvents.numGamepadsConnected) return; + + // Produce a new Gamepad API sample if we are ticking a new game frame, or if not using emscripten_set_main_loop() at all to drive animation. + if (Browser.mainLoop.currentFrameNumber !== JSEvents.lastGamepadStateFrame || !Browser.mainLoop.currentFrameNumber) { + JSEvents.lastGamepadState = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : null); + JSEvents.lastGamepadStateFrame = Browser.mainLoop.currentFrameNumber; + } + }function _emscripten_get_gamepad_status(index, gamepadState) { + __emscripten_sample_gamepad_data(); + if (!JSEvents.lastGamepadState) return -1; + + // INVALID_PARAM is returned on a Gamepad index that never was there. + if (index < 0 || index >= JSEvents.lastGamepadState.length) return -5; + + // NO_DATA is returned on a Gamepad index that was removed. + // For previously disconnected gamepads there should be an empty slot (null/undefined/false) at the index. + // This is because gamepads must keep their original position in the array. + // For example, removing the first of two gamepads produces [null/undefined/false, gamepad]. + if (!JSEvents.lastGamepadState[index]) return -7; + + JSEvents.fillGamepadEventData(gamepadState, JSEvents.lastGamepadState[index]); + return 0; + } + + var _llvm_pow_f64=Math_pow; + + function _emscripten_glCopyTexImage2D(x0, x1, x2, x3, x4, x5, x6, x7) { GLctx['copyTexImage2D'](x0, x1, x2, x3, x4, x5, x6, x7) } + + function _emscripten_glTexParameterfv(target, pname, params) { + var param = HEAPF32[((params)>>2)]; + GLctx.texParameterf(target, pname, param); + } + + + + + + + + + + function _SDL_CloseAudio() { + if (SDL.audio) { + _SDL_PauseAudio(1); + _free(SDL.audio.buffer); + SDL.audio = null; + SDL.allocateChannels(0); + } + } + + + + + + + + + + + + + + var _environ=STATICTOP; STATICTOP += 16;;var ___environ=_environ;function ___buildEnvironment(env) { + // WARNING: Arbitrary limit! + var MAX_ENV_VALUES = 64; + var TOTAL_ENV_SIZE = 1024; + + // Statically allocate memory for the environment. + var poolPtr; + var envPtr; + if (!___buildEnvironment.called) { + ___buildEnvironment.called = true; + // Set default values. Use string keys for Closure Compiler compatibility. + ENV['USER'] = ENV['LOGNAME'] = 'web_user'; + ENV['PATH'] = '/'; + ENV['PWD'] = '/'; + ENV['HOME'] = '/home/web_user'; + ENV['LANG'] = 'C'; + ENV['_'] = Module['thisProgram']; + // Allocate memory. + poolPtr = allocate(TOTAL_ENV_SIZE, 'i8', ALLOC_STATIC); + envPtr = allocate(MAX_ENV_VALUES * 4, + 'i8*', ALLOC_STATIC); + HEAP32[((envPtr)>>2)]=poolPtr; + HEAP32[((_environ)>>2)]=envPtr; + } else { + envPtr = HEAP32[((_environ)>>2)]; + poolPtr = HEAP32[((envPtr)>>2)]; + } + + // Collect key=value lines. + var strings = []; + var totalSize = 0; + for (var key in env) { + if (typeof env[key] === 'string') { + var line = key + '=' + env[key]; + strings.push(line); + totalSize += line.length; + } + } + if (totalSize > TOTAL_ENV_SIZE) { + throw new Error('Environment size exceeded TOTAL_ENV_SIZE!'); + } + + // Make new. + var ptrSize = 4; + for (var i = 0; i < strings.length; i++) { + var line = strings[i]; + writeAsciiToMemory(line, poolPtr); + HEAP32[(((envPtr)+(i * ptrSize))>>2)]=poolPtr; + poolPtr += line.length + 1; + } + HEAP32[(((envPtr)+(strings.length * ptrSize))>>2)]=0; + }var ENV={};function _getenv(name) { + // char *getenv(const char *name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/getenv.html + if (name === 0) return 0; + name = Pointer_stringify(name); + if (!ENV.hasOwnProperty(name)) return 0; + + if (_getenv.ret) _free(_getenv.ret); + _getenv.ret = allocate(intArrayFromString(ENV[name]), 'i8', ALLOC_NORMAL); + return _getenv.ret; + } + + function _putenv(string) { + // int putenv(char *string); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/putenv.html + // WARNING: According to the standard (and the glibc implementation), the + // string is taken by reference so future changes are reflected. + // We copy it instead, possibly breaking some uses. + if (string === 0) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + string = Pointer_stringify(string); + var splitPoint = string.indexOf('=') + if (string === '' || string.indexOf('=') === -1) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + var name = string.slice(0, splitPoint); + var value = string.slice(splitPoint + 1); + if (!(name in ENV) || ENV[name] !== value) { + ENV[name] = value; + ___buildEnvironment(ENV); + } + return 0; + } + + function _SDL_RWFromConstMem(mem, size) { + var id = SDL.rwops.length; // TODO: recycle ids when they are null + SDL.rwops.push({ bytes: mem, count: size }); + return id; + }function _TTF_FontHeight(font) { + var fontData = SDL.fonts[font]; + return fontData.size; + }function _TTF_SizeText(font, text, w, h) { + var fontData = SDL.fonts[font]; + if (w) { + HEAP32[((w)>>2)]=SDL.estimateTextWidth(fontData, Pointer_stringify(text)); + } + if (h) { + HEAP32[((h)>>2)]=fontData.size; + } + return 0; + }function _TTF_RenderText_Solid(font, text, color) { + // XXX the font and color are ignored + text = Pointer_stringify(text) || ' '; // if given an empty string, still return a valid surface + var fontData = SDL.fonts[font]; + var w = SDL.estimateTextWidth(fontData, text); + var h = fontData.size; + var color = SDL.loadColorToCSSRGB(color); // XXX alpha breaks fonts? + var fontString = SDL.makeFontString(h, fontData.name); + var surf = SDL.makeSurface(w, h, 0, false, 'text:' + text); // bogus numbers.. + var surfData = SDL.surfaces[surf]; + surfData.ctx.save(); + surfData.ctx.fillStyle = color; + surfData.ctx.font = fontString; + // use bottom alligment, because it works + // same in all browsers, more info here: + // https://bugzilla.mozilla.org/show_bug.cgi?id=737852 + surfData.ctx.textBaseline = 'bottom'; + surfData.ctx.fillText(text, 0, h|0); + surfData.ctx.restore(); + return surf; + }function _Mix_HaltMusic() { + var audio = SDL.music.audio; + if (audio) { + audio.src = audio.src; // rewind element + audio.currentPosition = 0; // rewind Web Audio graph playback. + audio.pause(); + } + SDL.music.audio = null; + if (SDL.hookMusicFinished) { + func(); + } + return 0; + }function _Mix_PlayMusic(id, loops) { + // Pause old music if it exists. + if (SDL.music.audio) { + if (!SDL.music.audio.paused) Module.printErr('Music is already playing. ' + SDL.music.source); + SDL.music.audio.pause(); + } + var info = SDL.audios[id]; + var audio; + if (info.webAudio) { // Play via Web Audio API + // Create an instance of the WebAudio object. + audio = {}; + audio.resource = info; // This new webAudio object is an instance that refers to this existing resource. + audio.paused = false; + audio.currentPosition = 0; + audio.play = function() { SDL.playWebAudio(this); } + audio.pause = function() { SDL.pauseWebAudio(this); } + } else if (info.audio) { // Play via the

o3#1nJY`)hgg$dCVfEj`3T+c;q+A#KYzfs6$2 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 z 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 *?lZr+aC*yDWD*PZ?+=G_n^=U+>T_yO6 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 RbNR8vXZr#G00004Sz6JJ9r^$O literal 0 HcmV?d00001 diff --git a/examples/others/resources/main.go b/examples/others/resources/main.go new file mode 100644 index 0000000..733b82b --- /dev/null +++ b/examples/others/resources/main.go @@ -0,0 +1,95 @@ +package main + +import ( + //"bytes" + + "github.com/gen2brain/raylib-go/raylib" +) + +const numTextures = 4 + +func main() { + screenWidth := int32(800) + screenHeight := int32(450) + + raylib.InitWindow(screenWidth, screenHeight, "raylib [core] example - resources loading") + + raylib.InitAudioDevice() + + // OpenAsset() will also work on Android (reads files from assets/) + reader, err := raylib.OpenAsset("data.rres") + if err != nil { + raylib.TraceLog(raylib.LogWarning, "[%s] rRES raylib resource file could not be opened: %v", "data.rres", err) + } + + defer reader.Close() + + // bindata + //b := MustAsset("data.rres") + //reader := bytes.NewReader(b) + + res := raylib.LoadResource(reader, 0, nil) + wav := raylib.LoadWaveEx(res.Data, int32(res.Param1), int32(res.Param2), int32(res.Param3), int32(res.Param4)) + snd := raylib.LoadSoundFromWave(wav) + raylib.UnloadWave(wav) + + textures := make([]raylib.Texture2D, numTextures) + for i := 0; i < numTextures; i++ { + r := raylib.LoadResource(reader, i+1, nil) + image := raylib.LoadImagePro(r.Data, int32(r.Param1), int32(r.Param2), raylib.TextureFormat(r.Param3)) + textures[i] = raylib.LoadTextureFromImage(image) + raylib.UnloadImage(image) + } + + currentTexture := 0 + + raylib.SetTargetFPS(60) + + for !raylib.WindowShouldClose() { + if raylib.IsKeyPressed(raylib.KeySpace) { + raylib.PlaySound(snd) + } + + if raylib.IsMouseButtonPressed(raylib.MouseLeftButton) { + currentTexture = (currentTexture + 1) % numTextures // Cycle between the textures + } + + raylib.BeginDrawing() + + raylib.ClearBackground(raylib.RayWhite) + + raylib.DrawTexture(textures[currentTexture], screenWidth/2-textures[currentTexture].Width/2, screenHeight/2-textures[currentTexture].Height/2, raylib.RayWhite) + + raylib.DrawText("MOUSE LEFT BUTTON to CYCLE TEXTURES", 40, 410, 10, raylib.Gray) + raylib.DrawText("SPACE to PLAY SOUND", 40, 430, 10, raylib.Gray) + + switch currentTexture { + case 0: + raylib.DrawText("GIF", 272, 70, 20, raylib.Gray) + break + case 1: + raylib.DrawText("JPEG", 272, 70, 20, raylib.Gray) + break + case 2: + raylib.DrawText("PNG", 272, 70, 20, raylib.Gray) + break + case 3: + raylib.DrawText("TGA", 272, 70, 20, raylib.Gray) + break + default: + break + } + + raylib.EndDrawing() + } + + raylib.UnloadSound(snd) + + for _, t := range textures { + raylib.UnloadTexture(t) + } + + raylib.CloseAudioDevice() + + raylib.CloseWindow() +} diff --git a/raylib/audio.go b/raylib/audio.go index 4dc8627..486f725 100644 --- a/raylib/audio.go +++ b/raylib/audio.go @@ -132,12 +132,13 @@ func LoadWave(fileName string) Wave { } // LoadWaveEx - Load wave data from float array data (32bit) -func LoadWaveEx(data unsafe.Pointer, sampleCount int32, sampleRate int32, sampleSize int32, channels int32) Wave { +func LoadWaveEx(data []byte, sampleCount int32, sampleRate int32, sampleSize int32, channels int32) Wave { + cdata := unsafe.Pointer(&data[0]) csampleCount := (C.int)(sampleCount) csampleRate := (C.int)(sampleRate) csampleSize := (C.int)(sampleSize) cchannels := (C.int)(channels) - ret := C.LoadWaveEx(data, csampleCount, csampleRate, csampleSize, cchannels) + ret := C.LoadWaveEx(cdata, csampleCount, csampleRate, csampleSize, cchannels) v := NewWaveFromPointer(unsafe.Pointer(&ret)) return v } diff --git a/raylib/rres.go b/raylib/rres.go index 39a182b..73992ff 100644 --- a/raylib/rres.go +++ b/raylib/rres.go @@ -2,212 +2,209 @@ package raylib import ( "bytes" - "compress/flate" + "crypto/aes" + "crypto/cipher" + "crypto/des" "encoding/binary" "fmt" + "io" "os" "unsafe" + + "github.com/dsnet/compress/bzip2" + "github.com/klauspost/compress/flate" + "github.com/pierrec/lz4" + "github.com/rootlch/encrypt" + "github.com/ulikunitz/xz" + + "github.com/gen2brain/raylib-go/rres" ) -// RRESFileHeader - rRES file header (8 byte) -type RRESFileHeader struct { - // File identifier: rRES (4 byte) - ID [4]int8 - // File version and subversion (2 byte) - Version uint16 - // Number of resources in this file (2 byte) - Count uint16 -} - -// RRESInfoHeader - rRES info header, every resource includes this header (16 byte + 16 byte) -type RRESInfoHeader struct { - // Resource unique identifier (4 byte) - ID uint32 - // Resource data type (1 byte) - DataType uint8 - // Resource data compression type (1 byte) - CompType uint8 - // Resource data encryption type (1 byte) - CryptoType uint8 - // Resource data parts count, used for splitted data (1 byte) - PartsCount uint8 - // Resource data size (compressed or not, only DATA) (4 byte) - DataSize uint32 - // Resource data size (uncompressed, only DATA) (4 byte) - UncompSize uint32 - // Resouce parameter 1 (4 byte) - Param1 uint32 - // Resouce parameter 2 (4 byte) - Param2 uint32 - // Resouce parameter 3 (4 byte) - Param3 uint32 - // Resouce parameter 4 (4 byte) - Param4 uint32 -} - -// rRES data types -const ( - RRESTypeRaw = iota - RRESTypeImage - RRESTypeWave - RRESTypeVertex - RRESTypeText - RRESTypeFontImage - RRESTypeFontData - RRESTypeDirectory -) - -// Compression types -const ( - // No data compression - RRESCompNone = iota - // DEFLATE compression - RRESCompDeflate - // LZ4 compression - RRESCompLz4 - // LZMA compression - RRESCompLzma - // BROTLI compression - RRESCompBrotli -) - -// Image formats -const ( - // 8 bit per pixel (no alpha) - RRESImUncompGrayscale = iota + 1 - // 16 bpp (2 channels) - RRESImUncompGrayAlpha - // 16 bpp - RRESImUncompR5g6b5 - // 24 bpp - RRESImUncompR8g8b8 - // 16 bpp (1 bit alpha) - RRESImUncompR5g5b5a1 - // 16 bpp (4 bit alpha) - RRESImUncompR4g4b4a4 - // 32 bpp - RRESImUncompR8g8b8a8 - // 4 bpp (no alpha) - RRESImCompDxt1Rgb - // 4 bpp (1 bit alpha) - RRESImCompDxt1Rgba - // 8 bpp - RRESImCompDxt3Rgba - // 8 bpp - RRESImCompDxt5Rgba - // 4 bpp - RRESImCompEtc1Rgb - // 4 bpp - RRESImCompEtc2Rgb - // 8 bpp - RRESImCompEtc2EacRgba - // 4 bpp - RRESImCompPvrtRgb - // 4 bpp - RRESImCompPvrtRgba - // 8 bpp - RRESImCompAstc4x4Rgba - // 2 bpp - RRESImCompAstc8x8Rgba -) - -// RRESVert -const ( - RRESVertPosition = iota - RRESVertTexcoord1 - RRESVertTexcoord2 - RRESVertTexcoord3 - RRESVertTexcoord4 - RRESVertNormal - RRESVertTangent - RRESVertColor - RRESVertIndex -) - -// RRESVert -const ( - RRESVertByte = iota - RRESVertShort - RRESVertInt - RRESVertHfloat - RRESVertFloat -) - -// LoadResource - Load resource from file (only one) -// NOTE: Returns uncompressed data with parameters, only first resource found -func LoadResource(fileName string) []byte { - return LoadResourceByID(fileName, 0) -} - -// LoadResourceByID - Load resource from file by id +// LoadResource - Load resource from file by id // NOTE: Returns uncompressed data with parameters, search resource by id -func LoadResourceByID(fileName string, rresID int) (data []byte) { - file, err := OpenAsset(fileName) - if err != nil { - TraceLog(LogWarning, "[%s] rRES raylib resource file could not be opened", fileName) - return - } - defer file.Close() +func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) { + var fileHeader rres.FileHeader + var infoHeader rres.InfoHeader - fileHeader := RRESFileHeader{} - infoHeader := RRESInfoHeader{} + reader.Seek(0, 0) // Read rres file header - err = binary.Read(file, binary.LittleEndian, &fileHeader) + err := binary.Read(reader, binary.LittleEndian, &fileHeader) if err != nil { TraceLog(LogWarning, err.Error()) return } - //fmt.Printf("%+v\n", fileHeader) - // Verify "rRES" identifier id := fmt.Sprintf("%c", fileHeader.ID) if id != "[r R E S]" { - TraceLog(LogWarning, "[%s] is not a valid raylib resource file", fileName) + TraceLog(LogWarning, "not a valid raylib resource file") return } - file.Seek(int64(unsafe.Sizeof(fileHeader)), os.SEEK_CUR) + reader.Seek(int64(unsafe.Sizeof(fileHeader)), os.SEEK_CUR) for i := 0; i < int(fileHeader.Count); i++ { // Read resource info and parameters - err = binary.Read(file, binary.LittleEndian, &infoHeader) + err = binary.Read(reader, binary.LittleEndian, &infoHeader) if err != nil { TraceLog(LogWarning, err.Error()) return } - //fmt.Printf("%+v\n", infoHeader) - - file.Seek(int64(unsafe.Sizeof(infoHeader)), os.SEEK_CUR) + reader.Seek(int64(unsafe.Sizeof(infoHeader)), os.SEEK_CUR) if int(infoHeader.ID) == rresID { + data.Type = uint32(infoHeader.DataType) + data.Param1 = infoHeader.Param1 + data.Param2 = infoHeader.Param2 + data.Param3 = infoHeader.Param3 + data.Param4 = infoHeader.Param4 + // Read resource data block - data = make([]byte, infoHeader.DataSize) - file.Read(data) + b := make([]byte, infoHeader.DataSize) + reader.Read(b) - if infoHeader.CompType == RRESCompDeflate { - // Uncompress data - b := bytes.NewReader(data) - r := flate.NewReader(b) + // Uncompress data + switch infoHeader.CompType { + case rres.CompNone: + data.Data = b + case rres.CompDeflate: + r := flate.NewReader(bytes.NewReader(b)) - data = make([]byte, infoHeader.UncompSize) - r.Read(data) + u := make([]byte, infoHeader.UncompSize) + r.Read(u) + + data.Data = u + + r.Close() + case rres.CompLZ4: + r := lz4.NewReader(bytes.NewReader(b)) + + u := make([]byte, infoHeader.UncompSize) + r.Read(u) + + data.Data = u + case rres.CompLZMA2: + r, err := xz.NewReader(bytes.NewReader(b)) + if err != nil { + TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) + } + + u := make([]byte, infoHeader.UncompSize) + r.Read(u) + + data.Data = u + case rres.CompBZIP2: + r, err := bzip2.NewReader(bytes.NewReader(b), &bzip2.ReaderConfig{}) + if err != nil { + TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) + } + + u := make([]byte, infoHeader.UncompSize) + r.Read(u) + + data.Data = u } - if len(data) > 0 { - TraceLog(LogInfo, "[%s][ID %d] Resource data loaded successfully", fileName, infoHeader.ID) + // Decrypt data + switch infoHeader.CryptoType { + case rres.CryptoXOR: + c, err := encrypt.NewXor(string(key)) + if err != nil { + TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) + } + + b := c.Encode(data.Data) + data.Data = b + case rres.CryptoAES: + b, err := decryptAES(key, data.Data) + if err != nil { + TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) + } + data.Data = b + case rres.Crypto3DES: + b, err := decrypt3DES(key, data.Data) + if err != nil { + TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) + } + data.Data = b + } + + if data.Data != nil { + TraceLog(LogInfo, "[ID %d] Resource data loaded successfully", infoHeader.ID) } } else { // Skip required data to read next resource infoHeader - file.Seek(int64(infoHeader.DataSize), os.SEEK_CUR) + reader.Seek(int64(infoHeader.DataSize), os.SEEK_CUR) } } - if len(data) == 0 { - TraceLog(LogInfo, "[%s][ID %d] Requested resource could not be found", fileName, rresID) + if data.Data == nil { + TraceLog(LogInfo, "[ID %d] Requested resource could not be found", rresID) } return } + +// unpad +func unpad(src []byte) ([]byte, error) { + length := len(src) + unpadding := int(src[length-1]) + + if unpadding > length { + return nil, fmt.Errorf("unpad error. This can happen when incorrect encryption key is used.") + } + + return src[:(length - unpadding)], nil +} + +// decryptAES +func decryptAES(key, text []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + if (len(text) % aes.BlockSize) != 0 { + return nil, fmt.Errorf("blocksize must be multiple of decoded message length") + } + + iv := text[:aes.BlockSize] + msg := text[aes.BlockSize:] + + cfb := cipher.NewCFBDecrypter(block, iv) + cfb.XORKeyStream(msg, msg) + + unpadMsg, err := unpad(msg) + if err != nil { + return nil, err + } + + return unpadMsg, nil +} + +// decrypt3DES +func decrypt3DES(key, text []byte) ([]byte, error) { + block, err := des.NewCipher(key) + if err != nil { + return nil, err + } + + if (len(text) % des.BlockSize) != 0 { + return nil, fmt.Errorf("blocksize must be multiple of decoded message length") + } + + iv := text[:des.BlockSize] + msg := text[des.BlockSize:] + + cbc := cipher.NewCBCDecrypter(block, iv) + cbc.CryptBlocks(msg, msg) + + unpadMsg, err := unpad(msg) + if err != nil { + return nil, err + } + + return unpadMsg, nil +} diff --git a/rres/README.md b/rres/README.md new file mode 100644 index 0000000..9844231 --- /dev/null +++ b/rres/README.md @@ -0,0 +1,3 @@ +## rres [![GoDoc](https://godoc.org/github.com/gen2brain/raylib-go/rres?status.svg)](https://godoc.org/github.com/gen2brain/raylib-go/rres) + +raylib resources. diff --git a/rres/cmd/rrem/README.md b/rres/cmd/rrem/README.md new file mode 100644 index 0000000..73018e4 --- /dev/null +++ b/rres/cmd/rrem/README.md @@ -0,0 +1,25 @@ +## rrem + +rREM - raylib Resource EMbedder. + +### Usage + +``` +Usage of ./rrem: + -base string + Resources file basename (default "data") + -bin + Generate Go bindata (.go file) + -comp int + Compression type, 0=None, 1=Deflate, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2 (default 5) + -enc int + Encryption type, 0=None, 1=XOR, 2=AES, 3=3DES + -header + Generate C header (.h file) + -key string + Encryption key +``` + +### Example + +[Example](https://github.com/gen2brain/raylib-go/tree/master/examples/others/resources). diff --git a/rres/cmd/rrem/main.go b/rres/cmd/rrem/main.go new file mode 100644 index 0000000..99997e1 --- /dev/null +++ b/rres/cmd/rrem/main.go @@ -0,0 +1,453 @@ +// rREM - raylib Resource EMbedder +package main + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rand" + "encoding/binary" + "flag" + "fmt" + "image" + "image/color" + "image/draw" + "io" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "strings" + "unsafe" + + _ "image/gif" + _ "image/jpeg" + _ "image/png" + + "github.com/blezek/tga" + _ "github.com/jbuchbinder/gopnm" + _ "golang.org/x/image/bmp" + + "github.com/dsnet/compress/bzip2" + "github.com/jfreymuth/oggvorbis" + "github.com/jteeuwen/go-bindata" + "github.com/klauspost/compress/flate" + "github.com/moutend/go-wav" + "github.com/pierrec/lz4" + "github.com/rootlch/encrypt" + "github.com/ulikunitz/xz" + + "github.com/gen2brain/raylib-go/rres" +) + +func init() { + tga.RegisterFormat() +} + +func main() { + base := flag.String("base", "data", "Resources file basename") + comp := flag.Int("comp", rres.CompLZMA2, "Compression type, 0=None, 1=Deflate, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2") + enc := flag.Int("enc", rres.CryptoNone, "Encryption type, 0=None, 1=XOR, 2=AES, 3=3DES") + key := flag.String("key", "", "Encryption key") + header := flag.Bool("header", false, "Generate C header (.h file)") + bin := flag.Bool("bin", false, "Generate Go bindata (.go file)") + flag.Parse() + + if len(flag.Args()) == 0 { + flag.Usage() + os.Exit(1) + } + + switch *comp { + case rres.CompNone: + case rres.CompDeflate: + case rres.CompLZ4: + case rres.CompLZMA2: + case rres.CompBZIP2: + default: + fmt.Printf("compression type %d not implemented\n", *comp) + os.Exit(1) + } + + switch *enc { + case rres.CryptoNone: + case rres.CryptoXOR: + case rres.CryptoAES: + case rres.Crypto3DES: + default: + fmt.Printf("encryption type %d not implemented\n", *enc) + os.Exit(1) + } + + if *enc != 0 { + if *key == "" { + fmt.Printf("encryption requires key (-k)\n") + os.Exit(1) + } + if len(*key) != 16 && len(*key) != 24 { + fmt.Printf("wrong key length, it should be 16 or 24\n") + os.Exit(1) + } + } + + rresFile, err := os.Create(fmt.Sprintf("%s.rres", *base)) + if err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } + + defer rresFile.Close() + + var headerFile *os.File + if *header { + headerFile, err = os.Create(fmt.Sprintf("%s.h", *base)) + if err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } + + defer headerFile.Close() + } + + var fileHeader rres.FileHeader + + // "rRES" identifier + copy(fileHeader.ID[:], "rRES") + fileHeader.Count = uint16(len(flag.Args())) + fileHeader.Version = 1 + + // Write file header + err = binary.Write(rresFile, binary.LittleEndian, &fileHeader) + if err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } + + rresFile.Seek(int64(unsafe.Sizeof(fileHeader)), os.SEEK_CUR) + + if *header { + // Write C header file + _, err = headerFile.Write([]byte(fmt.Sprintf("#define NUM_RESOURCES %d\n\n", flag.NArg()))) + if err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } + } + + for id, filename := range flag.Args() { + var data []byte + var infoHeader rres.InfoHeader + + file, err := os.Open(filename) + if err != nil { + fmt.Printf("%s: %v\n", filename, err) + continue + } + + data, err = ioutil.ReadAll(file) + if err != nil { + fmt.Printf("%s: %v\n", filename, err) + } + + file.Close() + + infoHeader.ID = uint32(id) + infoHeader.CompType = uint8(*comp) + infoHeader.CryptoType = uint8(*enc) + infoHeader.DataType = uint8(fileType(filename)) + infoHeader.PartsCount = uint8(1) + + // Params + switch infoHeader.DataType { + case rres.TypeImage: + img, _, err := image.Decode(bytes.NewReader(data)) + if err != nil { + fmt.Printf("%s: %v\n", filename, err) + continue + } + + rect := img.Bounds() + width, height := rect.Dx(), rect.Dy() + + infoHeader.Param1 = uint32(width) + infoHeader.Param2 = uint32(height) + + switch img.ColorModel() { + case color.GrayModel: + infoHeader.Param3 = rres.ImUncompGrayscale + + i := image.NewGray(rect) + draw.Draw(i, rect, img, rect.Min, draw.Src) + data = i.Pix + case color.Gray16Model: + infoHeader.Param3 = rres.ImUncompGrayAlpha + + i := image.NewGray16(rect) + draw.Draw(i, rect, img, rect.Min, draw.Src) + data = i.Pix + default: + infoHeader.Param3 = rres.ImUncompR8g8b8a8 + + i := image.NewNRGBA(rect) + draw.Draw(i, rect, img, rect.Min, draw.Src) + data = i.Pix + } + + case rres.TypeWave: + a := &wav.File{} + err := wav.Unmarshal(data, a) + if err != nil { + fmt.Printf("%s: %v\n", filename, err) + } + + data, err = ioutil.ReadAll(a) + if err != nil { + fmt.Printf("%s: %v\n", filename, err) + } + + infoHeader.Param1 = uint32(a.Samples()) + infoHeader.Param2 = uint32(a.SamplesPerSec()) + infoHeader.Param3 = uint32(a.BitsPerSample()) + infoHeader.Param4 = uint32(a.Channels()) + case rres.TypeVorbis: + r, err := oggvorbis.NewReader(bytes.NewReader(data)) + if err != nil { + fmt.Printf("%s: %v\n", filename, err) + } + + d, _, err := oggvorbis.ReadAll(bytes.NewReader(data)) + if err != nil { + fmt.Printf("%s: %v\n", filename, err) + } + + // Convert []float32 to []byte + header := *(*reflect.SliceHeader)(unsafe.Pointer(&d)) + header.Len *= 4 + header.Cap *= 4 + data = *(*[]byte)(unsafe.Pointer(&header)) + + infoHeader.Param1 = uint32(r.SampleRate()) + infoHeader.Param2 = uint32(r.Bitrate().Nominal) + infoHeader.Param3 = uint32(r.Channels()) + case rres.TypeVertex: + // TODO https://github.com/sheenobu/go-obj + case rres.TypeText, rres.TypeRaw: + } + + // Encryption + switch infoHeader.CryptoType { + case rres.CryptoXOR: + c, err := encrypt.NewXor(*key) + if err != nil { + fmt.Printf("%v\n", err) + } + + b := c.Encode(data) + data = b + case rres.CryptoAES: + b, err := encryptAES([]byte(*key), data) + if err != nil { + fmt.Printf("%v\n", err) + } + + data = b + case rres.Crypto3DES: + b, err := encrypt3DES([]byte(*key), data) + if err != nil { + fmt.Printf("%v\n", err) + } + + data = b + } + + infoHeader.UncompSize = uint32(len(data)) + + // Compression + switch infoHeader.CompType { + case rres.CompNone: + infoHeader.DataSize = uint32(len(data)) + case rres.CompDeflate: + buf := new(bytes.Buffer) + + w, err := flate.NewWriter(buf, flate.BestCompression) + if err != nil { + fmt.Printf("%v\n", err) + } + + _, err = w.Write(data) + if err != nil { + fmt.Printf("%v\n", err) + } + + w.Close() + + infoHeader.DataSize = uint32(len(buf.Bytes())) + data = buf.Bytes() + case rres.CompLZ4: + buf := new(bytes.Buffer) + + w := lz4.NewWriter(buf) + if err != nil { + fmt.Printf("%v\n", err) + } + + _, err = w.Write(data) + if err != nil { + fmt.Printf("%v\n", err) + } + + w.Close() + + infoHeader.DataSize = uint32(len(buf.Bytes())) + data = buf.Bytes() + case rres.CompLZMA2: + buf := new(bytes.Buffer) + + w, err := xz.NewWriter(buf) + if err != nil { + fmt.Printf("%v\n", err) + } + + _, err = w.Write(data) + if err != nil { + fmt.Printf("%v\n", err) + } + + w.Close() + + infoHeader.DataSize = uint32(len(buf.Bytes())) + data = buf.Bytes() + case rres.CompBZIP2: + buf := new(bytes.Buffer) + + w, err := bzip2.NewWriter(buf, &bzip2.WriterConfig{Level: bzip2.BestCompression}) + if err != nil { + fmt.Printf("%v\n", err) + } + + _, err = w.Write(data) + if err != nil { + fmt.Printf("%v\n", err) + } + + w.Close() + + infoHeader.DataSize = uint32(len(buf.Bytes())) + data = buf.Bytes() + } + + // Write resource info and parameters + err = binary.Write(rresFile, binary.LittleEndian, &infoHeader) + if err != nil { + fmt.Printf("%v\n", err) + } + + rresFile.Seek(int64(unsafe.Sizeof(infoHeader)), os.SEEK_CUR) + + // Write resource data + _, err = rresFile.Write(data) + if err != nil { + fmt.Printf("%v\n", err) + } + + var typeName string + switch infoHeader.DataType { + case rres.TypeImage: + typeName = "IMAGE" + case rres.TypeWave: + typeName = "WAVE" + case rres.TypeVorbis: + typeName = "VORBIS" + case rres.TypeText: + typeName = "TEXT" + default: + typeName = "RAW" + } + + fmt.Printf("%s %d // Embedded as %s\n", filepath.Base(filename), id, typeName) + + if *header { + headerFile.Write([]byte(fmt.Sprintf("#define RES_%s 0x%08x\t\t// Embedded as %s\n", filepath.Base(filename), id, typeName))) + } + } + + // Generate bindata + if *bin { + cfg := bindata.NewConfig() + cfg.NoCompress = true + cfg.Output = fmt.Sprintf("%s.go", *base) + cfg.Input = make([]bindata.InputConfig, 1) + cfg.Input[0] = bindata.InputConfig{Path: fmt.Sprintf("%s.rres", *base), Recursive: false} + + err := bindata.Translate(cfg) + if err != nil { + fmt.Printf("%v\n", err) + } + } +} + +// fileType returns resource file type +func fileType(f string) int { + switch strings.ToLower(filepath.Ext(f)) { + case ".jpg", ".jpeg", ".png", ".bmp", ".tga", ".gif": + return rres.TypeImage + case ".txt", ".csv", ".info", ".md": + return rres.TypeText + case ".wav": + return rres.TypeWave + case ".ogg": + return rres.TypeVorbis + case ".obj": + return rres.TypeVertex + default: + return rres.TypeRaw + } +} + +// pad to block size +func pad(src []byte, blockSize int) []byte { + padding := blockSize - len(src)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(src, padtext...) +} + +// encryptAES +func encryptAES(key, text []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + msg := pad(text, aes.BlockSize) + ciphertext := make([]byte, aes.BlockSize+len(msg)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + + cfb := cipher.NewCFBEncrypter(block, iv) + cfb.XORKeyStream(ciphertext[aes.BlockSize:], msg) + + return ciphertext, nil +} + +// encrypt3DES +func encrypt3DES(key, text []byte) ([]byte, error) { + block, err := des.NewTripleDESCipher([]byte(key)) + if err != nil { + return nil, err + } + + msg := pad(text, des.BlockSize) + ciphertext := make([]byte, des.BlockSize+len(msg)) + iv := ciphertext[:des.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + + cbc := cipher.NewCBCEncrypter(block, iv) + cbc.CryptBlocks(ciphertext[des.BlockSize:], msg) + + return ciphertext, nil +} diff --git a/rres/rres.go b/rres/rres.go new file mode 100644 index 0000000..1a13d67 --- /dev/null +++ b/rres/rres.go @@ -0,0 +1,163 @@ +package rres + +type Data struct { + // Resource type (4 byte) + Type uint32 + + // Resource parameter 1 (4 byte) + Param1 uint32 + // Resource parameter 2 (4 byte) + Param2 uint32 + // Resource parameter 3 (4 byte) + Param3 uint32 + // Resource parameter 4 (4 byte) + Param4 uint32 + + // Resource data + Data []byte +} + +// FileHeader - rRES file header (8 byte) +type FileHeader struct { + // File identifier: rRES (4 byte) + ID [4]byte + // File version and subversion (2 byte) + Version uint16 + // Number of resources in this file (2 byte) + Count uint16 +} + +// InfoHeader - rRES info header, every resource includes this header (16 byte + 16 byte) +type InfoHeader struct { + // Resource unique identifier (4 byte) + ID uint32 + // Resource data type (1 byte) + DataType uint8 + // Resource data compression type (1 byte) + CompType uint8 + // Resource data encryption type (1 byte) + CryptoType uint8 + // Resource data parts count, used for splitted data (1 byte) + PartsCount uint8 + // Resource data size (compressed or not, only DATA) (4 byte) + DataSize uint32 + // Resource data size (uncompressed, only DATA) (4 byte) + UncompSize uint32 + // Resource parameter 1 (4 byte) + Param1 uint32 + // Resource parameter 2 (4 byte) + Param2 uint32 + // Resource parameter 3 (4 byte) + Param3 uint32 + // Resource parameter 4 (4 byte) + Param4 uint32 +} + +// rRES data types +const ( + TypeRaw = iota + TypeImage + TypeWave + TypeVertex + TypeText + TypeFontImage + TypeFontCharData + TypeDirectory + TypeVorbis +) + +// Compression types +const ( + // No data compression + CompNone = iota + // DEFLATE compression + CompDeflate + // LZ4 compression + CompLZ4 + // LZMA compression + CompLZMA + // BROTLI compression + CompBrotli + // LZMA2 (XZ) compression + CompLZMA2 + // BZIP2 compression + CompBZIP2 +) + +// Encryption types +const ( + // No data encryption + CryptoNone = iota + // XOR (128 bit) encryption + CryptoXOR + // RIJNDAEL (128 bit) encryption (AES) + CryptoAES + // Triple DES encryption + Crypto3DES + // Blowfish encryption + CryptoBlowfish + // Extended TEA encryption + CryptoXTEA +) + +// Image formats +const ( + // 8 bit per pixel (no alpha) + ImUncompGrayscale = iota + 1 + // 16 bpp (2 channels) + ImUncompGrayAlpha + // 16 bpp + ImUncompR5g6b5 + // 24 bpp + ImUncompR8g8b8 + // 16 bpp (1 bit alpha) + ImUncompR5g5b5a1 + // 16 bpp (4 bit alpha) + ImUncompR4g4b4a4 + // 32 bpp + ImUncompR8g8b8a8 + // 4 bpp (no alpha) + ImCompDxt1Rgb + // 4 bpp (1 bit alpha) + ImCompDxt1Rgba + // 8 bpp + ImCompDxt3Rgba + // 8 bpp + ImCompDxt5Rgba + // 4 bpp + ImCompEtc1Rgb + // 4 bpp + ImCompEtc2Rgb + // 8 bpp + ImCompEtc2EacRgba + // 4 bpp + ImCompPvrtRgb + // 4 bpp + ImCompPvrtRgba + // 8 bpp + ImCompAstc4x4Rgba + // 2 bpp + ImCompAstc8x8Rgba +) + +// Vert +const ( + VertPosition = iota + VertTexcoord1 + VertTexcoord2 + VertTexcoord3 + VertTexcoord4 + VertNormal + VertTangent + VertColor + VertIndex +) + +// Vert +const ( + VertByte = iota + VertShort + VertInt + VertHfloat + VertFloat +) From be0df5e2d65dc07255bbe8c811531decf3861cb5 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 27 Nov 2017 05:38:02 +0100 Subject: [PATCH 03/48] Get deps --- .appveyor.yml | 1 + .travis.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index 775f7df..e83e325 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,6 +18,7 @@ install: - set PATH=%GOPATH%\bin;c:\go\bin;%MSYS_PATH%\usr\bin;%MSYS_PATH%\mingw32\bin;%PATH% - go version - go env + - go get -t ./... before_build: - bash -lc "pacman --noconfirm --needed -Sy mingw-w64-i686-openal" diff --git a/.travis.yml b/.travis.yml index 4a4f6f5..f757d1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,3 +6,4 @@ go: install: - sudo apt-get update -y - sudo apt-get install libopenal-dev libxi-dev libxinerama-dev libxcursor-dev libxxf86vm-dev libxrandr-dev -y + - go get -t ./... From b3ddf91c77c63555fd2f3d6c0a02c5b45d131268 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 27 Nov 2017 05:42:07 +0100 Subject: [PATCH 04/48] Get deps --- .appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index e83e325..300979e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,10 +18,9 @@ install: - set PATH=%GOPATH%\bin;c:\go\bin;%MSYS_PATH%\usr\bin;%MSYS_PATH%\mingw32\bin;%PATH% - go version - go env - - go get -t ./... before_build: - bash -lc "pacman --noconfirm --needed -Sy mingw-w64-i686-openal" build_script: - - bash -lc "cd /c/gopath/src/github.com/gen2brain/raylib-go && make" + - bash -lc "cd /c/gopath/src/github.com/gen2brain/raylib-go && go get -t ./... && make" From 9825f0a9164362e81c407d292f7ab09594208830 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 27 Nov 2017 05:56:25 +0100 Subject: [PATCH 05/48] Add Wayland support. Fixes #24 --- raylib/cgo_linux.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/raylib/cgo_linux.go b/raylib/cgo_linux.go index c7bca6b..333033e 100644 --- a/raylib/cgo_linux.go +++ b/raylib/cgo_linux.go @@ -10,10 +10,18 @@ package raylib #include "external/glfw/src/vulkan.c" #include "external/glfw/src/window.c" +#ifdef _GLFW_WAYLAND +#include "external/glfw/src/wl_init.c" +#include "external/glfw/src/wl_monitor.c" +#include "external/glfw/src/wl_window.c" +#endif +#ifdef _GLFW_X11 #include "external/glfw/src/x11_init.c" #include "external/glfw/src/x11_monitor.c" #include "external/glfw/src/x11_window.c" #include "external/glfw/src/glx_context.c" +#endif + #include "external/glfw/src/linux_joystick.c" #include "external/glfw/src/posix_time.c" #include "external/glfw/src/posix_tls.c" @@ -21,13 +29,15 @@ package raylib #include "external/glfw/src/egl_context.c" #cgo linux LDFLAGS: -lGL -lm -pthread -ldl -lrt -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor -#cgo linux CFLAGS: -D_GLFW_X11 -Iexternal/glfw/include -DPLATFORM_DESKTOP +#cgo linux CFLAGS: -Iexternal/glfw/include -DPLATFORM_DESKTOP #cgo linux,!noaudio LDFLAGS: -lopenal #cgo linux,!static CFLAGS: -DSHARED_OPENAL #cgo linux,static CFLAGS: -DAL_LIBTYPE_STATIC +#cgo linux,!wayland CFLAGS: -D_GLFW_X11 +#cgo linux,wayland CFLAGS: -D_GLFW_WAYLAND #cgo linux,opengl11 CFLAGS: -DGRAPHICS_API_OPENGL_11 #cgo linux,opengl21 CFLAGS: -DGRAPHICS_API_OPENGL_21 From 31a41399adb6341063ffbbdcb8f6ba073031e5e0 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 27 Nov 2017 14:31:53 +0100 Subject: [PATCH 06/48] Add Blowfish and XTEA --- raylib/rres.go | 64 +++++++++++++++++++++++++++++++++++++++++ rres/cmd/rrem/README.md | 4 +-- rres/cmd/rrem/main.go | 64 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/raylib/rres.go b/raylib/rres.go index 73992ff..dffb904 100644 --- a/raylib/rres.go +++ b/raylib/rres.go @@ -16,6 +16,8 @@ import ( "github.com/pierrec/lz4" "github.com/rootlch/encrypt" "github.com/ulikunitz/xz" + "golang.org/x/crypto/blowfish" + "golang.org/x/crypto/xtea" "github.com/gen2brain/raylib-go/rres" ) @@ -129,6 +131,18 @@ func LoadResource(reader io.ReadSeeker, rresID int, key []byte) (data rres.Data) TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) } data.Data = b + case rres.CryptoBlowfish: + b, err := decryptBlowfish(key, data.Data) + if err != nil { + TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) + } + data.Data = b + case rres.CryptoXTEA: + b, err := decryptXTEA(key, data.Data) + if err != nil { + TraceLog(LogWarning, "[ID %d] %v", infoHeader.ID, err) + } + data.Data = b } if data.Data != nil { @@ -208,3 +222,53 @@ func decrypt3DES(key, text []byte) ([]byte, error) { return unpadMsg, nil } + +// decryptBlowfish +func decryptBlowfish(key, text []byte) ([]byte, error) { + block, err := blowfish.NewCipher(key) + if err != nil { + return nil, err + } + + if (len(text) % blowfish.BlockSize) != 0 { + return nil, fmt.Errorf("blocksize must be multiple of decoded message length") + } + + iv := text[:blowfish.BlockSize] + msg := text[blowfish.BlockSize:] + + cbc := cipher.NewCBCDecrypter(block, iv) + cbc.CryptBlocks(msg, msg) + + unpadMsg, err := unpad(msg) + if err != nil { + return nil, err + } + + return unpadMsg, nil +} + +// decryptXTEA +func decryptXTEA(key, text []byte) ([]byte, error) { + block, err := xtea.NewCipher(key) + if err != nil { + return nil, err + } + + if (len(text) % xtea.BlockSize) != 0 { + return nil, fmt.Errorf("blocksize must be multiple of decoded message length") + } + + iv := text[:xtea.BlockSize] + msg := text[xtea.BlockSize:] + + cbc := cipher.NewCBCDecrypter(block, iv) + cbc.CryptBlocks(msg, msg) + + unpadMsg, err := unpad(msg) + if err != nil { + return nil, err + } + + return unpadMsg, nil +} diff --git a/rres/cmd/rrem/README.md b/rres/cmd/rrem/README.md index 73018e4..700bd55 100644 --- a/rres/cmd/rrem/README.md +++ b/rres/cmd/rrem/README.md @@ -11,9 +11,9 @@ Usage of ./rrem: -bin Generate Go bindata (.go file) -comp int - Compression type, 0=None, 1=Deflate, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2 (default 5) + Compression type, 0=NONE, 1=DEFLATE, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2 (default 5) -enc int - Encryption type, 0=None, 1=XOR, 2=AES, 3=3DES + Encryption type, 0=NONE, 1=XOR, 2=AES, 3=3DES, 4=Blowfish, 5=XTEA -header Generate C header (.h file) -key string diff --git a/rres/cmd/rrem/main.go b/rres/cmd/rrem/main.go index 99997e1..8de907d 100644 --- a/rres/cmd/rrem/main.go +++ b/rres/cmd/rrem/main.go @@ -37,6 +37,8 @@ import ( "github.com/pierrec/lz4" "github.com/rootlch/encrypt" "github.com/ulikunitz/xz" + "golang.org/x/crypto/blowfish" + "golang.org/x/crypto/xtea" "github.com/gen2brain/raylib-go/rres" ) @@ -47,8 +49,8 @@ func init() { func main() { base := flag.String("base", "data", "Resources file basename") - comp := flag.Int("comp", rres.CompLZMA2, "Compression type, 0=None, 1=Deflate, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2") - enc := flag.Int("enc", rres.CryptoNone, "Encryption type, 0=None, 1=XOR, 2=AES, 3=3DES") + comp := flag.Int("comp", rres.CompLZMA2, "Compression type, 0=NONE, 1=DEFLATE, 2=LZ4, 5=LZMA2 (XZ), 6=BZIP2") + enc := flag.Int("enc", rres.CryptoNone, "Encryption type, 0=NONE, 1=XOR, 2=AES, 3=3DES, 4=Blowfish, 5=XTEA") key := flag.String("key", "", "Encryption key") header := flag.Bool("header", false, "Generate C header (.h file)") bin := flag.Bool("bin", false, "Generate Go bindata (.go file)") @@ -75,6 +77,8 @@ func main() { case rres.CryptoXOR: case rres.CryptoAES: case rres.Crypto3DES: + case rres.CryptoBlowfish: + case rres.CryptoXTEA: default: fmt.Printf("encryption type %d not implemented\n", *enc) os.Exit(1) @@ -258,6 +262,20 @@ func main() { fmt.Printf("%v\n", err) } + data = b + case rres.CryptoBlowfish: + b, err := encryptBlowfish([]byte(*key), data) + if err != nil { + fmt.Printf("%v\n", err) + } + + data = b + case rres.CryptoXTEA: + b, err := encryptXTEA([]byte(*key), data) + if err != nil { + fmt.Printf("%v\n", err) + } + data = b } @@ -434,7 +452,7 @@ func encryptAES(key, text []byte) ([]byte, error) { // encrypt3DES func encrypt3DES(key, text []byte) ([]byte, error) { - block, err := des.NewTripleDESCipher([]byte(key)) + block, err := des.NewTripleDESCipher(key) if err != nil { return nil, err } @@ -451,3 +469,43 @@ func encrypt3DES(key, text []byte) ([]byte, error) { return ciphertext, nil } + +// encryptBlowfish +func encryptBlowfish(key, text []byte) ([]byte, error) { + block, err := blowfish.NewCipher(key) + if err != nil { + return nil, err + } + + msg := pad(text, blowfish.BlockSize) + ciphertext := make([]byte, blowfish.BlockSize+len(msg)) + iv := ciphertext[:blowfish.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + + cbc := cipher.NewCBCEncrypter(block, iv) + cbc.CryptBlocks(ciphertext[blowfish.BlockSize:], msg) + + return ciphertext, nil +} + +// encryptXTEA +func encryptXTEA(key, text []byte) ([]byte, error) { + block, err := xtea.NewCipher(key) + if err != nil { + return nil, err + } + + msg := pad(text, xtea.BlockSize) + ciphertext := make([]byte, xtea.BlockSize+len(msg)) + iv := ciphertext[:xtea.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + + cbc := cipher.NewCBCEncrypter(block, iv) + cbc.CryptBlocks(ciphertext[xtea.BlockSize:], msg) + + return ciphertext, nil +} From 4101a15c477ead0e400eebf3ea7cb08a4d1f2eef Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 27 Nov 2017 15:08:20 +0100 Subject: [PATCH 07/48] Add option to generate C source --- rres/cmd/rrem/README.md | 2 ++ rres/cmd/rrem/main.go | 56 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/rres/cmd/rrem/README.md b/rres/cmd/rrem/README.md index 700bd55..e55622b 100644 --- a/rres/cmd/rrem/README.md +++ b/rres/cmd/rrem/README.md @@ -18,6 +18,8 @@ Usage of ./rrem: Generate C header (.h file) -key string Encryption key + -source + Generate C source (.c file) ``` ### Example diff --git a/rres/cmd/rrem/main.go b/rres/cmd/rrem/main.go index 8de907d..0c7400d 100644 --- a/rres/cmd/rrem/main.go +++ b/rres/cmd/rrem/main.go @@ -53,6 +53,7 @@ func main() { enc := flag.Int("enc", rres.CryptoNone, "Encryption type, 0=NONE, 1=XOR, 2=AES, 3=3DES, 4=Blowfish, 5=XTEA") key := flag.String("key", "", "Encryption key") header := flag.Bool("header", false, "Generate C header (.h file)") + source := flag.Bool("source", false, "Generate C source (.c file)") bin := flag.Bool("bin", false, "Generate Go bindata (.go file)") flag.Parse() @@ -114,6 +115,17 @@ func main() { defer headerFile.Close() } + var sourceFile *os.File + if *source { + sourceFile, err = os.Create(fmt.Sprintf("%s.c", *base)) + if err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } + + defer sourceFile.Close() + } + var fileHeader rres.FileHeader // "rRES" identifier @@ -390,6 +402,50 @@ func main() { } } + // Generate C source + if *source { + rresFile.Seek(0, 0) + d, err := ioutil.ReadAll(rresFile) + if err != nil { + fmt.Printf("%v\n", err) + } + + _, err = sourceFile.Write([]byte("// This file has been automatically generated by rREM - raylib Resource Embedder\n\n")) + if err != nil { + fmt.Printf("%v\n", err) + } + + _, err = sourceFile.Write([]byte(fmt.Sprintf("const unsigned char data[%d] = {\n ", len(d)))) + if err != nil { + fmt.Printf("%v\n", err) + } + + blCounter := 0 // break line counter + + for i := 0; i < len(d)-1; i++ { + blCounter++ + + _, err = sourceFile.Write([]byte(fmt.Sprintf("0x%.2x, ", d[i]))) + if err != nil { + fmt.Printf("%v\n", err) + } + + if blCounter >= 24 { + _, err = sourceFile.Write([]byte("\n ")) + if err != nil { + fmt.Printf("%v\n", err) + } + + blCounter = 0 + } + } + + _, err = sourceFile.Write([]byte(fmt.Sprintf("0x%.2x };\n", d[len(d)-1]))) + if err != nil { + fmt.Printf("%v\n", err) + } + } + // Generate bindata if *bin { cfg := bindata.NewConfig() From d6cda5d3bf04fa89d89913b3200546175d99ff91 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Mon, 27 Nov 2017 16:49:59 +0100 Subject: [PATCH 08/48] Fix build for Wayland --- raylib/cgo_linux.go | 12 +- ...-constraints-unstable-v1-client-protocol.c | 98 +++ ...-constraints-unstable-v1-client-protocol.h | 649 ++++++++++++++++++ ...tive-pointer-unstable-v1-client-protocol.c | 69 ++ ...tive-pointer-unstable-v1-client-protocol.h | 295 ++++++++ raylib/platform_desktop.go | 2 +- raylib/platform_wayland.go | 93 +++ 7 files changed, 1213 insertions(+), 5 deletions(-) create mode 100644 raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c create mode 100644 raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h create mode 100644 raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c create mode 100644 raylib/external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.h create mode 100644 raylib/platform_wayland.go diff --git a/raylib/cgo_linux.go b/raylib/cgo_linux.go index 333033e..9046e14 100644 --- a/raylib/cgo_linux.go +++ b/raylib/cgo_linux.go @@ -14,6 +14,8 @@ package raylib #include "external/glfw/src/wl_init.c" #include "external/glfw/src/wl_monitor.c" #include "external/glfw/src/wl_window.c" +#include "external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c" +#include "external/glfw/src/wayland-relative-pointer-unstable-v1-client-protocol.c" #endif #ifdef _GLFW_X11 #include "external/glfw/src/x11_init.c" @@ -28,17 +30,19 @@ package raylib #include "external/glfw/src/xkb_unicode.c" #include "external/glfw/src/egl_context.c" -#cgo linux LDFLAGS: -lGL -lm -pthread -ldl -lrt -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor #cgo linux CFLAGS: -Iexternal/glfw/include -DPLATFORM_DESKTOP +#cgo linux,!wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor +#cgo linux,wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon + +#cgo linux,!wayland CFLAGS: -D_GLFW_X11 +#cgo linux,wayland CFLAGS: -D_GLFW_WAYLAND + #cgo linux,!noaudio LDFLAGS: -lopenal #cgo linux,!static CFLAGS: -DSHARED_OPENAL #cgo linux,static CFLAGS: -DAL_LIBTYPE_STATIC -#cgo linux,!wayland CFLAGS: -D_GLFW_X11 -#cgo linux,wayland CFLAGS: -D_GLFW_WAYLAND - #cgo linux,opengl11 CFLAGS: -DGRAPHICS_API_OPENGL_11 #cgo linux,opengl21 CFLAGS: -DGRAPHICS_API_OPENGL_21 #cgo linux,!opengl11,!opengl21 CFLAGS: -DGRAPHICS_API_OPENGL_33 diff --git a/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c new file mode 100644 index 0000000..48bc432 --- /dev/null +++ b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.c @@ -0,0 +1,98 @@ +/* Generated by wayland-scanner 1.12.0 */ + +/* + * Copyright © 2014 Jonas Ådahl + * Copyright © 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +extern const struct wl_interface wl_pointer_interface; +extern const struct wl_interface wl_region_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface zwp_confined_pointer_v1_interface; +extern const struct wl_interface zwp_locked_pointer_v1_interface; + +static const struct wl_interface *types[] = { + NULL, + NULL, + &zwp_locked_pointer_v1_interface, + &wl_surface_interface, + &wl_pointer_interface, + &wl_region_interface, + NULL, + &zwp_confined_pointer_v1_interface, + &wl_surface_interface, + &wl_pointer_interface, + &wl_region_interface, + NULL, + &wl_region_interface, + &wl_region_interface, +}; + +static const struct wl_message zwp_pointer_constraints_v1_requests[] = { + { "destroy", "", types + 0 }, + { "lock_pointer", "noo?ou", types + 2 }, + { "confine_pointer", "noo?ou", types + 7 }, +}; + +WL_EXPORT const struct wl_interface zwp_pointer_constraints_v1_interface = { + "zwp_pointer_constraints_v1", 1, + 3, zwp_pointer_constraints_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwp_locked_pointer_v1_requests[] = { + { "destroy", "", types + 0 }, + { "set_cursor_position_hint", "ff", types + 0 }, + { "set_region", "?o", types + 12 }, +}; + +static const struct wl_message zwp_locked_pointer_v1_events[] = { + { "locked", "", types + 0 }, + { "unlocked", "", types + 0 }, +}; + +WL_EXPORT const struct wl_interface zwp_locked_pointer_v1_interface = { + "zwp_locked_pointer_v1", 1, + 3, zwp_locked_pointer_v1_requests, + 2, zwp_locked_pointer_v1_events, +}; + +static const struct wl_message zwp_confined_pointer_v1_requests[] = { + { "destroy", "", types + 0 }, + { "set_region", "?o", types + 13 }, +}; + +static const struct wl_message zwp_confined_pointer_v1_events[] = { + { "confined", "", types + 0 }, + { "unconfined", "", types + 0 }, +}; + +WL_EXPORT const struct wl_interface zwp_confined_pointer_v1_interface = { + "zwp_confined_pointer_v1", 1, + 2, zwp_confined_pointer_v1_requests, + 2, zwp_confined_pointer_v1_events, +}; + diff --git a/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h new file mode 100644 index 0000000..91408c4 --- /dev/null +++ b/raylib/external/glfw/src/wayland-pointer-constraints-unstable-v1-client-protocol.h @@ -0,0 +1,649 @@ +/* Generated by wayland-scanner 1.12.0 */ + +#ifndef POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_pointer_constraints_unstable_v1 The pointer_constraints_unstable_v1 protocol + * protocol for constraining pointer motions + * + * @section page_desc_pointer_constraints_unstable_v1 Description + * + * This protocol specifies a set of interfaces used for adding constraints to + * the motion of a pointer. Possible constraints include confining pointer + * motions to a given region, or locking it to its current position. + * + * In order to constrain the pointer, a client must first bind the global + * interface "wp_pointer_constraints" which, if a compositor supports pointer + * constraints, is exposed by the registry. Using the bound global object, the + * client uses the request that corresponds to the type of constraint it wants + * to make. See wp_pointer_constraints for more details. + * + * Warning! The protocol described in this file is experimental and backward + * incompatible changes may be made. Backward compatible changes may be added + * together with the corresponding interface version bump. Backward + * incompatible changes are done by bumping the version number in the protocol + * and interface names and resetting the interface version. Once the protocol + * is to be declared stable, the 'z' prefix and the version number in the + * protocol and interface names are removed and the interface version number is + * reset. + * + * @section page_ifaces_pointer_constraints_unstable_v1 Interfaces + * - @subpage page_iface_zwp_pointer_constraints_v1 - constrain the movement of a pointer + * - @subpage page_iface_zwp_locked_pointer_v1 - receive relative pointer motion events + * - @subpage page_iface_zwp_confined_pointer_v1 - confined pointer object + * @section page_copyright_pointer_constraints_unstable_v1 Copyright + *

4xWjZ%39`j}U`{&^U4=-_1B7IfK9#M14geV%NV4v804`=mEXV zWfqo%w*NNFPfN^q$7`^se|n^1Xyq~uT$2ox*Om$Dpq7%=G{*yDxOvVKa8_nr(B+v{ zO|apobiSR^;6=Z3m=&1{$>C_9?G~h z9e7otwB3nc_V4FDq^i4YQxPf)0EHEMg_XdQn0OmSqn?0pmgwB+CHigTtZJT}cFNQUkG1|sIl_s~L zV}a1+^t?HiK{b3Do_5Uo;Ze5w@}1{4+B#IUtcnbPpKxsZkw$_Z1(`GFn(taph@qU; z86w6FFrAx&TPg(mxJMMC%Uyw&su9AZeN1bzHvPe1U62jC2Ys0Mxb>qXH=6`);)8=! zwG-L9g(W(LckM9`yMn?lVbvz=ZM+9pyknHE(diWI-R-ugvk<8hJ%E+G6R56yGs-GC z%=wUhG{I+VI-cnKhaxHZlo`kLoQB(0cUvmW4C4N_mV|`fu9SCt3%41S%OD4nzozhA z?n>BdVOGNhD`@)4#k`9~zAxzFHSCed;?+~I;5vUv)S{SJgNTy7k8=vlG}DIt3A?yJ zGKc~bU4Z%-BV)6w{Tr?jj@%v(Q^;M;iPJSUpFea0EQ30sLMF^xeJmN(D#R#hFNyqf zBQD}MQmxKrdnjU|R^BDErZ*W+=Y1Kd`2Y5ns}$TQxd6Bo-Bag;I>I2-#ylU5ThVG- z$Rj~g5-zhY4#gel==!2nN?Vjx)$r$>M_X78ztv|I%@T&ic?#f(r`##so-Zpj@*5rV zwpWy8jo?@$6MD<#w7aBazjnFN%eMaqPyP63iuAa3zi?KaN9%eDlIfD7Fr!iZOR%6q z%()A!w(2;kgsLRxj^S#88*%ouJ>tZq@+EP?tytSe2nEso42V?6(KEk>7Yb6&>SbfM zJX2*6LK`k}2w}eR{%=dk&yxlGLkSx%0^#0_5dbJVZh$%DW}o!(=Qlg;6c7I@&V`d= zh)op`9(V+TmvD~|f1Z9JWK$)pNbS8+_+>Eg5H<`NdMwM zg*B{8H@Wt_B&``u=7gybgGIAQGAlTlv2ffOALV}@CzB;gXBwXpOi4F@8ouh8vy~Xz z5Z9-@1BbefhOa@h6-_Ye4PVir4-Bk>iED4<8H0sXXqYcDf?>0!9RKoIxQ;QGy5# z{Hm@)ro56h_1h*HpCDA_&t;@34g#PA)VkOX8XwO3Gac^(U9XV33BA-DvpP^p*#yx|aS0%Z<;&OhcR9+mAr)#ip@ndY`}M|BEi` z&kpv>`gD(}ehgee`)UhOtQ35}e$bj>0C}q?KeOA3jWoYYsQV#UKtD8wGwgp<)f_;9 zJKlRwUcS?Om}eJ~)h=y~wmxccW{b~NCQFQYDY)qnb7U%l2n@;4t3jdu>p-t{JjeicwsbJF16^!1z^0&(RKTZ ze82v2t_1jm`&i1?YTe9XI11u)fi0-)G^+-cCM-&y^g#bpk4dF3Q7%(XKf?XY`D?G= z&v!hv`ZwW94+fWHD#s+Z^KP!XZaXB~uRjHp9>h8jL7M!X)wSF75{DU!tidkx-d=ys z#y9Mqf*IJ5Y02&8n7$0RgQ0vf%Z(0NHjoE}Mw#|6j>)~JlN-T4cozJ;i1|4JDZjez zPSdD?@(%XtD!HER(;oG-UriRB$%(Alp?hv|N>B#wu5w1yaLb5+=xj z^s&APybByyDy5OzyG5t%>3}_+<Q#O>r`j9xf&G#f0YZiI~ey3hJ*xJ&p~~o*=5X1b?dl7EDYBfEcC(76eZJ60WejC{%7?77cGyMlv5L?51r zXGoZ1;r_l&zY3oqmmpJvNTizt+M;P<(|5dIW~$>hvjF?VXp5h@^&uVlZv@F*oR0;x zf)3kXZjtOAJPTL z0!Mlcs=>f|CITWia&?hgxARerjNCi%#*n-J}CJq$d*KvLSfbB3g9|#bh(QXzAM5ZG(xd}z}+x~q5=ZE8h9Sjc6&hIboTlL_d zPt8K4A0l+)Gq(}vjM=TA>%X;?xbYK%qt~?=qk#MY>T&g^Upx?dDx5o9*cOB=>`b=CM@$`f$UeVT zt^2qK1{p4Dn6GgN3Iyo5mA$+syny7}FZJUu)dRS*$o#^jii_^0Ld}2Qz>I)RDj^qW*@=#YmF|Ck%3y$sGj>Z6XY@c{BB9{ z8fj)V%&fRK6?FH1>R&kkT#{jDnrJZM19m8rB1iZ?WS@cxY&Q6RD0H-s4N6S$04-LSWUKB21JKz zpDT=0*r!rq(lNAyhsndx_x1ikU00JD12C1~%o@udP~@;#LMj0W|E?-kY%fw|%oxk3 zCm}qp6p??G$nAnA(^5h`u!C<~^mqcOH){Hkdx9sV95!UNH2VP&V9k_HxeRV@#?HQj z!-g;_C+yrY-M?C^fhg{@*RcR6cB3v8az*mO8ul)}(Z>DU!XCb>0g zpF6g3^$c<8?1A%CrG0V>%3Jr*JT1rKC<|RY88e{6+-eV;G1g)m4Tl9d_R~7CVIAx8 zVal}pB6mTgw7+oZa8U3yk)9PShf_nypsyFRy5P29J{xuPg(3Z9HIs?Yu?hitytI8^ zX@$i-9vrfi7WKGbRHqzmjlYjtkE}AfFKIkfGZFkcr5;V zff}8W(t#t~Cs$2pdbG#-R%9B7kjbp)Z7c*p&yzd6a5isG@BeBDL@VIME0jfPinu39 zq0=nR5H+B&NPq}^JA^Vn4}ZbH;-Tl9)QFExAz4`Nb<|8D(Rf=0? z;Oq%LI$bCI2>6;P-iFks7!BAK<#5{ST?*gW0j@@d(R9~byWGcgCkuZKl584HoXv3B zzP19sDHVh5=3h;=L=*WtlvU6IUg<<__moF6K^OCx<{&b6;7x9g*5J2RpiK*wM%Ax?DvMs7~@T+NLhI4}JeEF#EB zp4gyLATl!7$$cB(bWg#lp^gK0=Pka*aNN~5PJ#pYZ}-Jz^6MYaH8Tr=-*)YQ#NsSc#`n@9;h7KcDx`C`@s+OC(HPU4{EbLlDXt(mghY*v^A^Hb$*k= zyUI4tk&y$>j`>`-k8$7||+UAIK~d}tUccxy1a44B!mdjJ0356H|M+}F4q zms{~74WJaQkv^jt;M+5wQ5iT}8G5sxjDKGV&LVS89^KCZo_y`G@8 z9fx!-?)o;2><{##i2o@m5AD{cb z4ST7j0~LloNwaksnAEJb|Mt0)OaYA-1Ct5%z+uq!5&w zkOr_sSPpT{j%nxn=b6`g@ll+fY{?ubQK!(3j$)E7=xeK);Ju4x_>9)k`ET{A`on8lz!-;g%rkT`-xZJO~ILHlC@tG z{eZD=w8YfqYKl@@{T0eo`TTppcjpm&@MagoR{`{Zt6jW--A32(%_T!_LgOs2u&0Si zny|>jleecv>)rMI+AqE~4cw2Vn&qe6i=af$KZ5oeYXp=BOD@fPi>p+y1YnqSxn`;U zmw#1%nORjGXd|?%N}+6cy7isu9uFt$wiVj}lW9=J>^%MtEQc3vSeSTFcYlNq3!Wt9G%Pg7m_17rNgH}}7R&|uu-E#b(cA-$_0z+QG5=)q& z!7&Z>diVE}-YzGLl*v%#=<|4=`$FGO#3d8~`<2iFas*x-Ez|Fm52o*)ckWhVZ28Mq z<}ah-t2x_`L#SaeM|CYsL3FvPWkokDRaDi^wtbd%`p*At1@sd<%2`h>!=xrsYe=O2 zx&fhJg}>I0^Tn}_^!Yu!InK+9w!C3jzTGjhq`U2$Jmm7Di6Ifgd(YC163Uc}e=jnp zADjt05X=#+#UY*qM??vnCe-JWfHqNQR)5s|!8;TH$~29E(3ePG)e&y`yF&-r*KQaP zthpzpT}$RH;VLky^bHga3T5(z-ylZ*{v)Ejp79-|ZJ##y4ee+(W^Sjr)m9%$Qp%@m zA&QWql@qSK_?&165AfJb$nOZk$uwSe<2=L$+B|#2(1JRtlDUAE4qFZ!+Rr$WhnB)3 zW^X>kM;eGp(TsWzc5>TMOEjq~`EOgCz;V-Z2i^~zPs|LjAi3%%q}-H?YR?|1+2dN% z1;*Z{Qi)&;%HkH&t=n-hc!a~LDwpeK_Wf*My2N{(ZLv|+u}HhU*us}tdUsByhzY;6 z?bbJyyoyE%I)dA>r#F)Z_LiaYKzrLxP$<+`-MgCVIYS*U0akkYn|e6;sGA%SLfm<7 z60fn{69piacMsZUFQWB7Mi<*e?$7luTC5wXT&Kz{8=JpPv zP2j-bi}NFz(Of zfKZdG!3M4hbO^*WF$kOYX())ZI?1rRgjBKFAjhsGRcpRikRTeLWSPL{uv$}Cw#43z ziq1aN#4W9})uu|UzQPH1HY994J_>N%TFze0o3DYH=z_&d(o6$}Q&_V|S7=M6-Y28M;XOdd0XxLe(Nur>T;6fI#%VitUppK5-s$Q?z?ZzZ{>dp}1K--17v|`k^g( zn4&LdpGn5O;z;U$G2;ez=!w1sS);TtjnR$5}7z(ME# z+8Xl4{Vc|d@aLj_W8eWuScIX%|xLI?Z6Mp(efiU^;G1`FctFppdH_mg)_f9SvFJW(IO zlcFB&Lojq^dx13(+@(0iqPnRtmz>x#9ET@sQHT%=28wsvFfL6f^q%%X`~N@MUzP!C ziG(iT2O07IS%RuM zMv_A!SRT*ABux^1dB%t-wXbvKMnZWV&}WxP4gW`N(_9`tGg%!BMufZNM9B_);ty{+ zGq!(TV@guw1B%~z4Uh}K8&j>l1uQEpvFCDgBu$c5rl$Ybkb77|*)SN^NnA*Q+21g+ zT9j{WSHi+M%5&w2ZAn_rKb8HLh(J&_?laSA($fzmQ~_Gff4J{GiU6D0K9SRkKR>!E!ea1h%=@$boIwS;!bA z=;JxKFT5fK-(dnKPDmIyJ;yURRB;>t2E0NZabqW2sfr8ble^N$E0Nrg?*>Ho8vsu z6LRTll?+YM!bwI-3n$4ltRTXX5OEfB7`BvF`Eae5`i&CLn>}`9$DZZP=bXkv1OB>E z@do`*{6Gr^#XOze>+>pBY>wm7OM`qwI0%Ea`ImL0&P6}K_XvTG{h@&0r2PXFgq40?K)ZUf9gi3evf9hv9~9=ec@r43#~0(r&hStM2JFK9e1N@8jW z>vLE$=>memI#sxy9<(8cs6}KIYR6emKM9|)-t#GabMyA01M2A24Ho_fQCq(FU>V*5 z1LptRA1xl~;C;ab6$pM*HPasRvKPVi@e1}lKDDPTa?s=egB`O`Vb%^LpOwZ9gJq*R zd_|8enC2nG5OF)16F4lb4cA^!rD^jTnaSX6IE zf>Ua{xysLBqN%@+3WM`TmceerL@rA_)|U*}e(T84&O}}0aiV55pFOkTh#X9Bp}PwZ zmnY9I4QYaqF#6RmggBK3>^J3@bwg6kL&^jR(uiKLEQ`O?!^4h>pnBm+^?7%y#7Sd4vLcgGoZfrMT#K?OBk(cy3wh{!bi9=QTENbhKJTO|N;BDuqjIaJbiXI}DoQ0{r zuZ=}GtbPv1%C_c`cKqcs!3l1Ad*TrDEldKLZi>oDQ=M5KFqJrVf5o9RI3TW^)Bwmp z@9A%Mznk`{ZJO2-v5;JN+<{aPmP6GWyKj0=-Rv4u~lXzkY4 zjL3Le7>)dcy|4MvZ8+KSE&4=n9^=$He$D0wX}Yr)5Hp&X+djsXE^bxn_JxJ~bmPV= z3g6y`dsC#b1~iBq0JCL=VKQ!(HAi);RGmNwkhTA`j)*=RZv5sMg3RmLk12Fkg1?pDk}agRt1qShgwLk zuvxoo^iOSS<)-5nh)9u{n3hCYAAZuBfJ=Qe)%yW9f;>ai9_GU2Lcz~oM*B04f~1`% z|Et;7K)97R`#4ZP>1zy(Uj_Se+LHr3T5}+X7$lD!|Ay@7ttgOV+mOGx4PPkcqh<>j zV$XF#;1LUGjnC^cHyac9^&fRX+gGgwKLZ$%gF?EaILe<1B6XYSKgYr+T<7J^iyTq5 zwoN1Wzc%B_{{3~{;VzF7B7~LETmv7{Jv{hB7Uj0cxm9*%)$=;y*;n1pYv0{m4&05} zMy%rPRx@%Dr*{#fa9{N%O{-=pY>IFEa5F$xVwWerxR0x)8J|KU=^uQMz3s0mTk`7XIOaR-TOn6 z_4e#W8Z%WeHixkth*bNj7?`N6+P3C16YqkJ7t&j%gpmcX%p|1R12W`-QwjIOrn*7e1V#bI@=gYil}?cBym zF;Qpq@gNUfq!!C$xb?MqMSfsRFcM$U9=8g~*qJ|5@$IMKTusB! z>IOjZb&69&{@|xRhFA%sB5}&J(v27flF?v7b0EipTO+fvB9R|1DJLwGG!337g8zAl zt?+RXH@$_3N9`$4t*-yBVWtg8G)`37G*|wOX^a;e5S98ekhOsU*-llH1a7I4joxd}j9h#MIdr_iHMIpOPUv#)hA=bK1 zdAKj}GF}vw8cokK)FUY2>wRX{=zHYTTD{1!hDGZRK61}aF2Ls-V7fn0)zF~9%Tw`X zp%`?+_T$FVSlZ`IBKH*m$VHdTh+WH-5}I$HKFg`$E~v7>ostF+kG^6ysafaQ?ccOnzzyDEIUhip7XD`+tT zy#>nch_Ko>=Q?%4ch+Mi6+7DM;)a0(v9M&>(pLrRFN$4L20nl}^kudTloH8P^PtP7 z!f5G%TQVHl0Oe|3Z0^|w(pY0~XiG-|k9JtJ!Me2w7D(iz70vhNr-)vG4A0`1qdyF2 zXmpgEWYU#6@SOTQS_$E6IcLm&ksO~%ynhu<2>_ygBr`gCm|`_yZ6;HwPG~gwEV954 z@;r|gW$Q$hxKllE&R%Yr&{n->#4LEywOoH{f6Ym4u+Am3Mj+M^x9PrEe{ z`p)e8?zJ4_#`818v%xDZ2UMKT2dc5wmocfXGyMK(iK_yVzJ8dt?$9@33a?+`3%*V< zp6Pmk2C)Gs&dADvBk_bw0FMRM6?I~X-`b*-3X1JT{echNZB;7?D}{^rD1t*jw%*s z!;u*|W^okgUspy6Yzs&cC9JH7h{!^o-vnIUwOF+jXA0IVLF~?Ym-qCVW70ILg;BU`w$w|T zf#O}vo~+;CwoAe1AZ(YnQFv#w5 zCtz)`ThbQE>Nf0+eexZYGM%&ImeQO;ev8uFbUSzAqOX#rS?u7c1k@}BkU@MV&SJ-m zo^9vf-QpsEwI37Hx*Z)7DNhDzS6KeXaXAmtE#18@BuoFS z;&8o^x7#t@uzE_seCmaoe|JBUvXsXO zrCC{nVUpbosWwGd2`I(cOV+tb?86V|@h*B~pq5n)cQr_a0x5c8V7!Lw*n-gJpT#6> zx;l06EF8LhEGz)toxAtL$ZRr;%8sphL`E~BfCGgG3&aL@0b!t|KQIy7_3)^&tJDdF zG0-5v*^~X)-ctwsLEY?KZ+~!>J>qVn-s!A8D1qKQ+B7%9O4=MfqK(;#1c8Qa8_h{% z1wymUxK9~sOVoPFwU^eTT$;lZ=0h{Qml3Pr3`Go{v)(3*OJ(biSAGx`amfRk5-e=p z{C^=S3_65a*>GPFY;Yjk)|vOMCfvWsM;-b80nfuBPO!N5>o{KJaPLD>E%c3nP=txp zlaQ099O|C5dauIO#0kk78TZT!$n#kmYR7uK*!qXh7P%C^^c}h$xcuUtrOoV(1rXa6 zf$2Jb>r9CL={QOPgUn_gLT{0Q|DZSdDzc3O$bea$uS>1f+yw00^a=E zI%n*StXsG!FN|+&a?9nW>d4WSPxKLh()vsuRKAabC2_2+Y^%!hQK2Dzm79Cs)wKe^ znNjX1%$b8cZ^0Iy-yq*K=SnkQnNR;n=}NC0Lt%ctcq0`qNSmcQ0}I?xSgS|KTGqgk z?`Qr6AR{iRL74L$0;hFZmscXaL5?a8?^lt#LA-r|x(h;1GtaPrLX3QWndPhC-ziP)NxTStLv_gNb;ZgUX6`3hi% zmKqZTP%rEbSCkfr5fQ+OrpI6GX!+RP3tE!yDFu{BJzM#Zp~XWOe&Uk;KQ zlQSzGY%UyE55XfY=OV&X3pVbejDX$OT>GkCnJOeSv(k|XAguc9T@E2Ol;tJb*0s11 zOl;b}fn_{1OsH{&==lA%`E&&?z5EKF37A!ml!zY;AwH%c=wQwONzM!1M}v{rG&_${Px-PlX zSq1}KEoDHcAkf#I@OY6)c)xU%6Mt{-F5MYJX6h0-@xHZ zD6XbWL%;L*UJtmO2H5d+gV(6SBTx9l`SkGUO-DD5q)Mh#TI7qC8b?fzL+s4( zi=l6gKFI7@AF{X?!%o=$u4>0I{7SS|JfU2b8hQ9+QPGU-Y{{yWcC2e)NG40uai<{UPZTci_ zh-zY?HZjrD`4C#v*-Kadi7MJp2jOCibXek_tKhtys~Ayp7OHP}P@CucCMgG#&rl_H*s55aqSI?RggZC>3vo&Le15MEd@|VGPPJI% zCfC9vZoD-g-*{F}!yh~$M#9WqG$0qK$R;|;c&W|Ye1~}U!PP7IaxtTC3$TZ$Owtdd z;sm8DR$;HG4}#fmijG~j%Gy5=i~?a9|3}Q&nZx8`M#uQRz__=oheO^Dl_aW*f$HD# z5!?hbrofPNiqkInkKK*@w-gImT}j)tEO5pMF+@}vG3fQq1dM}{XUFi zAf;TlDUizz=-j`&>N!UpZ;p6Mo8`{hB;GpjAN}c+Z(J8Bx5g~KbA>ZKJrxPXy0boo z4wX3QhsZ={Hk&PU{Trl(UC^;}ZLKG{jU(F%_Zg6cf>=`vQ#O)ws<}`7a(KVWAX$lSsaZq^`C8xE z#e+M<8R;5LmU3`w+(b|zj5}`JJ}5__XEVC>{-LthChqxMv9v|lL7zx|>-lhWl=L6u zWr5# z*XC=?9-@Q*o365hh(F0!Km0uhWHN2EirW48f*rn6g0h8>Cm8{4a3|)|$_-4je0zVLLeZeM1Tcg5ey&_@eLA8yEwFsCg|;fs;9gB04R1Dj?MJ1300TA2E=q8=KHU#oeCOk604Q zk-SQSI}d)&$uU49`PBVoC|Vx3)8Ut(Q|@*~{HWqVX%#00~$!4(jR!2m#$t&68!}0mG_?&y{uJLI(ej+BUIlRYC>3kuZ zgP*ig@0TEvhNiLDL%h+!pS^;OuAHJz- z5tP#=16=m)bQ2rGDIa#2=21+*Hi+;EZW9KZZ~sOkrL3O==3G!MXk=F0Wa2_M8kJ!c zAsA;bU_eWL-$<9gj5%#(C8PB_WK|BK+qMo5CExrN9BG{rr-6wYvSpKer{p4ZjGd_9nofkBAP(*eL1jwe@Toq)|T zmv#X~`ZJDwwOz`sPW---{29N27IA(zb?u*rI*6-BDPfXbXh`Z!{j3#oBCO)&x$gk+U1c6k~ z2l05X!#St-Q0zUPjL{Nau$so@BuIf^c zTfXym|0>&>VN=Pvc|7vM|s;n0j>nqb;Sox z*FZ_EqMjqE|49gcsU`mM9ztC{?5!pcb+&XIxr_L<96@b~9Z6`{`6DgxXz_`M(Ygm( zo_M*Nm}IO>p}!kag92FyaEMp09#^#+iPP)ozT^JhB;V1z5vDM4``ZgJ`4;Y54thYJ z-Od@*lXOQ6UTs&P1eiC6Ivt-36Ck#-4NO%!+EbzWMGKz9GnJ_M4>W;CP8*boi!g|Q z|00yzf~>@7BZ_GjNX#|Kc?=lW#;wC8;tAYDGi&Ndz>T0ZH-AlF7I0^!)#L2x0Ww{Z zW3S14tbAGq2amd|j_P07Hgd558}Kj#4a^3YaUqt1wi}y}neDPni*+hIuDqqCNZ^&# zQ|XPGK*!_a>?UN~nXqo7c2sK6d=({V)ni%vajnyva6o0>tDd^TRVxfgjg@!DIT2K6 zo}JdbnhuS#MXfWC!Q1R8R7V5Ep|Q`Zf^*AVLvC6>V?~ZHu}Qj2P^pb%6F8;g{MNbP zsb&48GhG3MCpyCg#jRC#tUl~WpZ>HgOPpW|=SC2XpSdrcvf_M-$$NRov0> zs$=P*^<)AUABtnZD^N9A>_s<_A5juV1%H)e&r^Kr>Zw^Ecloa6PIXP>iQBLG)XqUd zS|BL+7KZ~|(P^+#DF_u^It=^`lP;N(PpC-T)kliLY;{TRnLWMG!dQz?s#!1Uc&%pF zjG;-I11YrkH@?d1d{b&h*O;;igh6Wz=x#wrn zAaNgecKVthkWKqo%A=6@!Ys?c(n<0!2pddt3rDCk`V#_V!r-6Ih;E>CCW^j)V%IbI+?Jx(;$vuXIh0>Zsgkmiq5X!geG znReBu^OPxELT9j$&&yWbX;FQ5`v(+*;Yye}Br#W_<}Eca z0y>e8b8w8JNgJ>(>rzr0#q-p{J60pEN`(SuCW3tGu+^KPD6ggCvaG|!}?ok%4Z$5o~oeFEeCh&-z@*xtX@ z;K#RluvoRlIFf4Zgcncl&>E*}Dk(==2^d<{FI$A(v*p6jcMFCcN&C#XNCD0Fk7biFN)J6YHl3@ip!jA71L<3P^<#hDIV_2I-21PestC zE5eq5shdq_&zSbI2*n^tqwFvmQLuA46kb&g>eo}jm?}q>q%~5-aQh)%hKc`4Ar}C_g-ID-}V9y=F0!eGP?%a*2o% zA*g#GnFhX>+_aB|vU$Zzl%3&y)&CTj-PnV%izj{Qws)X*r~CZ^MutHba0(9%mjW>) zl~oe6gC(vqjiUK~@{~!g8j+*{O!Wy>f??NZAHT8byoX+$5XzO15k_;KS|6RT3G+Ss z7H_;i2WX{T+n^a{af}*gg}Ob*hLqssVk~i}?W+!3hmk(EU3{^&ETc7zy}ZHPMx97F zP*Co8zPsckwHUGsN)am>j)dXpjD*UlGCSuX4e z{WEC(v;TWHlw%nyu|kNE^2BJYYjl#O&AI-xWMOl=k?;cxs4E+m!*LN{u}05;`b#@u zjopf`!fRc{`^=f5ZA2i$CF>1yR3Aoad1HPV6d#iJhiZD0lM77eE3}&ns(zT-y zhLbCJ>VgEw?{ljYjF2fDpiN2Zwd&SyT&9B{qaiVYBNL4#?Fs+~#)X-_cQgwKU893^ z(mSs#1tZp|TRDdEsh>MUJcZuL`(xVLl7tG`kf94yI)dZ5mp{@2)>IB-R&83(_Bl;R zbp=Q*sdtJXn@mv*>nlu1`veRr10!7Ao#@hz-O)-yeoY`(PNq6e#QraB%JTHp;WH2? zk)HwizzreJlhxB9#lKfypOMzxPi3)40}h4Z%RVpD=J?*v2@Boc-7;OvhQ<+>8X4vU zGK)nky6(6c-f@ZCZF#qihV#I?b~sKla!E1CcNB4_7H3KVm9lcD#Dq&jX&(ytfr;`E z3CN#F82BAd**GSD?7w4OYy2VT@J*iVcoE%`l4ON{0-lyzt1V$jaF9Zl&*SYeJ4$2tpx^pT4|q9t z0y%2YX5novne|p6@i`YSzO8aycD-Ck8#uA!>*++6hB3bkFy^tZJikS+U$Q6$TEesn zI5}@_-fnVcbLe^=Uc&W?@{Pg61~p=UZQngfUJ|`|B6K#U6Fu#=<(lAhQb+u=Ull>o zlea*NL1s$pkvSTJp%J2R;I`?}c0Km_#-z^~c;C_6m!h@PF8hM1l@iWYie7?o2~<;n zC8{Xcsi|n{vhlO;*I?Ch*6nW zgSmnMn5reuYV%{QIu_$q11;p|&qPUBl;JR$I!V_JH@Ty*-u!D*;fR+UaW<6oQaqGd ziS~g~*(GXf=nsQ2`$n4%Ls3F8c@=%Ixmk`Tt?fb* zUwDR`1frqE4BVaujh{_ zSXINRD}DMZq8{T(?ZrDO2A>j|f>xl=JL7X_-a96T2g+zFbfBcs^?9Pl}YlXu-WaC*}43Fh%a8hZV zeIY@7(6NO0%(5?-kofdIG!dIe6ZqjqYy6x3zY!JMorjD^djzz|@{i(+RCD`en~M3S zc{V{u@Qe)jZ@4SGfQA>Q6_Qr8^0MIvCJRCDk0tcf9k$IL$Ct+$j6~3eabloCIf=QN z-ofLm6GJVhz(=iH;_ESxrPTem^(6A+;wPMEJWO25`U(bV4C7nRrU4?>fdZZ(B(+UYf1t%e2;Yt*ymxE} zs{oOd3BkZ5aLCzIDQ&tO2wqX&SSSGV{yJYY)P z`=_@v(7Mh*nWYtxzb?>~KI}qw2|-l!Q(PQP%IjHncb8QamAttpI!A@!dZ@cCWt2N< zk`2I`w52M$NqC#oYgwaG47Fnt&d&1r=a@gor(^r;YF~!V|2gA!&gfvW7&&H(tvY&* zcSwn{`jPoIP4B-(tGNIZVu2k1TG&RIoaF4eWlWzbLmc$LQ7n0l~#1VtB= zDAK-l+aahP!b{b9bNlb})E_54jouWgd{9OV8mux1yZl^dJ<-jA6xC%2`@D76L+LR_ zvPqcYpS`{bwU#8iKn_utftRy9M?mfFhKY^1e+g+AA^5<}LE6 zd7}?e%tO2_AFlW`()%HB>#9Cwx3hv89hkc?f0$Nn@&NvaQxCO9?*#K|%<#4nmi9fk z2a)Io#F6<4_@_pw6EOd}+Ns)an~1BXGB6YO3;#k5EzxWnHSI%Y8d%I8Gp)Dtp;J6n`oRpo*^PWasbnmp82PcTL`vhpB8+dwCU_13{Ms0P^Strhl0vF4ln~~rS=AGCJkt|K_ltq7Fc&-5I^6N7@X^I^H?D$BAuIDB z%hTd^5l(fMAF3F8IoaK!hQV${MmJh@Et@BQO4u7$gC2xB0+{}bRrWxxwZr`Jq_}Ed zPVf>Mgp>&`8}?iWe!<#0;gyZm+T|R&cWQ6Sfdls-lBRTqj3ITzBU|Y`E+LNuCYbZp z`=pURlwlqT{A1SI>LSp$Y7ocBC@V0<{hD%X zoYa95`1YRA#f@JkNBC;p`<5S{1xI$k64uJ0@+55S?ppNHkpc#x<%Sq^6e}6JiUvRA z@9LIbN&h^Sc8N@X4nN1SDO-%6!W9)+@hde{Yh5uboI0bCpCHO;pQ)al3l85jXq ze|{pJPo#N(!Niic)YG`kc0mc9XsC^S>nI(b%T&yYW^Cs6@QG~o{I(Sabhg{$!cozj z;1&|gH8Q=Jcm_DOHGpE=031HXVm7?Fz^e$@JM>Uc(hoFJA9vHrtagA4ePnX#Y+XbWfmJ&u#JVBF(N z^5cDgln2~>AcGN|n)2Ltf9RLcnPS$=f(^2ac5mlGFXMH{dLZ=mDgh+P}(Wv(TGf#rbs3W8-QHCj-nj7F0V@1~01z5SLzDPNkvYLb#41M~zLpMV}pRahg z|F&#(!oQOj27(+a9E~U4tIZ5}g%sqMf6XfCYMeLddup|Bi2}-CwNRUKK72dGrKxS= zwI@PP-g(A#QBH_A-uW$ADLJK(4WSkK#RvZ#C`(13bNOZYu3}A0_9dDB%9r6;{f<}P z;zJ4xe=3k!XM>6oneoTnAQ|FqH`s@OxRJc&p2QQ@P`?S-V6IE2JQ|Af6^CMf*T{|f zm&I#u#@W4&7m;ihv%_8rMwd-QZU#U!%bKd5i9V5-zxxv*kdl&Acum|5m3~HCG~fKI zudXr@Hk)^8mIZ~LzUe4M&fP&_Z@cTs57U#a{2R5J0jQC3GdpeD->miJ_QGi*D72=Y ziZ2=tRTssAiT!gkSAdgX9zhUn#d~(=-(&Z-IsXshA7e*mPiJEYJb^&ISKa*WED9P_ z`#cBUBt^=Y64&wJ7|U7jS)bm9RXMgo&*6E-j0Kio^2_prQS8BPMy38g#S0)`cUS>_CE1k2;^7FO9Gc+( zt~tw}E)zQmGsKlT4_6vQ=uKNWVO{M~5O;ydsc}Mvx99MvwP;MCDwf}}lQHzv2)Xnk zE%MD73Byz!PPL)n5t{ZC#OkZ-(l4Hj-pjIPxO)OJM;lV_1I_9>{4j7jlKv(78_=d4 z&4X~USSkv5_OpAL%R*aJjjy~vXsbKtp*~a{zxc6w)*S$RREmB92c(#?cHIHt+iW2L zU5wcdd07hP?lG0ve9g+S-J%C?U|0( zURO(hgRCM}`fd}%Ma$<9DC)C5Ixxt(<__Pbw}-}xs~}J@nR+n$$kNfPvnhO4m|~8v z_n26z*-KMfY20Ozc+zROGp(=|A*@gOHk8C-TzKq%O;klq{$X&_KM?{jwkVOu63Q$U z>sGry1l@5YyneMab6ojI6{Vs|#260#{)C595bh-jtLZcQx#R ze8hIpq4BdO)o3_zbnLmx0gN2ax$;k$nlBv@Ykg_qZ$kDt>ydBu+IIqQx{}U@EXMeN zD#_$TfbHeu0#+r^Ia>`V$Z#>Q0~_fG-12TbZCWdjSxp>~&P@oO&4z*+Wn#`7D+)Bv z1Ji1z+2F!9cc7Q$dl0AGXYIZ7s$21k*+;6zJ&0=#X1W7WveZvDPLbPYqIoQUp>g zB~8saVb^%YEY@z@We6-o)A=M;eAch(qM?aX@7A6CUN9A4QwozxhT0jBCieV4hNR6} z2`adG!^2=2oE?sx&dLxnt4JCv4dCaJBLQ1R@qi|v0FQBTPyd7On^ zX$ObBex`s|%;Oy!`l#ZvmD47Uns+`5Yfa_QW(guFcJM82m#RoI9+N#al?Qf&%Ndy8_>NZl z?7K4v-&9lKCVJnvkfDg(FcM}!R(pTG@A~GMj{;gaF|yMG$-L+x!MzrWI)(Y zz$o83t7b%Xxr4h5nDL9sOh0`&Zu>5A3$--d`Z7*ir+sL=& zg78M?t~M*%@sRG@45JoUMJCq~3iF|ez#fqwP5Yc^a~jaH)T>3s;eX_%2evc5#?h?4 zuk%)k9!0o4@GtFiPvd5?olwRQX3T%`k3y2*DhJp#sjfe3`l5lnDGkSD+;PGNB@}{! znk1Ky3J0760yoKfgYIMzEQNfzt7;vt=c$&CmGRc}<_~4%0kvZ0`6mnG6%uTp$9*!r z?PXK}lTj1S_fzqia>~{B9 zp;Yjfwdom^a(j7?@Ph*Cfu-~mB~QUM`n_Q6zK;=~6v!x;N=0z;PiW&z-SiHJv!}5% z>QFgB32LaIR3{IRn6>n8@X>j}hy$^-B0C>zqCS7vAA`N!15lNcJB~1%|3Qa1xe%!D z%!&1LXC)kOnW2&?5=<;z#6JSt#-NFtkfs z%1lL*w{eh~xVJOjZyHUtt98JDuYQ^oVF58^g>fGJ|G$!qr8a^wc-|hrbbl%96p6aI zi|CfJ;@2dVT}G`P7nSppEmS|k_>tTTPTD8YUxAKr+s{Qiwty*%cFOtZo9iX98fBA=8)@ zeyqSD*hyGH@6>#_1-77tMwV#rpC`PjOwGruCjLl$-lb5?Jfc#UBxa{Kl6k6akL7^v zrU9{~p9mkQnT`!-fl1vp2;h_(75Ba%i1pt{z+PHxc#{T%!>N?nSMhn=*+SrDLqjlr z&f7Hv+qaQP2Gb*t!6-wY?6elt5 z?KU2M!MKmnP(xF7f(phhHz67#m9?7XRu?*v49$}2Nzb=b-gv3L4&N8%OtzVWAHp#& z?(GOW+XAE|sF17GLU4kp6tX}LZOyyVE`X8c%zhS)<3PO)0pO`)>Z|Ff8W-NwkWW4o zDknoypgK;_KyAW->Pv_&G24IlzYbnyodo>iNuem&Uiu|Gon*sIzrSDU@$A13ujiSp6z5Q!nx0_! z_+otgPK6VOOWbwwj)iuY@~V!evG#>3$16BX6->J4i?0`au_G9IbbmQ7`s~NuG>=1G zpmJrB2)DVT-_1B|i)%0wuL>~pN;Uu?cmm@dS7K0KUG9P=4iM1yUFLr*+^Q^*G<%Kw zrK(N&n7$(eS9u&z89Tw9lCXn(i;0V2&`lK^3Aj4aF;$fF(L5C?U3F7w^*(KjmGDZG=9t>~RmM^g zp?&Rn_Bd;+Uzd-1q;6LFVbiDm(!uYRJ1I#4THo&mPK-NNz#dL{5T&lQTuuAXnYJF^ zK!joqX9x1$RLXs~p46$N0mxq9oYs{cN^Dg*GGIc;XovC2Ey)0GLgeZuE` zs~tOxaZOFlc~S_F7~RPvhr9y;7K^D|aBf?=-D0wfKG$Mp5*%{SkMwp0yuNf4eC#Jv@_9t$GZG5#R49a0TZlT~^e{ z2Zh8rvc;7v^a*>@^N2uBdytHYmb5A0Eku}B_d^7$qrH1J>b!balp1Xm|0x2W0lO0C zRLP^%jOwY2t~E3XzyP9gMw7==n8X+OJ{dSk$ZKK0t*u=a^zw(k_YTt|FiAO6`#wbo zfI?E3?dD5G{g5GTblWXl>fypV-tzi=dw?r2t*lO$L#VM)IB#{Hke^6$^#GHb;`>EB z???R69=Wg)9uO?D2pNQ4!iYl0$PTn;Zs^9K*jg*Co-}k(&1sjx#O$^$LBMGqcy$}D zO0RXG`~yYN&$Gk4h*x4A)6>Z5SVdVxC}UY+;6cq$MbNDY1vW8)^Lix&8j##vOBq^D zeWz5~z#~u43ttEB(WN*I=-x%kC3O8xmdHpub#-zs57Yb{9-}kXO;^G@Nb;EG<_v?% z(stbEFqhzrDIQUJVjsLrTxw~z+FF<@qpp3aubR&Z`9sx>&)Uhq&f-W zxlnA@6i|;Zg|$pZFpt(?9W#($I{O@A=87;e5N5ucW=i#%^%U|Y%zc{4$js6mia~5E z;c6skbM1L(w$S_N%`I)+*qBA<`oz5SDR;NGd2N~oRI{s(%cSNjdhEZu9$8Thi~ugy z0Xd=S$n|Fw%0Ky+sc1Fu16C47Hf4&0>KNZu(qg@g!yfMis$4iP+q@6yJ9N2Mhi6hU zLFyc^(*!e4+6Wb~z}u4p!(5MGTOtG_n&_??&uH@m42tJt8ybh_QsWz3Iq*vRx|$AE z&a$)tNwAq)ETNX1WJ!8K|G4$)$c(E0-ht1Y#cm(lW{7!P!MqaD+MZigm7!h_-F#-_ zX=i*$>rMZk_R2Ou4J+Wi&sCr3%+k1&diJc&;qDk8nH3eA#}JCBGqwV#qtS0CZ&kU8A8zSOEM)IGJsjRS zA^i15d^9ZeQspm(5octn-CGsi{FuXe+K}WSjrQ0=N{U{jm^fT$luEpRX4t9O^4ds6 zfL9jr)>ipU%vk6gHEA{fg+$btT^IbN0_SVmL?ewG`2 z!1CN7A>^3i6(|B`*&8vBH&%e)_|C6TYjK%N1v-H1kQgK&edc3FuRYvUvV)DjYIf`k z19zWLc}%^gR);I^)7H8#4VV_>hyDK)hf8i>Mnc$AQ*|co-Bs&*1YVO=R+Q|d3s@_< zyTKhoi_7B3Omvz~eAzAP?{8J|-j7`b=I45}Yz5qwG4+x?B$;ySjR z&O;OKPg1bav4xkW15XJf!m2O)xEorRpoQ3}7cX~xa>{okp&Q6?N@|FZ% zQc*yY*js$qF`^_l8gI~qrb92B{soD`FS$ONz)$vW!OCJUsBvN)KeSrMq=={Jv2nuG z5-eGuRxD)Nkui*gJhUHIQfH**foQ5P@q;)lf2U$izMx^$6{}6cUtDr#BY(JHA~oR3 zcPLT}jw~0X34B)YWHs%FPSxzR-T>$eMpa5ss_K2NvQq<$oWzpq;>?U;kH(*Mi?>v` zM4`O1mZwq{^}jGih-_pQ{_MeMbEwh^XZpIa1ih${xE->lnBw=2xjGNyq94R4rkO9@ z*nur5E;3VmE)55ILrMG-k@|Id&bh0<8))B4Q@Xi2+p0*~jvgK=S_*oEjyYKwq7cau zYxrkKlT~T{Ju-#_0n4b-aGO5ua$MtR7?}D06UJr_fiSMXp>+%Wq{Be;;x+ZnbCxaF zd+ir?(EG?BjKh1gjE&N~ghQ~%;?D4ov3rwtSxgrvI3=K87F7*R7_# zaRmj%H|cx~7z5iMUwAc%FveRvcI(sZfu(4=C4~vKR1*t*iI#&=fA($w?I&) z!8QNps;;1T(C`0Z&D@YJ#6O$-lsyibr$?`OB6cmW;H#-uM&LA*{k|(M<9PtWUB~M? z@e^OKKm11Ab?<+~BQAaq1^{#5So85RYT#yUCUm|yzQgndw5kJyrg`r%KRD=5}ucnr%I6J_o%CP;*FCq3rc~^eq^#UWrUc&MDV=hPZNaQmbYOP?;LK^$s z$xKXniAS{8|1pB7<%0KWXWgUZpS862nE48Xu(=%<fh zGU%~+x9$L-nKSgb8KMR&RoO_B=JR?^d-{+3@ZFuTQ%;!*_q9soAsm??Uix9lXGB3IL6x0!saHp{ebF ztWGNDpN*3O#HM&f_jioI{U@&3tMl~9pH_KnbYV6+b^%2N%dt$g-P+LXOFmUN|M8jn z6Xwy5-Syx)pbd;x+=**KlVeUxG~ySbaa+YHsC5nd7DE4{|EtUNYKh{&3^PYWHsqkt zONXc7Nh7_?$8$Y}O$9MZA{#L-bzqllBbr89UPBc;BZR6R7}ic?F09d1L!%3hpA}#O zIQh{ngX?9jodepG9j%KjeR1|ArOsBlv7TNvl6s{=-y5e322px~|Gz(*T}w~&Bo25A z4%QWh{a?+^dW;_Q7SxV<(@^BfVpnN|Vk>ddXHkKe*t+&%5^{5DHOyH$hJt;)fij2> zve<(ql$2Z?jIvA% z4mIqK@gb2j4P?V$^t|v0Np?Z_a#aTY6_4Nv8f1nrp7gYS9h6kRu}~4DiwlQok3b1C$ex| zp-YST0KtBvrVN2TFN0vf?qiPie)qdpB?=w57hzm(be28yFJ1_4%8hJZUr=;D1qXArp2A&FGJ91uri9+#7V@99mriHS1 z!IhfrmWRd70qMR)M_Vib?lV&4X+Q}!0}IqnmAkINF0O#-X8V*Y*k7nDq&coA-sX$RM= zP0kwQ!pe<5JxKHo!kJa;Gl4j=`l}4pbg})sDABunDJwi$>dodsV}G}5$pt^BrTSJ} zcZV_Qo9~XQeOMac$X1Nef-~uSPu>k!zW+@t4gLo0H2m_i-$af`O~M(CpABY#r!wsJo8cgLRLF(?{UJ-fvl zN!Cxw@+}%f${U9468Ff;%0v_gebU5N!@cUothcL$#%z!(7-a}pMTadwC&J!9U&8eX zBYvctlDAtacbnqQ%z@I|m>2ligsF|+ev}A$xG-!t=ey);4e1PkdJX2So=XTiLL_1> zFQo-TrTJPH`O1o2{`ebZ6-d$H2R~rzf_Sf{YfjsuogzHp`bCKU8@eV8i;pt?cbj&J zjqdmCBqlRC(fr#>y(a)Z4&}xD`VeAP27U{1m@r%&{}h&}M|tUdap5-tj=x%OPXS%y zwHWgtB#Y%OvN&xR5N=AhCLZmGj!w>CTet=HWQplRy+G~5hkK%> zTG`=k=_aau<}s>L5eaO3WIh_wq4ZB<`ln$fqJF3JO=0y(s3N&hX;cUUI`vB{|$-jXSx5m*D zVsiS`ZoC9lFZe_x?q!hIp#a{xpbl0RtFA3V$)e7|XIboE)7&3VF$ z@9AskX~_~Dm>$qpOW&kLJ7Q|0Btc1T3T)_)IqI}f+@u4=0{Ubc1C}M++g`Tgk7O+} z{u|Lp`a(l0{r(s+;%DDRFf_dGI#EE9Lt_`_ z7pb?$H_5HVmPw7WQP*!TD{FfSMspt?-GnfqlCzVa45H`WtjtW*Lt&>t3DRLrk;+6w zL8Kho#pvp9inq8g$_wvg{6@u+KSgH`WZ?1D8)y*_Duy&Z4y*5 zR+RS|Mt*MBMsnLJe5(nu<#PTo1lY0kX&@i@^>5gey2j*LBK?3C#UuU8w>~lA%VHY; z(5vCBB-Qlj!9i#L@a^H4l$UQe$=UShR_QLxBmCc^eYhlSK^H!Nb(za9`@=*-!MKVG z;o;pg9_$`~=>y)r&f$IA|LI*KfYdGuJP?zkD#x~Zt5`TDmk3E;%kt9m2|WeU@jxvPm;mPhF69cA+1VyytpEV1so*dn$~Ztt>2acO zaNVy&OUXrh;0-Ega6e_q%IPgZ43a!0qsY+YPSuyL5XJNJ2pUf=`BxUR^9R%Fw0K~h zzu|Td{izhWNyQrnWUrJ+hQg&%xdM8O`TV4S+?f{i1}BkEOJ-5w*!W|g@2vUq*fG%L z(TBoLTo@tOhitS3AWBX|!H$BT+9i?{z1I0Y+esd&lRZ6WjGU}a&x?!WD%M|tQJ*^M zb@d-*OELA|{BjQB8pC+e%2F@>DIN{O&4$#Dz-v^Jv)RTu55$p5DE_7n`C zXCIPPWa`OthHC5x3dox0k8)?7?~|541)sxG@T#bsRokgAM1ugkzcpFBW`KB6$@8&~ z>CH7Gm0GBOppz09v8bC76+Y%O2FHtg<5Qj#ztZ&v6?LQI47;;}@-k8&XUYu`6tnXQ z-=B?C7Iku62K5Hyp%#8Cp8+b@n<~=cuP+-QOR~p5DwG-@WGb^qj=b1`jzbvi2mtqe zXUr)dcIQ^!w`b=Q)kz7|rm)UEWYx?GZ8yn3?bREbd4ddT-65Kq~P z|J=?iaA0?R?Drx07}}=LvZ;t6AGoU!s^i^4Q9G{|CN+LQD7PtaVfhzT)gMenZn6mX zmLVjd%*n*h<_3Xz(qG^4@5=DyX94&B_*QnRIljY4X&A>$jA7=ASK|z(Yis&=wFFSv zVq$DfRkGL3elmB-A~Gw}#sGf)VGU!}$b{)_@O6C*Xf<(~7o%m_S-2LmZcNx5pv@;N zdC?~Km|TngUH}R9wR=1#%=MJ*w@KffY@3I1Gh<1KNmau(KP_JnH7UG4O}}8($#gKM z-9iqiiE_mxeSXP(W>;%h%)=g8H5z%^o3`L~2?oiz*`i@6#Vm$?FvW6Zg_zM~7d-SW zm#(}Aa3HO>z!jhef23LFx|B1N9A0DwO{zEpwiX%Y@0|jD`5vchvP8p1&+ruwHrRmk z_apY<`HQuFzXr6`K;Dm`>0ZI{ndfBSkj&Genu3#yjai1s>Wy(memf8B&>Box`oXF- zax8n7zQ%2UPkHIwwuhG@TWGdEIk=)`rrKaakm4pIFf}V>Ngi&Ubs^HQn+J3ZN~o^v zO9K;z#|h@gQ>IN{@`Qo*zRDA*RF-EXPWfQYwA3f);>V^ZAmM(ivxCLE%gp3*40h6j z(?g8^JHQ7>_*bN6ZSX7naBa>+)7hxbP#Z^xZKF_SF$UAPHHeT(Eaz9Jxx~1*SI^uy zS;8M!mP6d7-hdyF!%*zs<>XZvG|GXaOw?apyw`?yAIZAwmAIp(AQHYsAL^X_H4;KKF-8(vFRKIqkghHL9EfN)Z@(jyyoN z+*Zzexb_=sa-dh@A7w#a*qGZg1I=27$6iaG>`|H!5k``V5jA2j6pxX`yb;twA`R6h z=BRW#9y33d9H0<0DSSYZQIK~lWlzCHOH!wIX`Nzl)IzqV$bXE+ztOPHC(wDy=-ALt ztOVX8*C3nTjQ?7(r6cy0ODJR=VExVsTH1Ddcq1o~*lB1_z2uGx{Y0&y0Ak6jE5;XNZPd=+auVm%uh#v);blmpc9iOujPse5sf>3R{-#&0VsVuXbfb z^}c1DZIzKt=LXf)7=*gy$nlL*2!9J0KNQ4-VYBSweSLb=YGpsx7Cy@jo>A^l5YHfg zT1uq%7#^%2P1PKTI6 zp7n-Co*X}vACCg15cTb31OV16Tm&duoknXc-Hs!fo_~4ucwOZtcgavqm|Q5lY+q3h z5#E`J?1SL5PwhNdbg$@m0yD*z3`EyUXuxhq1(i8-sxLF+Ib;z5ZUV7^y!m}i-#J#7 zI8|l5pfh!Qd1<>~xNDs5)I)xIP10pRjD_f$)O_znM;VU^oprDGNc&}-g9AVrN0Vnf zjA$#j=l1wLAZe7g;}NP$X1@4Exm~LNQRBa$`%)bEZK0|clYyHbu$gjC20ZV;e>3?UGENY^;#OY+9%jB)+@U236b$8^y)Y(J z&0Z$3wzJFspAE)!8A;W2z|lbVUkuP#75pvha%5^!RQKNoyGfj!@tlt<)S<>#75l>5Wt zCnE;VBm%`hn4hV}lYMWhcfD8)e`4*$ zIyDq6@0@I6g(c}%$^P)kbzC4%j@qGEZyFuFg6Wz(t05lch|EZ@&< z$Z~eWQQk}DSZn_>h9_-25w}g(VbY)PMeO)UD}K3qySdrLRb3fbak1lr<1Pdw&xJ{Yv#fR=);kuTQ52%MHt~TL6M!-z;h{Vi-pxBq~lTk{H$wgbL%7Jtf>hi;BVh+!U320f@^Y zZSoOT$6^#))=77%=y3@=!bqS^+KcjImovsq&oPUwcXawN4<*7wik~YvYlyg%G>S}o z)BQ(Mz50kmL3k`G`e0U!L<5~F_I#PkzGw+O8KKs&teUHGjSvHE36{}$U7rwH4B^2< zykQA@Y)}CcYHBSJsdeg&7@^c4jVHuOUHYJhu4A-jkf#G>Mc>_gVZ@58`YX_75U^F9 zVYH9qI%$N!j*hX6Tfa}zyb-LnZswHf2)xuAc24l}mI;&dgz7#d{5?>JiYB?koMJ! znWSB$=H!c1545|};^nu!FoQ*kLfpdOm#_n@-p2urv4-?e(x(_Vaoga8ctthg%u?%ZNIeCgBhO zv%n8Ue&uEGXv*PR6=wblyXpOdYI1NGb{Nw2p7Z7qoz9NJ9NaPk&ixVGzK$C2!*uec z9Sw39Z!mhQKR1%$she8()pLC{o)a+gR9CW0^bOsFE|Ou>J?ahPKieC8L8rY!{iu(J zdZDrjdypKekOGRIpc4Z8^W9HKpMg|)-Blad2lcRVVtnXb`K_iewp$aMZneLK6UkL! zVz!pq9FoHWn@dQfsIiwg%o5nwdp+3_407Gwr8GEnKglK%_7c&4EX85M6JN=qZT6 z7R{J9>jr<;eIXjmsEFqEYc9DKyxY{}-)MzqCFoGhleKx7?uD|TW(JVgMAPUzU+_nT zbZPenH-aO14!TmX=9c}OY1t<)Z#5ZMQ%CJC#u8D=|n!qU_bE@B|>9?rk2ThBV)VF1RQsCil zhb|wMowDpF+l%1WBf4(;d12CwTV2A9W|;tAXnT7_qkj z>%B%di)2IOzpPk$mnvh)&A#%G44QF~1R?>?^%;Vt+D3rQ5lAzA#A8tailF+}{Xx2L zG&v}Oo)s3uqsSlMz1;V(WBsl__mBMqbmVj!$D{k1hrgMY@!BC(_M4X3bfZ9J-j}56 zYYdxzUrHJFyS)kMaIX7#(4No^Z#-*>pK(5i8tGA5PK!!=p2WDaYxYSRGgTouGMG=X zMSP6Ag$V^59-U__aR7_y5)rq%t~WL;tsEgisnbD7O$8P%f)hAUmKyDyaeGq}iOSGg zOEDxbl@r{pMIE=;8RoxmYU6i*J{L-%27jGVskXk4fTvUR{L&CM&dLS64eH{?2>l51 zdZ=&Q`YdD34=$k+M(?h*Z7q~6WiYSz_GnaZhD~k|7k~pt$luCLZk~uRr=u-$Mqse^ zs5|9g&SU2ZEFy;5XK+S*)_NwTXXYLb>lPQk=0FtfDCZ=#D_`nqX}zj2mk+eWRn49>Pmvyeho;LT>*z zTfg4fr!H0{!lhByu4kD|*a6$Pw|nM~0#7>~+}q)PXfBPkTCI4wP0WMJeKASQ3o*8+ z`OBoFWvPcn)`IaB%}o8KocyTR`F|VF66U+$9T-U^OH1$iQn>tmu9Uf1=%DI&h~}*N zmv0x#xbgea#~EXGFOMLJ`v4h)RNmwOK|sF0L_YPuk&LQYm`DUmeevUq*n5(SB!D$! zmvz5SbB1*gqKvUvidfFdgvlE(eo86a#Wj2Z0TOJtuBi=m`7RUe&5HCwHdtxO#VV#)VwB zam%Kx5@$SVxZv2)$6BL;i|Ai&WlA`emkiI?4 z;-twd&0B4thOg}gM(DevFI0UgdR7aaC4FB6Domr>=}MKRvf!a{3mEe(jCTa{e&z4V zy=t8}M7AV_TnFk~_PRV+VR=k?@=C@hO^Z>{xL#yS@-A}PRe2N`A}wZ1I(bnwMr8@s z(u4S&Nn{lv=a=mw483*^*24Xs6G3ANdE5>eC^|>@}% zn}$?{kAdXRTwqJ?>loYG0r@^5zT7i5b>E>c=bY<)eC6&k3&q~#Rx#|1~)o{D7nTU-_i2hI0vwr%u_op zUsF_b$H22G{80>mj(Ez2!r6qc2XJ1f1@b{YumMD0X)51IkM2@Lb=2{zbjD77`kOuQ7F8t@pruE`A&gmz`4 zQ%#Oqf&KSEQK`#YheZxysT+esrTBng`tH%PgO${uT9CfXmWz-JCYGYeB>Pr_LFNd( z6_gR{=t~P~*ny=P!j%A+#ru{K=m=UQS>GC1XZtY!idLyTd=m36eEIji{U;sjt0iRA z=WI9I#QLm#%<7d<*z1U$y#wJ63VS-7?mIWPr(*0I#qZ<$$Uq$EZ#yIIflD_%B}z!N zPA-2R6RZ6PWx4^}=ogX}|4buv&N%UlEE#kFU?lcJF@3nZclYu#c17sTDm{5v>u}nk zs#!{(c~23UWquK4=+dQ(+i3V$fZRFbG6WQPle1!T`3hp&zoDy`jmgqH*$VYNg7z8+#@m(7vusUl5ms7upjSyI!RA8h{5p9T`8QQ6__poA++o z<*8%FADWDAc0&zPz9&; z&^x=h7>=W+zmjpe1!v=M&o%)nyv(0X2RW)!j*r@Q7TG7G8O8r&@HQz)r32www~Y`? zU{b2<1EP0v9)(V6Wx5eW-_GT`5OE|r-msb=`~&x_Kv}I&ZhV%o;;ce%j9q$WB9$Ro z+v>l6N}uj5sCG`tsl(>pTnBA*GO}Lgar{fm?QJDoj_NkolIY7qbIAmi;drg23G<#; zUpZ%a4eeeg?O!J)Er}n@KDji%6mZ0@3E8}-U7Kv{1P*(08z--9QTtuc3( zL&EeUaaYZ1d^d@ngb>T%x51P(fwdd}Rq+`CcV&IV>0ALdPaxIjxw$4iRb&&7;*mZF z4P()I_w=+^sF7-Jc!yP5>9)xfRI|_6Gu>D)s_!k$krdfC-jcsDtmc@*e9QK zKD9UR<#Z&*#zi+*Gf71r>mmn5@YDR8RYwV(ojp&J^Nvig=Nkbud&`JC0;4e#9Qj}X=A{0Et7AyTPPVwXBmpMr&$S!=63+7PB&N` z?irWgOiPgP3jW_*=Fxw}2js~Wl+>M3UCXFw9Gdu^glcwFA|7FJjHgPC6`%bJOfC){ zlS6M9hS{E0D*9_A@eIh1zNTlGyLlAh*nES+2aBsDFk~i813Ue8yB1AI~ zZ)ckTvQ2h*2z9UjB`w^}kr?6b-5L4$jJpA$r$D)Aw|NJZW7W-}m%bF=`PEmJya}c# zvhJYtz0@@J@Yx79Z~X`a+kj26iTgUI+MI~gU+B+gjt#rc9z4s7{Lf`543WZQhWO1Q zZoIaC^L$b^KxL*V+nfR{Lk6?n3WBgb_y<*}9S>Sg4uCpY$&Tj!F@8_PWsG|vQdj-4 zzbQYXR@IO|Kl;l5zTj6?Wq#!OFhtOEWW9h0CTfqQNIygw1FHl#|j{w+y)7rBaCM#}Cv=#h%?9%_QVPrwUAQKt{apBw(9PtDb>uqWAiqVAqD!)GN|anCgB+35KTH#sMy zV-5(`F~Z#1jo(IMlK@qeOs#&;LZq6pGvWO^sKjZ$)A;IcCCeH8I{5mCx}{G|ze2YC z&lz-;wQr)xoB9u}rt z`nW~X#o6)9@PkEZe^J}fp4w^oBJ`Z9$9N;L)%ny3%ApR(rFDQ^ZqMngUnPs9)A=7r z*MtBOwa!rURQpq|!VUSBmbm^$Bmb;%8cVb2M$*^MQ{RL1PrsaFnJ z#{=9_JAA9TIGx{`8eD2yP1(xYLgy3VVBEC*8LsUbAbMt$z<(sKqja&`P=xqy(satF zEj8I76kLA^KKjQH)h4m*$QFrY>0hMS);alO5D_s1FR~LX94Hw3E{~rVv+Qfg3Ge_3 zvz&+%4U=6!bs0hoI*ffmCEp@Fd8w^pSdBH5DOUY&iNv_wQ<%~Tx{?V^To^{#b6B=^ z%Zv+!Aqh1Su_e^$l9~Wqd7`%)DOUm#m_i?AL5VksrOs84pKPa38ZyKvq7wdiBuoue zOWG;P*SewTI(jM(V~S!=?b1a&AV=y{95h_8v1b>o^{8jM#ZJ0~0P0A~UcWPtD1%V) zS7D6lVc;s^{gbKQeNW(iEGF~v1soS3_PRyl{t6_0@J%8RJ3ugKNgok-E*jtxqto^> za419pKWF?(zm(924Cp0)qybaRW^?c++zZpX5#)m&QxId#dd=x;azw=*F;H7z<{M{G z7SEa4=22(W&U3&nEN&TyFxv>q2Ha#}+$;^Yh$^u7g5|PB%sUb%eQWX*UyWsb_+aFs z;-;m3^4ff(tENABdv(9#n!7Y%u<7b-VWLowbLy9A_zp@N_{F;eSXeXgAnHJOW;+1k zn#|F4@1;U<|2#t{Ms!Ki;l^o{n7W!o7iZTsl(s{KASbVUjyYT3xq0m)R}NfXBVfl= znPJGQ@RASBF+{6vn*2#UuV3B#MMz@B2OyeXea zM5sx`@aNkHg16jx)xjK2_-Wo|W2dnX1;lpniC1Ac=EM8Vh^}SUuHKgR{*r4Dgnyvg z{Vmyd%S0tukqWshgR)=-7!`zg9`wHdJveDajqV-by0WqK(Nb3HL?rg{VLi|g*ve4M zmFj&MIu7_M7fl&Qd71imm{}!L@XfMAGyB$kf(ZhwwuP-yw)JYGL6clNG!TY2H@W5P zr|WLGo_Gj54W*oULNva}lYN^&@K`l?k1xSY{Rq9t%qIl>>CwUL7MLh_xR-eU;-rP& zKUn9YPIA=hRqh*fv*s>z_di9`tZ6Wg0Ty zzRuNhNX^DraZIbts}_I>ikau-R1%6&1QPA*2N9~r5n;rw0h3jCWb_F{XO<@28Rn7n z+^iVV=ke~(l0vU-9{%gVFKSGih)Wz_JyVX?3b#EuIBptKCxdE!`oz$KJ1%`ReQE7S z1oz}J(QfcG<)_>it|A70iYy@S-7hZqNok;mS5C8>d(`U>swpte6xK5%q#1Z)wDYk7 z){zWh-6DZY16B(~Z`*(oQz= z#?3F`lJT4%VY1LPA)-YzRVreEEv1?|?fY@DhX?nR%QRGi*afNUcN2X3v2?-Cg<^qi z8^IZYIK>}+G`B`st(3jluw}w|2?o!x<*!9R z<0H6naYzlVIlk{g@l@n|AP$qHuuv)rTx8-lL<)iFEW=NslL-09nT!C6ot3__djIux zYjO$Y?ISGi9HRj%8Sr@2VAa{?VDqGEGYA5&fBEENf_8>MzI(X??Cs?d$Kn8P^ zTy5%WrFDyfNto2*p>;vUhx9d}r9u*#=_9p`q2y+}Mj^UL=3izVAzv<7V#j;%CtiYX z<#aL-sdL9;q!ZXzDgm%g2S?y62z1$Y8{6k?8_>uUyahF=Zj6c21(OM_o6jzSP!6N^m0;pKzK$CeEruoD?V%@Eo526^%ZLpqe?x zjm#G?N4BH$7|XRr3oAMpAf>buzfWV$cU7f;RksM=8#moAfR#HuZ(FJ&j8UB7<7_>0 zh~z0P)p7YJ8DlFS0#j0mRV3ce@wxF2>lig zw)Clxx{M%uGn%qzD9`YiGE`0>SVdJuAME%%u}_)(9S0G=8<6=sPT`Bux39c|&jh%8 zAb5Zc=6(@fHD84wGy;-6)r}j+bkmY=YGfP|J`clMxpPOaK zc`yv(_;jL|zo8W`(tF!%zAVDllUTohdJEgYg_74>krBtwo-B*8l_wn!Q>JYKA??Dx zGF0fq2e(pR{PZQB#D}-fan$9<4DusT=T?X2V?fRU3txn+S8N16BT_W_Xve@T1X(hP zQ$H9SQJB{Ttq8QT)__fevn(e(bZ5G8n`TjFULW_^oFx|+b|qWo3dRhZnvhBQp+>6) z4P!ma6gH_ow~bG#J;U7@>Fbp}{Jbo!)0`pv(;1>4AqXYFvu%E<0uo?C!ktcpa5bJ^ zG!al1H>6;l{iF~P&OKS#2lx4&f@9KX)^-{a0;B7?YtsfjpePgzM7QQSMKjV@w})(4 z{win!+Th*WW7&=E<2g$3jproPtPjPD@Z{&FeU5^+Yk>L2pVLy9Cv5k&1pA^jN$35n zfgtO6>(TW>@7jWyozdS2ZvRu_W>&!~{hLazQu@xF5Km61I{yv}18BDyrsi9lmVZtw zg;4!%PeXfdseljv3dY{4wlAjADhz?U_E&)A!5ITPzv%!OwqdB8vK%Etku#Or;GAf% zipiz&w4)Gy+r?j}0VrTk0qI-n%FP)jB(y(XXBvAqkbC;mK(jwq$EGOWEm_kMP=$WH zVj3$5el{mYNy!+ZlElA76LlqVo}raEXAE~8%)Ia1AQTD2;-cD?>E0GTMjRGgg(@n^ zppEO`fD|!GSW#rFlWv8u4P2KoZ9a^_tH@U+X0?sN)fxR9HlW-NZC%jZ>+D%*RajCL zZD#z;f)5J~I2v)a3cKO{Tla=4*YS!Rf^xX0el5!0n%^L)cK1->`?Ah6E(;T-KNSfn|{qCl6g&oL{VS z(IE(EiCh<*On~*a?Mob(sVyoAf+YR)awYxA8m{TD!~xboeJn*C(>9<~^|1g&rl%ZU zglV8(^&$OCw2!NmtRpU3*REgpWGXjmr)LH7Z_Fmt>aCf+<>F&C107?KfFYdJBu9zM zu+Qn$oA#18N7&77cMC9UB$#`-X^i7m!mO2l$O3(2w1B@1pYwhV`W|+CHZs5??rxkC zztAVa@jMr3t6$%Zs+p8o!v%0=43M@}hSL8zdh2DHz>#FkEijJsM$g4qu-ldya8XJW zdG=#AvFVb)Q*0d4XK>{jX){f_nlNIhh=vp>^EI_S*@z6=No+@l)+{i>TG#gFA*h>5 zS;U)8*yZ1rR2SI~shL^@K~BdD1H-#DGa8U=yU&w>abY04lm$mKn&73b^e5wY*179N zS}yw>nk`e~0?W>A=gY6w=Rs{O@#ZF_56NSQF6v}q)e>05%H;O&7rjC@M&Qg_fAqm9I@0s*L|V*#rnjTILb9HS=j;6X+uhJ{%Z+JBR96fliX_^7l z+?H=bLrbnMElHJo^F zUMS_MEB$1V`7jT~n>Gp{WsgD!R5)|VqdRW7AQ6G@6_KUlQj_K zQrjVy$X}Juhaw_z%}#VGKId_GD{q}QLs0G(VW#zbv2dwjmGQy>zxZOY34_hA^ygU3 zxd&VVw-hr^CP#8Rp9w8pA`$@7U=SXu-KdF{=9!kW?Vf@!Zm%~3F=Es$-A2P>A2mAG zkd!7>{@JHw^U^;k=&T+@FWBxp*mr<8Qo$GvGo4V@e?v(#T{i z9ii53_|YfFY!HpsuGU&m=pZE9nMsJ$tO2Y>ZPNy?5k32}q z`QGDO+z2@e_rfPwuuEV!NnAYz_m%621*9XsC47%kqjAO`F~&0CY`U^SgWVeEp9j|` z;gy!(pNvtsdU!!O)y!wKtx47Mtw6Xc`$p+4_xHjqdl3BpBAW1219>RYdHLtVyKPxS z0yT$q0&YIH1QY|Pz+$4V@*-2?xP|OS@a<{zzB8`e0EgL5*s33 z@OEuxx5H|Jv+xqmN~G9g4ryS{T`FyQx5x%#{8j1nKvzK4%%`wfubp}>U-gdt_(?CB zoRZq^ydDw4QR^BAj*iivRJ93Yg^JG8evhyMG?wvjiQfEIAzRX8cq*fsGTeC|ZW;fR z<`N)vjZt#S#jQdSOtvgHpx&n#Fpf^%w z+-X=sP3S=E7v0Wo*p_DYRqS+su;|Kt<3LbcIqpX%ISvM7IqzqMtsmDwP_fJ(yU}#s zSPrI# zUFnIDzWOes)IbitDe$nx^+zk;je7tf5Y}$pBAXAq@!{T_G*tO5#ZIJvvlR(-1X4cp ze@^H}d(vYdi&M{12Yf6bB_?=O5cABLe|&R2M-FLlZ?w>Z@?XNl>1L!XKQ~(((4REAWQ;s;=nk}-I-){BF=^5C(xUVtC zKh97Kw6lQv=PnqcQi;NNV}@$BQ9Xb65###5AVl2pZMj~7qIiK5=Eb@s;_SokI#_VA zV$ef$WzPXDpyw9I({??MT4o>52h8d0o&})F23<4tdWM=3fXI80WvYDZO;z;rMjavK zI-eq0eHWXtKZ1=iqm@G#*Ty(b+QWF7_pObx^l}KOi#v$exTi0Rx$wSZ{l$^*Ri%q;hVk_lV>xV`% z*H&|qdSWJzW@`vzF@MUxXB*_{dJw*ECUS*a1L^%?d<5p}=wxQKwj zKHc~;mtO_x&Oq~H&tzvNS{Z}MIP^xunt4aS$Wa3|49o*Fqeh;UR*?7olOLsQ>U;9}VLuTgT=QyEG`7|Q%CyhSrtriTtTNijo4W*i?-Y69(M-H!W8Bq3ut0~)a#Q5><{CrBO0vJEO;$VApD#Tk{5=WSvZy2 z{_14UFNI*a-XI#R1q1~U$^1_Nwmgoeau1=Zf?Q3TDqQ{$UCOj-lM2(cK=?6;kStQY z)V6oknKZ`4+UE*?ys%%pd4qDHa#PULyB4Me?qjIL5ZKj8bH8D{>R6Fhz!TV7W2ws* z!6tPc?xGQS?C{~sK(+Ad`%mi%3LdUw*cl%#_d>IUyccMR_WJ8$ra)?Rx~Cm3*)8(& ziX_?sW0Xf&8pSUqV^H0?&jPi9;uHwWj&&Zy;Ial6MI#i6jLW3&OvGOI+$LTsPSA(k zQNpXm2`>>z1bT)9bpZsy4J-M2>ql;%(;USxnmZHr=dD!ojn{suhz;H((au6^9pZ9l^UwQ5F~%R8xk}{lqUe>bwK{Kb?9b?F zh#W>vjc3A^JrRZ+(U^CkfuNeOv1Hlc{PtT-cjyfUB@S$uU(PYA{`lM=dDv5v_Xl!2 zL<_jyht)SuDPc@rKaM{d0108BAV?2ng=|p+i7g|2VLveU86Mz)4?j@gyp&>ZQBb$4 zfbe*^a0^8tXO%y&DHLY`vGu~`+-0Z5O`@Aa!jwv=)k*7oL7vtl|4h80=ZzEbETxwm z8*e`kiNn)}YFOo^y!ljNKK3fxs~z zee7kNE*m8)y>?W8k_#g$u2Q5S6axiXVkAV_3o5hp^Ir22G3cB@xU|Gobb?SfVz2fp zv#esFf96^M)If)KLK=LzTC`iyS_z^s1s73LcF(R>OkQa;ILiojJ`#|sA3!WVy;|{9 zA1%&}FRz-~`_Gzg#eJJ|riWo&1N^mPD%`C8E%h1t?ZR8lUw9JH9KGDcFjEC}WiU?) z`k|2YlAzcOo;)cAH?vFpS_nWasJYGIQ|VMEJlEv7w_6dD(jhvriLqybK?GB5;^3%z zkrbNpEhFH5v%F&MNTG8XHHu%+!A=-Q-L)NUW?o7by+=uD5wnHO0Kw|>t56fn zHTvhd+Wav_RTxL2`U$;Pw#s2Tn90I>>Ti~0)FC5RzX~X&F$=w|OZ&(aLB=MejTxd| z{%`LyKu8dnMRfHtlq`H|MY*Th^;=<)V+przY)-#*-~}S$Xk;!vt@8zeklRdCnsR;Z zib8&3$avPSHirgL<%Z{t={{5LA tE?c^Kxc!(c6c%+N_b%tSJ%mfrNm>)LewGt^3iy=7u)f#5&?>J~ksX$Le#qZ44^uCN1_MT^QPbcPvf3Gn7o4czn(w#x z_sCS?{#g=>9H$yjOyf^b+$K|p$mc|Wo`M9A4T#R**Ks9f+a*kM!F4>SL2vQ>l=Vp_ zmS7%QOfcIpO_&XEPwz$0cG27;>Qv)i8cxL6;EVm>-!2|wd!;O4$q_}5-^EP&9##!w3FpC_@+7)8dnl7JaUj?P4VjRqF3*0F+0W2fM>N| z^tU1;d7H5qvQbjJTf-=-5kKX+94X#~C+O`l1O?n=B zEPO(NMP&PFIMJMBUSbJ$SfyXHL-8^yF7PNZUj$+bnkU#0x z3wJJ&E`rKaR+`ZS6lUe?pdC^PB`VxFU_>A9Ni_d#gV^NY|3m9#(>uG5VBnWiu6n5bcs@-?t~@Q0AgU@1QWZ^GhicPwE0?=AJuSQV|G+4!l$y0w83K9Cu#KijC^A z750Ir>7Kl83XAeVB$z6D4|;M!T`P= z)0LPlH@;aJG}k!oP`b{<^B@WT!!)&ix?a0k=Y3Kv?hL?>Y&6o-7VpW`YayeSc8hn+ z4$37#3%9kms%_i0P+oVEnk&!3Q<0T}H@4E}hgSL46*^uz>F~y4CURKxbrDV$G#yiJ z?S1Mj?*UNjSI{3CFV~O=S!+@dIU&~JmTJbX9qIv4OUV1YD0`TIzJ?jijOlA!Dg(>- z5y&NhI>uW@#f_MLCdrrZljiXsj{zEDdWg8CG1*+498R%vImaYRs4cj9CI1%}nq9=9 zn(}?_q=&;d;RB$>CXIlh1>Vr*2EHIk8<12nct}=`2^K>pZdIY|$*VI-sxNe7<;nQx zyp?cBke%1<4mgqlRUxXKPG6pCkh+TC(C4+`-9~SSzjDJQeU7NkmE0~;9m;b2PqZt+nHXXU}L(sr4U*f)${7BpAMr`ifmD zT=yDk<^aLRKoK*Ap%jxikp)meMNH!9G#lJa)hNkwdPy->X z*B|v$9ou)?E61(abfOauzyL#I$T<@bbD~Yw1WE@&%V`1=0CE;JuwqwoO905+&t6Gy zlHZLGUSn6HKQ71&6mAS|^PJ91W@2lbK7=+9Vn@_*{u_+ndp?OtSE9fvum3w*vW5q`;r^1ecK|Ij8eoxTuq$Z|7@o0${<-ZGZxL9 zy$uZ%l8_4nm%sA`an*0y(3N!!JAY{oeFo=6I%wW5MLiAuO7)Z4CmyXX>m6y7lns+0 zB|v^qrG#d2ayHuxd~Ivf>I1b(BQ$X3xeYRNDxtX(zMGTHpO^xW)27{$?71qtC#>u_ zEGaG$2>m=O+C$H!K#^Q{<<1RQqh?j4Pk4>mu7<43n$sT9%6F4r3=SMF<_&HCi_g=C zbCpZCO)$!_c5)=%r@DsN_{3xKx&UcK^w?a(T$^U^dZbZ@ew0Omli;#3i=r7 z0<^;(JvLXsPxYteuLnV)8fQgQ%@6`Ed8*Fa1=ItHVk+nMqtu`^rRR!!M!tAYQ&wF3 z!NXPO>`TxyvapUXTKI2o2(f8fBJ>iwBJ(>Xex0inL?nOz z+UWAKs%9IM%wbk==tR#yAC+nmdWI?}bl+DVE`7<@drw>zzr)Y%x}@Pa413#CnBj@fQr7IZ4`jVVh*BxsN_nZH3hh@~(QqreIZuBR zX9BcZ0}?*boR7rn5bO&Vx1mrAK`?{GykM^7{3WffKtgwuY!APmUBx#%4zWAwM zR{`xgX8;e3Q&yeqo4H*2%D1qtC0x9Sv`Nn(KyQ_i8W{iC=JoSmPAb5iXaR#5Z}X=5 z)gV(`PaysRRpV+jyTsDk)`;#ZJ){Mr*4&uHHMilQa||2sVBW zD@>w{^LP{pkQLT~Zc6)5FW+_9ob9NX#gah8#UTY~Re;&Wq)GTYnQ$%>mC7dgA&T>3 zkAEl=d)5O;oBh($85%6~(q0#bCEpF!c-=Xt6-j>R`0L>p6k=7@5=;;Yp&EeVcNB0HAP)>4l=16TN9dlQy z2$?i*BszQNN>5K;-4~|5Z%Ih#8HGN59;4~Zr$DG?>F=1iCkuANBT*7v>)^hR#^;@M z1hyUBuP3&%v-Lm!U*dicewMCfO4!}tr;rwa4M>xX=P5@dTaNERG-m3NqL1`OxH-+^ z5Vp$@i-nVnS+=1+clIv?OiBEg<{(4>Cf^*5LV#R{hi{L1IRHOM^-+p?DoiQEl0&Tc zuU4%vgU^rnl1mghAg5tB6DNMPS{tz!oD?1rfJp?A~0RSQ)~wAg{?y3#m}JcT8%z?rh9;Ua8SJ!|81E<^kaQNV2hx^ z=Ou8L^^J$b_sC!pCzFzo8i3pD!#al=v`%9QHh@Bdy`D+OT>2OX&9E5O>hR9ccE}Xt zG5dKvA!aE(SmXZ~5ro3YVZnN5$oL&C*NGoOPtxuuWX5w5ePh$cOAR_*nl65R`5bc- zUaYOfpLsSZiQwckdfqB#=?h?SMa8l~+iOu;X7L}+Yw)jsj)1~WA(foEyl9mB3LQh|{ zNMHi6Fa7{A*WoW=KG%I;h{>4?AO%2^)r^UmqCT~o6tP-NU7{(c?x0AIi2vV@1kEaz z+r|56H_%)aQ?<^ja)TaaUe-1>=fB!O(XPje(9>pDy+QEry93P}TlI9gSePTLA3eHaa)>-=8I_E#tu9K6JDT0@=0 zGjl^fIS-A!ML6KJ-6I81|6@_A-70b*H$skjp7r9dJINkj^@7Jf2S6t-?pKZ`sOG!` z?8X7LdNBuK;c?`nuK8nc-%EMEeIjKld|Lw`=%NkrV@$4N`~IA1p|QH16~ZOH%+27I z`u?VAW+sTcdtfYug+=>fqkPtIZRuB+)M>rhu&eoQ$xXC~)pu6E&xq5kyWYBwLphQ!Il@%59;8>hX z448PLCu1|o(`|PO8oj|O%wxE!!_9U@47M#h<-l{QT$D~sjk(%{$aI$ zIK`eTP^#uX9~M$wTrdZ6NAb6GI?GC;3#`l;N0wy9;1VbG<^C$J`@>A##W;}|1H^7S zH?8_lRJyECA+i%K*qZQxX9A4W5j+Jdyu_W!Zg*Em3sUbC=sP04m%6!ZFv2OScUE~K zP4C74#$l+kC{;=OUvMY_d0hH5(*4&^qt-r4`^~h|1gVU*9$d1PTH7MAeDnK9hjD4r zxx*KO586F=n5}J8KzEkGu zRIXXgvBMIaZ-Lqsn*i(_A6OoRh+u|cYr+K4JGuEYIkD@cj2_o3?++s4A8U&eye=NSR2s(Py+vVmov>FRdc5Vj5Ytv!!EgGHNVUcMbSvCiv~8Ij2xhjz04u$d+wVLqZYF2B>VGfT zKN%qO_>ec9We>>?UD7~loh!D*)H=p(-?7sG@`~8sOYR*fk16y=Ja?=trxiKi))sNl z>BDaLqnU|Xov}F*rE^#2ioQemYaqlO%#qB%^b?glExSc$wKitg`E!kX2^unQ4O;kNWdhQcFxJIXFY9KK0253Vq6ZwX+Jrc4LePafoBOqjAJ1@!CSiEy5m!65pb_CwD?0Wxh& zDx?T&FUxD%2P`J=lEU_T36$sudxfZezN1R%0xcaKs^oyqpriAZh3n#n`FD231b6Un z&k5gO?QG~avX+`=&+g7tqtEsdcsb_LcbXz@nr#TH3eu&Zso$JQ{@d}7LIZ^q=Xk^M zD=zJ-BCOAovhZ>+j>w%_UTG=u%Q*??@IaUKwa9nKCn_V|*i}n;)&;5u_AEAoI4F?( zuONq&bkJ~^CY|Y@AZQ^2bc!0 zg~T1r%Jnm=Sq~Euzp%MV1{ZEYwZj~UNrbY2@W{tv3Gl;Z2zR@kZ@2TjgoHu6MPIaf zcIVkgeydSmEVx1$un{Q(v9j5lZV}m=y#?MFQ7)B?vw2_}YG|##q)XMBJQz59m`ru_=3xf>3IzbTQ}66Tr_gi0p=NCm7Gv-h5=-?cgzV1Jg=2- zrpaVO;(A$Z7WK+B5WIHnhZ^N?`Nn&|>nix7hSAkfq6ASo7m zX3iMeJh$H{&ILA%{(OFD@2xy!+ItAUP9G+;V)RMI_cFfF_t1da$@CEBdC=+QgUMXF6eMb$*L^K|*T()~*V1yWe79prWVt zz&RuJF{KA8C*Fkuw*6$`kK)U!Oo42X>~(+TgWnDiieBbrl@y>TWXGh(khkbDYxW=y zqiyG@L?{(XUdXDSko0+}ChI!LxsjS&Elq!JJ%ls9eH7xYvt$2R*;P_fTb6&;WYQ-q z{O+UvfW%-}=#8CBWH}1_Xy7B}Rhs`K3rf9ev0@1XWwMID7E2N|&x#6^0yV33qd=+5~S zgw;WUVEt9i+OofNj?(AG3Dd@MBFI#DJ8HVGVemmH$b}y%83xv|i@agRC z@9`=TDXXG(xur}2SJ1&|==7o~q6~2juIpYmsIu-;~R01t1#GjTd`!+*p zYUXj-(~3YG){QT~GF0vpH(XqewCZ7D$ zsbmRjF}~9{kVi}^zABG%PyLdUNisRFnIYZQ00sXDd+5<0boXS9#z3fGTLG73@Kg;w zHjUg+fE++N@EpWZc}2sAL2~O(_^)2~81XF_BDbR?MONz_yusV{aEuCCrIWU^wW8Qu zl2Yhp1;c)qNbtJEPRD{QB7E&V*-d#@c8hKHZu!_AX@`p9fl-g<{Ud@1T!tMX-evC5CPGUP96q68b935-aP0@`I zAT>~WTGa6E^Lk#q@Ps)nPdLPkJPfy@ID$&j__HR5OsYWl?qDOnIVS$V6H(Ui!Au9- zH(+J))JLVFBa@7c3Qz7l?$H{FjA<|JC!*(>GB0l!4mh zR#+5Eh=;k4S7V0k*PfEn+Q6SrSX;lvQW0eW72HUtF9WDcYbw=|SR?Z9i#GnsS3@YZ zK0vU_n;Z8!$)E3@R%x-m+$Uk^4h{Ot_lOU9xX7tmdRe`Y#5ISKh?c|3-&NOBo)jsc zHw+{jGbwNIHHaM_G(62)C7`Kj%QJw0$v2i#u?;~0vOLu z@DAZQd$0ovn)krZe{2$Yd5XMN!~|wNsd&z*coKy_Q|t1AOSS|nNyz-GJX>O*4l1dQ=@>uqfyKjrhla_+5O}b^-b{mR zSg_O-(9&BN^|UD;^lhH`h#J?##?0RBChqSm;=R)SJp~!`{18UmlmZ;Ib-V@!<4kQ# zA(!8+DYu2d)Zq(W{@w;W@un ziOg#nYr@Ww0W`L9UQ%Gk!fh@+{c@TK)V_H<*nY!`E5jkq0@ycGy`%yFns6^fpa>L< zU9kopFDQmv9m6`lvZR(KK(*RmTy(|p?|(b1nu@k?<1pL(3DdA|^zO8?F5r3V_`Aey zipY5QEFvu$aIn9WQ1>G8M6Faj75g{B44>UNwOZ1khr}e{q__qLz-~E*)JNkoBV46p z)v3QmF}#eY#CC8V8=MYm+kGl1pI}=U_2Tw80Dx))xk1go{CJ6(SnLfi( zn*uh#wI!Xz?ZEAq3d4Y#>N7GTaaI3O0&8{6H=Mwr}h)}u;3ut0&vSM^ljQv4|-W^C04 zt5ugskVgNM9t`{2vs6Z^EAGA#3b4Do9RP<>Nl^G*YQjDp#`)zud(Yh++F!8ArXTZm z6y9W*wCBuBm=|w{gPS0R2jMZ-Is@Vx1fh|E^jln)EpFKrMoEWhGOQrP5tW#(az8Ws z)H)pBv4R9S#3G2(nX3i>dNxK-zOw)L%c^Nb>8|sxyMO)noV^Z_DW_7y#<#K#UBHlp@^Qc;D{RjAa5Rg ze!tEI>B9Gd;T@*wjEZK*^POU%q@O7^jmZPG?b(2#uwVh4TxjY2X0xCgkwI}a$tErW zVk+A`rlf;FUVecG66}OO(dEpTbMr13p{42}8!fzwoaE;J{9B@`fko5tc!vfth=Qo0 zC7+L`NSBv%X2R5+vKZ3cE0+#yuDMmXk+pzmybWW>ck$iDVmC8a0c}GWGzq3HkejAy z4MirSPkeUFMXfH{_q5iUz!CpCsvD%YjBbC#Te0GLq_~)U&uc_MN>gT%jWWOZNkMA& z?H5^T-XYfE3W^%aDdd7{dSvnFc-vxvD6#~7pdLtA7vcq%oKRm`=P@D5&pdK_k0b~j z9}AFYdQsWP+ky9vbMxR=E)&xZxwb}9-1s4Po6}7@DKm_JB}$LoGL*3_^$w8g46W}$z6EqpuMw!46a@vutYcHJzFm>uq5Beng6sBom;jrXm)t@F`&N_c{p+BdK0>Q* ztvXan52}HUaK|HW+&cwP(rn&dMJ6bTO_QLIqZ0+>dbM>Z{dzAo$K0vr)OBXhN##i_ zlr3nEZ`ULzp)T&gq4KU~%uIWxST%wzLu&aI?F@r^F*kY9OIh$y;`xcLiOsjCRI|pB zKiJmjgJ>CMCuQPRs^`rfnlNzXsb6v@f^=cdCOsV{mvU|W6wbN}60#7r7J4sr@8{|S zOFhZEkS9}_!iyOOus{6uY+Giq{g1W%v-)`#nRXa$RYF=svW|@Dfyv&lNXQrr>ao?s z)TO)Jh5lXgPT*|ZW96_3q@2^%dP*oiI{J7-|2(S^+let3#6_SpM3zt* zCm|Db$0sYWr-enYEcXQQKWD7u`3nNm`PVzdn#9XD$25LzrN5M)EK~^$Tz~v zC6!!vM{{w`O={OkPju%auRm6?S?_QLOTut(-|e8x+yX=##TQqVid==f;H2@tsoumU zRm0B;m)!IHMA<(niWm?rSQ5-Tv_Y%z4XHKjR-e)e;A8K_QrDx=q@-3)Lr?PKzB2un zxtUy`EKQMNm=(hb@My}BO|=Z0bPX>>c<`W(pRmbZs6;(s!JSHrJ{KF}OCmkdRWL8G zVJHB8F|+S4wFaiOX zmd(9=_Ig3PtnsocKk|SK(M*8wf`OqT0Vp=W6&{R@WzyKZOS$y#rep)q?+~a{qOq(> zh1h6%1+JNs6&pThjsU0~XqTMceG>dK5oHV@j=h6#%v^&Wp%O(yqleQi+Ip|cDtX<~ z%}5bcRPW6(1?a{6CY>a&W4YR~P5e-k;C{mf%QXVcHe;Uvz7~lsFqTwSQ?6lIOKzzs zPB*IyIFb9p66+6b{~YaM_{(r^jaP1-rfM3#mft38TBlFW8q zVt_&rG?lE!tWL|mu)xsd_2!3Pz7ch^BP7wwje`V)qt^M;p=@h9_H&u@R$4baDU>YS zi~7AGF-pTA#z;pd^*vm@Xsb02u06i=i+P{oYFXKqBJmcon(!wnQ@;PdbL)6bb-q z-ouDgPXgWl#_k=2StYz_XFz3c79G4^_G!=Af8QfX#Cdg4Rx8Q7pZ_h++aXESpeZ9%S{C)F4 zpqgm);o6tfUq$qrOlMY#g;1LMc-^k%J1}|$H&_eC#xX7w9p?@=;?NBUQOe>zqvPA( zWb$A)nKPA5%Y2+A`3pp{ay=zzIh*~Kt6JGIYq0bYBX~9jjtw>?q0BUra2VR{NQ4^) z<_e)AJl5 z#6)l^aH^NsjCB(^2`itXyl1}l%?wa}eE(&rnsL_rgC-a1M*;rv3kWfBw>sH~47AD}6Qz@{?|tL?V}nk0EH~7FNGfNsp>G`fypa~IJM#D? zm4Zi$L2|n(Vmf;I$Jf3L3(|GZl{i8NHQHzo<}zQ`$@eoXl7+-hySJbyt{Xg;;i;-a z)s8yCysOtK<)Pj3#mtL=;1p3aKz*t&F6%7)@m35s`#Mg8R&d`y6-1uSYxu6$@dXp# zJ{O5DGZ>~6?Hbb2F@B$tg=`xWp8{51%*mLNEqYCZJ>Xc3dUiZV8;GZlg|_M_cNWFp zQ6AkTEW$oCKs&XXZZW3tf#FCQCX&x^_caRH^>~-w`Ji0C(~hiZ@VIJa4%b@w(i;?n z+K}_6Imq>WZQzj<#>pKfFg%%*yzJ+}NgYet#L(dw2273K@Q}p}V~u$<$E0`iX?VtS zXCJ^+DI?Thbioezw($Bb`Z_J9S`M@db)syvNXBjbMzUL$7@x37S%i>Ymbv`3Ibk5F z$C))LIJuI?Mzoz;H}*xsa~8~|Urph9s8R#0S|*XpL0G#%i*oQF*UVp~43|f~&pZRN zl!lBOF&ie z-EmO&po$>uFh2rsb(zbUdP>R?h}^dxcs| z@~Ivl0X-$t$2SD<`C#rX9?`s`!e7;08#rBj=I5BpHRZCwvYHR0yVaJ>*#}?(8Eh0< zKo$IRNiqoQBVSnh${Xay=4!%xYn%ws!W`QAwfqU4f}Cu!(pzi7TBT5gvn=(n`A^aF zrz23_h)_v;c8RXGN`$2!CHkV}WR=0YT+W}S*)9cE@m9$Qw$?kP2}dVF9U}t5CZ_3Z zXlcIizL>(s#+&DPyy==++2atd_!iuC+`HifeaWx+QjrXmj3E`gx9=Yx6xi9bK#-VW zS={p2!#Pr)wpozRJ&SSpo>WMkGj8CTRMoB~jshYchadz zHx6&a*bnc~zD-}Xk=f~!Qmb@*l1sw}drsSsV%gqA}u~Ho&ulD1p=(noY zMT`mF3;IK+AiHlIvF{RcBR5!88aDqM6_>_)6IsP09Y>d%ghg#vusuWE(60uh0zD3- zq-@Obzc&k{iTNcLXU)!wK*5B;(MSLxvgDfj`ozsJpuwrwarD8qkg)oLfzn^9Qg)uX z6v^9@Agg2Y!dx>e)o)JE$ExU_(*o}J)l}fV6`)wwjsC+Io^WJzW}_9pKiH}8;Iw1` z=#KqOeE_#o`3=$Ev_QMWCk+!jo%QqRJL9=SWA(R_M|U)t*!xl~W~WA)d_NSZoDS`a zwqJF?CU>2qcpuuNeQ(vz|7Qn3$dyoU@mDmXg%oef&`8Kpfq+jxgF(2@MSr{w*~3my z5FaTwiYT~EKRBL2J}mJl!zU?cF(cHfIhy_5)X~^835RA74IVMnzj zLZm&G!po66wlamCZKS6)UdpfAlBQ-a5+zwV{Sy1aCuOJgX-|5|j0Jr>Emx3G4$gwN zbdoo%oQr82t*ZoC4n1!ht!I5n9vbLLVPl?bHN)G9EW}9t_Edh%j&-j<2z|xcnG=s_ z#M~m72)Kk_vj=lb9%QR{u^xS8T-Bu2C)p=c=Ppx>inM|ESLCCJiOZQKt9OXP6W