diff --git a/Makefile b/Makefile index 4dee35d5e..e7fa46479 100644 --- a/Makefile +++ b/Makefile @@ -88,6 +88,8 @@ ifeq ($(USING_RTAUDIO),true) 3RD_LIBS += $(MODULEDIR)/rtmidi.a endif +3RD_LIBS += $(MODULEDIR)/ysfx.a + ALL_LIBS += $(3RD_LIBS) 3rd: $(3RD_LIBS) diff --git a/source/Makefile.deps.mk b/source/Makefile.deps.mk index 130d49e4b..9c0072a3a 100644 --- a/source/Makefile.deps.mk +++ b/source/Makefile.deps.mk @@ -515,6 +515,14 @@ JUCE_GUI_BASICS_LIBS = -lgdi32 -limm32 -lcomdlg32 -lole32 endif # USING_JUCE endif # WIN32 +YSFX_FLAGS = -I$(CWD)/modules/ysfx/include +ifeq ($(MACOS),true) +YSFX_GRAPHICS_LIBS = -framework Cocoa -framework Carbon -framework Metal -framework Foundation +else ifneq ($(WIN32),true) +YSFX_GRAPHICS_FLAGS = $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags freetype2 fontconfig) +YSFX_GRAPHICS_LIBS = $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --libs freetype2 fontconfig) +endif + # --------------------------------------------------------------------------------------------------------------------- ifeq ($(STATIC_PLUGIN_TARGET),true) diff --git a/source/modules/ysfx/COPYING b/source/modules/ysfx/COPYING new file mode 100644 index 000000000..85d81ada5 --- /dev/null +++ b/source/modules/ysfx/COPYING @@ -0,0 +1,13 @@ +Copyright 2021 Jean Pierre Cimalando + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/source/modules/ysfx/LICENSE-2.0.txt b/source/modules/ysfx/LICENSE-2.0.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/source/modules/ysfx/LICENSE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/source/modules/ysfx/Makefile b/source/modules/ysfx/Makefile new file mode 100644 index 000000000..2bb986f82 --- /dev/null +++ b/source/modules/ysfx/Makefile @@ -0,0 +1,169 @@ +#!/usr/bin/make -f +# Makefile for eel2 # +# ----------------- # +# Created by falkTX +# + +CWD=../.. +MODULENAME=ysfx +include ../Makefile.mk + +# --------------------------------------------------------------------------------------------------------------------- + +BUILD_C_FLAGS += $(YSFX_FLAGS) +BUILD_C_FLAGS += $(YSFX_GRAPHICS_FLAGS) +BUILD_C_FLAGS += -Isources +BUILD_C_FLAGS += -Ithirdparty/WDL/source -Ithirdparty/dr_libs -Ithirdparty/stb +BUILD_C_FLAGS += -DYSFX_NO_STANDARD_MUTEX +BUILD_C_FLAGS += -DEELSCRIPT_NO_NET +BUILD_C_FLAGS += -DEELSCRIPT_NO_LICE +BUILD_C_FLAGS += -DWDL_FFT_REALSIZE=8 +BUILD_C_FLAGS += -DWDL_LINEPARSE_ATOF=ysfx_wdl_atof +BUILD_C_FLAGS += -DNSEEL_ATOF=ysfx_wdl_atof + +BUILD_CXX_FLAGS += $(YSFX_FLAGS) +BUILD_CXX_FLAGS += $(YSFX_GRAPHICS_FLAGS) +BUILD_CXX_FLAGS += -Isources +BUILD_CXX_FLAGS += -Ithirdparty/WDL/source -Ithirdparty/dr_libs -Ithirdparty/stb +BUILD_CXX_FLAGS += -DYSFX_NO_STANDARD_MUTEX +BUILD_CXX_FLAGS += -DEELSCRIPT_NO_NET +BUILD_CXX_FLAGS += -DEELSCRIPT_NO_LICE +BUILD_CXX_FLAGS += -DWDL_FFT_REALSIZE=8 +BUILD_CXX_FLAGS += -DWDL_LINEPARSE_ATOF=ysfx_wdl_atof +BUILD_CXX_FLAGS += -DNSEEL_ATOF=ysfx_wdl_atof + +ifneq ($(WIN32),true) +# NOTE: not compatible with MingGW, breaks win32_utf8 +BUILD_C_FLAGS += -D_FILE_OFFSET_BITS=64 +BUILD_CXX_FLAGS += -D_FILE_OFFSET_BITS=64 +endif + +ifeq ($(WIN32),true) +BUILD_C_FLAGS += -DNOMINMAX +BUILD_CXX_FLAGS += -DNOMINMAX +endif + +ifneq ($(WIN32),true) +ifneq ($(MACOS),true) +BUILD_C_FLAGS += -DSWELL_LICE_GDI -DSWELL_FONTCONFIG -DSWELL_FREETYPE +BUILD_CXX_FLAGS += -DSWELL_LICE_GDI -DSWELL_FONTCONFIG -DSWELL_FREETYPE +endif +endif + +# --------------------------------------------------------------------------------------------------------------------- + +OBJS = $(OBJDIR)/sources/ysfx.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_api_eel.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_api_file.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_api_gfx.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_api_reaper.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_audio_flac.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_audio_wav.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_config.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_eel_utils.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_midi.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_parse.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_reader.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_utils.cpp.o +OBJS += $(OBJDIR)/sources/ysfx_utils_fts.cpp.o +OBJS += $(OBJDIR)/sources/eel2-gas/sources/asm-nseel-x64-sse.S.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-caltab.c.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-cfunc.c.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-compiler.c.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-eval.c.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-lextab.c.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-ram.c.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-yylex.c.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/fft.c.o +ifeq ($(WIN32),true) +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/win32_utf8.c.o +endif +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_arc.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_colorspace.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_image.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_line.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_palette.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_texgen.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_text.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_textnew.cpp.o +OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_loaders.cpp.o +OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_bmp.cpp.o +OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_gif.cpp.o +OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_jpg.cpp.o +OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_png.cpp.o +OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_write.cpp.o +ifneq ($(WIN32),true) +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-ini.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell.cpp.o +ifeq ($(MACOS),true) +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-appstub.mm.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-dlg.mm.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-gdi.mm.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-kb.mm.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-menu.mm.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-misc.mm.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-miscdlg.mm.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-modstub.mm.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-wnd.mm.o +else +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-appstub-generic.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-dlg-generic.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-gdi-generic.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-gdi-lice.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-generic-gdk.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-generic-headless.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-kb-generic.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-menu-generic.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-misc-generic.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-miscdlg-generic.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-modstub-generic.cpp.o +OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-wnd-generic.cpp.o +endif +endif + +# --------------------------------------------------------------------------------------------------------------------- + +all: $(MODULEDIR)/$(MODULENAME).a + +# --------------------------------------------------------------------------------------------------------------------- + +clean: + rm -f $(OBJS) $(MODULEDIR)/$(MODULENAME)*.a + +debug: + $(MAKE) DEBUG=true + +# --------------------------------------------------------------------------------------------------------------------- + +$(MODULEDIR)/$(MODULENAME).a: $(OBJS) + -@mkdir -p $(MODULEDIR) + @echo "Creating $(MODULENAME).a" + @rm -f $@ + @$(AR) crs $@ $^ + +# --------------------------------------------------------------------------------------------------------------------- + +$(OBJDIR)/%.c.o: %.c + -@mkdir -p $(dir $@) + @echo "Compiling $<" + @$(CC) $< $(BUILD_C_FLAGS) -c -o $@ + +$(OBJDIR)/%.cpp.o: %.cpp + -@mkdir -p $(dir $@) + @echo "Compiling $<" + @$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ + +$(OBJDIR)/%.mm.o: %.mm + -@mkdir -p $(dir $@) + @echo "Compiling $<" + @$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ + +$(OBJDIR)/%.S.o: %.S + -@mkdir -p $(dir $@) + @echo "Compiling $<" + @$(CC) $< $(BUILD_ASM_FLAGS) -c -o $@ + +-include $(OBJS:%.o=%.d) + +# --------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/ysfx/include/ysfx.h b/source/modules/ysfx/include/ysfx.h new file mode 100644 index 000000000..a85d228da --- /dev/null +++ b/source/modules/ysfx/include/ysfx.h @@ -0,0 +1,484 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#if !defined(YSFX_INCLUDED_YSFX_H) +#define YSFX_INCLUDED_YSFX_H + +#include +#include +#include +#include +#if defined(_WIN32) +# include +#endif + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(YSFX_API) +# if defined(_WIN32) && defined(YSFX_DLL_BUILD) +# define YSFX_API __declspec(dllexport) +# elif defined(__GNUC__) +# define YSFX_API __attribute__((visibility("default"))) +# else +# define YSFX_API +# endif +#endif + +//------------------------------------------------------------------------------ +// YSFX definitions + +typedef double ysfx_real; + +enum { + ysfx_max_sliders = 64, + ysfx_max_channels = 64, + ysfx_max_midi_buses = 16, + ysfx_max_triggers = 10, +}; + +typedef enum ysfx_log_level_e { + ysfx_log_info, + ysfx_log_warning, + ysfx_log_error, +} ysfx_log_level; + +typedef void (ysfx_log_reporter_t)(intptr_t userdata, ysfx_log_level level, const char *message); + +//------------------------------------------------------------------------------ +// YSFX configuration + +typedef struct ysfx_config_s ysfx_config_t; +typedef struct ysfx_audio_format_s ysfx_audio_format_t; + +// create a new configuration +YSFX_API ysfx_config_t *ysfx_config_new(); +// delete a configuration +YSFX_API void ysfx_config_free(ysfx_config_t *config); +// increase the reference counter +YSFX_API void ysfx_config_add_ref(ysfx_config_t *config); +// set the path of the import root, a folder usually named "Effects" +YSFX_API void ysfx_set_import_root(ysfx_config_t *config, const char *root); +// set the path of the data root, a folder usually named "Data" +YSFX_API void ysfx_set_data_root(ysfx_config_t *config, const char *root); +// get the path of the import root, a folder usually named "Effects" +YSFX_API const char *ysfx_get_import_root(ysfx_config_t *config); +// get the path of the data root, a folder usually named "Data" +YSFX_API const char *ysfx_get_data_root(ysfx_config_t *config); +// guess the undefined root folders, based on the path to the JSFX file +YSFX_API void ysfx_guess_file_roots(ysfx_config_t *config, const char *sourcepath); +// register an audio format into the system +YSFX_API void ysfx_register_audio_format(ysfx_config_t *config, ysfx_audio_format_t *afmt); +// register the builtin audio formats (at least WAV file support) +YSFX_API void ysfx_register_builtin_audio_formats(ysfx_config_t *config); +// set the log reporting function +YSFX_API void ysfx_set_log_reporter(ysfx_config_t *config, ysfx_log_reporter_t *reporter); +// set the callback user data +YSFX_API void ysfx_set_user_data(ysfx_config_t *config, intptr_t userdata); + +// get a string which textually represents the log level +YSFX_API const char *ysfx_log_level_string(ysfx_log_level level); + +//------------------------------------------------------------------------------ +// YSFX effect + +typedef struct ysfx_s ysfx_t; + +// create a new effect, taking a reference to config +YSFX_API ysfx_t *ysfx_new(ysfx_config_t *config); +// delete an effect +YSFX_API void ysfx_free(ysfx_t *fx); +// increase the reference counter +YSFX_API void ysfx_add_ref(ysfx_t *fx); + +// get the configuration +YSFX_API ysfx_config_t *ysfx_get_config(ysfx_t *fx); + +typedef enum ysfx_load_option_e { + // skip imports; useful just for accessing header information and nothing else + ysfx_load_ignoring_imports = 1, +} ysfx_load_option_t; + +// load the source code from file without compiling +YSFX_API bool ysfx_load_file(ysfx_t *fx, const char *filepath, uint32_t loadopts); +// unload the source code and any compiled code +YSFX_API void ysfx_unload(ysfx_t *fx); +// check whether the effect is loaded +YSFX_API bool ysfx_is_loaded(ysfx_t *fx); + +// get the name of the effect +YSFX_API const char *ysfx_get_name(ysfx_t *fx); +// get the path of the file which is loaded +YSFX_API const char *ysfx_get_file_path(ysfx_t *fx); +// get the author of the effect +YSFX_API const char *ysfx_get_author(ysfx_t *fx); +// get the number of tags of the effect +#define ysfx_get_num_tags(fx) ysfx_get_tags((fx), NULL, 0) +// get the list of tags of the effect +YSFX_API uint32_t ysfx_get_tags(ysfx_t *fx, const char **dest, uint32_t destsize); +// get a single tag of the effect +YSFX_API const char *ysfx_get_tag(ysfx_t *fx, uint32_t index); +// get the number of inputs +YSFX_API uint32_t ysfx_get_num_inputs(ysfx_t *fx); +// get the number of outputs +YSFX_API uint32_t ysfx_get_num_outputs(ysfx_t *fx); +// get the name of the input +YSFX_API const char *ysfx_get_input_name(ysfx_t *fx, uint32_t index); +// get the name of the output +YSFX_API const char *ysfx_get_output_name(ysfx_t *fx, uint32_t index); +// get whether this effect wants metering +YSFX_API bool ysfx_wants_meters(ysfx_t *fx); +// get requested dimensions of the graphics area; 0 means host should decide +YSFX_API bool ysfx_get_gfx_dim(ysfx_t *fx, uint32_t dim[2]); + +typedef enum ysfx_section_type_e { + ysfx_section_init = 1, + ysfx_section_slider = 2, + ysfx_section_block = 3, + ysfx_section_sample = 4, + ysfx_section_gfx = 5, + ysfx_section_serialize = 6, +} ysfx_section_type_t; + +// get whether the source has the given section +YSFX_API bool ysfx_has_section(ysfx_t *fx, uint32_t type); + +typedef struct ysfx_slider_range_s { + ysfx_real def; + ysfx_real min; + ysfx_real max; + ysfx_real inc; +} ysfx_slider_range_t; + +// determine if slider exists; call from 0 to max-1 to scan available ones +YSFX_API bool ysfx_slider_exists(ysfx_t *fx, uint32_t index); +// get the name of a slider +YSFX_API const char *ysfx_slider_get_name(ysfx_t *fx, uint32_t index); +// get the range of a slider +YSFX_API bool ysfx_slider_get_range(ysfx_t *fx, uint32_t index, ysfx_slider_range_t *range); +// get whether the slider is an enumeration +YSFX_API bool ysfx_slider_is_enum(ysfx_t *fx, uint32_t index); +// get the number of labels for the enumeration slider +#define ysfx_slider_get_enum_size(fx, index) ysfx_slider_get_enum_names((fx), (index), NULL, 0) +// get the list of labels for the enumeration slider +YSFX_API uint32_t ysfx_slider_get_enum_names(ysfx_t *fx, uint32_t index, const char **dest, uint32_t destsize); +// get a single label for the enumeration slider +YSFX_API const char *ysfx_slider_get_enum_name(ysfx_t *fx, uint32_t slider_index, uint32_t enum_index); +// get whether the slider is a path (implies enumeration) +YSFX_API bool ysfx_slider_is_path(ysfx_t *fx, uint32_t index); +// get whether the slider is initially visible +YSFX_API bool ysfx_slider_is_initially_visible(ysfx_t *fx, uint32_t index); + +// get the value of the slider +YSFX_API ysfx_real ysfx_slider_get_value(ysfx_t *fx, uint32_t index); +// set the value of the slider, and call @slider later if value has changed +YSFX_API void ysfx_slider_set_value(ysfx_t *fx, uint32_t index, ysfx_real value); + +typedef enum ysfx_compile_option_e { + // skip compiling the @serialize section + ysfx_compile_no_serialize = 1 << 0, + // skip compiling the @gfx section + ysfx_compile_no_gfx = 1 << 1, +} ysfx_compile_option_t; + +// compile the previously loaded source +YSFX_API bool ysfx_compile(ysfx_t *fx, uint32_t compileopts); +// check whether the effect is compiled +YSFX_API bool ysfx_is_compiled(ysfx_t *fx); + +// get the block size +YSFX_API uint32_t ysfx_get_block_size(ysfx_t *fx); +// get the sample rate +YSFX_API ysfx_real ysfx_get_sample_rate(ysfx_t *fx); +// update the block size; don't forget to call @init +YSFX_API void ysfx_set_block_size(ysfx_t *fx, uint32_t blocksize); +// update the sample rate; don't forget to call @init +YSFX_API void ysfx_set_sample_rate(ysfx_t *fx, ysfx_real samplerate); + +// set the capacity of the MIDI buffer +YSFX_API void ysfx_set_midi_capacity(ysfx_t *fx, uint32_t capacity, bool extensible); + +// activate and invoke @init +YSFX_API void ysfx_init(ysfx_t *fx); + +// get the output latency +YSFX_API ysfx_real ysfx_get_pdc_delay(ysfx_t *fx); +// get the range of channels where output latency applies (end not included) +YSFX_API void ysfx_get_pdc_channels(ysfx_t *fx, uint32_t channels[2]); +// get whether the output latency applies to MIDI as well +YSFX_API bool ysfx_get_pdc_midi(ysfx_t *fx); + +typedef enum ysfx_playback_state_e { + ysfx_playback_error = 0, + ysfx_playback_playing = 1, + ysfx_playback_paused = 2, + ysfx_playback_recording = 5, + ysfx_playback_recording_paused = 6, +} ysfx_playback_state_t; + +typedef struct ysfx_time_info_s { + // tempo in beats/minute + ysfx_real tempo; + // state of the playback (ysfx_playback_state_t) + uint32_t playback_state; + // time position in seconds + ysfx_real time_position; + // time position in beats + ysfx_real beat_position; + // time signature in fraction form + uint32_t time_signature[2]; +} ysfx_time_info_t; + +// update time information; do this before processing the cycle +YSFX_API void ysfx_set_time_info(ysfx_t *fx, const ysfx_time_info_t *info); + +typedef struct ysfx_midi_event_s { + // the bus number + uint32_t bus; + // the frame when it happens within the cycle + uint32_t offset; + // the size of the message + uint32_t size; + // the contents of the message + const uint8_t *data; +} ysfx_midi_event_t; + +// send MIDI, it will be processed during the cycle +YSFX_API bool ysfx_send_midi(ysfx_t *fx, const ysfx_midi_event_t *event); +// receive MIDI, after having processed the cycle +YSFX_API bool ysfx_receive_midi(ysfx_t *fx, ysfx_midi_event_t *event); +// receive MIDI from a single bus (do not mix with API above, use either) +YSFX_API bool ysfx_receive_midi_from_bus(ysfx_t *fx, uint32_t bus, ysfx_midi_event_t *event); + +// send a trigger, it will be processed during the cycle +YSFX_API bool ysfx_send_trigger(ysfx_t *fx, uint32_t index); + +// get a bit mask of sliders whose values must be redisplayed, and clear it to zero +YSFX_API uint64_t ysfx_fetch_slider_changes(ysfx_t *fx); +// get a bit mask of sliders whose values must be automated, and clear it to zero +YSFX_API uint64_t ysfx_fetch_slider_automations(ysfx_t *fx); +// get a bit mask of sliders currently visible +YSFX_API uint64_t ysfx_get_slider_visibility(ysfx_t *fx); + +// process a cycle in 32-bit float +YSFX_API void ysfx_process_float(ysfx_t *fx, const float *const *ins, float *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames); +// process a cycle in 64-bit float +YSFX_API void ysfx_process_double(ysfx_t *fx, const double *const *ins, double *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames); + +typedef struct ysfx_state_slider_s { + // index of the slider + uint32_t index; + // value of the slider + ysfx_real value; +} ysfx_state_slider_t; + +typedef struct ysfx_state_s { + // values of the sliders + ysfx_state_slider_t *sliders; + // number of sliders + uint32_t slider_count; + // serialized data + uint8_t *data; + // size of serialized data + size_t data_size; +} ysfx_state_t; + +// load state +YSFX_API bool ysfx_load_state(ysfx_t *fx, ysfx_state_t *state); +// save current state; release this object when done +YSFX_API ysfx_state_t *ysfx_save_state(ysfx_t *fx); +// release a saved state object +YSFX_API void ysfx_state_free(ysfx_state_t *state); +// duplicate a state object +YSFX_API ysfx_state_t *ysfx_state_dup(ysfx_state_t *state); + +typedef struct ysfx_preset_s { + // name of the preset + char *name; + // state of the preset + ysfx_state_t *state; +} ysfx_preset_t; + +typedef struct ysfx_bank_s { + // name of the bank + char *name; + // list of presets + ysfx_preset_t *presets; + // number of programs + uint32_t preset_count; +} ysfx_bank_t; + +// get the path of the RPL preset bank of the loaded JSFX, if present +YSFX_API const char *ysfx_get_bank_path(ysfx_t *fx); +// read a preset bank from RPL file +YSFX_API ysfx_bank_t *ysfx_load_bank(const char *path); +// free a preset bank +YSFX_API void ysfx_bank_free(ysfx_bank_t *bank); + +// type of a function which can enumerate VM variables; returning 0 ends the search +typedef int (ysfx_enum_vars_callback_t)(const char *name, ysfx_real *var, void *userdata); +// enumerate all variables currently in the VM +YSFX_API void ysfx_enum_vars(ysfx_t *fx, ysfx_enum_vars_callback_t *callback, void *userdata); +// find a single variable in the VM +YSFX_API ysfx_real *ysfx_find_var(ysfx_t *fx, const char *name); +// read a chunk of virtual memory from the VM +YSFX_API void ysfx_read_vmem(ysfx_t *fx, uint32_t addr, ysfx_real *dest, uint32_t count); + +//------------------------------------------------------------------------------ +// YSFX graphics + +// NOTE: all `ysfx_gfx_*` functions must be invoked from a dedicated UI thread + +typedef struct ysfx_gfx_config_s { + // opaque user data passed to callbacks + void *user_data; + // the width of the frame buffer (having the scale factor applied) + uint32_t pixel_width; + // the height of the frame buffer (having the scale factor applied) + uint32_t pixel_height; + // the distance in bytes between lines; if 0, it defaults to (4*width) + // currently it is required to be a multiple of 4 + uint32_t pixel_stride; + // the pixel data of the frame buffer, of size (stride*height) bytes + // the byte order in little-endian is 'BGRA', big-endian is 'ARGB' + uint8_t *pixels; + // the scale factor of the display; 1.0 or greater, 2.0 for Retina display + ysfx_real scale_factor; + // show a menu and run it synchronously; returns an item ID >= 1, or 0 if none + int32_t (*show_menu)(void *user_data, const char *menu_spec, int32_t xpos, int32_t ypos); + // change the cursor + void (*set_cursor)(void *user_data, int32_t cursor); + // if index is not -1, get the dropped file at this index (otherwise null) + // if index is -1, clear the list of dropped files, and return null + const char *(*get_drop_file)(void *user_data, int32_t index); +} ysfx_gfx_config_t; + +// set up the graphics rendering +YSFX_API void ysfx_gfx_setup(ysfx_t *fx, ysfx_gfx_config_t *gc); +// get whether the current effect is requesting Retina support +YSFX_API bool ysfx_gfx_wants_retina(ysfx_t *fx); +// push a key to the input queue +YSFX_API void ysfx_gfx_add_key(ysfx_t *fx, uint32_t mods, uint32_t key, bool press); +// update mouse information; position is relative to canvas; wheel should be in steps normalized to ±1.0 +YSFX_API void ysfx_gfx_update_mouse(ysfx_t *fx, uint32_t mods, int32_t xpos, int32_t ypos, uint32_t buttons, ysfx_real wheel, ysfx_real hwheel); +// invoke @gfx to paint the graphics; returns whether the framer buffer is modified +YSFX_API bool ysfx_gfx_run(ysfx_t *fx); + +//------------------------------------------------------------------------------ +// YSFX key map + +// these key definitions match those of pugl + +enum { + ysfx_mod_shift = 1 << 0, + ysfx_mod_ctrl = 1 << 1, + ysfx_mod_alt = 1 << 2, + ysfx_mod_super = 1 << 3, +}; + +enum { + ysfx_key_backspace = 0x08, + ysfx_key_escape = 0x1b, + ysfx_key_delete = 0x7f, + + ysfx_key_f1 = 0xe000, + ysfx_key_f2, + ysfx_key_f3, + ysfx_key_f4, + ysfx_key_f5, + ysfx_key_f6, + ysfx_key_f7, + ysfx_key_f8, + ysfx_key_f9, + ysfx_key_f10, + ysfx_key_f11, + ysfx_key_f12, + ysfx_key_left, + ysfx_key_up, + ysfx_key_right, + ysfx_key_down, + ysfx_key_page_up, + ysfx_key_page_down, + ysfx_key_home, + ysfx_key_end, + ysfx_key_insert, +}; + +enum { + ysfx_button_left = 1 << 0, + ysfx_button_middle = 1 << 1, + ysfx_button_right = 1 << 2, +}; + +//------------------------------------------------------------------------------ +// YSFX audio formats + +typedef struct ysfx_audio_reader_s ysfx_audio_reader_t; + +typedef struct ysfx_audio_file_info_s { + uint32_t channels; + ysfx_real sample_rate; +} ysfx_audio_file_info_t; + +typedef struct ysfx_audio_format_s { + // quickly checks if this format would be able to handle the given file + bool (*can_handle)(const char *path); + // open an audio file of this format for reading + ysfx_audio_reader_t *(*open)(const char *path); + // close the audio file + void (*close)(ysfx_audio_reader_t *reader); + // get the sample rate and the channel count + ysfx_audio_file_info_t (*info)(ysfx_audio_reader_t *reader); + // get the number of samples left to read + uint64_t (*avail)(ysfx_audio_reader_t *reader); + // move the read pointer back to the beginning + void (*rewind)(ysfx_audio_reader_t *reader); + // read the next block of samples + uint64_t (*read)(ysfx_audio_reader_t *reader, ysfx_real *samples, uint64_t count); +} ysfx_audio_format_t; + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +//------------------------------------------------------------------------------ +// YSFX RAII helpers + +#if defined(__cplusplus) && (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSVC_LANG >= 201103L)) +#include + +#define YSFX_DEFINE_AUTO_PTR(aptr, styp, freefn) \ + struct aptr##_deleter { \ + void operator()(styp *x) const noexcept { freefn(x); } \ + }; \ + using aptr = std::unique_ptr + +YSFX_DEFINE_AUTO_PTR(ysfx_config_u, ysfx_config_t, ysfx_config_free); +YSFX_DEFINE_AUTO_PTR(ysfx_u, ysfx_t, ysfx_free); +YSFX_DEFINE_AUTO_PTR(ysfx_state_u, ysfx_state_t, ysfx_state_free); +YSFX_DEFINE_AUTO_PTR(ysfx_bank_u, ysfx_bank_t, ysfx_bank_free); +#endif // defined(__cplusplus) && (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSVC_LANG >= 201103L)) + +//------------------------------------------------------------------------------ + +#endif // !defined(YSFX_INCLUDED_YSFX_H) diff --git a/source/modules/ysfx/sources/base64/Base64.hpp b/source/modules/ysfx/sources/base64/Base64.hpp new file mode 100644 index 000000000..fcdd8ce6e --- /dev/null +++ b/source/modules/ysfx/sources/base64/Base64.hpp @@ -0,0 +1,139 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2022 Jean Pierre Cimalando + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once +#include +#include +#include +#include +#include + +// ----------------------------------------------------------------------- +// base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html + +/* + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author 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 source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + 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 source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ + +// ----------------------------------------------------------------------- +// Helpers + +#ifndef DOXYGEN +namespace DistrhoBase64Helpers { + +static +std::array createCharIndexTable() +{ + std::array table; + table.fill(-1); + int8_t index = 0; + for (uint8_t c = 'A'; c <= 'Z'; ++c) table[c] = index++; + for (uint8_t c = 'a'; c <= 'z'; ++c) table[c] = index++; + for (uint8_t c = '0'; c <= '9'; ++c) table[c] = index++; + table['+'] = index++; + table['/'] = index++; + return table; +} + +static const std::array kCharIndexTable = createCharIndexTable(); + +} // namespace DistrhoBase64Helpers +#endif + +// ----------------------------------------------------------------------- + +static inline +std::vector d_getChunkFromBase64String(const char* const base64string, std::size_t base64stringLen = ~std::size_t(0)) +{ + std::vector ret; + + if (! base64string) + return ret; + + uint32_t i=0, j=0; + uint32_t charArray3[3], charArray4[4]; + + if (base64stringLen == ~std::size_t(0)) + base64stringLen = std::strlen(base64string); + + ret.reserve(base64stringLen*3/4 + 4); + + for (std::size_t l=0; l filename && *p != '\\' && *p != '/' && *p != '.') + p--; + if (stricmp(p, ".bmp")) + return nullptr; + } + return LICE_LoadSTB(filename, bmpbase); + } + + static const char *get_extlist() + { + return "BMP files (*.BMP)\0*.BMP\0"; + } +}; + +void lice_stb_install_bmp_loader() +{ + static LICE_stb_BMPLoader loader; +} diff --git a/source/modules/ysfx/sources/lice_stb/lice_stb_generic.hpp b/source/modules/ysfx/sources/lice_stb/lice_stb_generic.hpp new file mode 100644 index 000000000..a00fa460c --- /dev/null +++ b/source/modules/ysfx/sources/lice_stb/lice_stb_generic.hpp @@ -0,0 +1,76 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_STATIC +#define STBI_WINDOWS_UTF8 + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +#endif +#include "stb_image.h" +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +//------------------------------------------------------------------------------ +#include "WDL/lice/lice.h" +#include "WDL/wdltypes.h" + +static LICE_IBitmap *LICE_LoadSTB(const char *filename, LICE_IBitmap *bmp) +{ + LICE_IBitmap *delbmp = nullptr; + stbi_uc *srcpx = nullptr; + LICE_pixel *dstpx = nullptr; + bool dstflip = false; + unsigned dstspan = 0; + unsigned w = 0; + unsigned h = 0; + unsigned ch = 0; + + srcpx = stbi_load(filename, (int *)&w, (int *)&h, (int *)&ch, 4); + if (!srcpx) + goto fail; + + if (bmp) + bmp->resize(w, h); + else + bmp = delbmp = new WDL_NEW LICE_MemBitmap(w, h); + + if (!bmp || (unsigned)bmp->getWidth() != w || (unsigned)bmp->getHeight() != h) + goto fail; + + dstpx = bmp->getBits(); + dstflip = bmp->isFlipped(); + dstspan = bmp->getRowSpan(); + + for (unsigned row = 0; row < h; ++row) { + const stbi_uc *src = srcpx + row * (4 * w); + LICE_pixel *dst = dstpx + dstspan * (dstflip ? (h - 1 - row) : row); + for (unsigned col = 0; col < w; ++col, src += 4, ++dst) + *dst = LICE_RGBA(src[0], src[1], src[2], src[3]); + } + + stbi_image_free(srcpx); + return bmp; + +fail: + delete delbmp; + stbi_image_free(srcpx); + return nullptr; +} diff --git a/source/modules/ysfx/sources/lice_stb/lice_stb_gif.cpp b/source/modules/ysfx/sources/lice_stb/lice_stb_gif.cpp new file mode 100644 index 000000000..bdcea3e8a --- /dev/null +++ b/source/modules/ysfx/sources/lice_stb/lice_stb_gif.cpp @@ -0,0 +1,67 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#define STBI_ONLY_GIF +#include "lice_stb_generic.hpp" + +LICE_IBitmap *LICE_LoadGIF(const char *filename, LICE_IBitmap *bmp, int *nframes) +{ + LICE_IBitmap *ret = LICE_LoadSTB(filename, bmp); + if (ret) { + if (nframes) // animation not implemented + *nframes = 1; + } + return ret; +} + +class LICE_stb_GIFLoader +{ + _LICE_ImageLoader_rec rec; + +public: + LICE_stb_GIFLoader() + { + rec.loadfunc = loadfunc; + rec.get_extlist = get_extlist; + rec._next = LICE_ImageLoader_list; + LICE_ImageLoader_list = &rec; + } + + static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase) + { + if (checkFileName) { + const char *p = filename; + while (*p) + p++; + while (p > filename && *p != '\\' && *p != '/' && *p != '.') + p--; + if (stricmp(p, ".gif")) + return nullptr; + } + return LICE_LoadSTB(filename, bmpbase); + } + + static const char *get_extlist() + { + return "GIF files (*.GIF)\0*.GIF\0"; + } +}; + +void lice_stb_install_gif_loader() +{ + static LICE_stb_GIFLoader loader; +} diff --git a/source/modules/ysfx/sources/lice_stb/lice_stb_jpg.cpp b/source/modules/ysfx/sources/lice_stb/lice_stb_jpg.cpp new file mode 100644 index 000000000..8ae493342 --- /dev/null +++ b/source/modules/ysfx/sources/lice_stb/lice_stb_jpg.cpp @@ -0,0 +1,62 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#define STBI_ONLY_JPEG +#include "lice_stb_generic.hpp" + +LICE_IBitmap *LICE_LoadJPG(const char *filename, LICE_IBitmap *bmp) +{ + return LICE_LoadSTB(filename, bmp); +} + +class LICE_stb_JPGLoader +{ + _LICE_ImageLoader_rec rec; + +public: + LICE_stb_JPGLoader() + { + rec.loadfunc = loadfunc; + rec.get_extlist = get_extlist; + rec._next = LICE_ImageLoader_list; + LICE_ImageLoader_list = &rec; + } + + static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *jpgbase) + { + if (checkFileName) { + const char *p = filename; + while (*p) + p++; + while (p > filename && *p != '\\' && *p != '/' && *p != '.') + p--; + if (stricmp(p, ".jpg") && stricmp(p, ".jpeg") && stricmp(p, ".jfif")) + return nullptr; + } + return LICE_LoadSTB(filename, jpgbase); + } + + static const char *get_extlist() + { + return "JPEG files (*.JPG;*.JPEG;*.JFIF)\0*.JPG;*.JPEG;*.JFIF\0"; + } +}; + +void lice_stb_install_jpg_loader() +{ + static LICE_stb_JPGLoader loader; +} diff --git a/source/modules/ysfx/sources/lice_stb/lice_stb_loaders.cpp b/source/modules/ysfx/sources/lice_stb/lice_stb_loaders.cpp new file mode 100644 index 000000000..2f80f1388 --- /dev/null +++ b/source/modules/ysfx/sources/lice_stb/lice_stb_loaders.cpp @@ -0,0 +1,26 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "lice_stb_loaders.hpp" + +void lice_stb_install_loaders() +{ + lice_stb_install_bmp_loader(); + lice_stb_install_gif_loader(); + lice_stb_install_jpg_loader(); + lice_stb_install_png_loader(); +} diff --git a/source/modules/ysfx/sources/lice_stb/lice_stb_loaders.hpp b/source/modules/ysfx/sources/lice_stb/lice_stb_loaders.hpp new file mode 100644 index 000000000..2ff28c564 --- /dev/null +++ b/source/modules/ysfx/sources/lice_stb/lice_stb_loaders.hpp @@ -0,0 +1,23 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +void lice_stb_install_loaders(); + +void lice_stb_install_bmp_loader(); +void lice_stb_install_gif_loader(); +void lice_stb_install_jpg_loader(); +void lice_stb_install_png_loader(); diff --git a/source/modules/ysfx/sources/lice_stb/lice_stb_png.cpp b/source/modules/ysfx/sources/lice_stb/lice_stb_png.cpp new file mode 100644 index 000000000..a750d36a3 --- /dev/null +++ b/source/modules/ysfx/sources/lice_stb/lice_stb_png.cpp @@ -0,0 +1,62 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#define STBI_ONLY_PNG +#include "lice_stb_generic.hpp" + +LICE_IBitmap *LICE_LoadPNG(const char *filename, LICE_IBitmap *bmp) +{ + return LICE_LoadSTB(filename, bmp); +} + +class LICE_stb_PNGLoader +{ + _LICE_ImageLoader_rec rec; + +public: + LICE_stb_PNGLoader() + { + rec.loadfunc = loadfunc; + rec.get_extlist = get_extlist; + rec._next = LICE_ImageLoader_list; + LICE_ImageLoader_list = &rec; + } + + static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase) + { + if (checkFileName) { + const char *p = filename; + while (*p) + p++; + while (p > filename && *p != '\\' && *p != '/' && *p != '.') + p--; + if (stricmp(p, ".png")) + return nullptr; + } + return LICE_LoadSTB(filename, bmpbase); + } + + static const char *get_extlist() + { + return "PNG files (*.PNG)\0*.PNG\0"; + } +}; + +void lice_stb_install_png_loader() +{ + static LICE_stb_PNGLoader loader; +} diff --git a/source/modules/ysfx/sources/lice_stb/lice_stb_write.cpp b/source/modules/ysfx/sources/lice_stb/lice_stb_write.cpp new file mode 100644 index 000000000..5af3f8deb --- /dev/null +++ b/source/modules/ysfx/sources/lice_stb/lice_stb_write.cpp @@ -0,0 +1,101 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#define STBIW_WINDOWS_UTF8 +#define STB_IMAGE_WRITE_STATIC +#define STB_IMAGE_WRITE_IMPLEMENTATION + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +#endif +#include "stb_image_write.h" +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +//------------------------------------------------------------------------------ +#define WDL_NO_DEFINE_MINMAX +#include "WDL/lice/lice.h" +#include "WDL/wdltypes.h" +#include + +static std::unique_ptr bmp_to_stbi(LICE_IBitmap *bmp, unsigned ch) +{ + unsigned w = (unsigned)bmp->getWidth(); + unsigned h = (unsigned)bmp->getHeight(); + unsigned srcspan = (unsigned)bmp->getRowSpan(); + bool srcflip = bmp->isFlipped(); + LICE_pixel *srcpx = bmp->getBits(); + std::unique_ptr result; + + if (ch == 4) { + unsigned char *dstpx = new unsigned char[4 * w * h]; + result.reset(dstpx); + for (unsigned row = 0; row < h; ++row) { + LICE_pixel *src = srcpx + srcspan * (srcflip ? (h - 1 - row) : row); + unsigned char *dst = dstpx + row * (4 * w); + for (unsigned col = 0; col < w; ++col, ++src, dst += 4) { + LICE_pixel px = *src; + dst[0] = LICE_GETR(px); + dst[1] = LICE_GETG(px); + dst[2] = LICE_GETB(px); + dst[3] = LICE_GETA(px); + } + } + } + else if (ch == 3) { + unsigned char *dstpx = new unsigned char[3 * w * h]; + result.reset(dstpx); + for (unsigned row = 0; row < h; ++row) { + LICE_pixel *src = srcpx + srcspan * (srcflip ? (h - 1 - row) : row); + unsigned char *dst = dstpx + row * (3 * w); + for (unsigned col = 0; col < w; ++col, ++src, dst += 3) { + LICE_pixel px = *src; + dst[0] = LICE_GETR(px); + dst[1] = LICE_GETG(px); + dst[2] = LICE_GETB(px); + } + } + } + + return result; +} + +bool LICE_WritePNG(const char *filename, LICE_IBitmap *bmp, bool wantalpha) +{ + unsigned ch = wantalpha ? 4 : 3; + std::unique_ptr data = bmp_to_stbi(bmp, ch); + + if (!data) + return false; + + return stbi_write_png(filename, bmp->getWidth(), bmp->getHeight(), (int)ch, data.get(), 0); +} + +bool LICE_WriteJPG(const char *filename, LICE_IBitmap *bmp, int quality, bool force_baseline) +{ + (void)force_baseline; // always baseline + + unsigned ch = 4; + std::unique_ptr data = bmp_to_stbi(bmp, ch); + + if (!data) + return false; + + return stbi_write_jpg(filename, bmp->getWidth(), bmp->getHeight(), (int)ch, data.get(), quality); +} diff --git a/source/modules/ysfx/sources/utility/sync_bitset.hpp b/source/modules/ysfx/sources/utility/sync_bitset.hpp new file mode 100644 index 000000000..064c4b18d --- /dev/null +++ b/source/modules/ysfx/sources/utility/sync_bitset.hpp @@ -0,0 +1,169 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include +#include +#include + +namespace ysfx { + +//------------------------------------------------------------------------------ +// sync_bitset64: A lock-free synchronized bitset of size 64 +// +// This is implemented by a single qword on 64-bit machine, otherwise a pair of +// dwords on 32-bit machine, which is to ensure lock-freedom. +// +// This bitset is synchronized but not atomic; a thread might see a +// partially updated set after a modification. Conceptually, it can be seen as +// atomic on individual bit flips (that is, masking operations do not race). + +class sync_bitset64_single; +class sync_bitset64_dual; + +// could use std::atomic::is_always_lock_free on C++17 and up +using sync_bitset64 = std::conditional< + sizeof(intptr_t) <= 4, sync_bitset64_dual, sync_bitset64_single>::type; + +//------------------------------------------------------------------------------ +class sync_bitset64_single { +public: + uint64_t load() const + { + return bits_.load(std::memory_order_relaxed); + } + + void store(uint64_t value) + { + bits_.store(value, std::memory_order_relaxed); + } + + uint64_t exchange(uint64_t value) + { + return bits_.exchange(value, std::memory_order_relaxed); + } + + uint64_t fetch_or(uint64_t value) + { + return bits_.fetch_or(value, std::memory_order_relaxed); + } + + uint64_t fetch_and(uint64_t value) + { + return bits_.fetch_and(value, std::memory_order_relaxed); + } + + uint64_t fetch_xor(uint64_t value) + { + return bits_.fetch_xor(value, std::memory_order_relaxed); + } + + void operator|=(uint64_t value) + { + fetch_or(value); + } + + void operator&=(uint64_t value) + { + fetch_and(value); + } + + void operator^=(uint64_t value) + { + fetch_xor(value); + } + +private: + std::atomic bits_{0}; +}; + +//------------------------------------------------------------------------------ +class sync_bitset64_dual { +public: + uint64_t load() const + { + return join(lobits_.load(std::memory_order_relaxed), + hibits_.load(std::memory_order_relaxed)); + } + + void store(uint64_t value) + { + lobits_.store(lo(value), std::memory_order_relaxed); + hibits_.store(hi(value), std::memory_order_relaxed); + } + + uint64_t exchange(uint64_t value) + { + return join(lobits_.exchange(lo(value), std::memory_order_relaxed), + hibits_.exchange(hi(value), std::memory_order_relaxed)); + } + + uint64_t fetch_or(uint64_t value) + { + return join(lobits_.fetch_or(lo(value), std::memory_order_relaxed), + hibits_.fetch_or(hi(value), std::memory_order_relaxed)); + } + + uint64_t fetch_and(uint64_t value) + { + return join(lobits_.fetch_and(lo(value), std::memory_order_relaxed), + hibits_.fetch_and(hi(value), std::memory_order_relaxed)); + } + + uint64_t fetch_xor(uint64_t value) + { + return join(lobits_.fetch_xor(lo(value), std::memory_order_relaxed), + hibits_.fetch_xor(hi(value), std::memory_order_relaxed)); + } + + void operator|=(uint64_t value) + { + fetch_or(value); + } + + void operator&=(uint64_t value) + { + fetch_and(value); + } + + void operator^=(uint64_t value) + { + fetch_xor(value); + } + +private: + static constexpr uint64_t join(uint32_t lo, uint32_t hi) + { + return (uint64_t)lo | ((uint64_t)hi << 32); + } + + static constexpr uint32_t lo(uint64_t value) + { + return (uint32_t)(value & 0xFFFFFFFFu); + } + + static constexpr uint32_t hi(uint64_t value) + { + return (uint32_t)(value >> 32); + } + +private: + std::atomic lobits_{0}; + std::atomic hibits_{0}; +}; + +} // namespace ysfx diff --git a/source/modules/ysfx/sources/ysfx.cpp b/source/modules/ysfx/sources/ysfx.cpp new file mode 100644 index 000000000..368d2ac88 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx.cpp @@ -0,0 +1,1540 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx.hpp" +#include "ysfx_config.hpp" +#include "ysfx_eel_utils.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static_assert(std::is_same::value, + "ysfx_real is incorrectly defined"); + +enum { + ysfx_max_file_handles = 64, // change if it needs more +}; + +//------------------------------------------------------------------------------ +static thread_local ysfx_thread_id_t ysfx_thread_id; + +ysfx_thread_id_t ysfx_get_thread_id() +{ + return ysfx_thread_id; +} + +void ysfx_set_thread_id(ysfx_thread_id_t id) +{ + ysfx_thread_id = id; +} + +//------------------------------------------------------------------------------ +struct ysfx_api_initializer { +private: + ysfx_api_initializer(); + ~ysfx_api_initializer(); +public: + static void init_once(); +}; + +ysfx_api_initializer::ysfx_api_initializer() +{ + if (NSEEL_init() != 0) + throw std::runtime_error("NSEEL_init"); + + ysfx_api_init_eel(); + ysfx_api_init_reaper(); + ysfx_api_init_file(); + ysfx_api_init_gfx(); +} + +ysfx_api_initializer::~ysfx_api_initializer() +{ + NSEEL_quit(); +} + +void ysfx_api_initializer::init_once() +{ + static ysfx_api_initializer init; +} + +//------------------------------------------------------------------------------ +ysfx_t *ysfx_new(ysfx_config_t *config) +{ + ysfx_u fx{new ysfx_t}; + + ysfx_config_add_ref(config); + fx->config.reset(config); + + fx->string_ctx.reset(ysfx_eel_string_context_new()); + + ysfx_api_initializer::init_once(); + + NSEEL_VMCTX vm = NSEEL_VM_alloc(); + if (!vm) + throw std::bad_alloc(); + fx->vm.reset(vm); + + NSEEL_VM_SetCustomFuncThis(vm, fx.get()); + + ysfx_eel_string_initvm(vm); + +#if !defined(YSFX_NO_GFX) + fx->gfx.state.reset(ysfx_gfx_state_new(fx.get())); +#endif + + auto var_resolver = [](void *userdata, const char *name) -> EEL_F * { + ysfx_t *fx = (ysfx_t *)userdata; + auto it = fx->source.slider_alias.find(name); + if (it != fx->source.slider_alias.end()) + return fx->var.slider[it->second]; + return nullptr; + }; + NSEEL_VM_set_var_resolver(vm, var_resolver, fx.get()); + + for (uint32_t i = 0; i < ysfx_max_channels; ++i) { + std::string name = "spl" + std::to_string(i); + EEL_F *var = NSEEL_VM_regvar(vm, name.c_str()); + *(fx->var.spl[i] = var) = 0; + } + for (uint32_t i = 0; i < ysfx_max_sliders; ++i) { + std::string name = "slider" + std::to_string(i + 1); + EEL_F *var = NSEEL_VM_regvar(vm, name.c_str()); + *(fx->var.slider[i] = var) = 0; + fx->slider_of_var[var] = i; + } + + #define AUTOVAR(name, value) *(fx->var.name = NSEEL_VM_regvar(vm, #name)) = (value) + AUTOVAR(srate, fx->sample_rate); + AUTOVAR(num_ch, fx->valid_input_channels); + AUTOVAR(samplesblock, fx->block_size); + AUTOVAR(trigger, 0); + AUTOVAR(tempo, 120); + AUTOVAR(play_state, 1); + AUTOVAR(play_position, 0); + AUTOVAR(beat_position, 0); + AUTOVAR(ts_num, 0); + AUTOVAR(ts_denom, 4); + AUTOVAR(ext_noinit, 0); + AUTOVAR(ext_nodenorm, 0); + AUTOVAR(ext_midi_bus, 0); + AUTOVAR(midi_bus, 0); + AUTOVAR(pdc_delay, 0); + AUTOVAR(pdc_bot_ch, 0); + AUTOVAR(pdc_top_ch, 0); + AUTOVAR(pdc_midi, 0); + // gfx variables + AUTOVAR(gfx_r, 0); + AUTOVAR(gfx_g, 0); + AUTOVAR(gfx_b, 0); + AUTOVAR(gfx_a, 0); + AUTOVAR(gfx_a2, 0); + AUTOVAR(gfx_w, 0); + AUTOVAR(gfx_h, 0); + AUTOVAR(gfx_x, 0); + AUTOVAR(gfx_y, 0); + AUTOVAR(gfx_mode, 0); + AUTOVAR(gfx_clear, 0); + AUTOVAR(gfx_texth, 0); + AUTOVAR(gfx_dest, 0); + AUTOVAR(gfx_ext_retina, 0); + AUTOVAR(mouse_x, 0); + AUTOVAR(mouse_y, 0); + AUTOVAR(mouse_cap, 0); + AUTOVAR(mouse_wheel, 0); + AUTOVAR(mouse_hwheel, 0); + #undef AUTOVAR + + fx->midi.in.reset(new ysfx_midi_buffer_t); + fx->midi.out.reset(new ysfx_midi_buffer_t); + ysfx_set_midi_capacity(fx.get(), 1024, true); + + fx->file.list.reserve(16); + fx->file.list.emplace_back(new ysfx_serializer_t(fx->vm.get())); + + return fx.release(); +} + +void ysfx_free(ysfx_t *fx) +{ + if (!fx) + return; + + if (fx->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) + delete fx; +} + +void ysfx_add_ref(ysfx_t *fx) +{ + fx->ref_count.fetch_add(1, std::memory_order_relaxed); +} + +ysfx_config_t *ysfx_get_config(ysfx_t *fx) +{ + return fx->config.get(); +} + +bool ysfx_load_file(ysfx_t *fx, const char *filepath, uint32_t loadopts) +{ + ysfx_unload(fx); + + //-------------------------------------------------------------------------- + // failure guard + + auto fail_guard = ysfx::defer([fx]() { ysfx_unload_source(fx); }); + + //-------------------------------------------------------------------------- + // load the main file + + ysfx::file_uid main_uid; + + { + ysfx_source_unit_u main{new ysfx_source_unit_t}; + + ysfx::FILE_u stream{ysfx::fopen_utf8(filepath, "rb")}; + if (!stream || !ysfx::get_stream_file_uid(stream.get(), main_uid)) { + ysfx_logf(*fx->config, ysfx_log_error, "%s: cannot open file for reading", ysfx::path_file_name(filepath).c_str()); + return false; + } + + ysfx::stdio_text_reader reader(stream.get()); + + ysfx_parse_error error; + if (!ysfx_parse_toplevel(reader, main->toplevel, &error)) { + ysfx_logf(*fx->config, ysfx_log_error, "%s:%u: %s", ysfx::path_file_name(filepath).c_str(), error.line + 1, error.message.c_str()); + return false; + } + ysfx_parse_header(main->toplevel.header.get(), main->header); + + // validity check + if (main->header.desc.empty()) { + ysfx_logf(*fx->config, ysfx_log_warning, "%s: the required `desc` field is missing", ysfx::path_file_name(filepath).c_str()); + main->header.desc = ysfx::path_file_name(filepath); + } + + if (loadopts & ysfx_load_ignoring_imports) + main->header.imports.clear(); + + // if no pins are specified and we have @sample, the default is stereo + if (main->toplevel.sample && !main->header.explicit_pins && + main->header.in_pins.empty() && main->header.out_pins.empty()) + { + main->header.in_pins = {"JS input 1", "JS input 2"}; + main->header.out_pins = {"JS output 1", "JS output 2"}; + } + + // register variables aliased to sliders + for (uint32_t i = 0; i < ysfx_max_sliders; ++i) { + if (main->header.sliders[i].exists) { + if (!main->header.sliders[i].var.empty()) + fx->source.slider_alias.insert({main->header.sliders[i].var, i}); + } + } + + fx->source.main = std::move(main); + fx->source.main_file_path.assign(filepath); + + // find the bank file, if present + ysfx::case_resolve( + ysfx::path_directory(filepath).c_str(), + (ysfx::path_file_name(filepath) + ".rpl").c_str(), + fx->source.bank_path); + + // fill the file enums with the contents of directories + ysfx_fill_file_enums(fx); + + // find incorrect enums and fix them + ysfx_fix_invalid_enums(fx); + + // set the initial mask of visible sliders + ysfx_update_slider_visibility_mask(fx); + } + + //-------------------------------------------------------------------------- + // load the imports + + // we load the imports recursively using post-order + + static constexpr uint32_t max_import_level = 32; + std::set seen; + + std::function do_next_import = + [fx, &seen, &do_next_import] + (const std::string &name, const std::string &origin, uint32_t level) -> bool + { + if (level >= max_import_level) { + ysfx_logf(*fx->config, ysfx_log_error, "%s: %s", ysfx::path_file_name(origin.c_str()).c_str(), "too many import levels"); + return false; + } + + std::string imported_path = ysfx_resolve_import_path(fx, name, origin); + if (imported_path.empty()) { + ysfx_logf(*fx->config, ysfx_log_error, "%s: cannot find import: %s", ysfx::path_file_name(origin.c_str()).c_str(), name.c_str()); + return false; + } + + ysfx::file_uid imported_uid; + ysfx::FILE_u stream{ysfx::fopen_utf8(imported_path.c_str(), "rb")}; + if (!stream || !ysfx::get_stream_file_uid(stream.get(), imported_uid)) { + ysfx_logf(*fx->config, ysfx_log_error, "%s: cannot open file for reading", ysfx::path_file_name(imported_path.c_str()).c_str()); + return false; + } + + // this file was already visited, skip + if (!seen.insert(imported_uid).second) + return true; + + // parse it + ysfx_source_unit_u unit{new ysfx_source_unit_t}; + ysfx::stdio_text_reader reader(stream.get()); + + ysfx_parse_error error; + if (!ysfx_parse_toplevel(reader, unit->toplevel, &error)) { + ysfx_logf(*fx->config, ysfx_log_error, "%s:%u: %s", ysfx::path_file_name(imported_path.c_str()).c_str(), error.line + 1, error.message.c_str()); + return false; + } + ysfx_parse_header(unit->toplevel.header.get(), unit->header); + + // process the imported dependencies, *first* + for (const std::string &name : unit->header.imports) { + if (!do_next_import(name, imported_path.c_str(), level + 1)) + return false; + } + + // add it to the import sources, *second* + fx->source.imports.push_back(std::move(unit)); + + return true; + }; + + for (const std::string &name : fx->source.main->header.imports) { + if (!do_next_import(name, filepath, 0)) + return false; + } + + //-------------------------------------------------------------------------- + // initialize the sliders to defaults + + for (uint32_t i = 0; i < ysfx_max_sliders; ++i) + *fx->var.slider[i] = fx->source.main->header.sliders[i].def; + + //-------------------------------------------------------------------------- + + fail_guard.disarm(); + return true; +} + +bool ysfx_compile(ysfx_t *fx, uint32_t compileopts) +{ + ysfx_unload_code(fx); + + if (!fx->source.main) { + ysfx_logf(*fx->config, ysfx_log_error, "???: no source is loaded, cannot compile"); + return false; + } + + //-------------------------------------------------------------------------- + // failure guard + + auto fail_guard = ysfx::defer([fx]() { ysfx_unload_code(fx); }); + + //-------------------------------------------------------------------------- + // configure VM + + NSEEL_VMCTX vm = fx->vm.get(); + + { + uint32_t maxmem = fx->source.main->header.options.maxmem; + if (maxmem == 0) + maxmem = 8 * 1024 * 1024; + if (maxmem > 32 * 1024 * 1024) + maxmem = 32 * 1024 * 1024; + + NSEEL_VM_setramsize(vm, (int)maxmem); + } + + //-------------------------------------------------------------------------- + // compile + + auto compile_section = + [fx](ysfx_section_t *section, const char *name, NSEEL_CODEHANDLE_u &dest) -> bool + { + NSEEL_VMCTX vm = fx->vm.get(); + if (section->text.empty()) { + // NOTE: check for empty source, which would return null code + dest.reset(); + return true; + } + NSEEL_CODEHANDLE_u code{NSEEL_code_compile_ex(vm, section->text.c_str(), section->line_offset, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS)}; + if (!code) { + ysfx_logf(*fx->config, ysfx_log_error, "%s: %s", name, NSEEL_code_getcodeerror(vm)); + return false; + } + dest = std::move(code); + return true; + }; + + // compile the multiple @init sections, imports first + { + std::vector secs; + secs.reserve(fx->source.imports.size() + 1); + + // collect init sections: imports first, main second + for (size_t i = 0; i < fx->source.imports.size(); ++i) + secs.push_back(fx->source.imports[i]->toplevel.init.get()); + secs.push_back(fx->source.main->toplevel.init.get()); + + for (ysfx_section_t *sec : secs) { + NSEEL_CODEHANDLE_u code; + if (sec && !compile_section(sec, "@init", code)) + return false; + fx->code.init.push_back(std::move(code)); + } + } + + // compile the other sections, single + // a non-@init section is searched in the main file first; + // if not found, it's inherited from the first import which has it. + ysfx_section_t *slider = ysfx_search_section(fx, ysfx_section_slider); + ysfx_section_t *block = ysfx_search_section(fx, ysfx_section_block); + ysfx_section_t *sample = ysfx_search_section(fx, ysfx_section_sample); + ysfx_section_t *gfx = nullptr; + ysfx_section_t *serialize = nullptr; + if ((compileopts & ysfx_compile_no_gfx) == 0) + gfx = ysfx_search_section(fx, ysfx_section_gfx); + if ((compileopts & ysfx_compile_no_serialize) == 0) + serialize = ysfx_search_section(fx, ysfx_section_serialize); + + if (slider && !compile_section(slider, "@slider", fx->code.slider)) + return false; + if (block && !compile_section(block, "@block", fx->code.block)) + return false; + if (sample && !compile_section(sample, "@sample", fx->code.sample)) + return false; + if (gfx && !compile_section(gfx, "@gfx", fx->code.gfx)) + return false; + if (serialize && !compile_section(serialize, "@serialize", fx->code.serialize)) + return false; + + fx->code.compiled = true; + fx->is_freshly_compiled = true; + fx->must_compute_init = true; + + /// + ysfx_eel_string_context_update_named_vars(fx->string_ctx.get(), vm); + + fail_guard.disarm(); + return true; +} + +bool ysfx_is_compiled(ysfx_t *fx) +{ + return fx->code.compiled; +} + +void ysfx_unload_source(ysfx_t *fx) +{ + fx->source = {}; +} + +void ysfx_unload_code(ysfx_t *fx) +{ +#if !defined(YSFX_NO_GFX) + // get rid of gfx first, to prevent a UI thread from trying + // to access VM and invoke code + { + std::lock_guard lock{fx->gfx.mutex}; + fx->gfx.ready = false; + fx->gfx.wants_retina = false; + fx->gfx.must_init.store(false); + } +#endif + + fx->code = {}; + + fx->is_freshly_compiled = false; + fx->must_compute_init = false; + fx->must_compute_slider = false; + + NSEEL_VMCTX vm = fx->vm.get(); + NSEEL_code_compile_ex(vm, nullptr, 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS_RESET); + NSEEL_VM_remove_unused_vars(vm); + NSEEL_VM_remove_all_nonreg_vars(vm); + NSEEL_VM_freeRAM(vm); +} + +void ysfx_unload(ysfx_t *fx) +{ + ysfx_unload_code(fx); + ysfx_unload_source(fx); +} + +bool ysfx_is_loaded(ysfx_t *fx) +{ + return fx->source.main != nullptr; +} + +void ysfx_fill_file_enums(ysfx_t *fx) +{ + if (fx->config->data_root.empty()) + return; + + for (uint32_t i = 0; i < ysfx_max_sliders; ++i) { + ysfx_slider_t &slider = fx->source.main->header.sliders[i]; + if (slider.path.empty()) + continue; + + std::string dirpath = ysfx::path_ensure_final_separator((fx->config->data_root + slider.path).c_str()); + ysfx::string_list entries = ysfx::list_directory(dirpath.c_str()); + + for (const std::string &filename : entries) { + if (!filename.empty() && ysfx::is_path_separator(filename.back())) + continue; + + std::string filepath = dirpath + filename; + + ysfx_file_type_t ftype = ysfx_detect_file_type(fx, filepath.c_str(), nullptr); + if (ftype == ysfx_file_type_none) + continue; + + slider.enum_names.push_back(std::move(filename)); + } + + if (!slider.enum_names.empty()) + slider.max = (EEL_F)(slider.enum_names.size() - 1); + } +} + +void ysfx_fix_invalid_enums(ysfx_t *fx) +{ + //NOTE: regardless of the range of enum sliders in source, it is <0,N-1,1> + // if there is a mismatch, correct and output a warning + + for (uint32_t i = 0; i < ysfx_max_sliders; ++i) { + ysfx_slider_t &slider = fx->source.main->header.sliders[i]; + if (!slider.is_enum) + continue; + + uint32_t count = (uint32_t)slider.enum_names.size(); + if (count == 0) { + bool is_file = !slider.path.empty(); + ysfx_logf(*fx->config, ysfx_log_warning, "slider%u: the enumeration does not contain any %s", i + 1, is_file ? "files" : "items"); + slider.enum_names.emplace_back(); + slider.min = 0; + slider.max = 0; + slider.inc = 1; + } + else if (slider.min != 0 || slider.inc != 1 || slider.max != (EEL_F)(count - 1)) { + ysfx_logf(*fx->config, ysfx_log_warning, "slider%u: the enumeration has an invalid range", i + 1); + slider.min = 0; + slider.max = (EEL_F)(count - 1); + slider.inc = 1; + } + } +} + +const char *ysfx_get_name(ysfx_t *fx) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (!main) + return ""; + return main->header.desc.c_str(); +} + +const char *ysfx_get_file_path(ysfx_t *fx) +{ + return fx->source.main_file_path.c_str(); +} + +const char *ysfx_get_author(ysfx_t *fx) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (!main) + return ""; + return main->header.author.c_str(); +} + +uint32_t ysfx_get_tags(ysfx_t *fx, const char **dest, uint32_t destsize) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (!main) + return 0; + + uint32_t count = (uint32_t)main->header.tags.size(); + + uint32_t copysize = (destsize < count) ? destsize : count; + for (uint32_t i = 0; i < copysize; ++i) + dest[i] = main->header.tags[i].c_str(); + + return count; +} + +const char *ysfx_get_tag(ysfx_t *fx, uint32_t index) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (!main || index >= main->header.tags.size()) + return ""; + return main->header.tags[index].c_str(); +} + +uint32_t ysfx_get_num_inputs(ysfx_t *fx) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (!main) + return 0; + return (uint32_t)main->header.in_pins.size(); +} + +uint32_t ysfx_get_num_outputs(ysfx_t *fx) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (!main) + return 0; + return (uint32_t)main->header.out_pins.size(); +} + +const char *ysfx_get_input_name(ysfx_t *fx, uint32_t index) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (!main || index >= main->header.in_pins.size()) + return ""; + return main->header.in_pins[index].c_str(); +} + +const char *ysfx_get_output_name(ysfx_t *fx, uint32_t index) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (!main || index >= main->header.out_pins.size()) + return ""; + return main->header.out_pins[index].c_str(); +} + +bool ysfx_wants_meters(ysfx_t *fx) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (!main) + return false; + + return !main->header.options.no_meter; +} + +bool ysfx_get_gfx_dim(ysfx_t *fx, uint32_t dim[2]) +{ + ysfx_toplevel_t *origin = nullptr; + ysfx_section_t *sec = ysfx_search_section(fx, ysfx_section_gfx, &origin); + + if (!sec) { + if (dim) { + dim[0] = 0; + dim[1] = 0; + } + return false; + } + + if (dim) { + dim[0] = origin->gfx_w; + dim[1] = origin->gfx_h; + } + return true; +} + +ysfx_section_t *ysfx_search_section(ysfx_t *fx, uint32_t type, ysfx_toplevel_t **origin) +{ + if (!fx->source.main) + return nullptr; + + auto search = + [fx](ysfx_section_t *(*test)(ysfx_toplevel_t &tl), ysfx_toplevel_t **origin) -> ysfx_section_t * + { + ysfx_toplevel_t *tl = &fx->source.main->toplevel; + ysfx_section_t *sec = test(*tl); + for (size_t i = 0; !sec && i < fx->source.imports.size(); ++i) { + tl = &fx->source.imports[i]->toplevel; + sec = test(*tl); + } + if (origin) + *origin = sec ? tl : nullptr; + return sec; + }; + + switch (type) { + case ysfx_section_init: + return search([](ysfx_toplevel_t &tl) { return tl.init.get(); }, origin); + case ysfx_section_slider: + return search([](ysfx_toplevel_t &tl) { return tl.slider.get(); }, origin); + case ysfx_section_block: + return search([](ysfx_toplevel_t &tl) { return tl.block.get(); }, origin); + case ysfx_section_sample: + return search([](ysfx_toplevel_t &tl) { return tl.sample.get(); }, origin); + case ysfx_section_gfx: + return search([](ysfx_toplevel_t &tl) { return tl.gfx.get(); }, origin); + case ysfx_section_serialize: + return search([](ysfx_toplevel_t &tl) { return tl.serialize.get(); }, origin); + default: + return nullptr; + } +} + +bool ysfx_has_section(ysfx_t *fx, uint32_t type) +{ + return ysfx_search_section(fx, type) != nullptr; +} + +bool ysfx_slider_exists(ysfx_t *fx, uint32_t index) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (index >= ysfx_max_sliders || !main) + return false; + + ysfx_slider_t &slider = main->header.sliders[index]; + return slider.exists; +} + +const char *ysfx_slider_get_name(ysfx_t *fx, uint32_t index) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (index >= ysfx_max_sliders || !main) + return ""; + + ysfx_slider_t &slider = main->header.sliders[index]; + return slider.desc.c_str(); +} + +bool ysfx_slider_get_range(ysfx_t *fx, uint32_t index, ysfx_slider_range_t *range) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (index >= ysfx_max_sliders || !main) + return false; + + ysfx_slider_t &slider = main->header.sliders[index]; + range->def = slider.def; + range->min = slider.min; + range->max = slider.max; + range->inc = slider.inc; + return true; +} + +bool ysfx_slider_is_enum(ysfx_t *fx, uint32_t index) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (index >= ysfx_max_sliders || !main) + return false; + + ysfx_slider_t &slider = main->header.sliders[index]; + return slider.is_enum; +} + +uint32_t ysfx_slider_get_enum_names(ysfx_t *fx, uint32_t index, const char **dest, uint32_t destsize) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (index >= ysfx_max_sliders || !main) + return 0; + + ysfx_slider_t &slider = main->header.sliders[index]; + uint32_t count = (uint32_t)slider.enum_names.size(); + + uint32_t copysize = (destsize < count) ? destsize : count; + for (uint32_t i = 0; i < copysize; ++i) + dest[i] = slider.enum_names[i].c_str(); + + return count; +} + +const char *ysfx_slider_get_enum_name(ysfx_t *fx, uint32_t slider_index, uint32_t enum_index) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (slider_index >= ysfx_max_sliders || !main) + return 0; + + ysfx_slider_t &slider = main->header.sliders[slider_index]; + if (enum_index >= slider.enum_names.size()) + return ""; + + return slider.enum_names[enum_index].c_str(); +} + +bool ysfx_slider_is_path(ysfx_t *fx, uint32_t index) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (index >= ysfx_max_sliders || !main) + return false; + + ysfx_slider_t &slider = main->header.sliders[index]; + return !slider.path.empty(); +} + +bool ysfx_slider_is_initially_visible(ysfx_t *fx, uint32_t index) +{ + ysfx_source_unit_t *main = fx->source.main.get(); + if (index >= ysfx_max_sliders || !main) + return false; + + ysfx_slider_t &slider = main->header.sliders[index]; + return slider.initially_visible; +} + +ysfx_real ysfx_slider_get_value(ysfx_t *fx, uint32_t index) +{ + if (index >= ysfx_max_sliders) + return 0; + return *fx->var.slider[index]; +} + +void ysfx_slider_set_value(ysfx_t *fx, uint32_t index, ysfx_real value) +{ + if (index >= ysfx_max_sliders) + return; + if (*fx->var.slider[index] != value) { + *fx->var.slider[index] = value; + fx->must_compute_slider = true; + } +} + +std::string ysfx_resolve_import_path(ysfx_t *fx, const std::string &name, const std::string &origin) +{ + std::vector dirs; + + // create the list of search directories + { + dirs.reserve(2); + + if (!origin.empty()) + dirs.push_back(ysfx::path_directory(origin.c_str())); + + const std::string &import_root = fx->config->import_root; + if (!import_root.empty() && dirs[0] != import_root) + dirs.push_back(import_root); + } + + // the search should be case-insensitive + static constexpr bool nocase = true; + + static auto *check_existence = +[](const std::string &dir, const std::string &file, std::string &result_path) -> int { + if (nocase) + return ysfx::case_resolve(dir.c_str(), file.c_str(), result_path); + else { + result_path = dir + file; + return ysfx::exists(result_path.c_str()); + } + }; + + // search for the file in these directories directly + for (const std::string &dir : dirs) { + std::string resolved; + if (check_existence(dir, name, resolved)) + return resolved; + } + + // search for the file recursively + for (const std::string &dir : dirs) { + struct visit_data { + const std::string *name = nullptr; + std::string resolved; + }; + visit_data vd; + vd.name = &name; + auto visit = [](const std::string &dir, void *data) -> bool { + visit_data &vd = *(visit_data *)data; + std::string resolved; + if (check_existence(dir, *vd.name, resolved)) { + vd.resolved = std::move(resolved); + return false; + } + return true; + }; + ysfx::visit_directories(dir.c_str(), +visit, &vd); + if (!vd.resolved.empty()) + return vd.resolved; + } + + return std::string{}; +} + + +uint32_t ysfx_get_block_size(ysfx_t *fx) +{ + return fx->block_size; +} + +ysfx_real ysfx_get_sample_rate(ysfx_t *fx) +{ + return fx->sample_rate; +} + +void ysfx_set_block_size(ysfx_t *fx, uint32_t blocksize) +{ + if (fx->block_size != blocksize) { + fx->block_size = blocksize; + fx->must_compute_init = true; + } +} + +void ysfx_set_sample_rate(ysfx_t *fx, ysfx_real samplerate) +{ + if (fx->sample_rate != samplerate) { + fx->sample_rate = samplerate; + fx->must_compute_init = true; + } +} + +void ysfx_set_midi_capacity(ysfx_t *fx, uint32_t capacity, bool extensible) +{ + ysfx_midi_reserve(fx->midi.in.get(), capacity, extensible); + ysfx_midi_reserve(fx->midi.out.get(), capacity, extensible); +} + +void ysfx_init(ysfx_t *fx) +{ + if (!fx->code.compiled) + return; + + if (fx->is_freshly_compiled) { + ysfx_first_init(fx); + fx->is_freshly_compiled = false; + } + + ysfx_clear_files(fx); + + for (size_t i = 0; i < fx->code.init.size(); ++i) + NSEEL_code_execute(fx->code.init[i].get()); + + fx->must_compute_init = false; + fx->must_compute_slider = true; + +#if !defined(YSFX_NO_GFX) + // do initializations on next @gfx, on the gfx thread + // release-acquire order is for VM `gfx_*` variables and `wants_retina` + fx->gfx.wants_retina = *fx->var.gfx_ext_retina > 0; + fx->gfx.must_init.store(true, std::memory_order_release); +#endif +} + +void ysfx_first_init(ysfx_t *fx) +{ + assert(fx->code.compiled); + assert(fx->is_freshly_compiled); + + *fx->var.samplesblock = (EEL_F)fx->block_size; + *fx->var.srate = fx->sample_rate; + + ysfx_clear_files(fx); + + fx->slider.automate_mask.store(0); + fx->slider.change_mask.store(0); + ysfx_update_slider_visibility_mask(fx); + + *fx->var.pdc_delay = 0; + *fx->var.pdc_bot_ch = 0; + *fx->var.pdc_top_ch = 0; + *fx->var.pdc_midi = 0; +} + +ysfx_real ysfx_get_pdc_delay(ysfx_t *fx) +{ + ysfx_real value = *fx->var.pdc_delay; + return (value > 0) ? value : 0; +} + +void ysfx_get_pdc_channels(ysfx_t *fx, uint32_t channels[2]) +{ + if (!channels) + return; + + int64_t bot = (int64_t)*fx->var.pdc_bot_ch; + bot = (bot > 0) ? bot : 0; + bot = (bot < ysfx_max_channels) ? bot : ysfx_max_channels; + channels[0] = (uint32_t)bot; + + int64_t top = (int64_t)*fx->var.pdc_top_ch; + top = (top > bot) ? top : bot; + top = (top < ysfx_max_channels) ? top : ysfx_max_channels; + channels[1] = (uint32_t)top; +} + +bool ysfx_get_pdc_midi(ysfx_t *fx) +{ + return (bool)*fx->var.pdc_midi; +} + +void ysfx_update_slider_visibility_mask(ysfx_t *fx) +{ + uint64_t visible = 0; + for (uint32_t i = 0; i < ysfx_max_sliders; ++i) { + ysfx_slider_t &slider = fx->source.main->header.sliders[i]; + visible |= (uint64_t)slider.initially_visible << i; + } + fx->slider.visible_mask.store(visible); +} + +void ysfx_set_time_info(ysfx_t *fx, const ysfx_time_info_t *info) +{ + uint32_t prev_state = (uint32_t)*fx->var.play_state; + uint32_t new_state = info->playback_state; + + // unless `ext_noinit`, we should call @init every transport restart + if (!*fx->var.ext_noinit) { + auto is_running = [](uint32_t state) { + return state == ysfx_playback_playing || + state == ysfx_playback_recording; + }; + if (!is_running(prev_state) && is_running(new_state)) + fx->must_compute_init = true; + } + + *fx->var.tempo = info->tempo; + *fx->var.play_state = (EEL_F)new_state; + *fx->var.play_position = info->time_position; + *fx->var.beat_position = info->beat_position; + *fx->var.ts_num = (EEL_F)info->time_signature[0]; + *fx->var.ts_denom = (EEL_F)info->time_signature[1]; +} + +bool ysfx_send_midi(ysfx_t *fx, const ysfx_midi_event_t *event) +{ + return ysfx_midi_push(fx->midi.in.get(), event); +} + +bool ysfx_receive_midi(ysfx_t *fx, ysfx_midi_event_t *event) +{ + return ysfx_midi_get_next(fx->midi.out.get(), event); +} + +bool ysfx_receive_midi_from_bus(ysfx_t *fx, uint32_t bus, ysfx_midi_event_t *event) +{ + return ysfx_midi_get_next_from_bus(fx->midi.out.get(), 0, event); +} + +uint32_t ysfx_current_midi_bus(ysfx_t *fx) +{ + uint32_t bus = 0; + if (*fx->var.ext_midi_bus) + bus = (int32_t)*fx->var.midi_bus; + return bus; +} + +bool ysfx_send_trigger(ysfx_t *fx, uint32_t index) +{ + if (index >= ysfx_max_triggers) + return false; + + fx->triggers |= 1u << index; + return true; +} + +uint64_t ysfx_fetch_slider_changes(ysfx_t *fx) +{ + return fx->slider.change_mask.exchange(0); +} + +uint64_t ysfx_fetch_slider_automations(ysfx_t *fx) +{ + return fx->slider.automate_mask.exchange(0); +} + +uint64_t ysfx_get_slider_visibility(ysfx_t *fx) +{ + return fx->slider.visible_mask.load(); +} + +template +void ysfx_process_generic(ysfx_t *fx, const Real *const *ins, Real *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames) +{ + ysfx_set_thread_id(ysfx_thread_id_dsp); + + // prepare MIDI input for reading, output for writing + assert(fx->midi.in->read_pos == 0); + ysfx_midi_clear(fx->midi.out.get()); + + // prepare triggers + *fx->var.trigger = (EEL_F)fx->triggers; + fx->triggers = 0; + + if (!fx->code.compiled) { + for (uint32_t ch = 0; ch < num_outs; ++ch) + memset(outs[ch], 0, num_frames * sizeof(Real)); + } + else { + // compute @init if needed + if (fx->must_compute_init) + ysfx_init(fx); + + const uint32_t orig_num_outs = num_outs; + const uint32_t num_code_ins = (uint32_t)fx->source.main->header.in_pins.size(); + const uint32_t num_code_outs = (uint32_t)fx->source.main->header.out_pins.size(); + if (num_ins > num_code_ins) + num_ins = num_code_ins; + if (num_outs > num_code_outs) + num_outs = num_code_outs; + + fx->valid_input_channels = num_ins; + + *fx->var.samplesblock = (EEL_F)num_frames; + *fx->var.num_ch = (EEL_F)num_ins; + + // compute @slider if needed + if (fx->must_compute_slider) { + NSEEL_code_execute(fx->code.slider.get()); + fx->must_compute_slider = false; + } + + // compute @block + NSEEL_code_execute(fx->code.block.get()); + + // compute @sample, once per frame + if (fx->code.sample) { + EEL_F **spl = fx->var.spl; + for (uint32_t i = 0; i < num_frames; ++i) { + for (uint32_t ch = 0; ch < num_ins; ++ch) + *spl[ch] = (EEL_F)ins[ch][i]; + for (uint32_t ch = num_ins; ch < num_code_ins; ++ch) + *spl[ch] = 0; + NSEEL_code_execute(fx->code.sample.get()); + for (uint32_t ch = 0; ch < num_outs; ++ch) + outs[ch][i] = (Real)*spl[ch]; + } + } + + // clear any output channels above the maximum count + for (uint32_t ch = num_outs; ch < orig_num_outs; ++ch) + memset(outs[ch], 0, num_frames * sizeof(Real)); + } + + // prepare MIDI input for writing, output for reading + assert(fx->midi.out->read_pos == 0); + ysfx_midi_clear(fx->midi.in.get()); + + ysfx_set_thread_id(ysfx_thread_id_none); +} + +void ysfx_process_float(ysfx_t *fx, const float *const *ins, float *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames) +{ + ysfx_process_generic(fx, ins, outs, num_ins, num_outs, num_frames); +} + +void ysfx_process_double(ysfx_t *fx, const double *const *ins, double *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames) +{ + ysfx_process_generic(fx, ins, outs, num_ins, num_outs, num_frames); +} + +void ysfx_clear_files(ysfx_t *fx) +{ + std::lock_guard list_lock(fx->file.list_mutex); + + // delete all except the serializer + while (fx->file.list.size() > 1) { + ysfx_file_t *file = fx->file.list.back().get(); + std::unique_ptr file_mutex; + std::unique_lock file_lock; + if (file) { + file_lock = std::unique_lock{*fx->file.list.back()->m_mutex}; + file_mutex = std::move(fx->file.list.back()->m_mutex); + } + fx->file.list.pop_back(); + } +} + +ysfx_file_t *ysfx_get_file(ysfx_t *fx, uint32_t handle, std::unique_lock &lock, std::unique_lock *list_lock) +{ + std::unique_lock local_list_lock; + if (list_lock) + *list_lock = std::unique_lock(fx->file.list_mutex); + else + local_list_lock = std::unique_lock(fx->file.list_mutex); + if (handle >= fx->file.list.size()) + return nullptr; + ysfx_file_t *file = fx->file.list[handle].get(); + if (!file) + return nullptr; + lock = std::unique_lock{*file->m_mutex}; + return file; +} + +int32_t ysfx_insert_file(ysfx_t *fx, ysfx_file_t *file) +{ + std::lock_guard lock(fx->file.list_mutex); + + // + size_t noneidx = ~(size_t)0; + size_t freeidx = noneidx; + + for (size_t i = 0, n = fx->file.list.size(); i < n && freeidx == noneidx; ++i) { + if (!fx->file.list[i]) + freeidx = i; + } + if (freeidx != noneidx) { + fx->file.list[freeidx].reset(file); + return (uint32_t)freeidx; + } + + enum { max_file_handles = 64 }; + + size_t pos = fx->file.list.size(); + if (pos >= max_file_handles) + return -1; + + fx->file.list.emplace_back(file); + return (uint32_t)pos; +} + +bool ysfx_load_state(ysfx_t *fx, ysfx_state_t *state) +{ + if (!fx->code.compiled) + return false; + + // restore the serialization + std::string buffer((char *)state->data, state->data_size); + + // restore the sliders + for (uint32_t i = 0; i < ysfx_max_sliders; ++i) + *fx->var.slider[i] = fx->source.main->header.sliders[i].def; + + for (uint32_t i = 0, n = state->slider_count; i < n; ++i) { + uint32_t j = state->sliders[i].index; + if (j < ysfx_max_sliders && fx->source.main->header.sliders[j].exists) + *fx->var.slider[j] = state->sliders[i].value; + } + fx->must_compute_slider = true; + + // invoke @serialize + { + std::unique_lock lock; + ysfx_serializer_t *serializer = static_cast(ysfx_get_file(fx, 0, lock)); + assert(serializer); + serializer->begin(false, buffer); + lock.unlock(); + ysfx_serialize(fx); + lock.lock(); + serializer->end(); + } + + return true; +} + +ysfx_state_t *ysfx_save_state(ysfx_t *fx) +{ + if (!fx->code.compiled) + return nullptr; + + std::string buffer; + + // invoke @serialize + { + std::unique_lock lock; + ysfx_serializer_t *serializer = static_cast(ysfx_get_file(fx, 0, lock)); + assert(serializer); + serializer->begin(true, buffer); + lock.unlock(); + ysfx_serialize(fx); + lock.lock(); + serializer->end(); + } + + // save the sliders + ysfx_state_u state{new ysfx_state_t}; + uint32_t slider_count = 0; + for (uint32_t i = 0; i < ysfx_max_sliders; ++i) + slider_count += fx->source.main->header.sliders[i].exists; + + state->sliders = new ysfx_state_slider_t[slider_count]{}; + state->slider_count = slider_count; + + for (uint32_t i = 0, j = 0; i < slider_count; ++i) { + if (fx->source.main->header.sliders[i].exists) { + state->sliders[j].index = i; + state->sliders[j].value = *fx->var.slider[i]; + ++j; + } + } + + // save the serialization + state->data_size = buffer.size(); + state->data = new uint8_t[state->data_size]; + memcpy(state->data, buffer.data(), state->data_size); + + // + return state.release(); +} + +void ysfx_state_free(ysfx_state_t *state) +{ + if (!state) + return; + + delete[] state->sliders; + delete[] state->data; + delete state; +} + +ysfx_state_t *ysfx_state_dup(ysfx_state_t *state_in) +{ + if (!state_in) + return nullptr; + + ysfx_state_u state_out{new ysfx_state_t}; + + uint32_t slider_count = state_out->slider_count = state_in->slider_count; + size_t data_size = state_out->data_size = state_in->data_size; + + state_out->sliders = new ysfx_state_slider_t[slider_count]; + memcpy(state_out->sliders, state_in->sliders, slider_count * sizeof(ysfx_state_slider_t)); + + state_out->data = new uint8_t[data_size]; + memcpy(state_out->data, state_in->data, data_size); + + return state_out.release(); +} + +void ysfx_serialize(ysfx_t *fx) +{ + if (fx->code.serialize) { + if (fx->must_compute_init) + ysfx_init(fx); + NSEEL_code_execute(fx->code.serialize.get()); + } +} + +uint32_t ysfx_get_slider_of_var(ysfx_t *fx, EEL_F *var) +{ + auto it = fx->slider_of_var.find(var); + if (it == fx->slider_of_var.end()) + return ~(uint32_t)0; + return it->second; +} + +const char *ysfx_get_bank_path(ysfx_t *fx) +{ + return fx->source.bank_path.c_str(); +} + +void ysfx_enum_vars(ysfx_t *fx, ysfx_enum_vars_callback_t *callback, void *userdata) +{ + NSEEL_VM_enumallvars(fx->vm.get(), callback, userdata); +} + +ysfx_real *ysfx_find_var(ysfx_t *fx, const char *name) +{ + struct find_data { + ysfx_real *var = nullptr; + const char *name = nullptr; + }; + find_data fd; + fd.name = name; + auto callback = [](const char *name, EEL_F *var, void *userdata) -> int { + find_data *fd = (find_data *)userdata; + if (strcmp(name, fd->name) != 0) + return 1; + fd->var = var; + return 0; + }; + NSEEL_VM_enumallvars(fx->vm.get(), +callback, &fd); + return fd.var; +} + +void ysfx_read_vmem(ysfx_t *fx, uint32_t addr, ysfx_real *dest, uint32_t count) +{ + ysfx_eel_ram_reader reader(fx->vm.get(), addr); + for (uint32_t i = 0; i < count; ++i) + dest[i] = reader.read_next(); +} + +bool ysfx_find_data_file(ysfx_t *fx, EEL_F *file, std::string &result) +{ + // 3 possibilities for file + // - slider + // - index of filename + // - string + + std::string filepart; + + bool accept_absolute = false; + bool accept_relative = false; + + int32_t index = ysfx_eel_round(*file); + uint32_t slideridx = ysfx_get_slider_of_var(fx, file); + ysfx_slider_t *slider = nullptr; + + if (slideridx != ~(uint32_t)0) + slider = &fx->source.main->header.sliders[slideridx]; + + if (slider && !slider->path.empty()) { + int32_t value = ysfx_eel_round(*fx->var.slider[slideridx]); + if (value < 0 || (uint32_t)value >= slider->enum_names.size()) + return false; + + filepart = slider->path + '/' + slider->enum_names[(uint32_t)value]; + accept_relative = true; + } + else if (index >= 0 && (uint32_t)index < fx->source.main->header.filenames.size()) { + filepart = fx->source.main->header.filenames[(uint32_t)index]; + accept_relative = true; + } + else if (ysfx_string_get(fx, *file, filepart)) { + accept_absolute = true; + accept_relative = true; + } + else + return false; + + std::vector filecandidates; + filecandidates.reserve(2); + + if (accept_absolute && !ysfx::path_is_relative(filepart.c_str())) + filecandidates.push_back(filepart); + else if (accept_relative) { + filecandidates.push_back(ysfx::path_directory(fx->source.main_file_path.c_str()) + filepart); + if (!fx->config->data_root.empty()) + filecandidates.push_back(fx->config->data_root + filepart); + } + + for (const std::string &filepath : filecandidates) { + if (ysfx::exists(filepath.c_str())) { + result.assign(filepath); + return true; + } + } + + return false; +} + +ysfx_file_type_t ysfx_detect_file_type(ysfx_t *fx, const char *path, void **fmtobj) +{ + if (ysfx::path_has_suffix(path, "txt")) + return ysfx_file_type_txt; + if (ysfx::path_has_suffix(path, "raw")) + return ysfx_file_type_raw; + for (ysfx_audio_format_t &fmt : fx->config->audio_formats) { + if (fmt.can_handle(path)) { + if (fmtobj) + *fmtobj = &fmt; + return ysfx_file_type_audio; + } + } + return ysfx_file_type_none; +} + +void ysfx_gfx_setup(ysfx_t *fx, ysfx_gfx_config_t *gc) +{ +#if !defined(YSFX_NO_GFX) + bool doinit = false; + ysfx_scoped_gfx_t scope{fx, doinit}; + + ysfx_gfx_state_set_bitmap(fx->gfx.state.get(), gc->pixels, gc->pixel_width, gc->pixel_height, gc->pixel_stride); + ysfx_real scale = fx->gfx.wants_retina ? gc->scale_factor : 1; + ysfx_gfx_state_set_scale_factor(fx->gfx.state.get(), scale); + + ysfx_gfx_state_set_callback_data(fx->gfx.state.get(), gc->user_data); + ysfx_gfx_state_set_show_menu_callback(fx->gfx.state.get(), gc->show_menu); + ysfx_gfx_state_set_set_cursor_callback(fx->gfx.state.get(), gc->set_cursor); + ysfx_gfx_state_set_get_drop_file_callback(fx->gfx.state.get(), gc->get_drop_file); +#else + (void)fx; + (void)gc; +#endif +} + +bool ysfx_gfx_wants_retina(ysfx_t *fx) +{ +#if !defined(YSFX_NO_GFX) + return fx->gfx.wants_retina; +#else + (void)fx; + return false; +#endif +} + +void ysfx_gfx_add_key(ysfx_t *fx, uint32_t mods, uint32_t key, bool press) +{ +#if !defined(YSFX_NO_GFX) + bool doinit = true; + ysfx_scoped_gfx_t scope{fx, doinit}; + + if (!fx->gfx.ready) + return; + + ysfx_gfx_state_add_key(fx->gfx.state.get(), mods, key, press); +#else + (void)fx; + (void)mods; + (void)key; +#endif +} + +void ysfx_gfx_update_mouse(ysfx_t *fx, uint32_t mods, int32_t xpos, int32_t ypos, uint32_t buttons, ysfx_real wheel, ysfx_real hwheel) +{ +#if !defined(YSFX_NO_GFX) + bool doinit = true; + ysfx_scoped_gfx_t scope{fx, doinit}; + + if (!fx->gfx.ready) + return; + + *fx->var.mouse_x = (EEL_F)xpos; + *fx->var.mouse_y = (EEL_F)ypos; + *fx->var.mouse_wheel += 120 * wheel; + *fx->var.mouse_hwheel += 120 * hwheel; + + uint32_t mouse_cap = 0; + if (mods & ysfx_mod_shift) + mouse_cap |= 8; + if (mods & ysfx_mod_ctrl) + mouse_cap |= 4; + if (mods & ysfx_mod_alt) + mouse_cap |= 16; + if (mods & ysfx_mod_super) + mouse_cap |= 32; + if (buttons & ysfx_button_left) + mouse_cap |= 1; + if (buttons & ysfx_button_middle) + mouse_cap |= 64; + if (buttons & ysfx_button_right) + mouse_cap |= 2; + *fx->var.mouse_cap = (EEL_F)mouse_cap; + +#else + (void)fx; + (void)mods; + (void)xpos; + (void)ypos; + (void)buttons; + (void)vwheel; + (void)hwheel; +#endif +} + +bool ysfx_gfx_run(ysfx_t *fx) +{ +#if !defined(YSFX_NO_GFX) + bool doinit = true; + ysfx_scoped_gfx_t scope{fx, doinit}; + + if (!fx->gfx.ready) + return false; + + ysfx_gfx_prepare(fx); + NSEEL_code_execute(fx->code.gfx.get()); + + return ysfx_gfx_state_is_dirty(fx->gfx.state.get()); +#else + (void)fx; +#endif +} diff --git a/source/modules/ysfx/sources/ysfx.hpp b/source/modules/ysfx/sources/ysfx.hpp new file mode 100644 index 000000000..94ae5fd42 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx.hpp @@ -0,0 +1,192 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" +#include "ysfx_midi.hpp" +#include "ysfx_parse.hpp" +#include "ysfx_api_eel.hpp" +#include "ysfx_api_reaper.hpp" +#include "ysfx_api_file.hpp" +#include "ysfx_api_gfx.hpp" +#include "ysfx_utils.hpp" +#include "utility/sync_bitset.hpp" +#include "WDL/eel2/ns-eel.h" +#include "WDL/eel2/ns-eel-int.h" +#include +#include + +YSFX_DEFINE_AUTO_PTR(NSEEL_VMCTX_u, void, NSEEL_VM_free); // NOTE: `NSEEL_VMCTX` is `void *` +YSFX_DEFINE_AUTO_PTR(NSEEL_CODEHANDLE_u, void, NSEEL_code_free); // NOTE: `NSEEL_CODEHANDLE` is `void *` + +struct ysfx_source_unit_t { + ysfx_toplevel_t toplevel; + ysfx_header_t header; +}; +using ysfx_source_unit_u = std::unique_ptr< ysfx_source_unit_t>; + +enum ysfx_file_type_t { + ysfx_file_type_none, + ysfx_file_type_txt, + ysfx_file_type_raw, + ysfx_file_type_audio, +}; + +enum ysfx_thread_id_t { + ysfx_thread_id_none, + ysfx_thread_id_dsp, + ysfx_thread_id_gfx, +}; + +struct ysfx_s { + ysfx_config_u config; + eel_string_context_state_u string_ctx; + ysfx::mutex string_mutex; + ysfx::mutex atomic_mutex; + NSEEL_VMCTX_u vm; + + // some default values, these are not standard, just arbitrary + uint32_t block_size = 128; + ysfx_real sample_rate = 44100; + uint32_t valid_input_channels = 2; + + bool is_freshly_compiled = false; + bool must_compute_init = false; + bool must_compute_slider = false; + + std::unordered_map slider_of_var; + + // source + struct { + std::string main_file_path; + std::string bank_path; + ysfx_source_unit_u main; + std::vector imports; + std::unordered_map slider_alias; + } source; + + // compilation + struct { + bool compiled = false; + std::vector init; + NSEEL_CODEHANDLE_u slider; + NSEEL_CODEHANDLE_u block; + NSEEL_CODEHANDLE_u sample; + NSEEL_CODEHANDLE_u gfx; + NSEEL_CODEHANDLE_u serialize; + } code; + + // VM variables + struct { + EEL_F *spl[ysfx_max_channels] = {}; + EEL_F *slider[ysfx_max_sliders] = {}; + EEL_F *srate = nullptr; + EEL_F *num_ch = nullptr; + EEL_F *samplesblock = nullptr; + EEL_F *trigger = nullptr; + EEL_F *tempo = nullptr; + EEL_F *play_state = nullptr; + EEL_F *play_position = nullptr; + EEL_F *beat_position = nullptr; + EEL_F *ts_num = nullptr; + EEL_F *ts_denom = nullptr; + EEL_F *ext_noinit = nullptr; + EEL_F *ext_nodenorm = nullptr; + EEL_F *ext_midi_bus = nullptr; + EEL_F *midi_bus = nullptr; + EEL_F *pdc_delay = nullptr; + EEL_F *pdc_bot_ch = nullptr; + EEL_F *pdc_top_ch = nullptr; + EEL_F *pdc_midi = nullptr; + // gfx variables + EEL_F *gfx_r = nullptr; + EEL_F *gfx_g = nullptr; + EEL_F *gfx_b = nullptr; + EEL_F *gfx_a = nullptr; + EEL_F *gfx_a2 = nullptr; + EEL_F *gfx_w = nullptr; + EEL_F *gfx_h = nullptr; + EEL_F *gfx_x = nullptr; + EEL_F *gfx_y = nullptr; + EEL_F *gfx_mode = nullptr; + EEL_F *gfx_clear = nullptr; + EEL_F *gfx_texth = nullptr; + EEL_F *gfx_dest = nullptr; + EEL_F *gfx_ext_retina = nullptr; + EEL_F *mouse_x = nullptr; + EEL_F *mouse_y = nullptr; + EEL_F *mouse_cap = nullptr; + EEL_F *mouse_wheel = nullptr; + EEL_F *mouse_hwheel = nullptr; + // other + EEL_F ret_temp = 0; + } var; + + // MIDI + struct { + ysfx_midi_buffer_u in; + ysfx_midi_buffer_u out; + } midi; + + // Slider + struct { + ysfx::sync_bitset64 automate_mask; + ysfx::sync_bitset64 change_mask; + ysfx::sync_bitset64 visible_mask; + } slider; + + // Triggers + uint32_t triggers = 0; + + // Files + struct { + std::vector list; + ysfx::mutex list_mutex; + } file; + +#if !defined(YSFX_NO_GFX) + // Graphics + struct { + ysfx_gfx_state_u state; + ysfx::mutex mutex; + volatile bool ready = false; + volatile bool wants_retina = false; + std::atomic must_init{false}; + } gfx; +#endif + + std::atomic ref_count{1}; +}; + +ysfx_thread_id_t ysfx_get_thread_id(); +void ysfx_set_thread_id(ysfx_thread_id_t id); +void ysfx_unload_source(ysfx_t *fx); +void ysfx_unload_code(ysfx_t *fx); +void ysfx_first_init(ysfx_t *fx); +void ysfx_update_slider_visibility_mask(ysfx_t *fx); +void ysfx_fill_file_enums(ysfx_t *fx); +void ysfx_fix_invalid_enums(ysfx_t *fx); +ysfx_section_t *ysfx_search_section(ysfx_t *fx, uint32_t type, ysfx_toplevel_t **origin = nullptr); +std::string ysfx_resolve_import_path(ysfx_t *fx, const std::string &name, const std::string &origin); +uint32_t ysfx_current_midi_bus(ysfx_t *fx); +void ysfx_clear_files(ysfx_t *fx); +ysfx_file_t *ysfx_get_file(ysfx_t *fx, uint32_t handle, std::unique_lock &lock, std::unique_lock *list_lock = nullptr); +int32_t ysfx_insert_file(ysfx_t *fx, ysfx_file_t *file); +void ysfx_serialize(ysfx_t *fx); +uint32_t ysfx_get_slider_of_var(ysfx_t *fx, EEL_F *var); +bool ysfx_find_data_file(ysfx_t *fx, EEL_F *file, std::string &result); +ysfx_file_type_t ysfx_detect_file_type(ysfx_t *fx, const char *path, void **fmtobj); diff --git a/source/modules/ysfx/sources/ysfx_api_eel.cpp b/source/modules/ysfx/sources/ysfx_api_eel.cpp new file mode 100644 index 000000000..2e59b0dbb --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_eel.cpp @@ -0,0 +1,144 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx.hpp" +#include "ysfx_api_eel.hpp" +#include "ysfx_utils.hpp" +#include +#include +#include + +#include "WDL/ptrlist.h" +#include "WDL/assocarray.h" +#include "WDL/mutex.h" + +#ifndef EELSCRIPT_NO_STDIO +# define EEL_STRING_STDOUT_WRITE(x,len) { fwrite(x,len,1,stdout); fflush(stdout); } +#endif + +//TODO: thread-safety considerations with strings + +#define EEL_STRING_MAXUSERSTRING_LENGTH_HINT ysfx_string_max_length + +static ysfx::mutex atomic_mutex; +#define EEL_ATOMIC_SET_SCOPE(opaque) ysfx::mutex *mutex = ((opaque) ? &((ysfx_t *)(opaque))->atomic_mutex : &atomic_mutex); +#define EEL_ATOMIC_ENTER mutex->lock() +#define EEL_ATOMIC_LEAVE mutex->unlock() + +#include "WDL/eel2/eel_strings.h" +#include "WDL/eel2/eel_misc.h" +#include "WDL/eel2/eel_fft.h" +#include "WDL/eel2/eel_mdct.h" +#include "WDL/eel2/eel_atomic.h" + +//------------------------------------------------------------------------------ +void ysfx_api_init_eel() +{ + EEL_string_register(); + EEL_fft_register(); + EEL_mdct_register(); + EEL_string_register(); + EEL_misc_register(); + EEL_atomic_register(); +} + +//------------------------------------------------------------------------------ +void ysfx_eel_string_initvm(NSEEL_VMCTX vm) +{ + eel_string_initvm(vm); +} + +//------------------------------------------------------------------------------ +eel_string_context_state *ysfx_eel_string_context_new() +{ + return new eel_string_context_state; +} + +void ysfx_eel_string_context_free(eel_string_context_state *state) +{ + delete state; +} + +void ysfx_eel_string_context_update_named_vars(eel_string_context_state *state, NSEEL_VMCTX vm) +{ + state->update_named_vars(vm); +} + +//------------------------------------------------------------------------------ +static_assert( + ysfx_string_max_length == EEL_STRING_MAXUSERSTRING_LENGTH_HINT, + "string max lengths do not match"); + +bool ysfx_string_access(ysfx_t *fx, ysfx_real id, bool for_write, void (*access)(void *, WDL_FastString &), void *userdata) +{ + void *opaque = fx; + eel_string_context_state *ctx = EEL_STRING_GET_CONTEXT_POINTER(opaque); + EEL_STRING_MUTEXLOCK_SCOPE + + EEL_STRING_STORAGECLASS *wr = nullptr; + ctx->GetStringForIndex(id, &wr, for_write); + if (!wr) + return false; + + access(userdata, *wr); + return true; +} + +bool ysfx_string_get(ysfx_t *fx, ysfx_real id, std::string &txt) +{ + return ysfx_string_access(fx, id, false, [](void *ud, WDL_FastString &str) { + ((std::string *)ud)->assign(str.Get(), (uint32_t)str.GetLength()); + }, &txt); +} + +bool ysfx_string_set(ysfx_t *fx, ysfx_real id, const std::string &txt) +{ + return ysfx_string_access(fx, id, true, [](void *ud, WDL_FastString &str) { + const std::string *txt = (const std::string *)ud; + size_t size = txt->size(); + if (size > ysfx_string_max_length) + size = ysfx_string_max_length; + str.SetRaw(txt->data(), (int)size); + }, (void *)&txt); +} + +void ysfx_string_lock(ysfx_t *fx) +{ + fx->string_mutex.lock(); +} + +void ysfx_string_unlock(ysfx_t *fx) +{ + fx->string_mutex.unlock(); +} + +const char *ysfx_string_access_unlocked(ysfx_t *fx, ysfx_real id, WDL_FastString **fs, bool for_write) +{ + return fx->string_ctx->GetStringForIndex(id, fs, for_write); +} + +//------------------------------------------------------------------------------ +// NOTE(jpc) implement this? I guess probably not. +// DSP and UI should not mutex each other. + +void NSEEL_HOSTSTUB_EnterMutex() +{ +} + +void NSEEL_HOSTSTUB_LeaveMutex() +{ +} diff --git a/source/modules/ysfx/sources/ysfx_api_eel.hpp b/source/modules/ysfx/sources/ysfx_api_eel.hpp new file mode 100644 index 000000000..494fbb18c --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_eel.hpp @@ -0,0 +1,57 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" +#include + +typedef void *NSEEL_VMCTX; +class WDL_FastString; + +//------------------------------------------------------------------------------ +void ysfx_api_init_eel(); + +//------------------------------------------------------------------------------ +void ysfx_eel_string_initvm(NSEEL_VMCTX vm); + +//------------------------------------------------------------------------------ +class eel_string_context_state; +eel_string_context_state *ysfx_eel_string_context_new(); +void ysfx_eel_string_context_free(eel_string_context_state *state); +void ysfx_eel_string_context_update_named_vars(eel_string_context_state *state, NSEEL_VMCTX vm); +YSFX_DEFINE_AUTO_PTR(eel_string_context_state_u, eel_string_context_state, ysfx_eel_string_context_free); + +//------------------------------------------------------------------------------ +enum { ysfx_string_max_length = 1 << 16 }; +bool ysfx_string_access(ysfx_t *fx, ysfx_real id, bool for_write, void (*access)(void *, WDL_FastString &), void *userdata); +bool ysfx_string_get(ysfx_t *fx, ysfx_real id, std::string &txt); +bool ysfx_string_set(ysfx_t *fx, ysfx_real id, const std::string &txt); +void ysfx_string_lock(ysfx_t *fx); +void ysfx_string_unlock(ysfx_t *fx); +const char *ysfx_string_access_unlocked(ysfx_t *fx, ysfx_real id, WDL_FastString **fs, bool for_write); + +struct ysfx_string_scoped_lock { + ysfx_string_scoped_lock(ysfx_t *fx) : m_fx(fx) { ysfx_string_lock(fx); } + ~ysfx_string_scoped_lock() { ysfx_string_unlock(m_fx); } +private: + ysfx_t *m_fx = nullptr; +}; + +#define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((ysfx_t *)(opaque))->string_ctx.get()) +#define EEL_STRING_GET_FOR_INDEX(x, wr) (ysfx_string_access_unlocked((ysfx_t *)(opaque), x, wr, false)) +#define EEL_STRING_GET_FOR_WRITE(x, wr) (ysfx_string_access_unlocked((ysfx_t *)(opaque), x, wr, true)) +#define EEL_STRING_MUTEXLOCK_SCOPE ysfx_string_scoped_lock lock{(ysfx_t *)(opaque)}; diff --git a/source/modules/ysfx/sources/ysfx_api_file.cpp b/source/modules/ysfx/sources/ysfx_api_file.cpp new file mode 100644 index 000000000..95a9f9a44 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_file.cpp @@ -0,0 +1,574 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx.hpp" +#include "ysfx_config.hpp" +#include "ysfx_api_file.hpp" +#include "ysfx_eel_utils.hpp" +#include +#include +#include + +ysfx_raw_file_t::ysfx_raw_file_t(NSEEL_VMCTX vm, const char *filename) + : m_vm(vm), + m_stream(ysfx::fopen_utf8(filename, "rb")) +{ +} + +int32_t ysfx_raw_file_t::avail() +{ + if (!m_stream) + return 0; + + int64_t cur_off = ysfx::ftell_lfs(m_stream.get()); + if (cur_off == -1) + return 0; + + if (ysfx::fseek_lfs(m_stream.get(), 0, SEEK_END) == -1) + return 0; + + int64_t end_off = ysfx::ftell_lfs(m_stream.get()); + if (end_off == -1) + return 0; + + if (ysfx::fseek_lfs(m_stream.get(), cur_off, SEEK_SET) == -1) + return 0; + + if ((uint64_t)end_off < (uint64_t)cur_off) + return 0; + + uint64_t byte_count = (uint64_t)end_off - (uint64_t)cur_off; + uint64_t f32_count = byte_count / 4; + return (f32_count > 0x7fffffff) ? 0x7fffffff : (uint32_t)f32_count; +} + +void ysfx_raw_file_t::rewind() +{ + if (!m_stream) + return; + + ::rewind(m_stream.get()); +} + +bool ysfx_raw_file_t::var(ysfx_real *var) +{ + if (!m_stream) + return false; + + uint8_t data[4]; + if (fread(data, 1, 4, m_stream.get()) != 4) + return false; + + *var = (EEL_F)ysfx::unpack_f32le(data); + return true; +} + +uint32_t ysfx_raw_file_t::mem(uint32_t offset, uint32_t length) +{ + if (!m_stream) + return 0; + + ysfx_eel_ram_writer writer{m_vm, offset}; + + uint32_t read; + for (read = 0; read < length; ++read) { + ysfx_real value; + if (!var(&value)) + break; + writer.write_next(value); + } + + return read; +} + +uint32_t ysfx_raw_file_t::string(std::string &str) +{ + if (!m_stream) + return 0; + + uint8_t data[4]; + if (fread(data, 1, 4, m_stream.get()) != 4) + return 0; + + str.clear(); + + uint32_t srclen = ysfx::unpack_u32le(data); + str.reserve((srclen < ysfx_string_max_length) ? srclen : ysfx_string_max_length); + + uint32_t count = 0; + for (int byte; count < srclen && (byte = fgetc(m_stream.get())) != EOF; ) { + if (str.size() < ysfx_string_max_length) + str.push_back((unsigned char)byte); + ++count; + } + return count; +} + +//------------------------------------------------------------------------------ +ysfx_text_file_t::ysfx_text_file_t(NSEEL_VMCTX vm, const char *filename) + : m_vm(vm), + m_stream(ysfx::fopen_utf8(filename, "rb")) +{ + m_buf.reserve(256); +} + +int32_t ysfx_text_file_t::avail() +{ + if (!m_stream || ferror(m_stream.get())) + return -1; + + return (feof(m_stream.get()) == 0) ? 0 : 1; +} + +void ysfx_text_file_t::rewind() +{ + if (!m_stream) + return; + + ::rewind(m_stream.get()); +} + +bool ysfx_text_file_t::var(ysfx_real *var) +{ + if (!m_stream) + return false; + + //TODO support the expression language for arithmetic + + int ch; + do { + // get the next number separated by newline or comma + // but skip invalid lines + m_buf.clear(); + while ((ch = fgetc(m_stream.get())) != EOF && ch != '\n' && ch != ',') + m_buf.push_back((unsigned char)ch); + const char *startp = m_buf.c_str(); + const char *endp = (char *)startp; + double value = ysfx::dot_strtod(startp, (char **)&endp); + if (endp != startp) { + *var = (EEL_F)value; + return true; + } + } while (ch != EOF); + + return false; +} + +uint32_t ysfx_text_file_t::mem(uint32_t offset, uint32_t length) +{ + if (!m_stream) + return 0; + + ysfx_eel_ram_writer writer{m_vm, offset}; + + uint32_t read; + for (read = 0; read < length; ++read) { + ysfx_real value; + if (!var(&value)) + break; + writer.write_next(value); + } + + return read; +} + +uint32_t ysfx_text_file_t::string(std::string &str) +{ + if (!m_stream) + return 0; + + str.clear(); + str.reserve(256); + + int ch; + do { + ch = fgetc(m_stream.get()); + if (ch != EOF && str.size() < ysfx_string_max_length) + str.push_back((unsigned char)ch); + } while (ch != EOF && ch != '\n'); + + return (uint32_t)str.size(); +} + +//------------------------------------------------------------------------------ +ysfx_audio_file_t::ysfx_audio_file_t(NSEEL_VMCTX vm, const ysfx_audio_format_t &fmt, const char *filename) + : m_vm(vm), + m_fmt(fmt), + m_reader(fmt.open(filename), fmt.close) +{ +} + +int32_t ysfx_audio_file_t::avail() +{ + if (!m_reader) + return -1; + + uint64_t avail = m_fmt.avail(m_reader.get()); + return (avail > 0x7fffffff) ? 0x7fffffff : (int32_t)avail; +} + +void ysfx_audio_file_t::rewind() +{ + if (!m_reader) + return; + + m_fmt.rewind(m_reader.get()); +} + +bool ysfx_audio_file_t::var(ysfx_real *var) +{ + if (!m_reader) + return false; + + return m_fmt.read(m_reader.get(), var, 1) == 1; +} + +uint32_t ysfx_audio_file_t::mem(uint32_t offset, uint32_t length) +{ + if (!m_reader) + return 0; + + uint32_t numread = 0; + ysfx_real *buf = m_buf.get(); + ysfx_eel_ram_writer writer(m_vm, offset); + + while (numread < length) { + uint32_t n = length - numread; + if (n > buffer_size) + n = buffer_size; + + uint32_t m = (uint32_t)m_fmt.read(m_reader.get(), buf, n); + for (uint32_t i = 0; i < m; ++i) + writer.write_next(buf[i]); + + numread += m; + if (m < n) + break; + } + + return numread; +} + +uint32_t ysfx_audio_file_t::string(std::string &str) +{ + (void)str; + return 0; +} + +bool ysfx_audio_file_t::riff(uint32_t &nch, ysfx_real &samplerate) +{ + if (!m_reader) + return false; + + ysfx_audio_file_info_t info = m_fmt.info(m_reader.get()); + nch = info.channels; + samplerate = info.sample_rate; + return true; +} + +//------------------------------------------------------------------------------ +ysfx_serializer_t::ysfx_serializer_t(NSEEL_VMCTX vm) + : m_vm(vm) +{ +} + +void ysfx_serializer_t::begin(bool write, std::string &buffer) +{ + m_write = (int)write; + m_buffer = &buffer; + m_pos = 0; +} + +void ysfx_serializer_t::end() +{ + m_write = -1; + m_buffer = nullptr; +} + +int32_t ysfx_serializer_t::avail() +{ + if (m_write) + return -1; + else + return 0; +} + +void ysfx_serializer_t::rewind() +{ +} + +bool ysfx_serializer_t::var(ysfx_real *var) +{ + if (m_write == 1) { + uint8_t buf[4]; + ysfx::pack_f32le((float)*var, buf); + m_buffer->append((char *)buf, 4); + return true; + } + else if (m_write == 0) { + if (m_pos + 4 > m_buffer->size()) { + m_pos = m_buffer->size(); + *var = 0; + return false; + } + *var = (EEL_F)ysfx::unpack_f32le((uint8_t *)&(*m_buffer)[m_pos]); + m_pos += 4; + return true; + } + return false; +} + +uint32_t ysfx_serializer_t::mem(uint32_t offset, uint32_t length) +{ + if (m_write == 1) { + ysfx_eel_ram_reader reader{m_vm, offset}; + for (uint32_t i = 0; i < length; ++i) { + ysfx_real value = reader.read_next(); + if (!var(&value)) + return i; + } + return length; + } + else if (m_write == 0) { + ysfx_eel_ram_writer writer{m_vm, offset}; + for (uint32_t i = 0; i < length; ++i) { + ysfx_real value{}; + if (!var(&value)) + return i; + writer.write_next(value); + } + return length; + } + return 0; +} + +uint32_t ysfx_serializer_t::string(std::string &str) +{ + // TODO implement me; docs claim support in Reaper 4.59+ but it seems + // non-working (as of Reaper 6.40) + return 0; +} + +//------------------------------------------------------------------------------ +static EEL_F NSEEL_CGEN_CALL ysfx_api_file_open(void *opaque, EEL_F *file_) +{ + ysfx_t *fx = (ysfx_t *)opaque; + + std::string filepath; + if (!ysfx_find_data_file(fx, file_, filepath)) + return -1; + + void *fmtobj = nullptr; + ysfx_file_type_t ftype = ysfx_detect_file_type(fx, filepath.c_str(), &fmtobj); + + ysfx_file_u file; + switch (ftype) { + case ysfx_file_type_txt: + file.reset(new ysfx_text_file_t(fx->vm.get(), filepath.c_str())); + break; + case ysfx_file_type_raw: + file.reset(new ysfx_raw_file_t(fx->vm.get(), filepath.c_str())); + break; + case ysfx_file_type_audio: + file.reset(new ysfx_audio_file_t(fx->vm.get(), *(ysfx_audio_format_t *)fmtobj, filepath.c_str())); + break; + case ysfx_file_type_none: + break; + default: + assert(false); + } + + if (file) { + int32_t handle = ysfx_insert_file(fx, file.get()); + if (handle == -1) + return -1; + (void)file.release(); + return (EEL_F)(uint32_t)handle; + } + + return -1; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_file_close(void *opaque, EEL_F *handle_) +{ + int32_t handle = ysfx_eel_round(*handle_); + if (handle <= 0) //NOTE: cannot close the serializer handle (0) + return -1; + + ysfx_t *fx = (ysfx_t *)opaque; + std::unique_ptr file_mutex; + std::unique_lock lock; + std::unique_lock list_lock; + + // hold both locks to protect file and list access during removal + if (!ysfx_get_file(fx, (uint32_t)handle, lock, &list_lock)) + return -1; + + // preserve the locked mutex of the object being removed + file_mutex = std::move(fx->file.list[(uint32_t)handle]->m_mutex); + + fx->file.list[(uint32_t)handle].reset(); + return 0; +} + +static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_rewind(void *opaque, EEL_F *handle_) +{ + int32_t handle = ysfx_eel_round(*handle_); + if (handle < 0) + return handle_; + + ysfx_t *fx = (ysfx_t *)opaque; + std::unique_lock lock; + ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); + if (!file) + return 0; + + file->rewind(); + return handle_; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_file_var(void *opaque, EEL_F *handle_, EEL_F *var) +{ + int32_t handle = ysfx_eel_round(*handle_); + if (handle < 0) + return 0; + + ysfx_t *fx = (ysfx_t *)opaque; + std::unique_lock lock; + ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); + if (!file) + return 0; + + if (!file->var(var)) + return 0; + + return 1; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_file_mem(void *opaque, EEL_F *handle_, EEL_F *offset_, EEL_F *length_) +{ + int32_t handle = ysfx_eel_round(*handle_); + int32_t offset = ysfx_eel_round(*offset_); + int32_t length = ysfx_eel_round(*length_); + if (handle < 0 || offset < 0 || length <= 0) + return 0; + + ysfx_t *fx = (ysfx_t *)opaque; + std::unique_lock lock; + ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); + if (!file) + return 0; + + return (EEL_F)file->mem((uint32_t)offset, (uint32_t)length); +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_file_avail(void *opaque, EEL_F *handle_) +{ + int32_t handle = ysfx_eel_round(*handle_); + if (handle < 0) + return 0; + + ysfx_t *fx = (ysfx_t *)opaque; + std::unique_lock lock; + ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); + if (!file) + return 0; + + return file->avail(); +} + +static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_riff(void *opaque, EEL_F *handle_, EEL_F *nch_, EEL_F *samplerate_) +{ + int32_t handle = ysfx_eel_round(*handle_); + if (handle < 0) + return 0; + + ysfx_t *fx = (ysfx_t *)opaque; + std::unique_lock lock; + ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); + if (!file) { + *nch_ = 0; + *samplerate_ = 0; + return nch_; + } + + uint32_t nch = 0; + ysfx_real samplerate = 0; + if (!file->riff(nch, samplerate)) { + *nch_ = 0; + *samplerate_ = 0; + return nch_; + } + + *nch_ = (EEL_F)nch; + *samplerate_ = samplerate; + return nch_; +} + + +static EEL_F NSEEL_CGEN_CALL ysfx_api_file_text(void *opaque, EEL_F *handle_) +{ + int32_t handle = ysfx_eel_round(*handle_); + if (handle < 0) + return 0; + + ysfx_t *fx = (ysfx_t *)opaque; + std::unique_lock lock; + ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); + if (!file) + return 0; + + return (EEL_F)file->is_text(); +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_file_string(void *opaque, EEL_F *handle_, EEL_F *string_) +{ + int32_t handle = ysfx_eel_round(*handle_); + if (handle < 0) + return 0; + + ysfx_t *fx = (ysfx_t *)opaque; + std::unique_lock lock; + ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); + if (!file) + return 0; + + std::string txt; + uint32_t count; + if (!file->is_in_write_mode()) { + count = file->string(txt); + ysfx_string_set(fx, *string_, txt); + } + else { + ysfx_string_get(fx, *string_, txt); + count = file->string(txt); + } + return (EEL_F)count; +} + +void ysfx_api_init_file() +{ + NSEEL_addfunc_retval("file_open", 1, NSEEL_PProc_THIS, &ysfx_api_file_open); + NSEEL_addfunc_retval("file_close", 1, NSEEL_PProc_THIS, &ysfx_api_file_close); + NSEEL_addfunc_retptr("file_rewind", 1, NSEEL_PProc_THIS, &ysfx_api_file_rewind); + NSEEL_addfunc_retval("file_var", 2, NSEEL_PProc_THIS, &ysfx_api_file_var); + NSEEL_addfunc_retval("file_mem", 3, NSEEL_PProc_THIS, &ysfx_api_file_mem); + NSEEL_addfunc_retval("file_avail", 1, NSEEL_PProc_THIS, &ysfx_api_file_avail); + NSEEL_addfunc_retptr("file_riff", 3, NSEEL_PProc_THIS, &ysfx_api_file_riff); + NSEEL_addfunc_retval("file_text", 1, NSEEL_PProc_THIS, &ysfx_api_file_text); + NSEEL_addfunc_retval("file_string", 2, NSEEL_PProc_THIS, &ysfx_api_file_string); +} diff --git a/source/modules/ysfx/sources/ysfx_api_file.hpp b/source/modules/ysfx/sources/ysfx_api_file.hpp new file mode 100644 index 000000000..f94e7ebb5 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_file.hpp @@ -0,0 +1,127 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" +#include "ysfx_utils.hpp" +#include "WDL/eel2/ns-eel.h" +#include "WDL/eel2/ns-eel-int.h" +#include +#include + +struct ysfx_file_t { + virtual ~ysfx_file_t() {} + + virtual int32_t avail() = 0; + virtual void rewind() = 0; + virtual bool var(ysfx_real *var) = 0; + virtual uint32_t mem(uint32_t offset, uint32_t length) = 0; + virtual uint32_t string(std::string &str) = 0; + virtual bool riff(uint32_t &nch, ysfx_real &samplerate) = 0; + virtual bool is_text() = 0; + virtual bool is_in_write_mode() = 0; + + std::unique_ptr m_mutex{new ysfx::mutex}; +}; + +using ysfx_file_u = std::unique_ptr; + +//------------------------------------------------------------------------------ + +struct ysfx_raw_file_t final : ysfx_file_t { + ysfx_raw_file_t(NSEEL_VMCTX vm, const char *filename); + + int32_t avail() override; + void rewind() override; + bool var(ysfx_real *var) override; + uint32_t mem(uint32_t offset, uint32_t length) override; + uint32_t string(std::string &str) override; + bool riff(uint32_t &, ysfx_real &) override { return false; } + bool is_text() override { return false; } + bool is_in_write_mode() override { return false; } + + NSEEL_VMCTX m_vm = nullptr; + ysfx::FILE_u m_stream; +}; + +//------------------------------------------------------------------------------ + +struct ysfx_text_file_t final : ysfx_file_t { + ysfx_text_file_t(NSEEL_VMCTX vm, const char *filename); + + int32_t avail() override; + void rewind() override; + bool var(ysfx_real *var) override; + uint32_t mem(uint32_t offset, uint32_t length) override; + uint32_t string(std::string &str) override; + bool riff(uint32_t &, ysfx_real &) override { return false; } + bool is_text() override { return true; } + bool is_in_write_mode() override { return false; } + + NSEEL_VMCTX m_vm = nullptr; + ysfx::FILE_u m_stream; + std::string m_buf; +}; + +//------------------------------------------------------------------------------ + +struct ysfx_audio_file_t final : ysfx_file_t { + ysfx_audio_file_t(NSEEL_VMCTX vm, const ysfx_audio_format_t &fmt, const char *filename); + + int32_t avail() override; + void rewind() override; + bool var(ysfx_real *var) override; + uint32_t mem(uint32_t offset, uint32_t length) override; + uint32_t string(std::string &str) override; + bool riff(uint32_t &nch, ysfx_real &samplerate) override; + bool is_text() override { return false; } + bool is_in_write_mode() override { return false; } + + NSEEL_VMCTX m_vm = nullptr; + ysfx_audio_format_t m_fmt{}; + std::unique_ptr m_reader; + enum { buffer_size = 256 }; + std::unique_ptr m_buf{new ysfx_real[buffer_size]}; +}; + +//------------------------------------------------------------------------------ + +struct ysfx_serializer_t final : ysfx_file_t { + explicit ysfx_serializer_t(NSEEL_VMCTX vm); + + void begin(bool write, std::string &buffer); + void end(); + + int32_t avail() override; + void rewind() override; + bool var(ysfx_real *var) override; + uint32_t mem(uint32_t offset, uint32_t length) override; + uint32_t string(std::string &str) override; + bool riff(uint32_t &, ysfx_real &) override { return false; } + bool is_text() override { return false; } + bool is_in_write_mode() override { return m_write == 1; } + + NSEEL_VMCTX m_vm{}; + int m_write = -1; + std::string *m_buffer = nullptr; + size_t m_pos = 0; +}; + +using ysfx_serializer_u = std::unique_ptr; + +//------------------------------------------------------------------------------ +void ysfx_api_init_file(); diff --git a/source/modules/ysfx/sources/ysfx_api_gfx.cpp b/source/modules/ysfx/sources/ysfx_api_gfx.cpp new file mode 100644 index 000000000..e9a0cfa55 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_gfx.cpp @@ -0,0 +1,470 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx.hpp" +#include "ysfx_config.hpp" +#include "ysfx_api_gfx.hpp" +#include "ysfx_eel_utils.hpp" +#if !defined(YSFX_NO_GFX) +# include "lice_stb/lice_stb_loaders.hpp" +# define WDL_NO_DEFINE_MINMAX +# include "WDL/swell/swell.h" +# include "WDL/lice/lice.h" +# include "WDL/lice/lice_text.h" +# include "WDL/wdlstring.h" +#endif +#include +#include +#include +#include +#include +#include + +#if !defined(YSFX_NO_GFX) +#define GFX_GET_CONTEXT(opaque) (((opaque)) ? ysfx_gfx_get_context((ysfx_t *)(opaque)) : nullptr) + +enum { + ysfx_gfx_max_images = 1024, + ysfx_gfx_max_fonts = 128, + ysfx_gfx_max_input = 1024, +}; + +class eel_lice_state; + +struct ysfx_gfx_state_t { + ysfx_gfx_state_t(ysfx_t *fx); + ~ysfx_gfx_state_t(); + std::unique_ptr lice; + std::queue input_queue; + std::unordered_set keys_pressed; + ysfx_real scale = 0.0; + void *callback_data = nullptr; + int (*show_menu)(void *, const char *, int32_t, int32_t) = nullptr; + void (*set_cursor)(void *, int32_t) = nullptr; + const char *(*get_drop_file)(void *user_data, int32_t index) = nullptr; +}; + +//------------------------------------------------------------------------------ +#if !defined(YSFX_NO_GFX) +static bool eel_lice_get_filename_for_string(void *opaque, EEL_F idx, WDL_FastString *fs, int iswrite) +{ + if (iswrite) + return false; // this is neither supported nor used + + ysfx_t *fx = (ysfx_t *)opaque; + + std::string filepath; + if (!ysfx_find_data_file(fx, &idx, filepath)) + return false; + + if (fs) fs->Set(filepath.data(), (uint32_t)filepath.size()); + return true; +} + +#define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) \ + eel_lice_get_filename_for_string(opaque, (idx), (fs), (p)) + +#endif + +//------------------------------------------------------------------------------ +#if !defined(YSFX_NO_GFX) +# include "ysfx_api_gfx_lice.hpp" +#else +# include "ysfx_api_gfx_dummy.hpp" +#endif + +//------------------------------------------------------------------------------ +#if !defined(YSFX_NO_GFX) + +static bool translate_special_key(uint32_t uni_key, uint32_t &jsfx_key) +{ + auto key_c = [](uint8_t a, uint8_t b, uint8_t c, uint8_t d) -> uint32_t { + return a | (b << 8) | (c << 16) | (d << 24); + }; + + switch (uni_key) { + default: return false; + case ysfx_key_delete: jsfx_key = key_c('d', 'e', 'l', 0); break; + case ysfx_key_f1: jsfx_key = key_c('f', '1', 0, 0); break; + case ysfx_key_f2: jsfx_key = key_c('f', '2', 0, 0); break; + case ysfx_key_f3: jsfx_key = key_c('f', '3', 0, 0); break; + case ysfx_key_f4: jsfx_key = key_c('f', '4', 0, 0); break; + case ysfx_key_f5: jsfx_key = key_c('f', '5', 0, 0); break; + case ysfx_key_f6: jsfx_key = key_c('f', '6', 0, 0); break; + case ysfx_key_f7: jsfx_key = key_c('f', '7', 0, 0); break; + case ysfx_key_f8: jsfx_key = key_c('f', '8', 0, 0); break; + case ysfx_key_f9: jsfx_key = key_c('f', '9', 0, 0); break; + case ysfx_key_f10: jsfx_key = key_c('f', '1', '0', 0); break; + case ysfx_key_f11: jsfx_key = key_c('f', '1', '1', 0); break; + case ysfx_key_f12: jsfx_key = key_c('f', '1', '2', 0); break; + case ysfx_key_left: jsfx_key = key_c('l', 'e', 'f', 't'); break; + case ysfx_key_up: jsfx_key = key_c('u', 'p', 0, 0); break; + case ysfx_key_right: jsfx_key = key_c('r', 'g', 'h', 't'); break; + case ysfx_key_down: jsfx_key = key_c('d', 'o', 'w', 'n'); break; + case ysfx_key_page_up: jsfx_key = key_c('p', 'g', 'u', 'p'); break; + case ysfx_key_page_down: jsfx_key = key_c('p', 'g', 'd', 'n'); break; + case ysfx_key_home: jsfx_key = key_c('h', 'o', 'm', 'e'); break; + case ysfx_key_end: jsfx_key = key_c('e', 'n', 'd', 0); break; + case ysfx_key_insert: jsfx_key = key_c('i', 'n', 's', 0); break; + } + + return true; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getchar(void *opaque, EEL_F *p) +{ + ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque); + if (!state) + return 0; + + if (*p >= 1/*2*/) { // NOTE(jpc) this is 2.0 originally, which seems wrong + if (*p == 65536) { + // TODO implement window flags + return 0; + } + + // current key down status + uint32_t key = (uint32_t)*p; + uint32_t key_id; + if (translate_special_key(key, key)) + key_id = key; + else if (key < 256) + key_id = ysfx::latin1_tolower(key); + else // support the Latin-1 character set only + return 0; + return (EEL_F)(state->keys_pressed.find(key_id) != state->keys_pressed.end()); + } + + if (!state->input_queue.empty()) { + uint32_t key = state->input_queue.front(); + state->input_queue.pop(); + return (EEL_F)key; + } + + return 0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_showmenu(void *opaque, INT_PTR nparms, EEL_F **parms) +{ + ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque); + if (!state || !state->show_menu) + return 0; + + ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx; + + std::string desc; + if (!ysfx_string_get(fx, *parms[0], desc) || desc.empty()) + return 0; + + int32_t x = (int32_t)*fx->var.gfx_x; + int32_t y = (int32_t)*fx->var.gfx_y; + return state->show_menu(state->callback_data, desc.c_str(), x, y); +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_setcursor(void *opaque, INT_PTR nparms, EEL_F **parms) +{ + ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque); + if (!state || !state->set_cursor) + return 0; + + int32_t id = (int32_t)*parms[0]; + state->set_cursor(state->callback_data, id); + return 0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getdropfile(void *opaque, INT_PTR np, EEL_F **parms) +{ + ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque); + if (!state || !state->get_drop_file) + return 0; + + const int32_t idx = (int)*parms[0]; + if (idx < 0) { + state->get_drop_file(state->callback_data, -1); + return 0; + } + + const char *file = state->get_drop_file(state->callback_data, idx); + if (!file) + return 0; + + if (np > 1) { + ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx; + ysfx_string_set(fx, *parms[1], file); + } + + return 1; +} + +#endif + +//------------------------------------------------------------------------------ +#if !defined(YSFX_NO_GFX) +ysfx_gfx_state_t::ysfx_gfx_state_t(ysfx_t *fx) + : lice{new eel_lice_state{fx->vm.get(), fx, ysfx_gfx_max_images, ysfx_gfx_max_fonts}} +{ + lice->m_framebuffer = new LICE_WrapperBitmap{nullptr, 0, 0, 0, false}; +} + +ysfx_gfx_state_t::~ysfx_gfx_state_t() +{ +} +#endif + +ysfx_gfx_state_t *ysfx_gfx_state_new(ysfx_t *fx) +{ + return new ysfx_gfx_state_t{fx}; +} + +void ysfx_gfx_state_free(ysfx_gfx_state_t *state) +{ + delete state; +} + +void ysfx_gfx_state_set_bitmap(ysfx_gfx_state_t *state, uint8_t *data, uint32_t w, uint32_t h, uint32_t stride) +{ + if (stride == 0) + stride = 4 * w; + + eel_lice_state *lice = state->lice.get(); + + assert(stride % 4 == 0); + *static_cast(lice->m_framebuffer) = LICE_WrapperBitmap{(LICE_pixel *)data, (int)w, (int)h, (int)(stride / 4), false}; +} + +void ysfx_gfx_state_set_scale_factor(ysfx_gfx_state_t *state, ysfx_real scale) +{ + state->scale = scale; +} + +void ysfx_gfx_state_set_callback_data(ysfx_gfx_state_t *state, void *callback_data) +{ + state->callback_data = callback_data; +} + +void ysfx_gfx_state_set_show_menu_callback(ysfx_gfx_state_t *state, int (*callback)(void *, const char *, int32_t, int32_t)) +{ + state->show_menu = callback; +} + +void ysfx_gfx_state_set_set_cursor_callback(ysfx_gfx_state_t *state, void (*callback)(void *, int32_t)) +{ + state->set_cursor = callback; +} + +void ysfx_gfx_state_set_get_drop_file_callback(ysfx_gfx_state_t *state, const char *(*callback)(void *, int32_t)) +{ + state->get_drop_file = callback; +} + +bool ysfx_gfx_state_is_dirty(ysfx_gfx_state_t *state) +{ + return state->lice->m_framebuffer_dirty; +} + +void ysfx_gfx_state_add_key(ysfx_gfx_state_t *state, uint32_t mods, uint32_t key, bool press) +{ + if (key < 1) + return; + + uint32_t key_id; + if (translate_special_key(key, key)) + key_id = key; + else if (key < 256) + key_id = ysfx::latin1_tolower(key); + else // support the Latin-1 character set only + return; + + uint32_t key_with_mod = key; + if (key_id >= 'a' && key_id <= 'z') { + uint32_t off = (uint32_t)(key_id - 'a'); + if (mods & (ysfx_mod_ctrl|ysfx_mod_alt)) + key_with_mod = off + 257; + else if (mods & ysfx_mod_ctrl) + key_with_mod = off + 1; + else if (mods & ysfx_mod_alt) + key_with_mod = off + 321; + } + + if (press && key_with_mod > 0) { + while (state->input_queue.size() >= ysfx_gfx_max_input) + state->input_queue.pop(); + state->input_queue.push(key_with_mod); + } + + if (press) + state->keys_pressed.insert(key_id); + else + state->keys_pressed.erase(key_id); +} + +//------------------------------------------------------------------------------ +void ysfx_gfx_enter(ysfx_t *fx, bool doinit) +{ + fx->gfx.mutex.lock(); + ysfx_gfx_state_t *state = fx->gfx.state.get(); + + if (doinit) { + if (fx->gfx.must_init.exchange(false, std::memory_order_acquire)) { + *fx->var.gfx_r = 1.0; + *fx->var.gfx_g = 1.0; + *fx->var.gfx_b = 1.0; + *fx->var.gfx_a = 1.0; + *fx->var.gfx_a2 = 1.0; + *fx->var.gfx_dest = -1.0; + *fx->var.mouse_wheel = 0.0; + *fx->var.mouse_hwheel = 0.0; + // NOTE the above are according to eel_lice.h `resetVarsToStock` + // it helps to reset a few more, especially for clearing + *fx->var.gfx_mode = 0; + *fx->var.gfx_clear = 0; + *fx->var.gfx_texth = 0; + *fx->var.mouse_cap = 0; + + // reset key state + state->input_queue = {}; + state->keys_pressed = {}; + + // reset lice + eel_lice_state *lice = state->lice.get(); + LICE_WrapperBitmap framebuffer = *static_cast(lice->m_framebuffer); + state->lice.reset(); + lice = new eel_lice_state{fx->vm.get(), fx, ysfx_gfx_max_images, ysfx_gfx_max_fonts}; + state->lice.reset(lice); + lice->m_framebuffer = new LICE_WrapperBitmap(framebuffer); + + // load images from filenames + uint32_t numfiles = (uint32_t)fx->source.main->header.filenames.size(); + for (uint32_t i = 0; i < numfiles; ++i) + lice->gfx_loadimg(fx, (int32_t)i, (EEL_F)i); + + fx->gfx.ready = true; + } + } + + ysfx_set_thread_id(ysfx_thread_id_gfx); +} + +void ysfx_gfx_leave(ysfx_t *fx) +{ + ysfx_set_thread_id(ysfx_thread_id_none); + + fx->gfx.mutex.unlock(); +} + +ysfx_gfx_state_t *ysfx_gfx_get_context(ysfx_t *fx) +{ + if (!fx) + return nullptr; + + // NOTE: make sure that this will be used from the @gfx thread only + if (ysfx_get_thread_id() != ysfx_thread_id_gfx) + return nullptr; + + return fx->gfx.state.get(); +} + +void ysfx_gfx_prepare(ysfx_t *fx) +{ + ysfx_gfx_state_t *state = ysfx_gfx_get_context(fx); + eel_lice_state *lice = state->lice.get(); + + lice->m_framebuffer_dirty = false; + + // set variables `gfx_w` and `gfx_h` + ysfx_real gfx_w = (ysfx_real)lice->m_framebuffer->getWidth(); + ysfx_real gfx_h = (ysfx_real)lice->m_framebuffer->getHeight(); + if (state->scale > 1.0) { + gfx_w *= state->scale; + gfx_h *= state->scale; + *fx->var.gfx_ext_retina = state->scale; + } + *fx->var.gfx_w = gfx_w; + *fx->var.gfx_h = gfx_h; +} +#endif + +//------------------------------------------------------------------------------ +void ysfx_api_init_gfx() +{ +#if !defined(YSFX_NO_GFX) + lice_stb_install_loaders(); +#endif + + NSEEL_addfunc_retptr("gfx_lineto", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_lineto); + NSEEL_addfunc_retptr("gfx_lineto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_lineto2); + NSEEL_addfunc_retptr("gfx_rectto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_rectto); + NSEEL_addfunc_varparm("gfx_rect", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_rect); + NSEEL_addfunc_varparm("gfx_line", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_line); + NSEEL_addfunc_varparm("gfx_gradrect", 8, NSEEL_PProc_THIS, &ysfx_api_gfx_gradrect); + NSEEL_addfunc_varparm("gfx_muladdrect", 7, NSEEL_PProc_THIS, &ysfx_api_gfx_muladdrect); + NSEEL_addfunc_varparm("gfx_deltablit", 9, NSEEL_PProc_THIS, &ysfx_api_gfx_deltablit); + NSEEL_addfunc_exparms("gfx_transformblit", 8, NSEEL_PProc_THIS, &ysfx_api_gfx_transformblit); + NSEEL_addfunc_varparm("gfx_circle", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_circle); + NSEEL_addfunc_varparm("gfx_triangle", 6, NSEEL_PProc_THIS, &ysfx_api_gfx_triangle); + NSEEL_addfunc_varparm("gfx_roundrect", 5, NSEEL_PProc_THIS, &ysfx_api_gfx_roundrect); + NSEEL_addfunc_varparm("gfx_arc", 5, NSEEL_PProc_THIS, &ysfx_api_gfx_arc); + NSEEL_addfunc_retptr("gfx_blurto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_blurto); + NSEEL_addfunc_exparms("gfx_showmenu", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_showmenu); + NSEEL_addfunc_varparm("gfx_setcursor", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_setcursor); + NSEEL_addfunc_retptr("gfx_drawnumber", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_drawnumber); + NSEEL_addfunc_retptr("gfx_drawchar", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_drawchar); + NSEEL_addfunc_varparm("gfx_drawstr", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_drawstr); + NSEEL_addfunc_retptr("gfx_measurestr", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_measurestr); + NSEEL_addfunc_retptr("gfx_measurechar", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_measurechar); + NSEEL_addfunc_varparm("gfx_printf", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_printf); + NSEEL_addfunc_retptr("gfx_setpixel", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_setpixel); + NSEEL_addfunc_retptr("gfx_getpixel", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_getpixel); + NSEEL_addfunc_retptr("gfx_getimgdim", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_getimgdim); + NSEEL_addfunc_retval("gfx_setimgdim", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_setimgdim); + NSEEL_addfunc_retval("gfx_loadimg", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_loadimg); + NSEEL_addfunc_retptr("gfx_blit", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_blit); + NSEEL_addfunc_retptr("gfx_blitext", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_blitext); + NSEEL_addfunc_varparm("gfx_blit", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_blit2); + NSEEL_addfunc_varparm("gfx_setfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_setfont); + NSEEL_addfunc_varparm("gfx_getfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getfont); + NSEEL_addfunc_varparm("gfx_set", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_set); + NSEEL_addfunc_varparm("gfx_getdropfile", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getdropfile); + NSEEL_addfunc_varparm("gfx_getsyscol", 0, NSEEL_PProc_THIS, &ysfx_api_gfx_getsyscol); + NSEEL_addfunc_retval("gfx_getchar", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getchar); +} + +//------------------------------------------------------------------------------ +// SWELL helpers + +#if !defined(YSFX_NO_GFX) + +// implement these GL functions on SWELL targets where they are missing + +#if !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX) +void SWELL_SetViewGL(HWND h, char wantGL) +{ +} + +bool SWELL_GetViewGL(HWND h) +{ + return false; +} + +bool SWELL_SetGLContextToView(HWND h) +{ + return false; +} + +#endif // !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX) + +#endif diff --git a/source/modules/ysfx/sources/ysfx_api_gfx.hpp b/source/modules/ysfx/sources/ysfx_api_gfx.hpp new file mode 100644 index 000000000..8bbc0c70a --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_gfx.hpp @@ -0,0 +1,50 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" + +#if !defined(YSFX_NO_GFX) +struct ysfx_gfx_state_t; +ysfx_gfx_state_t *ysfx_gfx_state_new(ysfx_t *fx); +void ysfx_gfx_state_free(ysfx_gfx_state_t *state); +YSFX_DEFINE_AUTO_PTR(ysfx_gfx_state_u, ysfx_gfx_state_t, ysfx_gfx_state_free); +void ysfx_gfx_state_set_bitmap(ysfx_gfx_state_t *state, uint8_t *data, uint32_t w, uint32_t h, uint32_t stride); +void ysfx_gfx_state_set_scale_factor(ysfx_gfx_state_t *state, ysfx_real scale); +void ysfx_gfx_state_set_callback_data(ysfx_gfx_state_t *state, void *callback_data); +void ysfx_gfx_state_set_show_menu_callback(ysfx_gfx_state_t *state, int (*callback)(void *, const char *, int32_t, int32_t)); +void ysfx_gfx_state_set_set_cursor_callback(ysfx_gfx_state_t *state, void (*callback)(void *, int32_t)); +void ysfx_gfx_state_set_get_drop_file_callback(ysfx_gfx_state_t *state, const char *(*callback)(void *, int32_t)); +bool ysfx_gfx_state_is_dirty(ysfx_gfx_state_t *state); +void ysfx_gfx_state_add_key(ysfx_gfx_state_t *state, uint32_t mods, uint32_t key, bool press); +void ysfx_gfx_state_update_mouse(ysfx_gfx_state_t *state, uint32_t mods, int xpos, int ypos, uint32_t buttons, int wheel, int hwheel); + +//------------------------------------------------------------------------------ +void ysfx_gfx_enter(ysfx_t *fx, bool doinit); +void ysfx_gfx_leave(ysfx_t *fx); +ysfx_gfx_state_t *ysfx_gfx_get_context(ysfx_t *fx); +void ysfx_gfx_prepare(ysfx_t *fx); + +struct ysfx_scoped_gfx_t { + ysfx_scoped_gfx_t(ysfx_t *fx, bool doinit) : m_fx(fx) { ysfx_gfx_enter(fx, doinit); } + ~ysfx_scoped_gfx_t() { ysfx_gfx_leave(m_fx); } + ysfx_t *m_fx = nullptr; +}; +#endif + +//------------------------------------------------------------------------------ +void ysfx_api_init_gfx(); diff --git a/source/modules/ysfx/sources/ysfx_api_gfx_dummy.hpp b/source/modules/ysfx/sources/ysfx_api_gfx_dummy.hpp new file mode 100644 index 000000000..10a10070f --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_gfx_dummy.hpp @@ -0,0 +1,91 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx_eel_utils.hpp" + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_varparm(void *, INT_PTR, EEL_F **) +{ + return 0; +} + +static EEL_F *NSEEL_CGEN_CALL ysfx_api_gfx_stub_retptr1(void *, EEL_F *arg) +{ + return arg; +} + +static EEL_F *NSEEL_CGEN_CALL ysfx_api_gfx_stub_retptr2(void *, EEL_F *arg, EEL_F *) +{ + return arg; +} + +static EEL_F *NSEEL_CGEN_CALL ysfx_api_gfx_stub_retptr3(void *, EEL_F *arg, EEL_F *, EEL_F *) +{ + return arg; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_retval1(void *, EEL_F *) +{ + return 0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_retval2(void *, EEL_F *, EEL_F *) +{ + return 0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_retval3(void *, EEL_F *, EEL_F *, EEL_F *) +{ + return 0; +} + +#define ysfx_api_gfx_lineto ysfx_api_gfx_stub_retptr3 +#define ysfx_api_gfx_lineto2 ysfx_api_gfx_stub_retptr2 +#define ysfx_api_gfx_rectto ysfx_api_gfx_stub_retptr2 +#define ysfx_api_gfx_rect ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_line ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_gradrect ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_muladdrect ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_deltablit ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_transformblit ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_circle ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_triangle ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_roundrect ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_arc ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_blurto ysfx_api_gfx_stub_retptr2 +#define ysfx_api_gfx_showmenu ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_setcursor ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_drawnumber ysfx_api_gfx_stub_retptr2 +#define ysfx_api_gfx_drawchar ysfx_api_gfx_stub_retptr1 +#define ysfx_api_gfx_drawstr ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_measurestr ysfx_api_gfx_stub_retptr3 +#define ysfx_api_gfx_measurechar ysfx_api_gfx_stub_retptr3 +#define ysfx_api_gfx_printf ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_setpixel ysfx_api_gfx_stub_retptr3 +#define ysfx_api_gfx_getpixel ysfx_api_gfx_stub_retptr3 +#define ysfx_api_gfx_getimgdim ysfx_api_gfx_stub_retptr3 +#define ysfx_api_gfx_setimgdim ysfx_api_gfx_stub_retval3 +#define ysfx_api_gfx_loadimg ysfx_api_gfx_stub_retval2 +#define ysfx_api_gfx_blit ysfx_api_gfx_stub_retptr3 +#define ysfx_api_gfx_blitext ysfx_api_gfx_stub_retptr3 +#define ysfx_api_gfx_blit2 ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_setfont ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_getfont ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_set ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_getdropfile ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_getsyscol ysfx_api_gfx_stub_varparm +#define ysfx_api_gfx_getchar ysfx_api_gfx_stub_retval1 diff --git a/source/modules/ysfx/sources/ysfx_api_gfx_lice.hpp b/source/modules/ysfx/sources/ysfx_api_gfx_lice.hpp new file mode 100644 index 000000000..e4151ca53 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_gfx_lice.hpp @@ -0,0 +1,1490 @@ +// +// This file is based in part on modified source code from `WDL/eel2/eel_lice.h`. +// The zlib license from the WDL applies to this source file. +// +//------------------------------------------------------------------------------ +// +// Copyright (C) 2021 and later Jean Pierre Cimalando +// Copyright (C) 2005 and later Cockos Incorporated +// +// +// Portions copyright other contributors, see each source file for more information +// +// 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. +// +// SPDX-License-Identifier: Zlib +// + +#pragma once +#include "ysfx_api_eel.hpp" +#include "WDL/wdlstring.h" +#include "WDL/wdlcstring.h" +#include "WDL/wdlutf8.h" +#include +#include +#include +#include + +// help clangd to figure things out +#if defined(__CLANGD__) +# include "ysfx_api_gfx.cpp" +#endif + +#define EEL_LICE_GET_CONTEXT(opaque) ((opaque) ? ((ysfx_t *)(opaque))->gfx.state->lice.get() : nullptr) + +//------------------------------------------------------------------------------ + +#define LICE_FUNCTION_VALID(x) (sizeof(int) > 0) + +static HDC LICE__GetDC(LICE_IBitmap *bm) +{ + return bm->getDC(); +} +static int LICE__GetWidth(LICE_IBitmap *bm) +{ + return bm->getWidth(); +} +static int LICE__GetHeight(LICE_IBitmap *bm) +{ + return bm->getHeight(); +} +static void LICE__Destroy(LICE_IBitmap *bm) +{ + delete bm; +} +static void LICE__SetFromHFont(LICE_IFont * ifont, HFONT font, int flags) +{ + if (ifont) ifont->SetFromHFont(font,flags); +} +static LICE_pixel LICE__SetTextColor(LICE_IFont* ifont, LICE_pixel color) +{ + if (ifont) return ifont->SetTextColor(color); + return 0; +} +static void LICE__SetTextCombineMode(LICE_IFont* ifont, int mode, float alpha) +{ + if (ifont) ifont->SetCombineMode(mode, alpha); +} +static int LICE__DrawText(LICE_IFont* ifont, LICE_IBitmap *bm, const char *str, int strcnt, RECT *rect, UINT dtFlags) +{ + if (ifont) return ifont->DrawText(bm, str, strcnt, rect, dtFlags); + return 0; +} + + +static LICE_IFont *LICE_CreateFont() +{ + return new LICE_CachedFont(); +} +static void LICE__DestroyFont(LICE_IFont *bm) +{ + delete bm; +} +static bool LICE__resize(LICE_IBitmap *bm, int w, int h) +{ + return bm->resize(w,h); +} + +static LICE_IBitmap *__LICE_CreateBitmap(int mode, int w, int h) +{ + if (mode==1) return new LICE_SysBitmap(w,h); + return new LICE_MemBitmap(w,h); +} + +class eel_lice_state +{ +public: + + eel_lice_state(NSEEL_VMCTX vm, void *ctx, int image_slots, int font_slots); + ~eel_lice_state(); + + LICE_IBitmap *m_framebuffer, *m_framebuffer_extra; + int m_framebuffer_dirty; + WDL_TypedBuf m_gfx_images; + struct gfxFontStruct { + LICE_IFont *font; + char last_fontname[128]; + char actual_fontname[128]; + int last_fontsize; + int last_fontflag; + + int use_fonth; + }; + WDL_TypedBuf m_gfx_fonts; + enum { + EELFONT_FLAG_BOLD = (1<<24), + EELFONT_FLAG_ITALIC = (2<<24), + EELFONT_FLAG_UNDERLINE = (4<<24), + EELFONT_FLAG_MASK = EELFONT_FLAG_BOLD|EELFONT_FLAG_ITALIC|EELFONT_FLAG_UNDERLINE + }; + + int m_gfx_font_active; // -1 for default, otherwise index into gfx_fonts (NOTE: this differs from the exposed API, which defines 0 as default, 1-n) + LICE_IFont *GetActiveFont() { return m_gfx_font_active>=0&&m_gfx_font_active-2.0) + { + if (idx < 0.0) return m_framebuffer; + + const int a = (int)idx; + if (a >= 0 && a < m_gfx_images.GetSize()) return m_gfx_images.Get()[a]; + } + return NULL; + }; + + void SetImageDirty(LICE_IBitmap *bm) + { + if (bm == m_framebuffer && !m_framebuffer_dirty) + { + if (m_gfx_clear && *m_gfx_clear > -1.0) + { + const int a=(int)*m_gfx_clear; + if (LICE_FUNCTION_VALID(LICE_Clear)) LICE_Clear(m_framebuffer,LICE_RGBA((a&0xff),((a>>8)&0xff),((a>>16)&0xff),0)); + } + m_framebuffer_dirty=1; + } + } + + // R, G, B, A, w, h, x, y, mode(1=add,0=copy) + EEL_F *m_gfx_r, *m_gfx_g, *m_gfx_b, *m_gfx_w, *m_gfx_h, *m_gfx_a, *m_gfx_x, *m_gfx_y, *m_gfx_mode, *m_gfx_clear, *m_gfx_texth,*m_gfx_dest, *m_gfx_a2; + EEL_F *m_mouse_x, *m_mouse_y, *m_mouse_cap, *m_mouse_wheel, *m_mouse_hwheel; + EEL_F *m_gfx_ext_retina; + + NSEEL_VMCTX m_vmref; + void *m_user_ctx; + + void gfx_lineto(EEL_F xpos, EEL_F ypos, EEL_F aaflag); + void gfx_rectto(EEL_F xpos, EEL_F ypos); + void gfx_line(int np, EEL_F **parms); + void gfx_rect(int np, EEL_F **parms); + void gfx_roundrect(int np, EEL_F **parms); + void gfx_arc(int np, EEL_F **parms); + void gfx_set(int np, EEL_F **parms); + void gfx_grad_or_muladd_rect(int mode, int np, EEL_F **parms); + void gfx_setpixel(EEL_F r, EEL_F g, EEL_F b); + void gfx_getpixel(EEL_F *r, EEL_F *g, EEL_F *b); + void gfx_drawnumber(EEL_F n, EEL_F ndigits); + void gfx_drawchar(EEL_F ch); + void gfx_getimgdim(EEL_F img, EEL_F *w, EEL_F *h); + EEL_F gfx_setimgdim(int img, EEL_F *w, EEL_F *h); + void gfx_blurto(EEL_F x, EEL_F y); + void gfx_blit(EEL_F img, EEL_F scale, EEL_F rotate); + void gfx_blitext(EEL_F img, EEL_F *coords, EEL_F angle); + void gfx_blitext2(int np, EEL_F **parms, int mode); // 0=blit, 1=deltablit + void gfx_transformblit(EEL_F **parms, int div_w, int div_h, EEL_F *tab); // parms[0]=src, 1-4=x,y,w,h + void gfx_circle(float x, float y, float r, bool fill, bool aaflag); + void gfx_triangle(EEL_F** parms, int nparms); + void gfx_drawstr(void *opaque, EEL_F **parms, int nparms, int formatmode); // formatmode=1 for format, 2 for purely measure no format, 3 for measure char + EEL_F gfx_loadimg(void *opaque, int img, EEL_F loadFrom); + EEL_F gfx_setfont(void *opaque, int np, EEL_F **parms); + EEL_F gfx_getfont(void *opaque, int np, EEL_F **parms); + + LICE_pixel getCurColor(); + int getCurMode(); + int getCurModeForBlit(bool isFBsrc); + + // these have to be **parms because of the hack for getting string from parm index + EEL_F gfx_setcursor(void* opaque, EEL_F** parms, int nparms); +}; + +eel_lice_state::eel_lice_state(NSEEL_VMCTX vm, void *ctx, int image_slots, int font_slots) +{ + m_user_ctx=ctx; + m_vmref= vm; + m_gfx_font_active=-1; + m_gfx_fonts.Resize(font_slots); + memset(m_gfx_fonts.Get(),0,m_gfx_fonts.GetSize()*sizeof(m_gfx_fonts.Get()[0])); + + m_gfx_images.Resize(image_slots); + memset(m_gfx_images.Get(),0,m_gfx_images.GetSize()*sizeof(m_gfx_images.Get()[0])); + m_framebuffer=m_framebuffer_extra=0; + m_framebuffer_dirty=0; + + m_gfx_r = NSEEL_VM_regvar(vm,"gfx_r"); + m_gfx_g = NSEEL_VM_regvar(vm,"gfx_g"); + m_gfx_b = NSEEL_VM_regvar(vm,"gfx_b"); + m_gfx_a = NSEEL_VM_regvar(vm,"gfx_a"); + m_gfx_a2 = NSEEL_VM_regvar(vm,"gfx_a2"); + + m_gfx_w = NSEEL_VM_regvar(vm,"gfx_w"); + m_gfx_h = NSEEL_VM_regvar(vm,"gfx_h"); + m_gfx_x = NSEEL_VM_regvar(vm,"gfx_x"); + m_gfx_y = NSEEL_VM_regvar(vm,"gfx_y"); + m_gfx_mode = NSEEL_VM_regvar(vm,"gfx_mode"); + m_gfx_clear = NSEEL_VM_regvar(vm,"gfx_clear"); + m_gfx_texth = NSEEL_VM_regvar(vm,"gfx_texth"); + m_gfx_dest = NSEEL_VM_regvar(vm,"gfx_dest"); + m_gfx_ext_retina = NSEEL_VM_regvar(vm,"gfx_ext_retina"); + + m_mouse_x = NSEEL_VM_regvar(vm,"mouse_x"); + m_mouse_y = NSEEL_VM_regvar(vm,"mouse_y"); + m_mouse_cap = NSEEL_VM_regvar(vm,"mouse_cap"); + m_mouse_wheel=NSEEL_VM_regvar(vm,"mouse_wheel"); + m_mouse_hwheel=NSEEL_VM_regvar(vm,"mouse_hwheel"); + + if (m_gfx_texth) *m_gfx_texth=8; +} +eel_lice_state::~eel_lice_state() +{ + if (LICE_FUNCTION_VALID(LICE__Destroy)) + { + LICE__Destroy(m_framebuffer_extra); + LICE__Destroy(m_framebuffer); + int x; + for (x=0;x>4)&0xf; + if (sm > LICE_BLIT_MODE_COPY && sm <= LICE_BLIT_MODE_HSVADJ) return sm; + + return (gmode&1) ? LICE_BLIT_MODE_ADD : LICE_BLIT_MODE_COPY; +} +int eel_lice_state::getCurModeForBlit(bool isFBsrc) +{ + const int gmode = (int) (*m_gfx_mode); + + const int sm=(gmode>>4)&0xf; + + int mode; + if (sm > LICE_BLIT_MODE_COPY && sm <= LICE_BLIT_MODE_HSVADJ) mode=sm; + else mode=((gmode&1) ? LICE_BLIT_MODE_ADD : LICE_BLIT_MODE_COPY); + + + if (!isFBsrc && !(gmode&2)) mode|=LICE_BLIT_USE_ALPHA; + if (!(gmode&4)) mode|=LICE_BLIT_FILTER_BILINEAR; + + return mode; +} +LICE_pixel eel_lice_state::getCurColor() +{ + int red=(int) (*m_gfx_r*255.0); + int green=(int) (*m_gfx_g*255.0); + int blue=(int) (*m_gfx_b*255.0); + int a2=(int) (*m_gfx_a2*255.0); + if (red<0) red=0;else if (red>255)red=255; + if (green<0) green=0;else if (green>255)green=255; + if (blue<0) blue=0; else if (blue>255) blue=255; + if (a2<0) a2=0; else if (a2>255) a2=255; + return LICE_RGBA(red,green,blue,a2); +} + + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_lineto(void *opaque, EEL_F *xpos, EEL_F *ypos, EEL_F *useaa) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_lineto(*xpos, *ypos, *useaa); + return xpos; +} +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_lineto2(void *opaque, EEL_F *xpos, EEL_F *ypos) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_lineto(*xpos, *ypos, 1.0f); + return xpos; +} + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_rectto(void *opaque, EEL_F *xpos, EEL_F *ypos) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_rectto(*xpos, *ypos); + return xpos; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_line(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_line((int)np,parms); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_rect(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_rect((int)np,parms); + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_roundrect(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_roundrect((int)np,parms); + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_arc(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_arc((int)np,parms); + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_set(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_set((int)np,parms); + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_gradrect(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_grad_or_muladd_rect(0,(int)np,parms); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_muladdrect(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_grad_or_muladd_rect(1,(int)np,parms); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_deltablit(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_blitext2((int)np,parms,1); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_transformblit(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { +#ifndef EEL_LICE_NO_RAM + const int divw = (int) (parms[5][0]+0.5); + const int divh = (int) (parms[6][0]+0.5); + if (divw < 1 || divh < 1) return 0.0; + const int sz = divw*divh*2; + +#ifdef EEL_LICE_RAMFUNC + EEL_F *d = EEL_LICE_RAMFUNC(opaque,7,sz); + if (!d) return 0.0; +#else + EEL_F **blocks = ctx->m_vmref ? ((compileContext*)ctx->m_vmref)->ram_state->blocks : 0; + if (!blocks || np < 8) return 0.0; + + const int addr1= (int) (parms[7][0]+0.5); + EEL_F *d=__NSEEL_RAMAlloc(blocks,addr1); + if (sz>NSEEL_RAM_ITEMSPERBLOCK) + { + int x; + for(x=NSEEL_RAM_ITEMSPERBLOCK;xgfx_transformblit(parms,divw,divh,d); +#endif + } + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_circle(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + bool aa = true, fill = false; + if (np>3) fill = parms[3][0] > 0.5; + if (np>4) aa = parms[4][0] > 0.5; + if (ctx) ctx->gfx_circle((float)parms[0][0], (float)parms[1][0], (float)parms[2][0], fill, aa); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_triangle(void* opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_triangle(parms, (int)np); + return 0.0; +} + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_drawnumber(void *opaque, EEL_F *n, EEL_F *nd) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_drawnumber(*n, *nd); + return n; +} + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_drawchar(void *opaque, EEL_F *n) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_drawchar(*n); + return n; +} + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_measurestr(void *opaque, EEL_F *str, EEL_F *xOut, EEL_F *yOut) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + EEL_F *p[3]={str,xOut,yOut}; + ctx->gfx_drawstr(opaque,p,3,2); + } + return str; +} +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_measurechar(void *opaque, EEL_F *str, EEL_F *xOut, EEL_F *yOut) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + EEL_F *p[3]={str,xOut,yOut}; + ctx->gfx_drawstr(opaque,p,3,3); + } + return str; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_drawstr(void *opaque, INT_PTR nparms, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_drawstr(opaque,parms,(int)nparms,0); + return parms[0][0]; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_printf(void *opaque, INT_PTR nparms, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx && nparms>0) + { + EEL_F v= **parms; + ctx->gfx_drawstr(opaque,parms,(int)nparms,1); + return v; + } + return 0.0; +} + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_setpixel(void *opaque, EEL_F *r, EEL_F *g, EEL_F *b) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_setpixel(*r, *g, *b); + return r; +} + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_getpixel(void *opaque, EEL_F *r, EEL_F *g, EEL_F *b) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_getpixel(r, g, b); + return r; +} + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_blit(void *opaque, EEL_F *img, EEL_F *scale, EEL_F *rotate) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_blit(*img,*scale,*rotate); + return img; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_setfont(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) return ctx->gfx_setfont(opaque,(int)np,parms); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getfont(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + const int idx=ctx->m_gfx_font_active; + if (idx>=0 && idx < ctx->m_gfx_fonts.GetSize()) + { + eel_lice_state::gfxFontStruct* f=ctx->m_gfx_fonts.Get()+idx; + + EEL_STRING_MUTEXLOCK_SCOPE + +#ifdef NOT_EEL_STRING_UPDATE_STRING + NOT_EEL_STRING_UPDATE_STRING(parms[0][0],f->actual_fontname); +#else + WDL_FastString *fs=NULL; + EEL_STRING_GET_FOR_WRITE(parms[0][0],&fs); + if (fs) fs->Set(f->actual_fontname); +#endif + } + return idx; + } + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_blit2(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx && np>=3) + { + ctx->gfx_blitext2((int)np,parms,0); + return *(parms[0]); + } + return 0.0; +} + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_blitext(void *opaque, EEL_F *img, EEL_F *coordidx, EEL_F *rotate) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { +#ifndef EEL_LICE_NO_RAM +#ifdef EEL_LICE_RAMFUNC + EEL_F *buf = EEL_LICE_RAMFUNC(opaque,1,10); + if (!buf) return img; +#else + EEL_F fc = *coordidx; + if (fc < -0.5 || fc >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) return img; + int a=(int)fc; + if (a<0) return img; + + EEL_F buf[10]; + int x; + EEL_F **blocks = ctx->m_vmref ? ((compileContext*)ctx->m_vmref)->ram_state->blocks : 0; + if (!blocks) return img; + for (x = 0;x < 10; x ++) + { + EEL_F *d=__NSEEL_RAMAlloc(blocks,a++); + if (!d || d==&nseel_ramalloc_onfail) return img; + buf[x]=*d; + } +#endif + // read megabuf + ctx->gfx_blitext(*img,buf,*rotate); +#endif + } + return img; +} + + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_blurto(void *opaque, EEL_F *x, EEL_F *y) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_blurto(*x,*y); + return x; +} + +static EEL_F * NSEEL_CGEN_CALL ysfx_api_gfx_getimgdim(void *opaque, EEL_F *img, EEL_F *w, EEL_F *h) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_getimgdim(*img,w,h); + return img; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_loadimg(void *opaque, EEL_F *img, EEL_F *fr) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) return ctx->gfx_loadimg(opaque,(int)*img,*fr); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_setimgdim(void *opaque, EEL_F *img, EEL_F *w, EEL_F *h) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) return ctx->gfx_setimgdim((int)*img,w,h); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getsyscol(void* ctxe, INT_PTR np, EEL_F **parms) +{ + return (EEL_F)LICE_RGBA_FROMNATIVE(GetSysColor(COLOR_3DFACE)); +} + +void eel_lice_state::gfx_lineto(EEL_F xpos, EEL_F ypos, EEL_F aaflag) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_lineto"); + if (!dest) return; + + int x1=(int)floor(xpos),y1=(int)floor(ypos),x2=(int)floor(*m_gfx_x), y2=(int)floor(*m_gfx_y); + if (LICE_FUNCTION_VALID(LICE__GetWidth) && LICE_FUNCTION_VALID(LICE__GetHeight) && LICE_FUNCTION_VALID(LICE_Line) && + LICE_FUNCTION_VALID(LICE_ClipLine) && + LICE_ClipLine(&x1,&y1,&x2,&y2,0,0,LICE__GetWidth(dest),LICE__GetHeight(dest))) + { + SetImageDirty(dest); + LICE_Line(dest,x1,y1,x2,y2,getCurColor(),(float) *m_gfx_a,getCurMode(),aaflag > 0.5); + } + *m_gfx_x = xpos; + *m_gfx_y = ypos; + +} + +void eel_lice_state::gfx_circle(float x, float y, float r, bool fill, bool aaflag) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_circle"); + if (!dest) return; + + if (LICE_FUNCTION_VALID(LICE_Circle) && LICE_FUNCTION_VALID(LICE_FillCircle)) + { + SetImageDirty(dest); + if(fill) + LICE_FillCircle(dest, x, y, r, getCurColor(), (float) *m_gfx_a, getCurMode(), aaflag); + else + LICE_Circle(dest, x, y, r, getCurColor(), (float) *m_gfx_a, getCurMode(), aaflag); + } +} + +void eel_lice_state::gfx_triangle(EEL_F** parms, int np) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest, "gfx_triangle"); + if (np >= 6) + { + np &= ~1; + SetImageDirty(dest); + if (np == 6) + { + if (!LICE_FUNCTION_VALID(LICE_FillTriangle)) return; + + LICE_FillTriangle(dest, (int)parms[0][0], (int)parms[1][0], (int)parms[2][0], (int)parms[3][0], + (int)parms[4][0], (int)parms[5][0], getCurColor(), (float)*m_gfx_a, getCurMode()); + } + else + { + if (!LICE_FUNCTION_VALID(LICE_FillConvexPolygon)) return; + + const int maxpt = 512; + const int n = wdl_min(np/2, maxpt); + int i, rdi=0; + int x[maxpt], y[maxpt]; + for (i=0; i < n; i++) + { + x[i]=(int)parms[rdi++][0]; + y[i]=(int)parms[rdi++][0]; + } + + LICE_FillConvexPolygon(dest, x, y, n, getCurColor(), (float)*m_gfx_a, getCurMode()); + } + } +} + +void eel_lice_state::gfx_rectto(EEL_F xpos, EEL_F ypos) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_rectto"); + if (!dest) return; + + EEL_F x1=xpos,y1=ypos,x2=*m_gfx_x, y2=*m_gfx_y; + if (x2 0.5 && y2-y1 > 0.5) + { + SetImageDirty(dest); + LICE_FillRect(dest,(int)x1,(int)y1,(int)(x2-x1),(int)(y2-y1),getCurColor(),(float)*m_gfx_a,getCurMode()); + } + *m_gfx_x = xpos; + *m_gfx_y = ypos; +} + + +void eel_lice_state::gfx_line(int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_line"); + if (!dest) return; + + int x1=(int)floor(parms[0][0]),y1=(int)floor(parms[1][0]),x2=(int)floor(parms[2][0]), y2=(int)floor(parms[3][0]); + if (LICE_FUNCTION_VALID(LICE__GetWidth) && + LICE_FUNCTION_VALID(LICE__GetHeight) && + LICE_FUNCTION_VALID(LICE_Line) && + LICE_FUNCTION_VALID(LICE_ClipLine) && LICE_ClipLine(&x1,&y1,&x2,&y2,0,0,LICE__GetWidth(dest),LICE__GetHeight(dest))) + { + SetImageDirty(dest); + LICE_Line(dest,x1,y1,x2,y2,getCurColor(),(float)*m_gfx_a,getCurMode(),np< 5 || parms[4][0] > 0.5); + } +} + +void eel_lice_state::gfx_rect(int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_rect"); + if (!dest) return; + + int x1=(int)floor(parms[0][0]),y1=(int)floor(parms[1][0]),w=(int)floor(parms[2][0]),h=(int)floor(parms[3][0]); + int filled=(np < 5 || parms[4][0] > 0.5); + + if (LICE_FUNCTION_VALID(LICE_FillRect) && LICE_FUNCTION_VALID(LICE_DrawRect) && w>0 && h>0) + { + SetImageDirty(dest); + if (filled) LICE_FillRect(dest,x1,y1,w,h,getCurColor(),(float)*m_gfx_a,getCurMode()); + else LICE_DrawRect(dest, x1, y1, w-1, h-1, getCurColor(), (float)*m_gfx_a, getCurMode()); + } +} + +void eel_lice_state::gfx_roundrect(int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_roundrect"); + if (!dest) return; + + const bool aa = np <= 5 || parms[5][0]>0.5; + + if (LICE_FUNCTION_VALID(LICE_RoundRect) && parms[2][0]>0 && parms[3][0]>0) + { + SetImageDirty(dest); + LICE_RoundRect(dest, (float)parms[0][0], (float)parms[1][0], (float)parms[2][0], (float)parms[3][0], (int)parms[4][0], getCurColor(), (float)*m_gfx_a, getCurMode(), aa); + } +} + +void eel_lice_state::gfx_arc(int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_arc"); + if (!dest) return; + + const bool aa = np <= 5 || parms[5][0]>0.5; + + if (LICE_FUNCTION_VALID(LICE_Arc)) + { + SetImageDirty(dest); + LICE_Arc(dest, (float)parms[0][0], (float)parms[1][0], (float)parms[2][0], (float)parms[3][0], (float)parms[4][0], getCurColor(), (float)*m_gfx_a, getCurMode(), aa); + } +} + +void eel_lice_state::gfx_grad_or_muladd_rect(int whichmode, int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,whichmode==0?"gfx_gradrect":"gfx_muladdrect"); + if (!dest) return; + + const int x1=(int)floor(parms[0][0]),y1=(int)floor(parms[1][0]),w=(int)floor(parms[2][0]), h=(int)floor(parms[3][0]); + + if (w>0 && h>0) + { + SetImageDirty(dest); + if (whichmode==0 && LICE_FUNCTION_VALID(LICE_GradRect) && np > 7) + { + LICE_GradRect(dest,x1,y1,w,h,(float)parms[4][0],(float)parms[5][0],(float)parms[6][0],(float)parms[7][0], + np > 8 ? (float)parms[8][0]:0.0f, np > 9 ? (float)parms[9][0]:0.0f, np > 10 ? (float)parms[10][0]:0.0f, np > 11 ? (float)parms[11][0]:0.0f, + np > 12 ? (float)parms[12][0]:0.0f, np > 13 ? (float)parms[13][0]:0.0f, np > 14 ? (float)parms[14][0]:0.0f, np > 15 ? (float)parms[15][0]:0.0f, + getCurMode()); + } + else if (whichmode==1 && LICE_FUNCTION_VALID(LICE_MultiplyAddRect) && np > 6) + { + const double sc = 255.0; + LICE_MultiplyAddRect(dest,x1,y1,w,h,(float)parms[4][0],(float)parms[5][0],(float)parms[6][0],np>7 ? (float)parms[7][0]:1.0f, + (float)(np > 8 ? sc*parms[8][0]:0.0), (float)(np > 9 ? sc*parms[9][0]:0.0), (float)(np > 10 ? sc*parms[10][0]:0.0), (float)(np > 11 ? sc*parms[11][0]:0.0)); + } + } +} + + + +void eel_lice_state::gfx_setpixel(EEL_F r, EEL_F g, EEL_F b) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_setpixel"); + if (!dest) return; + + int red=(int) (r*255.0); + int green=(int) (g*255.0); + int blue=(int) (b*255.0); + if (red<0) red=0;else if (red>255)red=255; + if (green<0) green=0;else if (green>255)green=255; + if (blue<0) blue=0; else if (blue>255) blue=255; + + if (LICE_FUNCTION_VALID(LICE_PutPixel)) + { + SetImageDirty(dest); + LICE_PutPixel(dest,(int)*m_gfx_x, (int)*m_gfx_y,LICE_RGBA(red,green,blue,255), (float)*m_gfx_a,getCurMode()); + } +} + +void eel_lice_state::gfx_getimgdim(EEL_F img, EEL_F *w, EEL_F *h) +{ + *w=*h=0; +#ifdef DYNAMIC_LICE + if (!LICE__GetWidth || !LICE__GetHeight) return; +#endif + + LICE_IBitmap *bm=GetImageForIndex(img,"gfx_getimgdim"); + if (bm) + { + *w=LICE__GetWidth(bm); + *h=LICE__GetHeight(bm); + } +} + +EEL_F eel_lice_state::gfx_loadimg(void *opaque, int img, EEL_F loadFrom) +{ +#ifdef DYNAMIC_LICE + if (!__LICE_LoadImage || !LICE__Destroy) return 0.0; +#endif + + if (img >= 0 && img < m_gfx_images.GetSize()) + { + WDL_FastString fs; + bool ok = EEL_LICE_GET_FILENAME_FOR_STRING(loadFrom,&fs,0); + + if (ok && fs.GetLength()) + { + LICE_IBitmap *bm = LICE_LoadImage(fs.Get(),NULL,false); + if (bm) + { + LICE__Destroy(m_gfx_images.Get()[img]); + m_gfx_images.Get()[img]=bm; + return img; + } + } + } + return -1.0; + +} + +EEL_F eel_lice_state::gfx_setimgdim(int img, EEL_F *w, EEL_F *h) +{ + int rv=0; +#ifdef DYNAMIC_LICE + if (!LICE__resize ||!LICE__GetWidth || !LICE__GetHeight||!__LICE_CreateBitmap) return 0.0; +#endif + + int use_w = (int)*w; + int use_h = (int)*h; + if (use_w<1 || use_h < 1) use_w=use_h=0; + if (use_w > 8192) use_w=8192; + if (use_h > 8192) use_h=8192; + + LICE_IBitmap *bm=NULL; + if (img >= 0 && img < m_gfx_images.GetSize()) + { + bm=m_gfx_images.Get()[img]; + if (!bm) + { + m_gfx_images.Get()[img] = bm = __LICE_CreateBitmap(1,use_w,use_h); + rv=!!bm; + } + else + { + rv=LICE__resize(bm,use_w,use_h); + } + } + + return rv?1.0:0.0; +} + +void eel_lice_state::gfx_blurto(EEL_F x, EEL_F y) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_blurto"); + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_Blur +#endif + ) return; + + SetImageDirty(dest); + + int srcx = (int)x; + int srcy = (int)y; + int srcw=(int) (*m_gfx_x-x); + int srch=(int) (*m_gfx_y-y); + if (srch < 0) { srch=-srch; srcy = (int)*m_gfx_y; } + if (srcw < 0) { srcw=-srcw; srcx = (int)*m_gfx_x; } + LICE_Blur(dest,dest,srcx,srcy,srcx,srcy,srcw,srch); + *m_gfx_x = x; + *m_gfx_y = y; +} + +static bool CoordsSrcDestOverlap(EEL_F *coords) +{ + if (coords[0]+coords[2] < coords[4]) return false; + if (coords[0] > coords[4] + coords[6]) return false; + if (coords[1]+coords[3] < coords[5]) return false; + if (coords[1] > coords[5] + coords[7]) return false; + return true; +} + +void eel_lice_state::gfx_transformblit(EEL_F **parms, int div_w, int div_h, EEL_F *tab) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_transformblit"); + + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_ScaledBlit || !LICE_TransformBlit2 ||!LICE__GetWidth||!LICE__GetHeight +#endif + ) return; + + LICE_IBitmap *bm=GetImageForIndex(parms[0][0],"gfx_transformblit:src"); + if (!bm) return; + + const int bmw=LICE__GetWidth(bm); + const int bmh=LICE__GetHeight(bm); + + const bool isFromFB = bm==m_framebuffer; + + SetImageDirty(dest); + + if (bm == dest) + { + if (!m_framebuffer_extra && LICE_FUNCTION_VALID(__LICE_CreateBitmap)) m_framebuffer_extra=__LICE_CreateBitmap(0,bmw,bmh); + if (m_framebuffer_extra) + { + + LICE__resize(bm=m_framebuffer_extra,bmw,bmh); + LICE_ScaledBlit(bm,dest, // copy the entire image + 0,0,bmw,bmh, + 0.0f,0.0f,(float)bmw,(float)bmh, + 1.0f,LICE_BLIT_MODE_COPY); + } + } + LICE_TransformBlit2(dest,bm,(int)floor(parms[1][0]),(int)floor(parms[2][0]),(int)floor(parms[3][0]),(int)floor(parms[4][0]),tab,div_w,div_h, (float)*m_gfx_a,getCurModeForBlit(isFromFB)); +} + +EEL_F eel_lice_state::gfx_setfont(void *opaque, int np, EEL_F **parms) +{ + int a = np>0 ? ((int)floor(parms[0][0]))-1 : -1; + + if (a>=0 && a < m_gfx_fonts.GetSize()) + { + gfxFontStruct *s = m_gfx_fonts.Get()+a; + if (np>1 && LICE_FUNCTION_VALID(LICE_CreateFont) && LICE_FUNCTION_VALID(LICE__SetFromHFont)) + { + const int sz=np>2 ? (int)parms[2][0] : 10; + + bool doCreate=false; + int fontflag=0; + if (!s->font) s->actual_fontname[0]=0; + + { + EEL_STRING_MUTEXLOCK_SCOPE + + const char *face=EEL_STRING_GET_FOR_INDEX(parms[1][0],NULL); + #ifdef EEL_STRING_DEBUGOUT + if (!face) EEL_STRING_DEBUGOUT("gfx_setfont: invalid string identifier %f",parms[1][0]); + #endif + if (!face || !*face) face="Arial"; + + { + unsigned int c = np > 3 ? (unsigned int) parms[3][0] : 0; + while (c) + { + switch (toupper(c&0xff)) + { + case 'B': fontflag|=EELFONT_FLAG_BOLD; break; + case 'I': fontflag|=EELFONT_FLAG_ITALIC; break; + case 'U': fontflag|=EELFONT_FLAG_UNDERLINE; break; + case 'R': fontflag|=16; break; //LICE_FONT_FLAG_FX_BLUR + case 'V': fontflag|=32; break; //LICE_FONT_FLAG_FX_INVERT + case 'M': fontflag|=64; break; //LICE_FONT_FLAG_FX_MONO + case 'S': fontflag|=128; break; //LICE_FONT_FLAG_FX_SHADOW + case 'O': fontflag|=256; break; //LICE_FONT_FLAG_FX_OUTLINE + case 'Z': fontflag|=1; break; //LICE_FONT_FLAG_VERTICAL + case 'Y': fontflag|=1|2; break; //LICE_FONT_FLAG_VERTICAL|LICE_FONT_FLAG_VERTICAL_BOTTOMUP + } + c>>=8; + } + } + + + if (fontflag != s->last_fontflag || sz!=s->last_fontsize || strncmp(s->last_fontname,face,sizeof(s->last_fontname)-1)) + { + lstrcpyn_safe(s->last_fontname,face,sizeof(s->last_fontname)); + s->last_fontsize=sz; + s->last_fontflag=fontflag; + doCreate=1; + } + } + + if (doCreate) + { + s->actual_fontname[0]=0; + if (!s->font) s->font=LICE_CreateFont(); + if (s->font) + { + const int fw = (fontflag&EELFONT_FLAG_BOLD) ? FW_BOLD : FW_NORMAL; + const bool italic = !!(fontflag&EELFONT_FLAG_ITALIC); + const bool underline = !!(fontflag&EELFONT_FLAG_UNDERLINE); + HFONT hf=NULL; +#if defined(_WIN32) && !defined(WDL_NO_SUPPORT_UTF8) + WCHAR wf[256]; + if (WDL_DetectUTF8(s->last_fontname)>0 && + GetVersion()<0x80000000 && + MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,s->last_fontname,-1,wf,256)) + { + hf = CreateFontW(sz,0,0,0,fw,italic,underline,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,wf); + } +#endif + if (!hf) hf = CreateFont(sz,0,0,0,fw,italic,underline,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,s->last_fontname); + + if (!hf) + { + s->use_fonth=0; // disable this font + } + else + { + TEXTMETRIC tm; + tm.tmHeight = sz; + + if (!m_framebuffer && LICE_FUNCTION_VALID(__LICE_CreateBitmap)) m_framebuffer=__LICE_CreateBitmap(1,64,64); + + if (m_framebuffer && LICE_FUNCTION_VALID(LICE__GetDC)) + { + HGDIOBJ oldFont = 0; + HDC hdc=LICE__GetDC(m_framebuffer); + if (hdc) + { + oldFont = SelectObject(hdc,hf); + GetTextMetrics(hdc,&tm); + +#if defined(_WIN32) && !defined(WDL_NO_SUPPORT_UTF8) + if (GetVersion()<0x80000000 && + GetTextFaceW(hdc,sizeof(wf)/sizeof(wf[0]),wf) && + WideCharToMultiByte(CP_UTF8,0,wf,-1,s->actual_fontname,sizeof(s->actual_fontname),NULL,NULL)) + { + s->actual_fontname[sizeof(s->actual_fontname)-1]=0; + } + else +#endif + GetTextFace(hdc, sizeof(s->actual_fontname), s->actual_fontname); + SelectObject(hdc,oldFont); + } + } + + s->use_fonth=wdl_max(tm.tmHeight,1); + LICE__SetFromHFont(s->font,hf, (fontflag & ~EELFONT_FLAG_MASK) | 512 /*LICE_FONT_FLAG_OWNS_HFONT*/); + } + } + } + } + + + if (s->font && s->use_fonth) + { + m_gfx_font_active=a; + if (m_gfx_texth) *m_gfx_texth=s->use_fonth; + return 1.0; + } + // try to init this font + } + #ifdef EEL_STRING_DEBUGOUT + if (a >= m_gfx_fonts.GetSize()) EEL_STRING_DEBUGOUT("gfx_setfont: invalid font %d specified",a); + #endif + + if (a<0||a>=m_gfx_fonts.GetSize()||!m_gfx_fonts.Get()[a].font) + { + m_gfx_font_active=-1; + if (m_gfx_texth) *m_gfx_texth=8; + return 1.0; + } + return 0.0; +} + +void eel_lice_state::gfx_blitext2(int np, EEL_F **parms, int blitmode) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_blitext2"); + + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_ScaledBlit || !LICE_RotatedBlit||!LICE__GetWidth||!LICE__GetHeight +#endif + ) return; + + LICE_IBitmap *bm=GetImageForIndex(parms[0][0],"gfx_blitext2:src"); + if (!bm) return; + + const int bmw=LICE__GetWidth(bm); + const int bmh=LICE__GetHeight(bm); + + // 0=img, 1=scale, 2=rotate + double coords[8]; + const double sc = blitmode==0 ? parms[1][0] : 1.0, + angle = blitmode==0 ? parms[2][0] : 0.0; + if (blitmode==0) + { + parms+=2; + np -= 2; + } + + coords[0]=np > 1 ? parms[1][0] : 0.0f; + coords[1]=np > 2 ? parms[2][0] : 0.0f; + coords[2]=np > 3 ? parms[3][0] : bmw; + coords[3]=np > 4 ? parms[4][0] : bmh; + coords[4]=np > 5 ? parms[5][0] : *m_gfx_x; + coords[5]=np > 6 ? parms[6][0] : *m_gfx_y; + coords[6]=np > 7 ? parms[7][0] : coords[2]*sc; + coords[7]=np > 8 ? parms[8][0] : coords[3]*sc; + + const bool isFromFB = bm == m_framebuffer; + SetImageDirty(dest); + + if (bm == dest && CoordsSrcDestOverlap(coords)) + { + if (!m_framebuffer_extra && LICE_FUNCTION_VALID(__LICE_CreateBitmap)) m_framebuffer_extra=__LICE_CreateBitmap(0,bmw,bmh); + if (m_framebuffer_extra) + { + + LICE__resize(bm=m_framebuffer_extra,bmw,bmh); + LICE_ScaledBlit(bm,dest, // copy the source portion + (int)coords[0],(int)coords[1],(int)coords[2],(int)coords[3], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], + 1.0f,LICE_BLIT_MODE_COPY); + } + } + + if (blitmode==1) + { + if (LICE_FUNCTION_VALID(LICE_DeltaBlit)) + LICE_DeltaBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], + np > 9 ? (float)parms[9][0]:1.0f, // dsdx + np > 10 ? (float)parms[10][0]:0.0f, // dtdx + np > 11 ? (float)parms[11][0]:0.0f, // dsdy + np > 12 ? (float)parms[12][0]:1.0f, // dtdy + np > 13 ? (float)parms[13][0]:0.0f, // dsdxdy + np > 14 ? (float)parms[14][0]:0.0f, // dtdxdy + np <= 15 || parms[15][0] > 0.5, (float)*m_gfx_a,getCurModeForBlit(isFromFB)); + } + else if (fabs(angle)>0.000000001) + { + LICE_RotatedBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], + (float)angle,true, (float)*m_gfx_a,getCurModeForBlit(isFromFB), + np > 9 ? (float)parms[9][0] : 0.0f, + np > 10 ? (float)parms[10][0] : 0.0f); + } + else + { + LICE_ScaledBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], (float)*m_gfx_a,getCurModeForBlit(isFromFB)); + } +} + +void eel_lice_state::gfx_blitext(EEL_F img, EEL_F *coords, EEL_F angle) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_blitext"); + + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_ScaledBlit || !LICE_RotatedBlit||!LICE__GetWidth||!LICE__GetHeight +#endif + ) return; + + LICE_IBitmap *bm=GetImageForIndex(img,"gfx_blitext:src"); + if (!bm) return; + + SetImageDirty(dest); + const bool isFromFB = bm == m_framebuffer; + + int bmw=LICE__GetWidth(bm); + int bmh=LICE__GetHeight(bm); + + if (bm == dest && CoordsSrcDestOverlap(coords)) + { + if (!m_framebuffer_extra && LICE_FUNCTION_VALID(__LICE_CreateBitmap)) m_framebuffer_extra=__LICE_CreateBitmap(0,bmw,bmh); + if ( m_framebuffer_extra) + { + + LICE__resize(bm=m_framebuffer_extra,bmw,bmh); + LICE_ScaledBlit(bm,dest, // copy the source portion + (int)coords[0],(int)coords[1],(int)coords[2],(int)coords[3], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], + 1.0f,LICE_BLIT_MODE_COPY); + } + } + + if (fabs(angle)>0.000000001) + { + LICE_RotatedBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3],(float)angle, + true, (float)*m_gfx_a,getCurModeForBlit(isFromFB), + (float)coords[8],(float)coords[9]); + } + else + { + LICE_ScaledBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], (float)*m_gfx_a,getCurModeForBlit(isFromFB)); + } +} + +void eel_lice_state::gfx_blit(EEL_F img, EEL_F scale, EEL_F rotate) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_blit"); + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_ScaledBlit || !LICE_RotatedBlit||!LICE__GetWidth||!LICE__GetHeight +#endif + ) return; + + LICE_IBitmap *bm=GetImageForIndex(img,"gfx_blit:src"); + + if (!bm) return; + + SetImageDirty(dest); + const bool isFromFB = bm == m_framebuffer; + + int bmw=LICE__GetWidth(bm); + int bmh=LICE__GetHeight(bm); + if (fabs(rotate)>0.000000001) + { + LICE_RotatedBlit(dest,bm,(int)*m_gfx_x,(int)*m_gfx_y,(int) (bmw*scale),(int) (bmh*scale),0.0f,0.0f,(float)bmw,(float)bmh,(float)rotate,true, (float)*m_gfx_a,getCurModeForBlit(isFromFB), + 0.0f,0.0f); + } + else + { + LICE_ScaledBlit(dest,bm,(int)*m_gfx_x,(int)*m_gfx_y,(int) (bmw*scale),(int) (bmh*scale),0.0f,0.0f,(float)bmw,(float)bmh, (float)*m_gfx_a,getCurModeForBlit(isFromFB)); + } +} + +void eel_lice_state::gfx_set(int np, EEL_F **parms) +{ + if (np < 1) return; + if (m_gfx_r) *m_gfx_r = parms[0][0]; + if (m_gfx_g) *m_gfx_g = np > 1 ? parms[1][0] : parms[0][0]; + if (m_gfx_b) *m_gfx_b = np > 2 ? parms[2][0] : parms[0][0]; + if (m_gfx_a) *m_gfx_a = np > 3 ? parms[3][0] : 1.0; + if (m_gfx_mode) *m_gfx_mode = np > 4 ? parms[4][0] : 0; + if (np > 5 && m_gfx_dest) *m_gfx_dest = parms[5][0]; + if (m_gfx_a2) *m_gfx_a2 = np > 6 ? parms[6][0] : 1.0; +} + +void eel_lice_state::gfx_getpixel(EEL_F *r, EEL_F *g, EEL_F *b) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_getpixel"); + if (!dest) return; + + int ret=LICE_FUNCTION_VALID(LICE_GetPixel)?LICE_GetPixel(dest,(int)*m_gfx_x, (int)*m_gfx_y):0; + + *r=LICE_GETR(ret)/255.0; + *g=LICE_GETG(ret)/255.0; + *b=LICE_GETB(ret)/255.0; + +} + + +static int __drawTextWithFont(LICE_IBitmap *dest, const RECT *rect, LICE_IFont *font, const char *buf, int buflen, + int fg, int mode, float alpha, int flags, EEL_F *wantYoutput, EEL_F **measureOnly) +{ + if (font && LICE_FUNCTION_VALID(LICE__DrawText)) + { + RECT tr=*rect; + LICE__SetTextColor(font,fg); + LICE__SetTextCombineMode(font,mode,alpha); + + int maxx=0; + RECT r={0,0,tr.left,0}; + while (buflen>0) + { + int thislen = 0; + while (thislen < buflen && buf[thislen] != '\n') thislen++; + memset(&r,0,sizeof(r)); + int lineh = LICE__DrawText(font,dest,buf,thislen?thislen:1,&r,DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT); + if (!measureOnly) + { + r.right += tr.left; + lineh = LICE__DrawText(font,dest,buf,thislen?thislen:1,&tr,DT_SINGLELINE|DT_NOPREFIX|flags); + if (wantYoutput) *wantYoutput = tr.top; + } + else + { + if (r.right > maxx) maxx=r.right; + } + tr.top += lineh; + + buflen -= thislen+1; + buf += thislen+1; + } + if (measureOnly) + { + measureOnly[0][0] = maxx; + measureOnly[1][0] = tr.top; + } + return r.right; + } + else + { + int xpos=rect->left, ypos=rect->top; + int x; + int maxx=0,maxy=0; + + LICE_SubBitmap sbm( +#ifdef DYNAMIC_LICE + (LICE_IBitmap_disabledAPI*) +#endif + dest,rect->left,rect->top,rect->right-rect->left,rect->bottom-rect->top); + + if (!measureOnly) + { + if (!(flags & DT_NOCLIP)) + { + if (rect->right <= rect->left || rect->bottom <= rect->top) return 0; // invalid clip rect hm + + xpos = ypos = 0; + dest = &sbm; + } + if (flags & (DT_RIGHT|DT_BOTTOM|DT_CENTER|DT_VCENTER)) + { + EEL_F w=0.0,h=0.0; + EEL_F *mo[2] = { &w,&h}; + RECT tr={0,}; + __drawTextWithFont(dest,&tr,NULL,buf,buflen,0,0,0.0f,0,NULL,mo); + + if (flags & DT_RIGHT) xpos += (rect->right-rect->left) - (int)floor(w); + else if (flags & DT_CENTER) xpos += (rect->right-rect->left)/2 - (int)floor(w*.5); + + if (flags & DT_BOTTOM) ypos += (rect->bottom-rect->top) - (int)floor(h); + else if (flags & DT_VCENTER) ypos += (rect->bottom-rect->top)/2 - (int)floor(h*.5); + } + } + const int sxpos = xpos; + + if (LICE_FUNCTION_VALID(LICE_DrawChar)) for(x=0;x maxx) maxx=xpos; + maxy = ypos + 8; + break; + } + } + if (measureOnly) + { + measureOnly[0][0]=maxx; + measureOnly[1][0]=maxy; + } + else + { + if (wantYoutput) *wantYoutput=ypos; + } + return xpos; + } +} + +void eel_lice_state::gfx_drawstr(void *opaque, EEL_F **parms, int nparms, int formatmode)// formatmode=1 for format, 2 for purely measure no format +{ + int nfmtparms = nparms-1; + EEL_F **fmtparms = parms+1; + const char *funcname = formatmode==1?"gfx_printf": + formatmode==2?"gfx_measurestr": + formatmode==3?"gfx_measurechar" : "gfx_drawstr"; + + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,funcname); + if (!dest) return; + +#ifdef DYNAMIC_LICE + if (!LICE__GetWidth || !LICE__GetHeight) return; +#endif + + EEL_STRING_MUTEXLOCK_SCOPE + + WDL_FastString *fs=NULL; + char buf[4096]; + int s_len=0; + + const char *s; + if (formatmode==3) + { + s_len = WDL_MakeUTFChar(buf, (int)parms[0][0], sizeof(buf)); + s=buf; + } + else + { + s=EEL_STRING_GET_FOR_INDEX(parms[0][0],&fs); + #ifdef EEL_STRING_DEBUGOUT + if (!s) EEL_STRING_DEBUGOUT("gfx_%s: invalid string identifier %f",funcname,parms[0][0]); + #endif + if (!s) + { + s=""; + s_len = 12; + } + else if (formatmode==1) + { + extern int eel_format_strings(void *, const char *s, const char *ep, char *, int, int, EEL_F **); + s_len = eel_format_strings(opaque,s,fs?(s+fs->GetLength()):NULL,buf,sizeof(buf),nfmtparms,fmtparms); + if (s_len<1) return; + s=buf; + } + else + { + s_len = fs?fs->GetLength():(int)strlen(s); + } + } + + if (s_len) + { + SetImageDirty(dest); + if (formatmode>=2) + { + if (nfmtparms==2) + { + RECT r={0,0,0,0}; + __drawTextWithFont(dest,&r,GetActiveFont(),s,s_len, + getCurColor(),getCurMode(),(float)*m_gfx_a,0,NULL,fmtparms); + } + } + else + { + RECT r={(int)floor(*m_gfx_x),(int)floor(*m_gfx_y),0,0}; + int flags=DT_NOCLIP; + if (formatmode == 0 && nparms >= 4) + { + flags=(int)*parms[1]; + flags &= (DT_CENTER|DT_RIGHT|DT_VCENTER|DT_BOTTOM|DT_NOCLIP); + r.right=(int)*parms[2]; + r.bottom=(int)*parms[3]; + } + *m_gfx_x=__drawTextWithFont(dest,&r,GetActiveFont(),s,s_len, + getCurColor(),getCurMode(),(float)*m_gfx_a,flags,m_gfx_y,NULL); + } + } +} + +void eel_lice_state::gfx_drawchar(EEL_F ch) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_drawchar"); + if (!dest) return; + + SetImageDirty(dest); + + int a=(int)(ch+0.5); + if (a == '\r' || a=='\n') a=' '; + + char buf[32]; + const int buflen = WDL_MakeUTFChar(buf, a, sizeof(buf)); + + RECT r={(int)floor(*m_gfx_x),(int)floor(*m_gfx_y),0,0}; + *m_gfx_x = __drawTextWithFont(dest,&r, + GetActiveFont(),buf,buflen, + getCurColor(),getCurMode(),(float)*m_gfx_a,DT_NOCLIP,NULL,NULL); + +} + + +void eel_lice_state::gfx_drawnumber(EEL_F n, EEL_F ndigits) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_drawnumber"); + if (!dest) return; + + SetImageDirty(dest); + + char buf[512]; + int a=(int)(ndigits+0.5); + if (a <0)a=0; + else if (a > 16) a=16; + snprintf(buf,sizeof(buf),"%.*f",a,n); + + RECT r={(int)floor(*m_gfx_x),(int)floor(*m_gfx_y),0,0}; + *m_gfx_x = __drawTextWithFont(dest,&r, + GetActiveFont(),buf,(int)strlen(buf), + getCurColor(),getCurMode(),(float)*m_gfx_a,DT_NOCLIP,NULL,NULL); +} diff --git a/source/modules/ysfx/sources/ysfx_api_reaper.cpp b/source/modules/ysfx/sources/ysfx_api_reaper.cpp new file mode 100644 index 000000000..2fdcb7274 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_reaper.cpp @@ -0,0 +1,439 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx.hpp" +#include "ysfx_api_reaper.hpp" +#include "ysfx_api_eel.hpp" +#include "ysfx_eel_utils.hpp" +#include "ysfx_utils.hpp" +#include +#include +#include + +#include "WDL/wdlstring.h" + +#define REAPER_GET_INTERFACE(opaque) ((opaque) ? (ysfx_t *)(opaque) : nullptr) + +static EEL_F *NSEEL_CGEN_CALL ysfx_api_spl(void *opaque, EEL_F *n_) +{ + //NOTE: callable from @gfx thread + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + int32_t n = ysfx_eel_round(*n_); + + if (n < 0 || n >= ysfx_max_channels) { + fx->var.ret_temp = 0; + return &fx->var.ret_temp; + } + + return fx->var.spl[(uint32_t)n]; +} + +static EEL_F *NSEEL_CGEN_CALL ysfx_api_slider(void *opaque, EEL_F *n_) +{ + //NOTE: callable from @gfx thread + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + int32_t n = ysfx_eel_round(*n_); + + if (n < 1 || n > ysfx_max_sliders) { + fx->var.ret_temp = 0; + return &fx->var.ret_temp; + } + + n -= 1; + return fx->var.slider[(uint32_t)n]; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_next_chg(void *opaque, EEL_F *index_, EEL_F *val_) +{ + //TODO frame-accurate slider changes + (void)opaque; + (void)index_; + (void)val_; + return -1; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_automate(void *opaque, EEL_F *mask_or_slider_) +{ + //NOTE: callable from @gfx thread + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_); + + uint64_t mask; + if (slider < ysfx_max_sliders) + mask = (uint64_t)1 << slider; + else + mask = ysfx_eel_round(std::fabs(*mask_or_slider_)); + + fx->slider.automate_mask |= mask; + fx->slider.change_mask |= mask; + return 0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_sliderchange(void *opaque, EEL_F *mask_or_slider_) +{ + //NOTE: callable from @gfx thread + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_); + + uint64_t mask; + if (slider < ysfx_max_sliders) + mask = (uint64_t)1 << slider; + else + mask = ysfx_eel_round(std::fabs(*mask_or_slider_)); + + fx->slider.change_mask |= mask; + return 0; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_show(void *opaque, EEL_F *mask_or_slider_, EEL_F *value_) +{ + //NOTE: callable from @gfx thread + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_); + + uint64_t mask; + if (slider < ysfx_max_sliders) + mask = (uint64_t)1 << slider; + else + mask = ysfx_eel_round(std::fabs(*mask_or_slider_)); + + if (*value_ >= (EEL_F)+0.5) { + // show + fx->slider.visible_mask |= mask; + } + else if (*value_ >= (EEL_F)-0.5) { + // hide + mask = ~mask; + fx->slider.visible_mask &= mask; + } + else { + // toggle + mask = fx->slider.visible_mask.fetch_xor(mask) ^ mask; + } + + return (EEL_F)mask; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend(void *opaque, INT_PTR np, EEL_F **parms) +{ + if (ysfx_get_thread_id() != ysfx_thread_id_dsp) + return 0; + + int32_t offset; + uint8_t msg1; + uint8_t msg2; + uint8_t msg3; + + switch (np) { + case 3: + { + offset = ysfx_eel_round(*parms[0]); + msg1 = (uint8_t)ysfx_eel_round(*parms[1]); + const uint32_t msg23 = ysfx_eel_round(*parms[2]); + msg2 = (uint8_t)(msg23 & 0xff); + msg3 = (uint8_t)(msg23 >> 8); + break; + } + case 4: + offset = ysfx_eel_round(*parms[0]); + msg1 = (uint8_t)ysfx_eel_round(*parms[1]); + msg2 = (uint8_t)ysfx_eel_round(*parms[2]); + msg3 = (uint8_t)ysfx_eel_round(*parms[3]); + break; + default: + assert(false); + return 0; + } + + if (offset < 0) + offset = 0; + + // NOTE(jpc) correct the length of the message + // in case it should be less than 3 bytes + uint32_t length = ysfx_midi_sizeof(msg1); + if (length == 0) // don't know what message this is + length = 3; + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + ysfx_midi_event_t event; + const uint8_t data[] = {msg1, msg2, msg3}; + event.bus = ysfx_current_midi_bus(fx); + event.offset = (uint32_t)offset; + event.size = length; + event.data = data; + if (!ysfx_midi_push(fx->midi.out.get(), &event)) + return 0; + + return msg1; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_) +{ + if (ysfx_get_thread_id() != ysfx_thread_id_dsp) + return 0; + + int32_t offset = ysfx_eel_round(*offset_); + int32_t buf = ysfx_eel_round(*buf_); + int32_t len = ysfx_eel_round(*len_); + + if (len <= 0) + return 0; + if (offset < 0) + offset = 0; + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + + ysfx_midi_push_t mp; + if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp)) + return 0; + + ysfx_eel_ram_reader reader{fx->vm.get(), buf}; + for (uint32_t i = 0; i < (uint32_t)len; ++i) { + uint8_t byte = (uint8_t)ysfx_eel_round(reader.read_next()); + if (!ysfx_midi_push_data(&mp, &byte, 1)) + break; + } + + if (!ysfx_midi_push_end(&mp)) + return 0; + + return len; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_str(void *opaque, EEL_F *offset_, EEL_F *str_) +{ + if (ysfx_get_thread_id() != ysfx_thread_id_dsp) + return 0; + + int32_t offset = ysfx_eel_round(*offset_); + + if (offset < 0) + offset = 0; + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + + /// + struct process_data { + ysfx_t *fx = nullptr; + uint32_t offset = 0; + uint32_t result = 0; + }; + + process_data pdata; + pdata.offset = (uint32_t)offset; + pdata.fx = fx; + + auto process_str = [](void *userdata, WDL_FastString &str) { + process_data *pdata = (process_data *)userdata; + ysfx_t *fx = pdata->fx; + ysfx_midi_event_t event; + event.bus = ysfx_current_midi_bus(fx); + event.offset = pdata->offset; + event.size = (uint32_t)str.GetLength(); + event.data = (const uint8_t *)str.Get(); + pdata->result = ysfx_midi_push(fx->midi.out.get(), &event) ? event.size : 0; + }; + + /// + if (!ysfx_string_access(fx, *str_, false, +process_str, &pdata)) + return 0; + + return pdata.result; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_midisyx(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_) +{ + if (ysfx_get_thread_id() != ysfx_thread_id_dsp) + return 0; + + int32_t offset = ysfx_eel_round(*offset_); + int32_t buf = ysfx_eel_round(*buf_); + int32_t len = ysfx_eel_round(*len_); + + if (len <= 0) + return 0; + if (offset < 0) + offset = 0; + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + + ysfx_midi_push_t mp; + if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp)) + return 0; + + ysfx_eel_ram_reader reader{fx->vm.get(), buf}; + for (uint32_t i = 0; i < (uint32_t)len; ++i) { + uint8_t byte = (uint8_t)ysfx_eel_round(reader.read_next()); + const uint8_t head = 0xf0; + const uint8_t tail = 0xf7; + if (i == 0 && byte != head) { + if (!ysfx_midi_push_data(&mp, &head, 1)) + break; + } + if (!ysfx_midi_push_data(&mp, &byte, 1)) + break; + if (i + 1 == (uint32_t)len && byte != tail) { + if (!ysfx_midi_push_data(&mp, &tail, 1)) + break; + } + } + + if (!ysfx_midi_push_end(&mp)) + return 0; + + return len; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv(void *opaque, INT_PTR np, EEL_F **parms) +{ + if (ysfx_get_thread_id() != ysfx_thread_id_dsp) + return 0; + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + uint32_t bus = ysfx_current_midi_bus(fx); + + ysfx_midi_event_t event; + bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); + // pass through the sysex events + while (have_event && event.size > 3) { + ysfx_midi_push(fx->midi.out.get(), &event); + have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); + } + if (!have_event) + return 0; + + uint8_t msg1 = 0; + uint8_t msg2 = 0; + uint8_t msg3 = 0; + + switch (event.size) { + case 3: msg3 = event.data[2]; // fall through + case 2: msg2 = event.data[1]; // fall through + case 1: msg1 = event.data[0]; break; + } + + *parms[0] = (EEL_F)event.offset; + *parms[1] = (EEL_F)msg1; + switch (np) { + case 4: + *parms[2] = (EEL_F)msg2; + *parms[3] = (EEL_F)msg3; + break; + case 3: + *parms[2] = (EEL_F)(msg2 + (msg3 << 8)); + break; + default: + assert(false); + return 0; + } + + return 1; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *recvlen_) +{ + if (ysfx_get_thread_id() != ysfx_thread_id_dsp) + return 0; + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + NSEEL_VMCTX vm = fx->vm.get(); + + int32_t buf = ysfx_eel_round(*buf_); + int32_t recvlen = ysfx_eel_round(*recvlen_); + + if (recvlen < 0) + recvlen = 0; + + uint32_t bus = ysfx_current_midi_bus(fx); + + ysfx_midi_event_t event; + bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); + // pass through the events larger than the buffer + while (have_event && event.size > (uint32_t)recvlen) { + ysfx_midi_push(fx->midi.out.get(), &event); + have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); + } + if (!have_event) + return 0; + + *offset_ = (EEL_F)event.offset; + + ysfx_eel_ram_writer writer{vm, buf}; + for (uint32_t i = 0; i < event.size; ++i) + writer.write_next(event.data[i]); + + return event.size; +} + +static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_str(void *opaque, EEL_F *offset_, EEL_F *str_) +{ + if (ysfx_get_thread_id() != ysfx_thread_id_dsp) + return 0; + + ysfx_t *fx = REAPER_GET_INTERFACE(opaque); + + uint32_t bus = ysfx_current_midi_bus(fx); + + ysfx_midi_event_t event; + bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); + // pass through the events larger than the maximum string + while (have_event && event.size > ysfx_string_max_length) { + ysfx_midi_push(fx->midi.out.get(), &event); + have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); + } + if (!have_event) + return 0; + + auto process_str = [](void *userdata, WDL_FastString &str) { + ysfx_midi_event_t *event = (ysfx_midi_event_t *)userdata; + str.SetRaw((const char *)event->data, (int32_t)event->size); + }; + + /// + if (!ysfx_string_access(fx, *str_, true, +process_str, &event)) + return 0; + + *offset_ = (EEL_F)event.offset; + return event.size; +} + +//------------------------------------------------------------------------------ +void ysfx_api_init_reaper() +{ + NSEEL_addfunc_retptr("spl", 1, NSEEL_PProc_THIS, &ysfx_api_spl); + NSEEL_addfunc_retptr("slider", 1, NSEEL_PProc_THIS, &ysfx_api_slider); + + NSEEL_addfunc_retval("slider_next_chg", 2, NSEEL_PProc_THIS, &ysfx_api_slider_next_chg); + NSEEL_addfunc_retval("slider_automate", 1, NSEEL_PProc_THIS, &ysfx_api_slider_automate); + NSEEL_addfunc_retval("sliderchange", 1, NSEEL_PProc_THIS, &ysfx_api_sliderchange); + NSEEL_addfunc_retval("slider_show", 2, NSEEL_PProc_THIS, &ysfx_api_slider_show); + + NSEEL_addfunc_exparms("midisend", 3, NSEEL_PProc_THIS, &ysfx_api_midisend); + NSEEL_addfunc_exparms("midisend", 4, NSEEL_PProc_THIS, &ysfx_api_midisend); + NSEEL_addfunc_retval("midisend_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midisend_buf); + NSEEL_addfunc_retval("midisend_str", 2, NSEEL_PProc_THIS, &ysfx_api_midisend_str); + NSEEL_addfunc_exparms("midirecv", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv); + NSEEL_addfunc_exparms("midirecv", 4, NSEEL_PProc_THIS, &ysfx_api_midirecv); + NSEEL_addfunc_retval("midirecv_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv_buf); + NSEEL_addfunc_retval("midirecv_str", 2, NSEEL_PProc_THIS, &ysfx_api_midirecv_str); + NSEEL_addfunc_retval("midisyx", 3, NSEEL_PProc_THIS, &ysfx_api_midisyx); +} diff --git a/source/modules/ysfx/sources/ysfx_api_reaper.hpp b/source/modules/ysfx/sources/ysfx_api_reaper.hpp new file mode 100644 index 000000000..153797b94 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_api_reaper.hpp @@ -0,0 +1,20 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +void ysfx_api_init_reaper(); diff --git a/source/modules/ysfx/sources/ysfx_audio_flac.cpp b/source/modules/ysfx/sources/ysfx_audio_flac.cpp new file mode 100644 index 000000000..35c6e329c --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_audio_flac.cpp @@ -0,0 +1,166 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx_audio_flac.hpp" +#include "ysfx_utils.hpp" +#include +#include + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +#define DR_FLAC_IMPLEMENTATION +#define DRFLAC_API static +#define DRFLAC_PRIVATE static +#include "dr_flac.h" + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +struct drflac_u_deleter { + void operator()(drflac *x) const noexcept { drflac_close(x); } +}; +using drflac_u = std::unique_ptr; + +/// +struct ysfx_flac_reader_t { + drflac_u flac; + uint32_t nbuff = 0; + std::unique_ptr buff; +}; + +static bool ysfx_flac_can_handle(const char *path) +{ + return ysfx::path_has_suffix(path, "flac"); +} + +static ysfx_audio_reader_t *ysfx_flac_open(const char *path) +{ +#if !defined(_WIN32) + drflac_u flac{drflac_open_file(path, NULL)}; +#else + drflac_u flac{drflac_open_file_w(ysfx::widen(path).c_str(), NULL)}; +#endif + if (!flac) + return nullptr; + std::unique_ptr reader{new ysfx_flac_reader_t}; + reader->flac = std::move(flac); + reader->buff.reset(new float[reader->flac->channels]); + return (ysfx_audio_reader_t *)reader.release(); +} + +static void ysfx_flac_close(ysfx_audio_reader_t *reader_) +{ + ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; + delete reader; +} + +static ysfx_audio_file_info_t ysfx_flac_info(ysfx_audio_reader_t *reader_) +{ + ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; + ysfx_audio_file_info_t info; + info.channels = reader->flac->channels; + info.sample_rate = (ysfx_real)reader->flac->sampleRate; + return info; +} + +static uint64_t ysfx_flac_avail(ysfx_audio_reader_t *reader_) +{ + ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; + return reader->nbuff + reader->flac->channels * (reader->flac->totalPCMFrameCount - reader->flac->currentPCMFrame); +} + +static void ysfx_flac_rewind(ysfx_audio_reader_t *reader_) +{ + ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; + drflac_seek_to_pcm_frame(reader->flac.get(), 0); + reader->nbuff = 0; +} + +static uint64_t ysfx_flac_unload_buffer(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) +{ + ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; + + uint32_t nbuff = reader->nbuff; + if (nbuff > count) + nbuff = (uint32_t)count; + + if (nbuff == 0) + return 0; + + const float *src = &reader->buff[reader->flac->channels - reader->nbuff]; + for (uint32_t i = 0; i < nbuff; ++i) + samples[i] = src[i]; + + reader->nbuff -= nbuff; + return nbuff; +} + +static uint64_t ysfx_flac_read(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) +{ + ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; + uint32_t channels = reader->flac->channels; + uint64_t readtotal = 0; + + if (count == 0) + return readtotal; + else { + uint64_t copied = ysfx_flac_unload_buffer(reader_, samples, count); + samples += copied; + count -= copied; + readtotal += copied; + } + + if (count == 0) + return readtotal; + else { + float *f32buf = (float *)samples; + uint64_t readframes = drflac_read_pcm_frames_f32(reader->flac.get(), count / channels, f32buf); + uint64_t readsamples = channels * readframes; + // f32->f64 + for (uint64_t i = readsamples; i-- > 0; ) + samples[i] = f32buf[i]; + samples += readsamples; + count -= readsamples; + readtotal += readsamples; + } + + if (count == 0) + return readtotal; + else if (drflac_read_pcm_frames_f32(reader->flac.get(), 1, reader->buff.get()) == 1) { + reader->nbuff = channels; + uint64_t copied = ysfx_flac_unload_buffer(reader_, samples, count); + samples += copied; + count -= copied; + readtotal += copied; + } + + return readtotal; +} + +const ysfx_audio_format_t ysfx_audio_format_flac = { + &ysfx_flac_can_handle, + &ysfx_flac_open, + &ysfx_flac_close, + &ysfx_flac_info, + &ysfx_flac_avail, + &ysfx_flac_rewind, + &ysfx_flac_read, +}; diff --git a/source/modules/ysfx/sources/ysfx_audio_flac.hpp b/source/modules/ysfx/sources/ysfx_audio_flac.hpp new file mode 100644 index 000000000..c886ebe92 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_audio_flac.hpp @@ -0,0 +1,21 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" + +extern const ysfx_audio_format_t ysfx_audio_format_flac; diff --git a/source/modules/ysfx/sources/ysfx_audio_wav.cpp b/source/modules/ysfx/sources/ysfx_audio_wav.cpp new file mode 100644 index 000000000..a88756858 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_audio_wav.cpp @@ -0,0 +1,162 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx_audio_wav.hpp" +#include "ysfx_utils.hpp" +#include +#include + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +#define DR_WAV_IMPLEMENTATION +#define DRWAV_API static +#define DRWAV_PRIVATE static +#include "dr_wav.h" + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +struct ysfx_wav_reader_t { + ~ysfx_wav_reader_t() { drwav_uninit(wav.get()); } + std::unique_ptr wav; + uint32_t nbuff = 0; + std::unique_ptr buff; +}; + +static bool ysfx_wav_can_handle(const char *path) +{ + return ysfx::path_has_suffix(path, "wav"); +} + +static ysfx_audio_reader_t *ysfx_wav_open(const char *path) +{ + std::unique_ptr wav{new drwav}; +#if !defined(_WIN32) + drwav_bool32 initok = drwav_init_file(wav.get(), path, nullptr); +#else + drwav_bool32 initok = drwav_init_file_w(wav.get(), ysfx::widen(path).c_str(), nullptr); +#endif + if (!initok) + return nullptr; + std::unique_ptr reader{new ysfx_wav_reader_t}; + reader->wav = std::move(wav); + reader->buff.reset(new float[reader->wav->channels]); + return (ysfx_audio_reader_t *)reader.release(); +} + +static void ysfx_wav_close(ysfx_audio_reader_t *reader_) +{ + ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; + delete reader; +} + +static ysfx_audio_file_info_t ysfx_wav_info(ysfx_audio_reader_t *reader_) +{ + ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; + ysfx_audio_file_info_t info; + info.channels = reader->wav->channels; + info.sample_rate = (ysfx_real)reader->wav->sampleRate; + return info; +} + +static uint64_t ysfx_wav_avail(ysfx_audio_reader_t *reader_) +{ + ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; + return reader->nbuff + reader->wav->channels * (reader->wav->totalPCMFrameCount - reader->wav->readCursorInPCMFrames); +} + +static void ysfx_wav_rewind(ysfx_audio_reader_t *reader_) +{ + ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; + drwav_seek_to_pcm_frame(reader->wav.get(), 0); + reader->nbuff = 0; +} + +static uint64_t ysfx_wav_unload_buffer(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) +{ + ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; + + uint32_t nbuff = reader->nbuff; + if (nbuff > count) + nbuff = (uint32_t)count; + + if (nbuff == 0) + return 0; + + const float *src = &reader->buff[reader->wav->channels - reader->nbuff]; + for (uint32_t i = 0; i < nbuff; ++i) + samples[i] = src[i]; + + reader->nbuff -= nbuff; + return nbuff; +} + +static uint64_t ysfx_wav_read(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) +{ + ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; + uint32_t channels = reader->wav->channels; + uint64_t readtotal = 0; + + if (count == 0) + return readtotal; + else { + uint64_t copied = ysfx_wav_unload_buffer(reader_, samples, count); + samples += copied; + count -= copied; + readtotal += copied; + } + + if (count == 0) + return readtotal; + else { + float *f32buf = (float *)samples; + uint64_t readframes = drwav_read_pcm_frames_f32(reader->wav.get(), count / channels, f32buf); + uint64_t readsamples = channels * readframes; + // f32->f64 + for (uint64_t i = readsamples; i-- > 0; ) + samples[i] = f32buf[i]; + samples += readsamples; + count -= readsamples; + readtotal += readsamples; + } + + if (count == 0) + return readtotal; + else if (drwav_read_pcm_frames_f32(reader->wav.get(), 1, reader->buff.get()) == 1) { + reader->nbuff = channels; + uint64_t copied = ysfx_wav_unload_buffer(reader_, samples, count); + samples += copied; + count -= copied; + readtotal += copied; + } + + return readtotal; +} + +const ysfx_audio_format_t ysfx_audio_format_wav = { + &ysfx_wav_can_handle, + &ysfx_wav_open, + &ysfx_wav_close, + &ysfx_wav_info, + &ysfx_wav_avail, + &ysfx_wav_rewind, + &ysfx_wav_read, +}; diff --git a/source/modules/ysfx/sources/ysfx_audio_wav.hpp b/source/modules/ysfx/sources/ysfx_audio_wav.hpp new file mode 100644 index 000000000..3873c2b06 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_audio_wav.hpp @@ -0,0 +1,21 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" + +extern const ysfx_audio_format_t ysfx_audio_format_wav; diff --git a/source/modules/ysfx/sources/ysfx_config.cpp b/source/modules/ysfx/sources/ysfx_config.cpp new file mode 100644 index 000000000..35552de0b --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_config.cpp @@ -0,0 +1,159 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx_config.hpp" +#include "ysfx_utils.hpp" +#include "ysfx_audio_wav.hpp" +#include "ysfx_audio_flac.hpp" +#include + +ysfx_config_t *ysfx_config_new() +{ + return new ysfx_config_t; +} + +void ysfx_config_free(ysfx_config_t *config) +{ + if (!config) + return; + + if (config->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) + delete config; +} + +void ysfx_config_add_ref(ysfx_config_t *config) +{ + config->ref_count.fetch_add(1, std::memory_order_relaxed); +} + +void ysfx_set_import_root(ysfx_config_t *config, const char *root) +{ + config->import_root = ysfx::path_ensure_final_separator(root ? root : ""); +} + +void ysfx_set_data_root(ysfx_config_t *config, const char *root) +{ + config->data_root = ysfx::path_ensure_final_separator(root ? root : ""); +} + +const char *ysfx_get_import_root(ysfx_config_t *config) +{ + return config->import_root.c_str(); +} + +const char *ysfx_get_data_root(ysfx_config_t *config) +{ + return config->data_root.c_str(); +} + +void ysfx_guess_file_roots(ysfx_config_t *config, const char *sourcepath) +{ + if (config->import_root.empty()) { + bool stop = false; + const std::string sourcedir = ysfx::path_directory(sourcepath); + + std::string cur_dir = sourcedir + "../"; + ysfx::file_uid cur_uid; + if (!ysfx::get_file_uid(cur_dir.c_str(), cur_uid)) + stop = true; + + while (!stop) { + bool match = + ysfx::exists((cur_dir + "Effects/").c_str()) && + ysfx::exists((cur_dir + "Data/").c_str()); + if (match) { + stop = true; + config->import_root = cur_dir + "Effects/"; + } + else { + cur_dir += "../"; + ysfx::file_uid old_uid = cur_uid; + if (!ysfx::get_file_uid(cur_dir.c_str(), cur_uid) || old_uid == cur_uid) + stop = true; + } + } + } + + if (config->data_root.empty() && !config->import_root.empty()) { + const std::string datadir = config->import_root + "../Data/"; + + bool match = ysfx::exists(datadir.c_str()); + if (match) + config->data_root = datadir; + } +} + +void ysfx_register_audio_format(ysfx_config_t *config, ysfx_audio_format_t *afmt) +{ + config->audio_formats.push_back(*afmt); +} + +void ysfx_register_builtin_audio_formats(ysfx_config_t *config) +{ + config->audio_formats.push_back(ysfx_audio_format_wav); + config->audio_formats.push_back(ysfx_audio_format_flac); +} + +void ysfx_set_log_reporter(ysfx_config_t *config, ysfx_log_reporter_t *reporter) +{ + config->log_reporter = reporter; +} + +void ysfx_set_user_data(ysfx_config_t *config, intptr_t userdata) +{ + config->userdata = userdata; +} + +//------------------------------------------------------------------------------ +const char *ysfx_log_level_string(ysfx_log_level level) +{ + switch (level) { + case ysfx_log_info: + return "info"; + case ysfx_log_warning: + return "warning"; + case ysfx_log_error: + return "error"; + default: + assert(false); + return "?"; + } +} + +void ysfx_log(ysfx_config_t &conf, ysfx_log_level level, const char *message) +{ + if (conf.log_reporter) + conf.log_reporter(conf.userdata, level, message); + else + fprintf(stderr, "[ysfx] %s: %s\n", ysfx_log_level_string(level), message); +} + +void ysfx_logfv(ysfx_config_t &conf, ysfx_log_level level, const char *format, va_list ap) +{ + char buf[256]; + vsnprintf(buf, sizeof(buf), format, ap); + buf[sizeof(buf)-1] = '\0'; + ysfx_log(conf, level, buf); +} + +void ysfx_logf(ysfx_config_t &conf, ysfx_log_level level, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + ysfx_logfv(conf, level, format, ap); + va_end(ap); +} diff --git a/source/modules/ysfx/sources/ysfx_config.hpp b/source/modules/ysfx/sources/ysfx_config.hpp new file mode 100644 index 000000000..40b84562b --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_config.hpp @@ -0,0 +1,39 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" +#include +#include +#include +#include + +struct ysfx_config_s { + std::string import_root; + std::string data_root; + std::vector audio_formats; + ysfx_log_reporter_t *log_reporter = nullptr; + intptr_t userdata = 0; + std::atomic ref_count{1}; +}; + +void ysfx_log(ysfx_config_t &conf, ysfx_log_level level, const char *message); +void ysfx_logfv(ysfx_config_t &conf, ysfx_log_level level, const char *format, va_list ap); +#if defined(__GNUC__) +__attribute__((format(printf, 3, 4))) +#endif +void ysfx_logf(ysfx_config_t &conf, ysfx_log_level level, const char *format, ...); diff --git a/source/modules/ysfx/sources/ysfx_eel_utils.cpp b/source/modules/ysfx/sources/ysfx_eel_utils.cpp new file mode 100644 index 000000000..a9a247b29 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_eel_utils.cpp @@ -0,0 +1,66 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx_eel_utils.hpp" + +ysfx_eel_ram_reader::ysfx_eel_ram_reader(NSEEL_VMCTX vm, int64_t addr) + : m_vm(vm), + m_addr(addr) +{ +} + +EEL_F ysfx_eel_ram_reader::read_next() +{ + if (m_block_avail == 0) { + m_block = (m_addr < 0 || m_addr > 0xFFFFFFFFu) ? nullptr : + NSEEL_VM_getramptr_noalloc(m_vm, (uint32_t)m_addr, (int32_t *)&m_block_avail); + if (m_block) + m_addr += m_block_avail; + else { + m_addr += 1; + m_block_avail = 1; + } + } + EEL_F value = m_block ? *m_block++ : 0; + m_block_avail -= 1; + return value; +} + +//------------------------------------------------------------------------------ +ysfx_eel_ram_writer::ysfx_eel_ram_writer(NSEEL_VMCTX vm, int64_t addr) + : m_vm(vm), + m_addr(addr) +{ +} + +bool ysfx_eel_ram_writer::write_next(EEL_F value) +{ + if (m_block_avail == 0) { + m_block = (m_addr < 0 || m_addr > 0xFFFFFFFFu) ? nullptr : + NSEEL_VM_getramptr(m_vm, (uint32_t)m_addr, (int32_t *)&m_block_avail); + if (m_block) + m_addr += m_block_avail; + else { + m_addr += 1; + m_block_avail = 1; + } + } + if (m_block) + *m_block++ = value; + m_block_avail -= 1; + return true; +} diff --git a/source/modules/ysfx/sources/ysfx_eel_utils.hpp b/source/modules/ysfx/sources/ysfx_eel_utils.hpp new file mode 100644 index 000000000..3933decc2 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_eel_utils.hpp @@ -0,0 +1,57 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "WDL/eel2/ns-eel.h" +#include "WDL/eel2/ns-eel-int.h" +#include +#include + +template +inline typename std::enable_if::value, T>::type +ysfx_eel_round(EEL_F value) +{ + return (T)(value + (EEL_F)0.0001); // same one as used in eel2 everywhere +} + +//------------------------------------------------------------------------------ +class ysfx_eel_ram_reader { +public: + ysfx_eel_ram_reader() = default; + ysfx_eel_ram_reader(NSEEL_VMCTX vm, int64_t addr); + EEL_F read_next(); + +private: + NSEEL_VMCTX m_vm{}; + int64_t m_addr = 0; + const EEL_F *m_block = nullptr; + uint32_t m_block_avail = 0; +}; + +//------------------------------------------------------------------------------ +class ysfx_eel_ram_writer { +public: + ysfx_eel_ram_writer() = default; + ysfx_eel_ram_writer(NSEEL_VMCTX vm, int64_t addr); + bool write_next(EEL_F value); + +private: + NSEEL_VMCTX m_vm{}; + int64_t m_addr = 0; + EEL_F *m_block = nullptr; + uint32_t m_block_avail = 0; +}; diff --git a/source/modules/ysfx/sources/ysfx_midi.cpp b/source/modules/ysfx/sources/ysfx_midi.cpp new file mode 100644 index 000000000..2e13faf1b --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_midi.cpp @@ -0,0 +1,214 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx_midi.hpp" +#include +#include + +void ysfx_midi_reserve(ysfx_midi_buffer_t *midi, uint32_t capacity, bool extensible) +{ + std::vector data; + data.reserve(capacity); + std::swap(data, midi->data); + midi->extensible = extensible; + ysfx_midi_rewind(midi); +} + +void ysfx_midi_clear(ysfx_midi_buffer_t *midi) +{ + midi->data.clear(); + ysfx_midi_rewind(midi); +} + +bool ysfx_midi_push(ysfx_midi_buffer_t *midi, const ysfx_midi_event_t *event) +{ + if (event->size > ysfx_midi_message_max_size) + return false; + if (event->bus >= ysfx_max_midi_buses) + return false; + + ysfx_midi_header_t header; + + if (!midi->extensible) { + size_t writable = midi->data.capacity() - midi->data.size(); + if (writable < sizeof(header) + event->size) + return false; + } + + const uint8_t *data = event->data; + const uint8_t *headp = (const uint8_t *)&header; + header.bus = event->bus; + header.offset = event->offset; + header.size = event->size; + + midi->data.insert(midi->data.end(), headp, headp + sizeof(header)); + midi->data.insert(midi->data.end(), data, data + header.size); + return true; +} + +void ysfx_midi_rewind(ysfx_midi_buffer_t *midi) +{ + midi->read_pos = 0; + for (uint32_t i = 0; i < ysfx_max_midi_buses; ++i) + midi->read_pos_for_bus[i] = 0; +} + +bool ysfx_midi_get_next(ysfx_midi_buffer_t *midi, ysfx_midi_event_t *event) +{ + size_t *pos_ptr = &midi->read_pos; + size_t pos = *pos_ptr; + + size_t avail = midi->data.size() - pos; + ysfx_midi_header_t header; + if (avail == 0) + return false; + + assert(avail >= sizeof(header)); + memcpy(&header, &midi->data[pos], sizeof(header)); + assert(avail >= sizeof(header) + header.size); + + event->bus = header.bus; + event->offset = header.offset; + event->size = header.size; + event->data = &midi->data[pos + sizeof(header)]; + *pos_ptr = pos + (sizeof(header) + header.size); + return true; +} + +bool ysfx_midi_get_next_from_bus(ysfx_midi_buffer_t *midi, uint32_t bus, ysfx_midi_event_t *event) +{ + if (bus >= ysfx_max_midi_buses) + return false; + + size_t *pos_ptr = &midi->read_pos_for_bus[bus]; + size_t pos = *pos_ptr; + size_t avail = midi->data.size() - pos; + ysfx_midi_header_t header; + + bool found = false; + while (!found && avail > 0) { + assert(avail >= sizeof(header)); + memcpy(&header, &midi->data[pos], sizeof(header)); + assert(avail >= sizeof(header) + header.size); + + found = header.bus == bus; + if (!found) { + pos += sizeof(header) + header.size; + avail -= sizeof(header) + header.size; + } + } + + if (!found) { + *pos_ptr = pos; + return false; + } + + event->bus = header.bus; + event->offset = header.offset; + event->size = header.size; + event->data = &midi->data[pos + sizeof(header)]; + *pos_ptr = pos + (sizeof(header) + header.size); + return true; +} + +bool ysfx_midi_push_begin(ysfx_midi_buffer_t *midi, uint32_t bus, uint32_t offset, ysfx_midi_push_t *mp) +{ + ysfx_midi_header_t header; + + mp->midi = midi; + mp->start = midi->data.size(); + mp->count = 0; + mp->eob = false; + + if (!midi->extensible) { + size_t writable = midi->data.capacity() - midi->data.size(); + if (writable < sizeof(header)) { + mp->eob = true; + return false; + } + } + + header.bus = bus; + header.offset = offset; + header.size = 0; + + const uint8_t *headp = (const uint8_t *)&header; + midi->data.insert(midi->data.end(), headp, headp + sizeof(header)); + + return true; +} + +bool ysfx_midi_push_data(ysfx_midi_push_t *mp, const uint8_t *data, uint32_t size) +{ + if (mp->eob) + return false; + + if (size > ysfx_midi_message_max_size || mp->count + size > ysfx_midi_message_max_size) { + mp->eob = true; + return false; + } + + ysfx_midi_buffer_t *midi = mp->midi; + + if (!midi->extensible) { + size_t writable = midi->data.capacity() - midi->data.size(); + if (writable < size) { + mp->eob = true; + return false; + } + } + + midi->data.insert(midi->data.end(), data, data + size); + mp->count += size; + return true; +} + +bool ysfx_midi_push_end(ysfx_midi_push_t *mp) +{ + if (mp->eob) { + mp->midi->data.resize(mp->start); + return false; + } + + ysfx_midi_header_t header; + uint8_t *headp = &mp->midi->data[mp->start]; + memcpy(&header, headp, sizeof(header)); + header.size = mp->count; + memcpy(headp, &header, sizeof(header)); + return true; +} + +//------------------------------------------------------------------------------ +uint32_t ysfx_midi_sizeof(uint8_t id) +{ + if ((id >> 7) == 0) { + return 0; + } + else if ((id >> 4) != 0b1111) { + static const uint8_t sizetable[8] = { + 3, 3, 3, 3, 2, 2, 3, + }; + return sizetable[(id >> 4) & 0b111]; + } + else { + static const uint8_t sizetable[16] = { + 0, 2, 3, 2, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + }; + return sizetable[id & 0b1111]; + } +} diff --git a/source/modules/ysfx/sources/ysfx_midi.hpp b/source/modules/ysfx/sources/ysfx_midi.hpp new file mode 100644 index 000000000..a36c21c46 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_midi.hpp @@ -0,0 +1,72 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" +#include +#include + +struct ysfx_midi_header_t { + uint32_t bus; + uint32_t offset; + uint32_t size; +}; + +struct ysfx_midi_buffer_t { + std::vector data; + size_t read_pos = 0; + size_t read_pos_for_bus[ysfx_max_midi_buses] = {}; + bool extensible = false; +}; +using ysfx_midi_buffer_u = std::unique_ptr; + +enum { + ysfx_midi_message_max_size = 1 << 24, +}; + +// NOTE: regarding buses, +// The buffer keeps 2 kinds of read positions: global, and per-bus. +// +// These are tracked separately, so use either global/per-bus reading API, +// but not both mixed in the same piece of code. +// +// The JSFX API `midi*` implementations should always use per-bus access: +// if `ext_midi_bus` is true, use the bus defined by `midi_bus`, otherwise 0. + +void ysfx_midi_reserve(ysfx_midi_buffer_t *midi, uint32_t capacity, bool extensible); +void ysfx_midi_clear(ysfx_midi_buffer_t *midi); +bool ysfx_midi_push(ysfx_midi_buffer_t *midi, const ysfx_midi_event_t *event); +void ysfx_midi_rewind(ysfx_midi_buffer_t *midi); +bool ysfx_midi_get_next(ysfx_midi_buffer_t *midi, ysfx_midi_event_t *event); +bool ysfx_midi_get_next_from_bus(ysfx_midi_buffer_t *midi, uint32_t bus, ysfx_midi_event_t *event); + +// incremental writer into a midi buffer +struct ysfx_midi_push_t { + ysfx_midi_buffer_t *midi = nullptr; + size_t start = 0; + uint32_t count = 0; + bool eob = false; +}; +bool ysfx_midi_push_begin(ysfx_midi_buffer_t *midi, uint32_t bus, uint32_t offset, ysfx_midi_push_t *mp); +bool ysfx_midi_push_data(ysfx_midi_push_t *mp, const uint8_t *data, uint32_t size); +bool ysfx_midi_push_end(ysfx_midi_push_t *mp); + +//------------------------------------------------------------------------------ + +// determine the length of a midi message according to its status byte +// if length is dynamic, returns 0 +uint32_t ysfx_midi_sizeof(uint8_t id); diff --git a/source/modules/ysfx/sources/ysfx_parse.cpp b/source/modules/ysfx/sources/ysfx_parse.cpp new file mode 100644 index 000000000..8e9f93f35 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_parse.cpp @@ -0,0 +1,380 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx_parse.hpp" +#include "ysfx_utils.hpp" +#include +#include + +bool ysfx_parse_toplevel(ysfx::text_reader &reader, ysfx_toplevel_t &toplevel, ysfx_parse_error *error) +{ + toplevel = ysfx_toplevel_t{}; + + ysfx_section_t *current = new ysfx_section_t; + toplevel.header.reset(current); + + std::string line; + uint32_t lineno = 0; + + line.reserve(256); + + while (reader.read_next_line(line)) { + const char *linep = line.c_str(); + + if (linep[0] == '@') { + // a new section starts + ysfx::string_list tokens = ysfx::split_strings_noempty(linep, &ysfx::ascii_isspace); + + current = new ysfx_section_t; + + if (tokens[0] == "@init") + toplevel.init.reset(current); + else if (tokens[0] == "@slider") + toplevel.slider.reset(current); + else if (tokens[0] == "@block") + toplevel.block.reset(current); + else if (tokens[0] == "@sample") + toplevel.sample.reset(current); + else if (tokens[0] == "@serialize") + toplevel.serialize.reset(current); + else if (tokens[0] == "@gfx") { + toplevel.gfx.reset(current); + long gfx_w = 0; + long gfx_h = 0; + if (tokens.size() > 1) + gfx_w = (long)ysfx::dot_atof(tokens[1].c_str()); + if (tokens.size() > 2) + gfx_h = (long)ysfx::dot_atof(tokens[2].c_str()); + toplevel.gfx_w = (gfx_w > 0) ? (uint32_t)gfx_w : 0; + toplevel.gfx_h = (gfx_h > 0) ? (uint32_t)gfx_h : 0; + } + else { + delete current; + if (error) { + error->line = lineno; + error->message = std::string("Invalid section: ") + line; + } + return false; + } + current->line_offset = lineno + 1; + } + else { + current->text.append(line); + current->text.push_back('\n'); + } + + ++lineno; + } + + return true; +} + +void ysfx_parse_header(ysfx_section_t *section, ysfx_header_t &header) +{ + header = ysfx_header_t{}; + + ysfx::string_text_reader reader(section->text.c_str()); + + std::string line; + //uint32_t lineno = section->line_offset; + + line.reserve(256); + + /// + auto unprefix = [](const char *text, const char **restp, const char *prefix) -> bool { + size_t len = strlen(prefix); + if (strncmp(text, prefix, len)) + return false; + if (restp) + *restp = text + len; + return true; + }; + + //-------------------------------------------------------------------------- + // pass 1: regular metadata + + while (reader.read_next_line(line)) { + const char *linep = line.c_str(); + const char *rest = nullptr; + + ysfx_slider_t slider; + ysfx_parsed_filename_t filename; + + /// + if (unprefix(linep, &rest, "desc:")) { + if (header.desc.empty()) + header.desc = ysfx::trim(rest, &ysfx::ascii_isspace); + } + else if (unprefix(linep, &rest, "author:")) { + if (header.author.empty()) + header.author = ysfx::trim(rest, &ysfx::ascii_isspace); + } + else if (unprefix(linep, &rest, "tags:")) { + if (header.tags.empty()) { + for (const std::string &tag : ysfx::split_strings_noempty(rest, &ysfx::ascii_isspace)) + header.tags.push_back(tag); + } + } + else if (unprefix(linep, &rest, "in_pin:")) { + header.explicit_pins = true; + header.in_pins.push_back(ysfx::trim(rest, &ysfx::ascii_isspace)); + } + else if (unprefix(linep, &rest, "out_pin:")) { + header.explicit_pins = true; + header.out_pins.push_back(ysfx::trim(rest, &ysfx::ascii_isspace)); + } + else if (unprefix(linep, &rest, "options:")) { + for (const std::string &opt : ysfx::split_strings_noempty(rest, &ysfx::ascii_isspace)) { + size_t pos = opt.find('='); + std::string name = (pos == opt.npos) ? opt : opt.substr(0, pos); + std::string value = (pos == opt.npos) ? std::string{} : opt.substr(pos + 1); + if (name == "gmem") + header.options.gmem = value; + else if (name == "maxmem") { + int32_t maxmem = (int32_t)ysfx::dot_atof(value.c_str()); + header.options.maxmem = (maxmem < 0) ? 0 : (uint32_t)maxmem; + } + else if (name == "want_all_kb") + header.options.want_all_kb = true; + else if (name == "no_meter") + header.options.no_meter = true; + } + } + else if (unprefix(linep, &rest, "import") && ysfx::ascii_isspace(rest[0])) + header.imports.push_back(ysfx::trim(rest + 1, &ysfx::ascii_isspace)); + else if (ysfx_parse_slider(linep, slider)) { + if (slider.id >= ysfx_max_sliders) + continue; + slider.exists = true; + header.sliders[slider.id] = slider; + } + else if (ysfx_parse_filename(linep, filename)) { + if (filename.index != header.filenames.size()) + continue; + header.filenames.push_back(std::move(filename.filename)); + } + + //++lineno; + } + + //-------------------------------------------------------------------------- + // pass 2: comments + + reader = ysfx::string_text_reader{section->text.c_str()}; + + while (reader.read_next_line(line)) { + const char *linep = line.c_str(); + const char *rest = nullptr; + + // some files contain metadata in the form of comments + // this is not part of spec, but we'll take this info regardless + + if (unprefix(linep, &rest, "//author:")) { + if (header.author.empty()) + header.author = ysfx::trim(rest, &ysfx::ascii_isspace); + } + else if (unprefix(linep, &rest, "//tags:")) { + if (header.tags.empty()) { + for (const std::string &tag : ysfx::split_strings_noempty(rest, &ysfx::ascii_isspace)) + header.tags.push_back(tag); + } + } + } + + //-------------------------------------------------------------------------- + if (header.in_pins.size() == 1 && !ysfx::ascii_casecmp(header.in_pins.front().c_str(), "none")) + header.in_pins.clear(); + if (header.out_pins.size() == 1 && !ysfx::ascii_casecmp(header.out_pins.front().c_str(), "none")) + header.out_pins.clear(); + + if (header.in_pins.size() > ysfx_max_channels) + header.in_pins.resize(ysfx_max_channels); + if (header.out_pins.size() > ysfx_max_channels) + header.out_pins.resize(ysfx_max_channels); +} + +bool ysfx_parse_slider(const char *line, ysfx_slider_t &slider) +{ + // NOTE this parser is intentionally very permissive, + // in order to match the reference behavior + + slider = ysfx_slider_t{}; + + #define PARSE_FAIL do { \ + /*fprintf(stderr, "parse error (line %d): `%s`\n", __LINE__, line);*/ \ + return false; \ + } while (0) + + const char *cur = line; + + for (const char *p = "slider"; *p; ++p) { + if (*cur++ != *p) + PARSE_FAIL; + } + + // parse ID (1-index) + unsigned long id = strtoul(cur, (char **)&cur, 10); + if (id < 1 || id > ysfx_max_sliders) + PARSE_FAIL; + slider.id = (uint32_t)--id; + + // semicolon + if (*cur++ != ':') + PARSE_FAIL; + + // search if there is an '=' sign prior to any '<' or ',' + // if there is, it's a custom variable + { + const char *pos = cur; + for (char c; pos && (c = *pos) && c != '='; ) + pos = (c == '<' || c == ',') ? nullptr : (pos + 1); + if (pos && *pos) { + slider.var.assign(cur, pos); + cur = pos + 1; + } + else + slider.var = "slider" + std::to_string(id + 1); + } + + // a regular slider + if (*cur != '/') { + slider.def = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); + + while (*cur && *cur != ',' && *cur != '<') ++cur; + if (!*cur) PARSE_FAIL; + + // no range specification + if (*cur == ',') + ++cur; + // range specification + else if (*cur == '<') { + ++cur; + + // min + slider.min = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); + + while (*cur && *cur != ',' && *cur != '>') ++cur; + if (!*cur) PARSE_FAIL; + + // max + if (*cur == ',') { + ++cur; + slider.max = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); + + while (*cur && *cur != ',' && *cur != '>') ++cur; + if (!*cur) PARSE_FAIL; + } + + // inc + if (*cur == ',') { + ++cur; + slider.inc = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); + + while (*cur && *cur != '{' && *cur != ',' && *cur != '>') ++cur; + if (!*cur) PARSE_FAIL; + + // enumeration values + if (*cur == '{') { + const char *pos = ++cur; + + while (*cur && *cur != '}' && *cur != '>') ++cur; + if (!*cur) PARSE_FAIL; + + slider.is_enum = true; + slider.enum_names = ysfx::split_strings_noempty( + std::string(pos, cur).c_str(), + [](char c) -> bool { return c == ','; }); + for (std::string &name : slider.enum_names) + name = ysfx::trim(name.c_str(), &ysfx::ascii_isspace); + } + } + + while (*cur && *cur != '>') ++cur; + if (!*cur) PARSE_FAIL; + + ++cur; + } + else + PARSE_FAIL; + + // NOTE: skip ',' and whitespace. not sure why, it's how it is + while (*cur && (*cur == ',' || ysfx::ascii_isspace(*cur))) ++cur; + if (!*cur) PARSE_FAIL; + } + // a path slider + else + { + const char *pos = cur; + while (*cur && *cur != ':') ++cur; + if (!*cur) PARSE_FAIL; + + slider.path.assign(pos, cur); + ++cur; + slider.def = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); + slider.inc = 1; + slider.is_enum = true; + + while (*cur && *cur != ':') ++cur; + if (!*cur) PARSE_FAIL; + + ++cur; + } + + // description and visibility + while (ysfx::ascii_isspace(*cur)) + ++cur; + + slider.initially_visible = true; + if (*cur == '-') { + ++cur; + slider.initially_visible = false; + } + + slider.desc = ysfx::trim(cur, &ysfx::ascii_isspace); + if (slider.desc.empty()) + PARSE_FAIL; + + // + #undef PARSE_FAIL + + return true; +} + +bool ysfx_parse_filename(const char *line, ysfx_parsed_filename_t &filename) +{ + filename = ysfx_parsed_filename_t{}; + + const char *cur = line; + + for (const char *p = "filename:"; *p; ++p) { + if (*cur++ != *p) + return false; + } + + int64_t index = (int64_t)ysfx::dot_strtod(cur, (char **)&cur); + if (index < 0 || index > ~(uint32_t)0) + return false; + + while (*cur && *cur != ',') ++cur; + if (!*cur) return false;; + + ++cur; + + filename.index = (uint32_t)index; + filename.filename.assign(cur); + return true; +} diff --git a/source/modules/ysfx/sources/ysfx_parse.hpp b/source/modules/ysfx/sources/ysfx_parse.hpp new file mode 100644 index 000000000..cfbae62ac --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_parse.hpp @@ -0,0 +1,101 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" +#include "ysfx_reader.hpp" +#include "ysfx_utils.hpp" +#include +#include +#include + +struct ysfx_section_t; +struct ysfx_toplevel_t; +struct ysfx_slider_t; +struct ysfx_header_t; +using ysfx_section_u = std::unique_ptr; +using ysfx_toplevel_u = std::unique_ptr; +using ysfx_slider_u = std::unique_ptr; +using ysfx_header_u = std::unique_ptr; + +struct ysfx_section_t { + uint32_t line_offset = 0; + std::string text; +}; + +struct ysfx_toplevel_t { + ysfx_section_u header; + ysfx_section_u init; + ysfx_section_u slider; + ysfx_section_u block; + ysfx_section_u sample; + ysfx_section_u serialize; + ysfx_section_u gfx; + uint32_t gfx_w = 0; + uint32_t gfx_h = 0; +}; + +struct ysfx_parse_error { + uint32_t line = 0; + std::string message; + explicit operator bool() { return !message.empty(); } +}; + +struct ysfx_slider_t { + uint32_t id = 0; + bool exists = false; + ysfx_real def = 0; + ysfx_real min = 0; + ysfx_real max = 0; + ysfx_real inc = 0; + std::string var; + std::string path; + bool is_enum = false; + ysfx::string_list enum_names; + std::string desc; + bool initially_visible = false; +}; + +struct ysfx_options_t { + std::string gmem; + uint32_t maxmem = 0; + bool want_all_kb = false; + bool no_meter = false; +}; + +struct ysfx_header_t { + std::string desc; + std::string author; + ysfx::string_list tags; + ysfx::string_list imports; + ysfx::string_list in_pins; + ysfx::string_list out_pins; + bool explicit_pins = false; + ysfx::string_list filenames; + ysfx_options_t options; + ysfx_slider_t sliders[ysfx_max_sliders]; +}; + +struct ysfx_parsed_filename_t { + uint32_t index; + std::string filename; +}; + +bool ysfx_parse_toplevel(ysfx::text_reader &reader, ysfx_toplevel_t &toplevel, ysfx_parse_error *error); +bool ysfx_parse_slider(const char *line, ysfx_slider_t &slider); +bool ysfx_parse_filename(const char *line, ysfx_parsed_filename_t &filename); +void ysfx_parse_header(ysfx_section_t *section, ysfx_header_t &header); diff --git a/source/modules/ysfx/sources/ysfx_preset.cpp b/source/modules/ysfx/sources/ysfx_preset.cpp new file mode 100644 index 000000000..297d71b2d --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_preset.cpp @@ -0,0 +1,171 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx.h" +#include "ysfx_preset.hpp" +#include "ysfx_utils.hpp" +#include +#include +#include + +#include "WDL/lineparse.h" + +static void ysfx_preset_clear(ysfx_preset_t *preset); +static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text); +static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector &data); + +ysfx_bank_t *ysfx_load_bank(const char *path) +{ + ysfx::FILE_u stream{fopen(path, "rb")}; + if (!stream) + return nullptr; + + std::string input; + constexpr uint32_t max_input = 1u << 24; + input.reserve(1u << 16); + + for (int ch; input.size() < max_input && (ch = fgetc(stream.get())) != EOF; ) { + ch = (ch == '\r' || ch == '\n') ? ' ' : ch; + input.push_back((unsigned char)ch); + } + + if (ferror(stream.get())) + return nullptr; + + stream.reset(); + return ysfx_load_bank_from_rpl_text(input); +} + +void ysfx_bank_free(ysfx_bank_t *bank) +{ + if (!bank) + return; + + delete[] bank->name; + + if (ysfx_preset_t *presets = bank->presets) { + uint32_t count = bank->preset_count; + for (uint32_t i = 0; i < count; ++i) + ysfx_preset_clear(&presets[i]); + delete[] presets; + } + + delete bank; +} + +static void ysfx_preset_clear(ysfx_preset_t *preset) +{ + delete[] preset->name; + preset->name = nullptr; + + ysfx_state_free(preset->state); + preset->state = nullptr; +} + +static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text) +{ + LineParser parser; + if (parser.parse(text.c_str()) < 0) + return nullptr; + + /// + std::vector preset_list; + preset_list.reserve(256); + + auto list_cleanup = ysfx::defer([&preset_list]() { + for (ysfx_preset_t &pst : preset_list) + ysfx_preset_clear(&pst); + }); + + /// + int ntok = parser.getnumtokens(); + int itok = 0; + + if (strcmp("", (part = parser.gettoken_str(itok++))) != 0; ) + base64.append(part); + + preset_list.emplace_back(); + ysfx_preset_t &preset = preset_list.back(); + + ysfx_parse_preset_from_rpl_blob( + &preset, preset_name, + ysfx::decode_base64(base64.data(), base64.size())); + } + } + + /// + ysfx_bank_u bank{new ysfx_bank_t{}}; + bank->name = ysfx::strdup_using_new(bank_name); + bank->presets = new ysfx_preset_t[(uint32_t)preset_list.size()]{}; + bank->preset_count = (uint32_t)preset_list.size(); + + for (uint32_t i = (uint32_t)preset_list.size(); i-- > 0; ) { + bank->presets[i] = preset_list[i]; + preset_list.pop_back(); + } + + return bank.release(); +} + +static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector &data) +{ + ysfx_state_t state{}; + std::vector sliders; + + size_t len = data.size(); + size_t pos = 0; + while (pos < len && data[pos] != 0) ++pos; + + if (pos++ < len) { + state.data = const_cast(&data[pos]); + state.data_size = len - pos; + + LineParser parser; + if (parser.parse((const char *)data.data()) >= 0) { + sliders.reserve(ysfx_max_sliders); + + for (uint32_t i = 0; i < 64; ++i) { + int success = false; + ysfx_state_slider_t slider{}; + slider.index = i; + slider.value = (ysfx_real)parser.gettoken_float(i, &success); + if (success) + sliders.push_back(slider); + } + + state.sliders = sliders.data(); + state.slider_count = (uint32_t)sliders.size(); + } + } + + preset->name = ysfx::strdup_using_new(name); + preset->state = ysfx_state_dup(&state); +} diff --git a/source/modules/ysfx/sources/ysfx_preset.hpp b/source/modules/ysfx/sources/ysfx_preset.hpp new file mode 100644 index 000000000..8435637ae --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_preset.hpp @@ -0,0 +1,18 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once diff --git a/source/modules/ysfx/sources/ysfx_reader.cpp b/source/modules/ysfx/sources/ysfx_reader.cpp new file mode 100644 index 000000000..9b3cb2ad9 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_reader.cpp @@ -0,0 +1,98 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx_reader.hpp" + +namespace ysfx { + +//------------------------------------------------------------------------------ +bool text_reader::read_next_line(std::string &line) +{ + line.clear(); + + char next = read_next_char(); + if (next == '\0') + return false; + + while (next != '\0' && next != '\r' && next != '\n') { + line.push_back(next); + next = read_next_char(); + } + + if (next == '\r') { + next = peek_next_char(); + if (next == '\n') + read_next_char(); + } + + return true; +} + +//------------------------------------------------------------------------------ +char string_text_reader::read_next_char() +{ + const char *ptr = m_char_ptr; + + if (!ptr || *ptr == '\0') + return '\0'; + + char next = *ptr; + m_char_ptr = ptr + 1; + return next; +} + +char string_text_reader::peek_next_char() +{ + const char *ptr = m_char_ptr; + + if (!ptr) + return '\0'; + + return *ptr; +} + +//------------------------------------------------------------------------------ +char stdio_text_reader::read_next_char() +{ + FILE *stream = m_stream; + + if (!stream) + return '\0'; + + int next = fgetc(stream); + if (next == EOF) + return '\0'; + + return (unsigned char)next; +} + +char stdio_text_reader::peek_next_char() +{ + FILE *stream = m_stream; + + if (!stream) + return '\0'; + + int next = fgetc(stream); + if (next == EOF) + return '\0'; + + ungetc(next, stream); + return (unsigned char)next; +} + +} // namespace ysfx diff --git a/source/modules/ysfx/sources/ysfx_reader.hpp b/source/modules/ysfx/sources/ysfx_reader.hpp new file mode 100644 index 000000000..a24c1d807 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_reader.hpp @@ -0,0 +1,56 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include +#include +#include + +namespace ysfx { + +class text_reader +{ +public: + virtual ~text_reader() = default; + virtual char read_next_char() = 0; + virtual char peek_next_char() = 0; + bool read_next_line(std::string &line); +}; + +//------------------------------------------------------------------------------ +class string_text_reader : public text_reader +{ +public: + explicit string_text_reader(const char *text) : m_char_ptr(text) {} + char read_next_char() override; + char peek_next_char() override; +private: + const char *m_char_ptr = nullptr; +}; + +//------------------------------------------------------------------------------ +class stdio_text_reader : public text_reader +{ +public: + explicit stdio_text_reader(FILE *stream) : m_stream(stream) {} + char read_next_char() override; + char peek_next_char() override; +private: + FILE *m_stream = nullptr; +}; + +} // namespace ysfx diff --git a/source/modules/ysfx/sources/ysfx_utils.cpp b/source/modules/ysfx/sources/ysfx_utils.cpp new file mode 100644 index 000000000..c859072f5 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_utils.cpp @@ -0,0 +1,730 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ysfx_utils.hpp" +#include "base64/Base64.hpp" +#include +#include +#include +#include +#include +#include +#if !defined(_WIN32) +# include +# include +# include +# include +# include +#else +# include +# include +#endif + +namespace ysfx { + +#if !defined(_WIN32) +static_assert(sizeof(off_t) == 8, "64-bit large file support is not enabled"); +#endif + +FILE *fopen_utf8(const char *path, const char *mode) +{ +#if defined(_WIN32) + return _wfopen(widen(path).c_str(), widen(mode).c_str()); +#else + return fopen(path, mode); +#endif +} + +int64_t fseek_lfs(FILE *stream, int64_t off, int whence) +{ +#if defined(_WIN32) + return _fseeki64(stream, off, whence); +#else + return fseeko(stream, off, whence); +#endif +} + +int64_t ftell_lfs(FILE *stream) +{ +#if defined(_WIN32) + return _ftelli64(stream); +#else + return ftello(stream); +#endif +} + +//------------------------------------------------------------------------------ + +namespace { + +struct scoped_c_locale +{ + scoped_c_locale(int lc, const char *name); + ~scoped_c_locale(); + c_locale_t m_loc{}; + + scoped_c_locale(const scoped_c_locale &) = delete; + scoped_c_locale &operator=(const scoped_c_locale &) = delete; +}; + +scoped_c_locale::scoped_c_locale(int lc, const char *name) +{ +#if defined(_WIN32) + m_loc = _create_locale(lc, name); +#else + switch (lc) { + case LC_ALL: + m_loc = newlocale(LC_ALL_MASK, name, c_locale_t{}); + break; + case LC_CTYPE: + m_loc = newlocale(LC_CTYPE_MASK, name, c_locale_t{}); + break; + case LC_COLLATE: + m_loc = newlocale(LC_COLLATE_MASK, name, c_locale_t{}); + break; + case LC_MONETARY: + m_loc = newlocale(LC_MONETARY_MASK, name, c_locale_t{}); + break; + case LC_NUMERIC: + m_loc = newlocale(LC_NUMERIC_MASK, name, c_locale_t{}); + break; + case LC_TIME: + m_loc = newlocale(LC_TIME_MASK, name, c_locale_t{}); + break; + case LC_MESSAGES: + m_loc = newlocale(LC_MESSAGES_MASK, name, c_locale_t{}); + break; + default: + errno = EINVAL; + break; + } +#endif + if (!m_loc) + throw std::system_error(errno, std::generic_category()); +} + +scoped_c_locale::~scoped_c_locale() +{ + if (!m_loc) return; +#if !defined(_WIN32) + freelocale(m_loc); +#else + _free_locale(m_loc); +#endif +} + +#if !defined(_WIN32) +struct scoped_posix_uselocale { + explicit scoped_posix_uselocale(c_locale_t loc); + ~scoped_posix_uselocale(); + + c_locale_t m_loc{}; + c_locale_t m_old{}; + + scoped_posix_uselocale(const scoped_posix_uselocale &) = delete; + scoped_posix_uselocale &operator=(const scoped_posix_uselocale &) = delete; +}; + +scoped_posix_uselocale::scoped_posix_uselocale(c_locale_t loc) +{ + if (loc) + { + m_loc = loc; + m_old = uselocale(loc); + } +} + +scoped_posix_uselocale::~scoped_posix_uselocale() +{ + if (m_loc) + uselocale(m_old); +} +#endif + +} // namespace + +//------------------------------------------------------------------------------ + +c_locale_t c_numeric_locale() +{ + static scoped_c_locale loc(LC_NUMERIC, "C"); + return loc.m_loc; +} + +//------------------------------------------------------------------------------ + +double c_atof(const char *text, c_locale_t loc) +{ +#if defined(_WIN32) + return _atof_l(text, loc); +#else + scoped_posix_uselocale use(loc); + return atof(text); +#endif +} + +double c_strtod(const char *text, char **endp, c_locale_t loc) +{ +#if defined(_WIN32) + return _strtod_l(text, endp, loc); +#else + scoped_posix_uselocale use(loc); + return strtod(text, endp); +#endif +} + +double dot_atof(const char *text) +{ + return c_atof(text, c_numeric_locale()); +} + +double dot_strtod(const char *text, char **endp) +{ + return c_strtod(text, endp, c_numeric_locale()); +} + +bool ascii_isspace(char c) +{ + switch (c) { + case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': + return true; + default: + return false; + } +} + +bool ascii_isalpha(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +char ascii_tolower(char c) +{ + return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; +} + +char ascii_toupper(char c) +{ + return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c; +} + +int ascii_casecmp(const char *a, const char *b) +{ + for (char ca, cb; (ca = *a++) | (cb = *b++); ) { + ca = ascii_tolower(ca); + cb = ascii_tolower(cb); + if (ca < cb) return -1; + if (ca > cb) return +1; + } + return 0; +} + +uint32_t latin1_toupper(uint32_t c) +{ + if (c >= 'a' && c <= 'z') + return c - 'a' + 'A'; + if ((c >= 0xe0 && c <= 0xf6) || (c >= 0xf8 && c <= 0xfe)) + return c - 0x20; + return c; +} + +uint32_t latin1_tolower(uint32_t c) +{ + if (c >= 'A' && c <= 'Z') + return c - 'A' + 'a'; + if ((c >= 0xc0 && c <= 0xd6) || (c >= 0xd8 && c <= 0xde)) + return c + 0x20; + return c; +} + +char *strdup_using_new(const char *src) +{ + size_t len = strlen(src); + char *dst = new char[len + 1]; + memcpy(dst, src, len + 1); + return dst; +} + +string_list split_strings_noempty(const char *input, bool(*pred)(char)) +{ + string_list list; + + if (input) { + std::string acc; + acc.reserve(256); + + for (char c; (c = *input++) != '\0'; ) { + if (!pred(c)) + acc.push_back(c); + else { + if (!acc.empty()) { + list.push_back(acc); + acc.clear(); + } + } + } + + if (!acc.empty()) + list.push_back(acc); + } + + return list; +} + +std::string trim(const char *input, bool(*pred)(char)) +{ + const char *start = input; + while (*start != '\0' && pred(*start)) + ++start; + + const char *end = start + strlen(start); + while (end > start && pred(*(end - 1))) + --end; + + return std::string(start, end); +} + +//------------------------------------------------------------------------------ + +void pack_u32le(uint32_t value, uint8_t data[4]) +{ + data[0] = value & 0xff; + data[1] = (value >> 8) & 0xff; + data[2] = (value >> 16) & 0xff; + data[3] = value >> 24; +} + +void pack_f32le(float value, uint8_t data[4]) +{ + uint32_t u; + memcpy(&u, &value, 4); + pack_u32le(u, data); +} + +uint32_t unpack_u32le(const uint8_t data[4]) +{ + return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +} + +float unpack_f32le(const uint8_t data[4]) +{ + float value; + uint32_t u = unpack_u32le(data); + memcpy(&value, &u, 4); + return value; +} + +//------------------------------------------------------------------------------ + +std::vector decode_base64(const char *text, size_t len) +{ + return d_getChunkFromBase64String(text, len); +} + +//------------------------------------------------------------------------------ + +bool get_file_uid(const char *path, file_uid &uid) +{ +#ifdef _WIN32 + HANDLE handle = CreateFileW(widen(path).c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (handle == INVALID_HANDLE_VALUE) + return false; + bool success = get_handle_file_uid((void *)handle, uid); + CloseHandle(handle); + return success; +#else + int fd = open(path, O_RDONLY); + if (fd == -1) + return false; + bool success = get_descriptor_file_uid(fd, uid); + close(fd); + return success; +#endif +} + +bool get_stream_file_uid(FILE *stream, file_uid &uid) +{ +#if !defined(_WIN32) + int fd = fileno(stream); + if (fd == -1) + return false; +#else + int fd = _fileno(stream); + if (fd == -1) + return false; +#endif + return get_descriptor_file_uid(fd, uid); +} + +bool get_descriptor_file_uid(int fd, file_uid &uid) +{ +#if !defined(_WIN32) + struct stat st; + if (fstat(fd, &st) != 0) + return false; + uid.first = (uint64_t)st.st_dev; + uid.second = (uint64_t)st.st_ino; + return true; +#else + HANDLE handle = (HANDLE)_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) + return false; + return get_handle_file_uid((void *)handle, uid); +#endif +} + +#if defined(_WIN32) +bool get_handle_file_uid(void *handle, file_uid &uid) +{ + BY_HANDLE_FILE_INFORMATION info; + if (!GetFileInformationByHandle((HANDLE)handle, &info)) + return false; + uid.first = info.dwVolumeSerialNumber; + uid.second = (uint64_t)info.nFileIndexLow | ((uint64_t)info.nFileIndexHigh << 32); + return true; +} +#endif + +//------------------------------------------------------------------------------ + +bool is_path_separator(char ch) +{ +#if !defined(_WIN32) + return ch == '/'; +#else + return ch == '/' || ch == '\\'; +#endif +} + +split_path_t split_path(const char *path) +{ + split_path_t sp; +#if !defined(_WIN32) + size_t npos = ~(size_t)0; + size_t pos = npos; + for (size_t i = 0; path[i] != '\0'; ++i) { + if (is_path_separator(path[i])) + pos = i; + } + if (pos == npos) + sp.file.assign(path); + else { + sp.dir.assign(path, pos + 1); + sp.file.assign(path + pos + 1); + } +#else + std::wstring wpath = widen(path); + std::unique_ptr drive{new wchar_t[wpath.size() + 1]{}}; + std::unique_ptr dir{new wchar_t[wpath.size() + 1]{}}; + std::unique_ptr fname{new wchar_t[wpath.size() + 1]{}}; + std::unique_ptr ext{new wchar_t[wpath.size() + 1]{}}; + _wsplitpath(wpath.c_str(), drive.get(), dir.get(), fname.get(), ext.get()); + sp.drive = narrow(drive.get()); + if (!drive[0] || dir[0] == L'/' || dir[0] == L'\\') + sp.dir = narrow(dir.get()); + else + sp.dir = narrow(L'\\' + std::wstring(dir.get())); + sp.file = narrow(std::wstring(fname.get()) + std::wstring(ext.get())); +#endif + return sp; +} + +std::string path_file_name(const char *path) +{ + return split_path(path).file; +} + +std::string path_directory(const char *path) +{ + split_path_t sp = split_path(path); + return sp.dir.empty() ? std::string("./") : (sp.drive + sp.dir); +} + +std::string path_ensure_final_separator(const char *path) +{ + std::string result(path); + + if (!result.empty() && !is_path_separator(result.back())) + result.push_back('/'); + + return result; +} + +bool path_has_suffix(const char *path, const char *suffix) +{ + if (*suffix == '.') + ++suffix; + + size_t plen = strlen(path); + size_t slen = strlen(suffix); + if (plen < slen + 2) + return false; + + return path[plen - slen - 1] == '.' && + ascii_casecmp(suffix, &path[plen - slen]) == 0; +} + +bool path_is_relative(const char *path) +{ +#if !defined(_WIN32) + return !is_path_separator(path[0]); +#else + return !is_path_separator(split_path(path).dir.c_str()[0]); +#endif +} + +//------------------------------------------------------------------------------ + +#if !defined(_WIN32) +bool exists(const char *path) +{ + return access(path, F_OK) == 0; +} + +string_list list_directory(const char *path) +{ + string_list list; + + DIR *dir = opendir(path); + if (!dir) + return list; + auto dir_cleanup = defer([dir]() { closedir(dir); }); + + list.reserve(256); + + std::string pathbuf; + pathbuf.reserve(1024); + + while (dirent *ent = readdir(dir)) { + const char *name = ent->d_name; + if (!strcmp(name, ".") || !strcmp(name, "..")) + continue; + + pathbuf.assign(name); + if (ent->d_type == DT_DIR) + pathbuf.push_back('/'); + + list.push_back(pathbuf); + } + + std::sort(list.begin(), list.end()); + return list; +} + +// void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data); +// NOTE: implemented in separate file `ysfx_utils_fts.cpp` +#else +bool exists(const char *path) +{ + return _waccess(widen(path).c_str(), 0) == 0; +} + +string_list list_directory(const char *path) +{ + string_list list; + + std::wstring pattern = widen(path) + L"\\*"; + + WIN32_FIND_DATAW fd; + HANDLE handle = FindFirstFileW(pattern.c_str(), &fd); + if (handle == INVALID_HANDLE_VALUE) + return list; + auto handle_cleanup = defer([handle]() { FindClose(handle); }); + + list.reserve(256); + + do { + const wchar_t *name = fd.cFileName; + if (!wcscmp(name, L".") || !wcscmp(name, L"..")) + continue; + + std::string entry = narrow(name); + if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + entry.push_back('/'); + + list.push_back(std::move(entry)); + } while (FindNextFileW(handle, &fd)); + + std::sort(list.begin(), list.end()); + return list; +} + +void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data) +{ + std::deque dirs; + dirs.push_back(widen(path_ensure_final_separator(rootpath))); + + std::wstring pathbuf; + pathbuf.reserve(1024); + + std::vector entries; + entries.reserve(256); + + while (!dirs.empty()) { + std::wstring dir = std::move(dirs.front()); + dirs.pop_front(); + + if (!visit(narrow(dir), data)) + return; + + pathbuf.assign(dir); + pathbuf.append(L"\\*"); + + WIN32_FIND_DATAW fd; + HANDLE handle = FindFirstFileW(pathbuf.c_str(), &fd); + if (handle == INVALID_HANDLE_VALUE) + continue; + auto handle_cleanup = defer([handle]() { FindClose(handle); }); + + entries.clear(); + do { + if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + continue; + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + const wchar_t *name = fd.cFileName; + if (!wcscmp(name, L".") || !wcscmp(name, L"..")) + continue; + pathbuf.assign(dir); + pathbuf.append(name); + pathbuf.push_back(L'\\'); + entries.push_back(pathbuf); + } + } while (FindNextFileW(handle, &fd)); + + std::sort(entries.begin(), entries.end()); + for (size_t n = entries.size(); n-- > 0; ) + dirs.push_front(std::move(entries[n])); + } +} +#endif + +int case_resolve(const char *root_, const char *fragment, std::string &result) +{ + if (fragment[0] == '\0') + return 0; + + std::string root = path_ensure_final_separator(root_); + + std::string pathbuf; + pathbuf.reserve(1024); + + pathbuf.assign(root); + pathbuf.append(fragment); + if (exists(pathbuf.c_str())) { + result = std::move(pathbuf); + return 1; + } + + struct Item { + std::string root; + string_list components; + }; + + std::deque worklist; + + { + Item item; + item.root = root; + item.components = split_strings_noempty(fragment, &is_path_separator); + if (item.components.empty()) + return 0; + for (size_t i = 0; i + 1 < item.components.size(); ++i) + item.components[i].push_back('/'); + if (is_path_separator(fragment[strlen(fragment) - 1])) + item.components.back().push_back('/'); + worklist.push_back(std::move(item)); + } + + while (!worklist.empty()) { + Item item = std::move(worklist.front()); + worklist.pop_front(); + + for (const std::string &entry : list_directory(item.root.c_str())) { + if (ascii_casecmp(entry.c_str(), item.components[0].c_str()) != 0) + continue; + + if (item.components.size() == 1) { + pathbuf.assign(item.root); + pathbuf.append(entry); + if (exists(pathbuf.c_str())) { + result = std::move(pathbuf); + return 2; + } + } + else { + assert(item.components.size() > 1); + Item newitem; + newitem.root = item.root + entry; + newitem.components.assign(item.components.begin() + 1, item.components.end()); + worklist.push_front(std::move(newitem)); + } + } + } + + return 0; +} + +//------------------------------------------------------------------------------ + +#if defined(_WIN32) +std::wstring widen(const std::string &u8str) +{ + return widen(u8str.data(), u8str.size()); +} + +std::wstring widen(const char *u8data, size_t u8len) +{ + if (u8len == ~(size_t)0) + u8len = strlen(u8data); + std::wstring wstr; + int wch = MultiByteToWideChar(CP_UTF8, 0, u8data, (int)u8len, nullptr, 0); + if (wch != 0) { + wstr.resize((size_t)wch); + MultiByteToWideChar(CP_UTF8, 0, u8data, (int)u8len, &wstr[0], wch); + } + return wstr; +} + +std::string narrow(const std::wstring &wstr) +{ + return narrow(wstr.data(), wstr.size()); +} + +std::string narrow(const wchar_t *wdata, size_t wlen) +{ + if (wlen == ~(size_t)0) + wlen = wcslen(wdata); + std::string u8str; + int u8ch = WideCharToMultiByte(CP_UTF8, 0, wdata, (int)wlen, nullptr, 0, nullptr, nullptr); + if (u8ch != 0) { + u8str.resize((size_t)u8ch); + WideCharToMultiByte(CP_UTF8, 0, wdata, (int)wlen, &u8str[0], u8ch, nullptr, nullptr); + } + return u8str; +} +#endif + +} // namespace ysfx + +//------------------------------------------------------------------------------ +// WDL helpers + +// our replacement `atof` for WDL, which is unaffected by current locale +extern "C" double ysfx_wdl_atof(const char *text) +{ + return ysfx::dot_atof(text); +} diff --git a/source/modules/ysfx/sources/ysfx_utils.hpp b/source/modules/ysfx/sources/ysfx_utils.hpp new file mode 100644 index 000000000..3940ad024 --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_utils.hpp @@ -0,0 +1,185 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "ysfx.h" +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +# include +#endif +#if !defined(_WIN32) +# include +#else +# include +#endif + +#if defined(YSFX_NO_STANDARD_MUTEX) +# include "WDL/mutex.h" +#endif + +namespace ysfx { + +YSFX_DEFINE_AUTO_PTR(FILE_u, FILE, fclose); + +FILE *fopen_utf8(const char *path, const char *mode); +int64_t fseek_lfs(FILE *stream, int64_t off, int whence); +int64_t ftell_lfs(FILE *stream); + +//------------------------------------------------------------------------------ + +#if !defined(_WIN32) +using c_locale_t = locale_t; +#else +using c_locale_t = _locale_t; +#endif + +c_locale_t c_numeric_locale(); + +//------------------------------------------------------------------------------ + +#if !defined(_WIN32) +# define ysfx_alloca(n) alloca((n)) +#else +# define ysfx_alloca(n) _malloca((n)) +#endif + +//------------------------------------------------------------------------------ + +#if !defined(YSFX_NO_STANDARD_MUTEX) +using mutex = std::mutex; +#else +class mutex +{ +public: + void lock() { m_mutex.Enter(); } + void unlock() { m_mutex.Leave(); } + +private: + WDL_Mutex m_mutex; +}; +#endif + +//------------------------------------------------------------------------------ + +using string_list = std::vector; + +double c_atof(const char *text, c_locale_t loc); +double c_strtod(const char *text, char **endp, c_locale_t loc); +double dot_atof(const char *text); +double dot_strtod(const char *text, char **endp); +bool ascii_isspace(char c); +bool ascii_isalpha(char c); +char ascii_tolower(char c); +char ascii_toupper(char c); +int ascii_casecmp(const char *a, const char *b); +uint32_t latin1_toupper(uint32_t c); +uint32_t latin1_tolower(uint32_t c); +char *strdup_using_new(const char *src); +string_list split_strings_noempty(const char *input, bool(*pred)(char)); +std::string trim(const char *input, bool(*pred)(char)); + +//------------------------------------------------------------------------------ + +void pack_u32le(uint32_t value, uint8_t data[4]); +void pack_f32le(float value, uint8_t data[4]); +uint32_t unpack_u32le(const uint8_t data[4]); +float unpack_f32le(const uint8_t data[4]); + +//------------------------------------------------------------------------------ + +std::vector decode_base64(const char *text, size_t len = ~(size_t)0); + +//------------------------------------------------------------------------------ + +using file_uid = std::pair; +bool get_file_uid(const char *path, file_uid &uid); +bool get_stream_file_uid(FILE *stream, file_uid &uid); +bool get_descriptor_file_uid(int fd, file_uid &uid); +#if defined(_WIN32) +bool get_handle_file_uid(void *handle, file_uid &uid); +#endif + +//------------------------------------------------------------------------------ + +struct split_path_t { + std::string drive; + std::string dir; + std::string file; +}; + +// check if the character is a path separator +bool is_path_separator(char ch); +// break down a path into individual components +split_path_t split_path(const char *path); +// get the file name part (all after the final '/' separator) +std::string path_file_name(const char *path); +// get the directory part (all up to the '/' separator, inclusive) +std::string path_directory(const char *path); +// add the final '/' separator if absent; if empty, does nothing +std::string path_ensure_final_separator(const char *path); +// compare the tail of the path with the suffix, case-insensitively +bool path_has_suffix(const char *path, const char *suffix); +// check whether the path is relative +bool path_is_relative(const char *path); + +//------------------------------------------------------------------------------ + +// check whether a file exists on disk +bool exists(const char *path); +// list the elements of a directory; directories are distinguished with a final '/' +string_list list_directory(const char *path); +// visit the root and subdirectories in depth-first order +void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data); +// resolve a path which matches root/fragment, where fragment is case-insensitive (0=failed, 1=exact, 2=inexact) +int case_resolve(const char *root, const char *fragment, std::string &result); + +//------------------------------------------------------------------------------ + +#if defined(_WIN32) +std::wstring widen(const std::string &u8str); +std::wstring widen(const char *u8data, size_t u8len = ~(size_t)0); +std::string narrow(const std::wstring &wstr); +std::string narrow(const wchar_t *wdata, size_t wlen = ~(size_t)0); +#endif + +//------------------------------------------------------------------------------ + +template +class scope_guard { +public: + explicit scope_guard(F &&f) : f(std::forward(f)), a(true) {} + scope_guard(scope_guard &&o) : f(std::move(o.f)), a(o.a) { o.a = false; } + ~scope_guard() { if (a) f(); } + void disarm() { a = false; } +private: + F f; + bool a; + scope_guard(const scope_guard &) = delete; + scope_guard &operator=(const scope_guard &) = delete; +}; + +template scope_guard defer(F &&f) +{ + return scope_guard(std::forward(f)); +} + +} // namespace ysfx diff --git a/source/modules/ysfx/sources/ysfx_utils_fts.cpp b/source/modules/ysfx/sources/ysfx_utils_fts.cpp new file mode 100644 index 000000000..c607866ce --- /dev/null +++ b/source/modules/ysfx/sources/ysfx_utils_fts.cpp @@ -0,0 +1,60 @@ +// Copyright 2021 Jean Pierre Cimalando +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +// + +#if !defined(_WIN32) + +// fts lacks large file support in glibc < 2.23 +#if defined(YSFX_FTS_LACKS_LFS_SUPPORT) +# undef _FILE_OFFSET_BITS +#endif + +#include "ysfx_utils.hpp" +#include +#include +#include + +namespace ysfx { + +void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data) +{ + char *argv[] = {(char *)rootpath, nullptr}; + + auto compar = [](const FTSENT **a, const FTSENT **b) -> int { + return strcmp((*a)->fts_name, (*b)->fts_name); + }; + + FTS *fts = fts_open(argv, FTS_NOCHDIR|FTS_PHYSICAL, +compar); + if (!fts) + return; + auto fts_cleanup = defer([fts]() { fts_close(fts); }); + + std::string pathbuf; + pathbuf.reserve(1024); + + while (FTSENT *ent = fts_read(fts)) { + if (ent->fts_info == FTS_D) { + pathbuf.assign(ent->fts_path); + pathbuf.push_back('/'); + if (!visit(pathbuf, data)) + return; + } + } +} + +} // namespace ysfx + +#endif // !defined(_WIN32) diff --git a/source/modules/ysfx/thirdparty/WDL/LICENSE.txt b/source/modules/ysfx/thirdparty/WDL/LICENSE.txt new file mode 100644 index 000000000..f97eec725 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (C) 2005 and later Cockos Incorporated + +Portions copyright other contributors, see each source file for more information + +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. diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/assocarray.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/assocarray.h new file mode 100644 index 000000000..f9a9d70e2 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/assocarray.h @@ -0,0 +1,482 @@ +#ifndef _WDL_ASSOCARRAY_H_ +#define _WDL_ASSOCARRAY_H_ + +#include "heapbuf.h" +#include "mergesort.h" + +// on all of these, if valdispose is set, the array will dispose of values as needed. +// if keydup/keydispose are set, copies of (any) key data will be made/destroyed as necessary + + +// WDL_AssocArrayImpl can be used on its own, and can contain structs for keys or values +template class WDL_AssocArrayImpl +{ + WDL_AssocArrayImpl(const WDL_AssocArrayImpl &cp) { CopyContents(cp); } + + WDL_AssocArrayImpl &operator=(const WDL_AssocArrayImpl &cp) { CopyContents(cp); return *this; } + +public: + + explicit WDL_AssocArrayImpl(int (*keycmp)(KEY *k1, KEY *k2), KEY (*keydup)(KEY)=0, void (*keydispose)(KEY)=0, void (*valdispose)(VAL)=0) + { + m_keycmp = keycmp; + m_keydup = keydup; + m_keydispose = keydispose; + m_valdispose = valdispose; + } + + ~WDL_AssocArrayImpl() + { + DeleteAll(); + } + + VAL* GetPtr(KEY key, KEY *keyPtrOut=NULL) const + { + bool ismatch = false; + int i = LowerBound(key, &ismatch); + if (ismatch) + { + KeyVal* kv = m_data.Get()+i; + if (keyPtrOut) *keyPtrOut = kv->key; + return &(kv->val); + } + return 0; + } + + bool Exists(KEY key) const + { + bool ismatch = false; + LowerBound(key, &ismatch); + return ismatch; + } + + int Insert(KEY key, VAL val) + { + bool ismatch = false; + int i = LowerBound(key, &ismatch); + if (ismatch) + { + KeyVal* kv = m_data.Get()+i; + if (m_valdispose) m_valdispose(kv->val); + kv->val = val; + } + else + { + KeyVal* kv = m_data.Resize(m_data.GetSize()+1)+i; + memmove(kv+1, kv, (m_data.GetSize()-i-1)*(unsigned int)sizeof(KeyVal)); + if (m_keydup) key = m_keydup(key); + kv->key = key; + kv->val = val; + } + return i; + } + + void Delete(KEY key) + { + bool ismatch = false; + int i = LowerBound(key, &ismatch); + if (ismatch) + { + KeyVal* kv = m_data.Get()+i; + if (m_keydispose) m_keydispose(kv->key); + if (m_valdispose) m_valdispose(kv->val); + m_data.Delete(i); + } + } + + void DeleteByIndex(int idx) + { + if (idx >= 0 && idx < m_data.GetSize()) + { + KeyVal* kv = m_data.Get()+idx; + if (m_keydispose) m_keydispose(kv->key); + if (m_valdispose) m_valdispose(kv->val); + m_data.Delete(idx); + } + } + + void DeleteAll(bool resizedown=false) + { + if (m_keydispose || m_valdispose) + { + int i; + for (i = 0; i < m_data.GetSize(); ++i) + { + KeyVal* kv = m_data.Get()+i; + if (m_keydispose) m_keydispose(kv->key); + if (m_valdispose) m_valdispose(kv->val); + } + } + m_data.Resize(0, resizedown); + } + + int GetSize() const + { + return m_data.GetSize(); + } + + VAL* EnumeratePtr(int i, KEY* key=0) const + { + if (i >= 0 && i < m_data.GetSize()) + { + KeyVal* kv = m_data.Get()+i; + if (key) *key = kv->key; + return &(kv->val); + } + return 0; + } + + KEY* ReverseLookupPtr(VAL val) const + { + int i; + for (i = 0; i < m_data.GetSize(); ++i) + { + KeyVal* kv = m_data.Get()+i; + if (kv->val == val) return &kv->key; + } + return 0; + } + + void ChangeKey(KEY oldkey, KEY newkey) + { + bool ismatch=false; + int i=LowerBound(oldkey, &ismatch); + if (ismatch) ChangeKeyByIndex(i, newkey, true); + } + + void ChangeKeyByIndex(int idx, KEY newkey, bool needsort) + { + if (idx >= 0 && idx < m_data.GetSize()) + { + KeyVal* kv=m_data.Get()+idx; + if (!needsort) + { + if (m_keydispose) m_keydispose(kv->key); + if (m_keydup) newkey=m_keydup(newkey); + kv->key=newkey; + } + else + { + VAL val=kv->val; + m_data.Delete(idx); + Insert(newkey, val); + } + } + } + + // fast add-block mode + void AddUnsorted(KEY key, VAL val) + { + int i=m_data.GetSize(); + KeyVal* kv = m_data.Resize(i+1)+i; + if (m_keydup) key = m_keydup(key); + kv->key = key; + kv->val = val; + } + + void Resort(int (*new_keycmp)(KEY *k1, KEY *k2)=NULL) + { + if (new_keycmp) m_keycmp = new_keycmp; + if (m_data.GetSize() > 1 && m_keycmp) + { + qsort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal), + (int(*)(const void*, const void*))m_keycmp); + if (!new_keycmp) + RemoveDuplicateKeys(); + } + } + + void ResortStable() + { + if (m_data.GetSize() > 1 && m_keycmp) + { + char *tmp=(char*)malloc(m_data.GetSize()*sizeof(KeyVal)); + if (WDL_NORMALLY(tmp)) + { + WDL_mergesort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal), + (int(*)(const void*, const void*))m_keycmp, tmp); + free(tmp); + } + else + { + qsort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal), + (int(*)(const void*, const void*))m_keycmp); + } + + RemoveDuplicateKeys(); + } + } + + int LowerBound(KEY key, bool* ismatch) const + { + int a = 0; + int c = m_data.GetSize(); + while (a != c) + { + int b = (a+c)/2; + KeyVal* kv=m_data.Get()+b; + int cmp = m_keycmp(&key, &kv->key); + if (cmp > 0) a = b+1; + else if (cmp < 0) c = b; + else + { + *ismatch = true; + return b; + } + } + *ismatch = false; + return a; + } + + int GetIdx(KEY key) const + { + bool ismatch=false; + int i = LowerBound(key, &ismatch); + if (ismatch) return i; + return -1; + } + + void SetGranul(int gran) + { + m_data.SetGranul(gran); + } + + void CopyContents(const WDL_AssocArrayImpl &cp) + { + m_data=cp.m_data; + m_keycmp = cp.m_keycmp; + m_keydup = cp.m_keydup; + m_keydispose = m_keydup ? cp.m_keydispose : NULL; + m_valdispose = NULL; // avoid disposing of values twice, since we don't have a valdup, we can't have a fully valid copy + if (m_keydup) + { + int x; + const int n=m_data.GetSize(); + for (x=0;xkey) kv->key = m_keydup(kv->key); + } + } + } + + void CopyContentsAsReference(const WDL_AssocArrayImpl &cp) + { + DeleteAll(true); + m_keycmp = cp.m_keycmp; + m_keydup = NULL; // this no longer can own any data + m_keydispose = NULL; + m_valdispose = NULL; + + m_data=cp.m_data; + } + + +// private data, but exposed in case the caller wants to manipulate at its own risk + struct KeyVal + { + KEY key; + VAL val; + }; + WDL_TypedBuf m_data; + +protected: + + int (*m_keycmp)(KEY *k1, KEY *k2); + KEY (*m_keydup)(KEY); + void (*m_keydispose)(KEY); + void (*m_valdispose)(VAL); + +private: + + void RemoveDuplicateKeys() // after resorting + { + const int sz = m_data.GetSize(); + + int cnt = 1; + KeyVal *rd = m_data.Get() + 1, *wr = rd; + for (int x = 1; x < sz; x ++) + { + if (m_keycmp(&rd->key, &wr[-1].key)) + { + if (rd != wr) *wr=*rd; + wr++; + cnt++; + } + else + { + if (m_keydispose) m_keydispose(rd->key); + if (m_valdispose) m_valdispose(rd->val); + } + rd++; + } + if (cnt < sz) m_data.Resize(cnt,false); + } +}; + + +// WDL_AssocArray adds useful functions but cannot contain structs for keys or values +template class WDL_AssocArray : public WDL_AssocArrayImpl +{ +public: + + explicit WDL_AssocArray(int (*keycmp)(KEY *k1, KEY *k2), KEY (*keydup)(KEY)=0, void (*keydispose)(KEY)=0, void (*valdispose)(VAL)=0) + : WDL_AssocArrayImpl(keycmp, keydup, keydispose, valdispose) + { + } + + VAL Get(KEY key, VAL notfound=0) const + { + VAL* p = this->GetPtr(key); + if (p) return *p; + return notfound; + } + + VAL Enumerate(int i, KEY* key=0, VAL notfound=0) const + { + VAL* p = this->EnumeratePtr(i, key); + if (p) return *p; + return notfound; + } + + KEY ReverseLookup(VAL val, KEY notfound=0) const + { + KEY* p=this->ReverseLookupPtr(val); + if (p) return *p; + return notfound; + } +}; + + +template class WDL_IntKeyedArray : public WDL_AssocArray +{ +public: + + explicit WDL_IntKeyedArray(void (*valdispose)(VAL)=0) : WDL_AssocArray(cmpint, NULL, NULL, valdispose) {} + ~WDL_IntKeyedArray() {} + +private: + + static int cmpint(int *i1, int *i2) { return *i1-*i2; } +}; + +template class WDL_IntKeyedArray2 : public WDL_AssocArrayImpl +{ +public: + + explicit WDL_IntKeyedArray2(void (*valdispose)(VAL)=0) : WDL_AssocArrayImpl(cmpint, NULL, NULL, valdispose) {} + ~WDL_IntKeyedArray2() {} + +private: + + static int cmpint(int *i1, int *i2) { return *i1-*i2; } +}; + +template class WDL_StringKeyedArray : public WDL_AssocArray +{ +public: + + explicit WDL_StringKeyedArray(bool caseSensitive=true, void (*valdispose)(VAL)=0) : WDL_AssocArray(caseSensitive?cmpstr:cmpistr, dupstr, freestr, valdispose) {} + + ~WDL_StringKeyedArray() { } + + static const char *dupstr(const char *s) { return strdup(s); } // these might not be necessary but depending on the libc maybe... + static int cmpstr(const char **s1, const char **s2) { return strcmp(*s1, *s2); } + static int cmpistr(const char **a, const char **b) { return stricmp(*a,*b); } + static void freestr(const char* s) { free((void*)s); } + static void freecharptr(char *p) { free(p); } +}; + + +template class WDL_StringKeyedArray2 : public WDL_AssocArrayImpl +{ +public: + + explicit WDL_StringKeyedArray2(bool caseSensitive=true, void (*valdispose)(VAL)=0) : WDL_AssocArrayImpl(caseSensitive?cmpstr:cmpistr, dupstr, freestr, valdispose) {} + + ~WDL_StringKeyedArray2() { } + + static const char *dupstr(const char *s) { return strdup(s); } // these might not be necessary but depending on the libc maybe... + static int cmpstr(const char **s1, const char **s2) { return strcmp(*s1, *s2); } + static int cmpistr(const char **a, const char **b) { return stricmp(*a,*b); } + static void freestr(const char* s) { free((void*)s); } + static void freecharptr(char *p) { free(p); } +}; + +// sorts text as text, sorts anything that looks like a number as a number +template class WDL_LogicalSortStringKeyedArray : public WDL_StringKeyedArray +{ +public: + + explicit WDL_LogicalSortStringKeyedArray(bool caseSensitive=true, void (*valdispose)(VAL)=0) : WDL_StringKeyedArray(caseSensitive, valdispose) + { + WDL_StringKeyedArray::m_keycmp = caseSensitive?cmpstr:cmpistr; // override + } + + ~WDL_LogicalSortStringKeyedArray() { } + + static int cmpstr(const char **a, const char **b) { return _cmpstr(*a, *b, true); } + static int cmpistr(const char **a, const char **b) { return _cmpstr(*a, *b, false); } + +private: + + static int _cmpstr(const char *s1, const char *s2, bool case_sensitive) + { + // this also exists as WDL_strcmp_logical in wdlcstring.h + + for (;;) + { + if (*s1 >= '0' && *s1 <= '9' && *s2 >= '0' && *s2 <= '9') + { + int lzdiff=0, len1=0, len2=0; + + while (*s1 == '0') { s1++; lzdiff--; } + while (*s2 == '0') { s2++; lzdiff++; } + + while (s1[len1] >= '0' && s1[len1] <= '9') len1++; + while (s2[len2] >= '0' && s2[len2] <= '9') len2++; + + if (len1 != len2) return len1-len2; + + while (len1--) + { + const int d = *s1++ - *s2++; + if (d) return d; + } + + if (lzdiff) return lzdiff; + } + else + { + char c1 = *s1++, c2 = *s2++; + if (c1 != c2) + { + if (case_sensitive) return c1-c2; + + if (c1>='a' && c1<='z') c1+='A'-'a'; + if (c2>='a' && c2<='z') c2+='A'-'a'; + if (c1 != c2) return c1-c2; + } + else if (!c1) return 0; + } + } + } +}; + + +template class WDL_PtrKeyedArray : public WDL_AssocArray +{ +public: + + explicit WDL_PtrKeyedArray(void (*valdispose)(VAL)=0) : WDL_AssocArray(cmpptr, 0, 0, valdispose) {} + + ~WDL_PtrKeyedArray() {} + +private: + + static int cmpptr(INT_PTR* a, INT_PTR* b) { const INT_PTR d = *a - *b; return d<0?-1:(d!=0); } +}; + + +#endif + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/denormal.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/denormal.h new file mode 100644 index 000000000..681d4d801 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/denormal.h @@ -0,0 +1,248 @@ +#ifndef _WDL_DENORMAL_H_ +#define _WDL_DENORMAL_H_ + +#include +#include "wdltypes.h" +// note: the _aggressive versions filter out anything less than around 1.0e-16 or so (approximately) to 0.0, including -0.0 (becomes 0.0) +// note: new! the _aggressive versions also filter inf and NaN to 0.0 + +#ifdef __cplusplus +#define WDL_DENORMAL_INLINE inline +#elif defined(_MSC_VER) +#define WDL_DENORMAL_INLINE __inline +#else + #ifdef WDL_STATICFUNC_UNUSED + #define WDL_DENORMAL_INLINE WDL_STATICFUNC_UNUSED + #else + #define WDL_DENORMAL_INLINE + #endif +#endif + +static WDL_DENORMAL_INLINE unsigned int WDL_DENORMAL_FLOAT_W(const float *a) { unsigned int v; memcpy(&v,a,sizeof(v)); return v; } +static WDL_DENORMAL_INLINE unsigned int WDL_DENORMAL_DOUBLE_HW(const double *a) { WDL_UINT64 v; memcpy(&v,(char*)a,sizeof(v)); return (unsigned int) (v>>32); } + +#define WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF 0x3cA00000 // 0x3B8000000 maybe instead? that's 10^-5 smaller or so +#define WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF 0x25000000 + + +// define WDL_DENORMAL_WANTS_SCOPED_FTZ, and then use a WDL_denormal_ftz_scope in addition to denormal_*(), then +// if FTZ is available it will be used instead... +// +#ifdef WDL_DENORMAL_WANTS_SCOPED_FTZ + +#if defined(__SSE2__) || _M_IX86_FP >= 2 || defined(_WIN64) + #define WDL_DENORMAL_FTZMODE + #define WDL_DENORMAL_FTZSTATE_TYPE unsigned int + #ifdef _MSC_VER + #include + #else + #include + #endif + #define wdl_denorm_mm_getcsr() _mm_getcsr() + #define wdl_denorm_mm_setcsr(x) _mm_setcsr(x) + #if defined(__SSE3__) + #define wdl_denorm_mm_csr_mask ((1<<15)|(1<<11) | (1<<8) | (1<<6)) // FTZ, underflow, denormal mask, DAZ + #else + #define wdl_denorm_mm_csr_mask ((1<<15)|(1<<11)) // FTZ and underflow only (target SSE2) + #endif +#elif defined(__arm__) || defined(__aarch64__) + #define WDL_DENORMAL_FTZMODE + #define WDL_DENORMAL_FTZSTATE_TYPE unsigned long + static unsigned long __attribute__((unused)) wdl_denorm_mm_getcsr() + { + unsigned long rv; +#ifdef __aarch64__ + asm volatile ( "mrs %0, fpcr" : "=r" (rv)); +#else + asm volatile ( "fmrx %0, fpscr" : "=r" (rv)); +#endif + return rv; + } + static void __attribute__((unused)) wdl_denorm_mm_setcsr(unsigned long v) + { +#ifdef __aarch64__ + asm volatile ( "msr fpcr, %0" :: "r"(v)); +#else + asm volatile ( "fmxr fpscr, %0" :: "r"(v)); +#endif + } + #define wdl_denorm_mm_csr_mask (1<<24) +#endif + +class WDL_denormal_ftz_scope +{ + public: + WDL_denormal_ftz_scope() + { +#ifdef WDL_DENORMAL_FTZMODE + const WDL_DENORMAL_FTZSTATE_TYPE b = wdl_denorm_mm_csr_mask; + old_state = wdl_denorm_mm_getcsr(); + if ((need_restore = (old_state & b) != b)) + wdl_denorm_mm_setcsr(old_state|b); +#endif + } + ~WDL_denormal_ftz_scope() + { +#ifdef WDL_DENORMAL_FTZMODE + if (need_restore) wdl_denorm_mm_setcsr(old_state); +#endif + } + +#ifdef WDL_DENORMAL_FTZMODE + WDL_DENORMAL_FTZSTATE_TYPE old_state; + bool need_restore; +#endif + +}; + + +#endif + + +#if !defined(WDL_DENORMAL_FTZMODE) && !defined(WDL_DENORMAL_DO_NOT_FILTER) + +static double WDL_DENORMAL_INLINE denormal_filter_double(double a) +{ + return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0; +} + +static double WDL_DENORMAL_INLINE denormal_filter_double2(double a) +{ + return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) > 0x100000 ? a : 0.0; +} + +static double WDL_DENORMAL_INLINE denormal_filter_double_aggressive(double a) +{ + return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0; +} + +static float WDL_DENORMAL_INLINE denormal_filter_float(float a) +{ + return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f; +} + +static float WDL_DENORMAL_INLINE denormal_filter_float2(float a) +{ + return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) > 0x800000 ? a : 0.0f; +} + + +static float WDL_DENORMAL_INLINE denormal_filter_float_aggressive(float a) +{ + return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f; +} +static void WDL_DENORMAL_INLINE denormal_fix_double(double *a) +{ + if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0; +} + +static void WDL_DENORMAL_INLINE denormal_fix_double_aggressive(double *a) +{ + if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0; +} + +static void WDL_DENORMAL_INLINE denormal_fix_float(float *a) +{ + if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f; +} +static void WDL_DENORMAL_INLINE denormal_fix_float_aggressive(float *a) +{ + if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f; +} + + + +#ifdef __cplusplus // automatic typed versions (though one should probably use the explicit versions... + + +static double WDL_DENORMAL_INLINE denormal_filter(double a) +{ + return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0; +} +static double WDL_DENORMAL_INLINE denormal_filter_aggressive(double a) +{ + return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0; +} + +static float WDL_DENORMAL_INLINE denormal_filter(float a) +{ + return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f; +} + +static float WDL_DENORMAL_INLINE denormal_filter_aggressive(float a) +{ + return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f; +} + +static void WDL_DENORMAL_INLINE denormal_fix(double *a) +{ + if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0; +} +static void WDL_DENORMAL_INLINE denormal_fix_aggressive(double *a) +{ + if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0; +} +static void WDL_DENORMAL_INLINE denormal_fix(float *a) +{ + if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f; +} +static void WDL_DENORMAL_INLINE denormal_fix_aggressive(float *a) +{ + if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f; +} + + + +#endif // cplusplus versions + +#else // end of !WDL_DENORMAL_DO_NOT_FILTER (and other platform-specific checks) + +#define denormal_filter(x) (x) +#define denormal_filter2(x) (x) +#define denormal_filter_double(x) (x) +#define denormal_filter_double2(x) (x) +#define denormal_filter_double_aggressive(x) (x) +#define denormal_filter_float(x) (x) +#define denormal_filter_float2(x) (x) +#define denormal_filter_float_aggressive(x) (x) +#define denormal_filter_aggressive(x) (x) +#define denormal_fix(x) do { } while(0) +#define denormal_fix_aggressive(x) do { } while(0) +#define denormal_fix_double(x) do { } while(0) +#define denormal_fix_double_aggressive(x) do { } while(0) +#define denormal_fix_float(x) do { } while(0) +#define denormal_fix_float_aggressive(x) do { } while(0) + +#endif + + +//////////////////// +// this isnt a denormal function but it is similar, so we'll put it here as a bonus + +static void WDL_DENORMAL_INLINE GetDoubleMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0 +{ + WDL_UINT64 i, o; + memcpy(&i,in,sizeof(i)); + memcpy(&o,out,sizeof(o)); + i &= WDL_UINT64_CONST(0x7fffffffffffffff); + if (i > o) memcpy(out,&i,sizeof(i)); +} + +static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(float *out, const float *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0 +{ + unsigned int i, o; + memcpy(&i, in, sizeof(i)); + memcpy(&o, out, sizeof(o)); + i &= 0x7fffffff; + if (i > o) memcpy(out, &i, sizeof(i)); +} + + +#ifdef __cplusplus +static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0 +{ + GetDoubleMaxAbsValue(out,in); +} +#endif + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/dirscan.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/dirscan.h new file mode 100644 index 000000000..46d065bd1 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/dirscan.h @@ -0,0 +1,329 @@ +/* + WDL - dirscan.h + Copyright (C) 2005 and later Cockos Incorporated + + 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. + +*/ + +/* + + This file provides the interface and implementation for WDL_DirScan, a simple + (and somewhat portable) directory reading class. On non-Win32 systems it wraps + opendir()/readdir()/etc. On Win32, it uses FindFirst*, and supports wildcards as + well. + + +*/ + + +#ifndef _WDL_DIRSCAN_H_ +#define _WDL_DIRSCAN_H_ + +#include "wdlstring.h" + +#ifndef _WIN32 +#include +#include +#include +extern struct stat wdl_stat_chk; +// if this fails on linux, use CFLAGS += -D_FILE_OFFSET_BITS=64 +typedef char wdl_dirscan_assert_failed_stat_not_64[sizeof(wdl_stat_chk.st_size)!=8 ? -1 : 1]; +#endif + +class WDL_DirScan +{ + public: + WDL_DirScan() : +#ifdef _WIN32 + m_h(INVALID_HANDLE_VALUE) + #ifndef WDL_NO_SUPPORT_UTF8 + , m_wcmode(false) + #endif +#else + m_h(NULL), m_ent(NULL) +#endif + { + } + + ~WDL_DirScan() + { + Close(); + } + + int First(const char *dirname +#ifdef _WIN32 + , int isExactSpec=0 +#endif + ) // returns 0 if success + { + WDL_FastString scanstr(dirname); + const int l = scanstr.GetLength(); + if (l < 1) return -1; + +#ifdef _WIN32 + if (!isExactSpec) + { + if (dirname[l-1] == '\\' || dirname[l-1] == '/') scanstr.SetLen(l-1); + m_leading_path = scanstr; + scanstr.Append("\\*"); + } + else + { + m_leading_path = scanstr; + + // remove trailing wildcards and directory separator from m_leading_path + const char *sp = m_leading_path.Get(); + int idx = m_leading_path.GetLength() - 1; + while (idx > 0 && sp[idx] != '/' && sp[idx] != '\\') idx--; + if (idx > 0) m_leading_path.SetLen(idx); + } +#else + if (dirname[l-1] == '\\' || dirname[l-1] == '/') scanstr.SetLen(l-1); + m_leading_path = scanstr; + if (!scanstr.GetLength()) scanstr.Set("/"); // fix for scanning / +#endif + + Close(); +#ifdef _WIN32 + #ifndef WDL_NO_SUPPORT_UTF8 + m_h=INVALID_HANDLE_VALUE; + #ifdef WDL_SUPPORT_WIN9X + m_wcmode = GetVersion()< 0x80000000; + #else + m_wcmode = true; + #endif + + if (m_wcmode) + { + int reqbuf = MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,NULL,0); + if (reqbuf > 1000) + { + WDL_TypedBuf tmp; + tmp.Resize(reqbuf+20); + if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,tmp.Get(),tmp.GetSize()-10)) + { + correctlongpath(tmp.Get()); + m_h=FindFirstFileW(tmp.Get(),&m_fd); + } + } + else + { + WCHAR wfilename[1024]; + if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,wfilename,1024-10)) + { + correctlongpath(wfilename); + m_h=FindFirstFileW(wfilename,&m_fd); + } + } + } + + if (m_h==INVALID_HANDLE_VALUE) m_wcmode=false; + + if (m_h==INVALID_HANDLE_VALUE) + #endif + m_h=FindFirstFile(scanstr.Get(),(WIN32_FIND_DATA*)&m_fd); + return (m_h == INVALID_HANDLE_VALUE); +#else + m_ent=0; + m_h=opendir(scanstr.Get()); + return !m_h || Next(); +#endif + } + int Next() // returns 0 on success + { +#ifdef _WIN32 + if (m_h == INVALID_HANDLE_VALUE) return -1; + #ifndef WDL_NO_SUPPORT_UTF8 + if (m_wcmode) return !FindNextFileW(m_h,&m_fd); + #endif + return !FindNextFile(m_h,(WIN32_FIND_DATA*)&m_fd); +#else + if (!m_h) return -1; + return !(m_ent=readdir(m_h)); +#endif + } + void Close() + { +#ifdef _WIN32 + if (m_h != INVALID_HANDLE_VALUE) FindClose(m_h); + m_h=INVALID_HANDLE_VALUE; +#else + if (m_h) closedir(m_h); + m_h=0; m_ent=0; +#endif + } + +#ifdef _WIN32 + const char *GetCurrentFN() + { +#ifndef WDL_NO_SUPPORT_UTF8 + if (m_wcmode) + { + if (!WideCharToMultiByte(CP_UTF8,0,m_fd.cFileName,-1,m_tmpbuf,sizeof(m_tmpbuf),NULL,NULL)) + m_tmpbuf[0]=0; + return m_tmpbuf; + } +#endif + return ((WIN32_FIND_DATA *)&m_fd)->cFileName; + } +#else + const char *GetCurrentFN() const { return m_ent?m_ent->d_name : ""; } +#endif + template void GetCurrentFullFN(T *str) + { + str->Set(m_leading_path.Get()); +#ifdef _WIN32 + str->Append("\\"); +#else + str->Append("/"); +#endif + str->Append(GetCurrentFN()); + } + int GetCurrentIsDirectory() const // returns 1 if dir, 2 if symlink to dir, 4 if possibly-recursive symlink to dir + { +#ifdef _WIN32 + return !!(m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); +#else + char tmp[2048]; + if (m_ent) switch (m_ent->d_type) + { + case DT_DIR: return 1; + case DT_LNK: + { + snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),m_ent->d_name); + char *rp = realpath(tmp,NULL); + if (!rp) return 0; + + struct stat sb; + int ret = (!stat(rp,&sb) && (sb.st_mode & S_IFMT) == S_IFDIR) ? 2 : 0; + if (ret) + { + // treat symlinks of /path/to/foo -> /path from being resolved (avoiding obvious feedback loops) + const int rpl = (int) strlen(rp); + if ( +#ifdef __APPLE__ + !strnicmp(rp,m_leading_path.Get(),rpl) +#else + !strncmp(rp,m_leading_path.Get(),rpl) +#endif + && (m_leading_path.Get()[rpl] == '/' || m_leading_path.Get()[rpl] == 0) + ) ret = 4; + } + free(rp); + return ret; + } + case DT_UNKNOWN: + { + snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),m_ent->d_name); + DIR *d = opendir(tmp); + if (d) { closedir(d); return 1; } + return 0; + } + } + return 0; +#endif + } + + // these are somewhat windows specific calls, eh +#ifdef _WIN32 + DWORD GetCurrentFileSize(DWORD *HighWord=NULL) const { if (HighWord) *HighWord = m_fd.nFileSizeHigh; return m_fd.nFileSizeLow; } + void GetCurrentLastWriteTime(FILETIME *ft) const { *ft = m_fd.ftLastWriteTime; } + void GetCurrentLastAccessTime(FILETIME *ft) const { *ft = m_fd.ftLastAccessTime; } + void GetCurrentCreationTime(FILETIME *ft) const { *ft = m_fd.ftCreationTime; } + DWORD GetFileAttributes() const { return m_fd.dwFileAttributes; } +#elif defined(_WDL_SWELL_H_) + + void GetCurrentCreationTime(FILETIME *ft) + { + char tmp[2048]; + snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN()); + struct stat st={0,}; + stat(tmp,&st); + unsigned long long a=(unsigned long long)st.st_ctime; // seconds since january 1st, 1970 + a+=11644473600ull; // 1601->1970 + a*=10000000; // seconds to 1/10th microseconds (100 nanoseconds) + ft->dwLowDateTime=a & 0xffffffff; + ft->dwHighDateTime=a>>32; + } + + void GetCurrentLastWriteTime(FILETIME *ft) + { + char tmp[2048]; + snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN()); + struct stat st={0,}; + stat(tmp,&st); + unsigned long long a=(unsigned long long)st.st_mtime; // seconds since january 1st, 1970 + a+=11644473600ull; // 1601->1970 + a*=10000000; // seconds to 1/10th microseconds (100 nanoseconds) + ft->dwLowDateTime=a & 0xffffffff; + ft->dwHighDateTime=a>>32; + } + DWORD GetCurrentFileSize(DWORD *HighWord=NULL) + { + char tmp[2048]; + snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN()); + struct stat st={0,}; + stat(tmp,&st); + + if (HighWord) *HighWord = (DWORD)(st.st_size>>32); + return (DWORD)(st.st_size&0xffffffff); + } + +#endif + + private: +#ifdef _WIN32 + +#ifndef WDL_NO_SUPPORT_UTF8 + bool m_wcmode; + WIN32_FIND_DATAW m_fd; + char m_tmpbuf[MAX_PATH*5]; // even if each byte gets encoded as 4 utf-8 bytes this should be plenty ;) +#else + WIN32_FIND_DATA m_fd; +#endif + HANDLE m_h; +#else + DIR *m_h; + struct dirent *m_ent; +#endif + WDL_FastString m_leading_path; + +#ifdef _WIN32 + static void correctlongpath(WCHAR *buf) // this also exists as wdl_utf8_correctlongpath + { + const WCHAR *insert; + WCHAR *wr; + int skip = 0; + if (!buf || !buf[0] || wcslen(buf) < 256) return; + if (buf[1] == ':') insert=L"\\\\?\\"; + else if (buf[0] == '\\' && buf[1] == '\\') { insert = L"\\\\?\\UNC\\"; skip=2; } + else return; + + wr = buf + wcslen(insert); + memmove(wr, buf + skip, (wcslen(buf+skip)+1)*2); + memmove(buf,insert,wcslen(insert)*2); + while (*wr) + { + if (*wr == '/') *wr = '\\'; + wr++; + } + } +#endif +} WDL_FIXALIGN; + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-aarch64-gcc.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-aarch64-gcc.c new file mode 100644 index 000000000..a6e1d1cef --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-aarch64-gcc.c @@ -0,0 +1,1270 @@ +#define FUNCTION_MARKER "mov x0, x0\n" \ + "mov x1, x1\n" \ + "mov x2, x2\n" + +#if EEL_F_SIZE == 8 + +void nseel_asm_1pdd(void) +{ + + __asm__ __volatile__( + FUNCTION_MARKER + "mov x3, 0xdead\n" + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "str x30, [sp, #-16]!\n" + "blr x3\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + :: ); +} + +void nseel_asm_2pdd(void) +{ + + __asm__ __volatile__( + FUNCTION_MARKER + "mov x3, 0xdead\n" + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "fmov d2, d0\n" + "fmov d0, d1\n" + "fmov d1, d2\n" + "str x30, [sp, #-16]!\n" + "blr x3\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + :: ); +}; + +void nseel_asm_2pdds(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x3, 0xdead\n" + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "stp x1, x30, [sp, #-16]!\n" + "fmov d1, d0\n" + "ldr d0, [x1]\n" + "blr x3\n" + "ldp x0, x30, [sp], 16\n" + "str d0, [x0]\n" + FUNCTION_MARKER + :: ); +} + +#else // 32 bit floating point calls + +#error no 32 bit float support + +#endif + +//--------------------------------------------------------------------------------------------------------------- + + +void nseel_asm_invsqrt(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x0, 0x59df\n" + "movk x0, 0x5f37, lsl 16\n" + "fcvt s2, d0\n" + "ldr d3, [x21, #32]\n" + "fmov w1, s2\n" + "fmul d0, d0, d3\n" + "asr w1, w1, #1\n" + + "sub w0, w0, w1\n" + + "fmov s4, w0\n" + "fcvt d1, s4\n" + + "ldr d2, [x21, #40]\n" + "fmul d0, d0, d1\n" + "fmul d0, d0, d1\n" + "fadd d0, d0, d2\n" + "fmul d0, d0, d1\n" + + FUNCTION_MARKER + ); +} + +void nseel_asm_dbg_getstackptr(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x0, sp\n" + "ucvtf d0, x0\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_sqr(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fmul d0, d0, d0\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_abs(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fabs d0, d0\n" + FUNCTION_MARKER + ); +} + + +#define FLUSH_TO_ZERO \ + "ldr x1, [x1]\n" \ + "mov x3, #0x10000000000000\n" \ + "mov x2, #0x20000000000000\n" \ + "add x1, x1, x3\n" \ + "and x1, x1, #0x7ff0000000000000\n" \ + "cmp x1, x2\n" \ + "bgt 0f\n" \ + "str xzr, [x0]\n" \ + "0:\n" + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_assign(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d0, [x0]\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} +// +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_assign_fromfp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x0, x1\n" + "str d0, [x1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_assign_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d0, [x0]\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FUNCTION_MARKER + ); +} +// +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_assign_fast_fromfp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x0, x1\n" + "str d0, [x1]\n" + FUNCTION_MARKER + ); +} + + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_add(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fadd d0, d1, d0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_add_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fadd d0, d1, d0\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +void nseel_asm_add_op_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fadd d0, d1, d0\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_sub(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fsub d0, d1, d0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_sub_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fsub d0, d1, d0\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +void nseel_asm_sub_op_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fsub d0, d1, d0\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_mul(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fmul d0, d1, d0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_mul_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fmul d0, d0, d1\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +void nseel_asm_mul_op_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fmul d0, d0, d1\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_div(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fdiv d0, d1, d0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_div_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fdiv d0, d1, d0\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +void nseel_asm_div_op_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fdiv d0, d1, d0\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_mod(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fabs d0, d0\n" + "fabs d1, d1\n" + "fcvtzu w1, d0\n" + "fcvtzu w0, d1\n" + "udiv w2, w0, w1\n" + "msub w0, w2, w1, w0\n" + "ucvtf d0, w0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_shl(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcvtzs w0, d1\n" + "fcvtzs w1, d0\n" + "lsl w0, w0, w1\n" + "scvtf d0, w0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_shr(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcvtzs w0, d1\n" + "fcvtzs w1, d0\n" + "asr w0, w0, w1\n" + "scvtf d0, w0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_mod_op(void) +{ + + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fabs d0, d0\n" + "fabs d1, d1\n" + "fcvtzu w3, d0\n" + "fcvtzu w0, d1\n" + "udiv w2, w0, w3\n" + "msub w0, w2, w3, w0\n" + "ucvtf d0, w0\n" + + "str d0, [x1]\n" + "mov x0, x1\n" + FUNCTION_MARKER + ); + +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_or(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcvtzs x0, d0\n" + "fcvtzs x1, d1\n" + "orr x0, x0, x1\n" + "scvtf d0, x0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_or0(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcvtzs x0, d0\n" + "scvtf d0, x0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_or_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fcvtzs x0, d0\n" + "fcvtzs x3, d1\n" + "orr x0, x0, x3\n" + "scvtf d0, x0\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_xor(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcvtzs x0, d0\n" + "fcvtzs x1, d1\n" + "eor x0, x0, x1\n" + "scvtf d0, x0\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_xor_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fcvtzs x0, d0\n" + "fcvtzs x3, d1\n" + "eor x0, x0, x3\n" + "scvtf d0, x0\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_and(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcvtzs x0, d0\n" + "fcvtzs x1, d1\n" + "and x0, x0, x1\n" + "scvtf d0, x0\n" + FUNCTION_MARKER + );} + +void nseel_asm_and_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x1]\n" + "fcvtzs x0, d0\n" + "fcvtzs x3, d1\n" + "and x0, x0, x3\n" + "scvtf d0, x0\n" + "mov x0, x1\n" + "str d0, [x1]\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_uminus(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fneg d0, d0\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_sign(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fmov d1, #-1.0\n" + "fmov d2, #1.0\n" + "fcmpe d0, #0.0\n" + "fcsel d0, d0, d1, gt\n" + "fcsel d0, d0, d2, lt\n" + FUNCTION_MARKER + :: + ); +} + + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_bnot(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "cmp w0, #0\n" + "cset w0, eq\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_if(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str x30, [sp, #-16]!\n" + "mov x1, 0xdead\n" + "movk x1, 0xbeef, lsl 16\n" + "movk x1, 0xbeef, lsl 32\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "cmp w0, #0\n" + "csel x1, x1, x2, ne\n" + "blr x1\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + :: ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_repeat(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcvtzs w3, d0\n" + "cmp w3, #0\n" + "ble 0f\n" +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + "mov x2, %0\n" + "movk x2, %1, lsl 16\n" + "cmp w3, w2\n" + "csel w3, w2, w3, gt\n" +#endif + "stp x24, x30, [sp, #-16]!\n" + + "mov x24, 0xdead\n" + "movk x24, 0xbeef, lsl 16\n" + "movk x24, 0xbeef, lsl 32\n" + "1:\n" + "stp x3, x22, [sp, #-16]!\n" + "blr x24\n" + "ldp x3, x22, [sp], 16\n" + "sub x3, x3, #1\n" + "cmp x3, #0\n" + "bgt 1b\n" + "ldp x24, x30, [sp], 16\n" + "0:\n" + FUNCTION_MARKER +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + ::"g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN&65535), + "g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN>>16) +#endif + ); +} + +void nseel_asm_repeatwhile(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + "mov x3, %0\n" + "movk x3, %1, lsl 16\n" +#endif + "stp x24, x30, [sp, #-16]!\n" + + "mov x24, 0xdead\n" + "movk x24, 0xbeef, lsl 16\n" + "movk x24, 0xbeef, lsl 32\n" + "0:\n" + "stp x3, x22, [sp, #-16]!\n" + "blr x24\n" + "ldp x3, x22, [sp], 16\n" + "cmp w0, #0\n" +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + "beq 0f\n" + "sub x3, x3, #1\n" + "cmp x3, #0\n" + "bne 0b\n" + "0:\n" +#else + "bne 0b\n" +#endif + "ldp x24, x30, [sp], 16\n" + + FUNCTION_MARKER +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + ::"g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN&65535), + "g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN>>16) +#endif + ); +} + + +void nseel_asm_band(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "cmp w0, #0\n" + "beq 0f\n" + "mov x3, 0xdead\n" + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "str x30, [sp, #-16]!\n" + "blr x3\n" + "ldr x30, [sp], #16\n" + "0:\n" + FUNCTION_MARKER + :: ); +} + +void nseel_asm_bor(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "cmp w0, #0\n" + "bne 0f\n" + "mov x3, 0xdead\n" + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "str x30, [sp, #-16]!\n" + "blr x3\n" + "ldr x30, [sp], #16\n" + "0:\n" + FUNCTION_MARKER + :: ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_equal(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d2, [x21]\n" + "fsub d0, d0, d1\n" + "fabs d0, d0\n" + "fcmp d0, d2\n" + "cset w0, lt\n" + FUNCTION_MARKER + :: + ); +} +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_equal_exact(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmp d1, d0\n" + "cset w0, eq\n" + FUNCTION_MARKER + :: + ); +} +// +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_notequal_exact(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmp d1, d0\n" + "cset w0, ne\n" + FUNCTION_MARKER + :: + ); +} +// +// +// +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_notequal(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d2, [x21]\n" + "fsub d0, d0, d1\n" + "fabs d0, d0\n" + "fcmp d0, d2\n" + "cset w0, ge\n" + FUNCTION_MARKER + :: + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_below(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmp d1, d0\n" + "cset w0, lt\n" + FUNCTION_MARKER + :: + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_beloweq(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmp d1, d0\n" + "cset w0, le\n" + FUNCTION_MARKER + :: + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_above(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmp d1, d0\n" + "cset w0, gt\n" + FUNCTION_MARKER + :: + ); +} + +void nseel_asm_aboveeq(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmp d1, d0\n" + "cset w0, ge\n" + FUNCTION_MARKER + :: + ); +} + + + +void nseel_asm_min(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d0, [x0]\n" + "ldr d1, [x1]\n" + "fcmp d1, d0\n" + "csel x0, x1, x0, lt\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_max(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d0, [x0]\n" + "ldr d1, [x1]\n" + "fcmp d1, d0\n" + "csel x0, x1, x0, gt\n" + FUNCTION_MARKER + ); +} + + + +void nseel_asm_min_fp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmp d1, d0\n" + "fcsel d0, d1, d0, lt\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_max_fp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmp d1, d0\n" + "fcsel d0, d1, d0, gt\n" + FUNCTION_MARKER + ); +} + + + + + + + +void _asm_generic3parm(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str x30, [sp, #-16]!\n" // input: r0 last, r1=second to last, r2=third to last + // output: r0=context, r1=r2, r2=r1, r3=r0 + "mov x3, x0\n" // r0 (last parameter) -> r3 + + "mov x0, 0xdead\n" // r0 is first parm (context) + "movk x0, 0xbeef, lsl 16\n" + "movk x0, 0xbeef, lsl 32\n" + + "mov x4, 0xdead\n" + "movk x4, 0xbeef, lsl 16\n" + "movk x4, 0xbeef, lsl 32\n" + + "mov x5, x1\n" // swap x1/x2 + "mov x1, x2\n" + "mov x2, x5\n" + + "blr x4\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + :: + ); +} + +void _asm_generic3parm_retd(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str x30, [sp, #-16]!\n" // input: r0 last, r1=second to last, r2=third to last + "mov x3, x0\n" // r0 (last parameter) -> r3 + + "mov x0, 0xdead\n" // r0 is first parm (context) + "movk x0, 0xbeef, lsl 16\n" + "movk x0, 0xbeef, lsl 32\n" + + "mov x4, 0xdead\n" + "movk x4, 0xbeef, lsl 16\n" + "movk x4, 0xbeef, lsl 32\n" + + "mov x5, x1\n" // swap x1/x2 + "mov x1, x2\n" + "mov x2, x5\n" + + "blr x4\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + :: + ); +} + + +void _asm_generic2parm(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str x30, [sp, #-16]!\n" // input: r0 last, r1=second to last + "mov x2, x0\n" + + "mov x0, 0xdead\n" // r0 is first parm (context) + "movk x0, 0xbeef, lsl 16\n" + "movk x0, 0xbeef, lsl 32\n" + + "mov x4, 0xdead\n" + "movk x4, 0xbeef, lsl 16\n" + "movk x4, 0xbeef, lsl 32\n" + + "blr x4\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + :: + ); +} + + +void _asm_generic2parm_retd(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str x30, [sp, #-16]!\n" // input: r0 last, r1=second to last + "mov x2, x0\n" + + "mov x0, 0xdead\n" // r0 is first parm (context) + "movk x0, 0xbeef, lsl 16\n" + "movk x0, 0xbeef, lsl 32\n" + + "mov x4, 0xdead\n" + "movk x4, 0xbeef, lsl 16\n" + "movk x4, 0xbeef, lsl 32\n" + + "blr x4\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + :: + ); +} + +void _asm_generic2xparm_retd(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str x30, [sp, #-16]!\n" // input: r0 last, r1=second to last + "mov x2, x1\n" + "mov x3, x0\n" + + "mov x0, 0xdead\n" // r0 is first parm (context) + "movk x0, 0xbeef, lsl 16\n" + "movk x0, 0xbeef, lsl 32\n" + + "mov x1, 0xdead\n" // second parm + "movk x1, 0xbeef, lsl 16\n" + "movk x1, 0xbeef, lsl 32\n" + + "mov x4, 0xdead\n" + "movk x4, 0xbeef, lsl 16\n" + "movk x4, 0xbeef, lsl 32\n" + + "blr x4\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + :: + ); +} + + +void _asm_generic1parm(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str x30, [sp, #-16]!\n" + "mov x1, x0\n" + + "mov x0, 0xdead\n" // r0 is first parm (context) + "movk x0, 0xbeef, lsl 16\n" + "movk x0, 0xbeef, lsl 32\n" + + "mov x4, 0xdead\n" + "movk x4, 0xbeef, lsl 16\n" + "movk x4, 0xbeef, lsl 32\n" + + "blr x4\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + :: + ); +} + + + +void _asm_generic1parm_retd(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str x30, [sp, #-16]!\n" + "mov x1, x0\n" + + "mov x0, 0xdead\n" // r0 is first parm (context) + "movk x0, 0xbeef, lsl 16\n" + "movk x0, 0xbeef, lsl 32\n" + + "mov x4, 0xdead\n" + "movk x4, 0xbeef, lsl 16\n" + "movk x4, 0xbeef, lsl 32\n" + + "blr x4\n" + "ldr x30, [sp], #16\n" + + FUNCTION_MARKER + :: + ); +} + + + + +void _asm_megabuf(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x20, #-8]\n" + "fadd d0, d0, d1\n" + "fcvtzu w3, d0\n" + "asr w2, w3, %0\n" + "bic w2, w2, #7\n" // r2 is page index*8 + "cmp w2, %1\n" + "bhs 0f\n" + + "add x2, x2, x20\n" + "ldr x2, [x2]\n" + "cmp x2, #0\n" + "beq 0f\n" + + "mov x0, %2\n" + "and x3, x3, x0\n" // r3 mask item in slot + "add x0, x2, x3, lsl #3\n" // set result + "b 1f\n" + "0:\n" + + // failed, call stub function + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "str x30, [sp, #-16]!\n" + "mov x0, x20\n" // first parameter: blocks + "mov x1, x3\n" // second parameter: slot index + "blr x2\n" + "ldr x30, [sp], #16\n" + "1:\n" + + FUNCTION_MARKER + :: + "i" (NSEEL_RAM_ITEMSPERBLOCK_LOG2 - 3/*log2(sizeof(void*))*/), + "i" (NSEEL_RAM_BLOCKS*sizeof(void*)), + "i" (NSEEL_RAM_ITEMSPERBLOCK-1) + ); +} + + +void _asm_gmegabuf(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x0, 0xdead\n" + "movk x0, 0xbeef, lsl 16\n" + "movk x0, 0xbeef, lsl 32\n" + + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + + "ldr d1, [x20, #-8]\n" + "fadd d0, d0, d1\n" + + "fcvtzu w1, d0\n" + + "str x30, [sp, #-16]!\n" + + "blr x2\n" + + "ldr x30, [sp], #16\n" + + FUNCTION_MARKER + :: + ); +} + + +void nseel_asm_fcall(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x0, 0xdead\n" + "movk x0, 0xbeef, lsl 16\n" + "movk x0, 0xbeef, lsl 32\n" + "str x30, [sp, #-16]!\n" + "blr x0\n" + "ldr x30, [sp], #16\n" + FUNCTION_MARKER + ); +} + + + +void nseel_asm_stack_push(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d0, [x0]\n" + + "mov x3, 0xdead\n" // r3 is stack + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "ldr x0, [x3]\n" + + "add x0, x0, #8\n" + + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "and x0, x0, x2\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "orr x0, x0, x2\n" + + "str x0, [x3]\n" + "str d0, [x0]\n" + + FUNCTION_MARKER + ); +} + +void nseel_asm_stack_pop(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x3, 0xdead\n" // r3 is stack + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "ldr x1, [x3]\n" + "ldr d0, [x1]\n" + "sub x1, x1, #8\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "str d0, [x0]\n" + "and x1, x1, x2\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "orr x1, x1, x2\n" + + "str x1, [x3]\n" + FUNCTION_MARKER + ); +} + + + +void nseel_asm_stack_pop_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x3, 0xdead\n" // r3 is stack + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "ldr x1, [x3]\n" + "mov x0, x1\n" + "sub x1, x1, #8\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "and x1, x1, x2\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "orr x1, x1, x2\n" + "str x1, [x3]\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_stack_peek(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x3, 0xdead\n" // r3 is stack + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + + "fcvtzs w2, d0\n" + + "ldr x1, [x3]\n" + "sub x1, x1, x2, lsl #3\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "and x1, x1, x2\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "orr x0, x1, x2\n" + FUNCTION_MARKER + ); +} + + +void nseel_asm_stack_peek_top(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x3, 0xdead\n" // r3 is stack + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "ldr x0, [x3]\n" + FUNCTION_MARKER + ); +} + + +void nseel_asm_stack_peek_int(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x3, 0xdead\n" // r3 is stack + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + + "ldr x1, [x3]\n" + "sub x1, x1, x2\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "and x1, x1, x2\n" + "mov x2, 0xdead\n" + "movk x2, 0xbeef, lsl 16\n" + "movk x2, 0xbeef, lsl 32\n" + "orr x0, x1, x2\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_stack_exch(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov x3, 0xdead\n" // r3 is stack + "movk x3, 0xbeef, lsl 16\n" + "movk x3, 0xbeef, lsl 32\n" + "ldr x1, [x3]\n" + "ldr d0, [x0]\n" + "ldr d1, [x1]\n" + "str d0, [x1]\n" + "str d1, [x0]\n" + FUNCTION_MARKER + ); +} + + +void nseel_asm_booltofp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "cmp w0, #0\n" + "fmov d0, #1.0\n" + "movi d1, #0\n" + "fcsel d0, d0, d1, ne\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_fptobool(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x21]\n" + "fabs d0, d0\n" + "fcmp d0, d1\n" + "cset w0, ge\n" + FUNCTION_MARKER + :: + ); +} + +void nseel_asm_fptobool_rev(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ldr d1, [x21]\n" + "fabs d0, d0\n" + "fcmp d0, d1\n" + "cset w0, lt\n" + FUNCTION_MARKER + :: + ); +} + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-arm-gcc.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-arm-gcc.c new file mode 100644 index 000000000..8893796a0 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-arm-gcc.c @@ -0,0 +1,1308 @@ +#define FUNCTION_MARKER "mov r0, r0\n" \ + "mov r1, r1\n" \ + "mov r2, r2\n" + +#if EEL_F_SIZE == 8 + +__attribute__((naked)) void nseel_asm_1pdd(void) +{ + + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, 0xdead\n" + "movt r3, 0xbeef\n" + "str lr, [sp, #-8]!\n" + "blx r3\n" + "ldr lr, [sp], #8\n" + FUNCTION_MARKER + :: ); +} + +__attribute__((naked)) void nseel_asm_2pdd(void) +{ + + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, 0xdead\n" + "movt r3, 0xbeef\n" + "fcpyd d2, d0\n" + "fcpyd d0, d1\n" + "fcpyd d1, d2\n" + "str lr, [sp, #-8]!\n" + "blx r3\n" + "ldr lr, [sp], #8\n" + FUNCTION_MARKER + :: ); +}; + +__attribute__((naked)) void nseel_asm_2pdds(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, 0xdead\n" + "movt r3, 0xbeef\n" + "push {r1, lr}\n" + "fcpyd d1, d0\n" + "fldd d0, [r1]\n" + "blx r3\n" + "pop {r0, lr}\n" + "fstd d0, [r0]\n" + FUNCTION_MARKER + :: ); +} + +#else // 32 bit floating point calls + +#error no 32 bit float support + +#endif + +//--------------------------------------------------------------------------------------------------------------- + + +__attribute__((naked)) void nseel_asm_invsqrt(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r0, 0x59df\n" + "movt r0, 0x5f37\n" + "fcvtsd s2, d0\n" + "fldd d3, [r6, #32]\n" + "fmrs r1, s2\n" + "fmuld d0, d0, d3\n" + "mov r1, r1, asr #1\n" + + "sub r0, r0, r1\n" + + "fmsr s4, r0\n" + "fcvtds d1, s4\n" + + "fldd d2, [r6, #40]\n" + "fmuld d0, d0, d1\n" + "fmuld d0, d0, d1\n" + "faddd d0, d0, d2\n" + "fmuld d0, d0, d1\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_dbg_getstackptr(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fmsr s0, sp\n" + "fsitod d0, s0\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_sqr(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fmuld d0, d0, d0\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_abs(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fabsd d0, d0\n" + FUNCTION_MARKER + ); +} + +#define FLUSH_TO_ZERO \ + "ldr r1, [r0, #4]\n" \ + "movs r2, #0\n" \ + "movt r2, #0x7ff0\n" \ + "add r1, r1, #0x00100000\n" \ + "ands r1, r1, r2\n" \ + "cmp r1, #0x00200000\n" \ + "bgt 0f\n" \ + "movs r2, #0\n" \ + "movs r3, #0\n" \ + "strd r2, [r0]\n" \ + "0:\n" + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_assign(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d0, [r0]\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} +// +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_assign_fromfp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov r0, r1\n" + "fstd d0, [r1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_assign_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d0, [r0]\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FUNCTION_MARKER + ); +} +// +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_assign_fast_fromfp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "mov r0, r1\n" + "fstd d0, [r1]\n" + FUNCTION_MARKER + ); +} + + + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_add(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "faddd d0, d1, d0\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_add_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "faddd d0, d1, d0\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_add_op_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "faddd d0, d1, d0\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_sub(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fsubd d0, d1, d0\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_sub_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "fsubd d0, d1, d0\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_sub_op_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "fsubd d0, d1, d0\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_mul(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fmuld d0, d0, d1\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_mul_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "fmuld d0, d0, d1\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_mul_op_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "fmuld d0, d0, d1\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_div(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fdivd d0, d1, d0\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_div_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "fdivd d0, d1, d0\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_div_op_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "fdivd d0, d1, d0\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_mod(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ftouizd s0, d0\n" // round to unsigned integers + "fmrs r3, s0\n" + "fuitod d0, s0\n" // divisor + "ftouizd s2, d1\n" + + "cmp r3, #0\n" + "beq 0f\n" + + "fuitod d1, s2\n" // value + "fdivd d2, d1, d0\n" + "ftouizd s4, d2\n" + "fuitod d2, s4\n" + "fmuld d2, d2, d0\n" + "fsubd d0, d1, d2\n" + "0:\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_shl(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ftosizd s0, d0\n" + "ftosizd s1, d1\n" + "fmrs r3, s0\n" + "fmrs r2, s1\n" + "mov r3, r2, asl r3\n" + "fmsr s0, r3\n" + "fsitod d0, s0\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_shr(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ftosizd s0, d0\n" + "ftosizd s1, d1\n" + "fmrs r3, s0\n" + "fmrs r2, s1\n" + "mov r3, r2, asr r3\n" + "fmsr s0, r3\n" + "fsitod d0, s0\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_mod_op(void) +{ + + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "ftouizd s0, d0\n" // round to unsigned integers + "fmrs r3, s0\n" + "fuitod d0, s0\n" // divisor + "ftouizd s2, d1\n" + + "cmp r3, #0\n" + "beq 0f\n" + + "fuitod d1, s2\n" // value + "fdivd d2, d1, d0\n" + "ftouizd s4, d2\n" + "fuitod d2, s4\n" + "fmuld d2, d2, d0\n" + "fsubd d0, d1, d2\n" + "0:\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FLUSH_TO_ZERO + FUNCTION_MARKER + ); + +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_or(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ftosizd s0, d0\n" + "ftosizd s1, d1\n" + "fmrs r3, s0\n" + "fmrs r2, s1\n" + "orr r3, r3, r2\n" + "fmsr s0, r3\n" + "fsitod d0, s0\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_or0(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ftosizd s0, d0\n" + "fsitod d0, s0\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_or_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "ftosizd s0, d0\n" + "ftosizd s1, d1\n" + "fmrs r3, s0\n" + "fmrs r2, s1\n" + "orr r3, r3, r2\n" + "fmsr s0, r3\n" + "fsitod d0, s0\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_xor(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ftosizd s0, d0\n" + "ftosizd s1, d1\n" + "fmrs r3, s0\n" + "fmrs r2, s1\n" + "eor r3, r3, r2\n" + "fmsr s0, r3\n" + "fsitod d0, s0\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_xor_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "ftosizd s0, d0\n" + "ftosizd s1, d1\n" + "fmrs r3, s0\n" + "fmrs r2, s1\n" + "eor r3, r3, r2\n" + "fmsr s0, r3\n" + "fsitod d0, s0\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_and(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "ftosizd s0, d0\n" + "ftosizd s1, d1\n" + "fmrs r3, s0\n" + "fmrs r2, s1\n" + "and r3, r3, r2\n" + "fmsr s0, r3\n" + "fsitod d0, s0\n" + FUNCTION_MARKER + );} + +__attribute__((naked)) void nseel_asm_and_op(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r1]\n" + "ftosizd s0, d0\n" + "ftosizd s1, d1\n" + "fmrs r3, s0\n" + "fmrs r2, s1\n" + "and r3, r3, r2\n" + "fmsr s0, r3\n" + "fsitod d0, s0\n" + "mov r0, r1\n" + "fstd d0, [r1]\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_uminus(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fnegd d0, d0\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_sign(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmpzd d0\n" + "fmstat\n" + "flddgt d0, [r6, #16]\n" + "flddlt d0, [r6, #24]\n" + FUNCTION_MARKER + :: + ); +} + + + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_bnot(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "cmp r0, #0\n" + "movne r0, #0\n" + "moveq r0, #1\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_if(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str lr, [sp, #-8]!\n" + "movw r1, 0xdead\n" + "movt r1, 0xbeef\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "cmp r0, #0\n" + "moveq r1, r2\n" + "blx r1\n" + "ldr lr, [sp], #8\n" + FUNCTION_MARKER + :: ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_repeat(void) +{ +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + __asm__ __volatile__( + FUNCTION_MARKER + "ftosizd s0, d0\n" + "fmrs r3, s0\n" + "cmp r3, #0\n" + "ble 0f\n" + "movw r2, %0\n" + "movt r2, %1\n" + "cmp r3, r2\n" + "movgt r3, r2\n" + "push {r10,lr}\n" + + "movw r10, 0xdead\n" + "movt r10, 0xbeef\n" + "1:\n" + "push {r3,r5}\n" // save counter + worktable + "blx r10\n" + "pop {r3,r5}\n" + "sub r3, r3, #1\n" + "cmp r3, #0\n" + "bgt 1b\n" + "pop {r10,lr}\n" + + "0:\n" + FUNCTION_MARKER + ::"g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN&65535), + "g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN>>16) + ); +#else + __asm__ __volatile__( + FUNCTION_MARKER + "ftosizd s0, d0\n" + "fmrs r3, s0\n" + "cmp r3, #0\n" + "ble 0f\n" + "push {r10,lr}\n" + + "movw r10, 0xdead\n" + "movt r10, 0xbeef\n" + "1:\n" + "push {r3,r5}\n" // save counter + worktable + "blx r10\n" + "pop {r3,r5}\n" + "sub r3, r3, #1\n" + "cmp r3, #0\n" + "bgt 1b\n" + "pop {r10,lr}\n" + + "0:\n" + FUNCTION_MARKER + ); +#endif +} + +__attribute__((naked)) void nseel_asm_repeatwhile(void) +{ +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, %0\n" + "movt r3, %1\n" + "push {r10,lr}\n" + + "movw r10, 0xdead\n" + "movt r10, 0xbeef\n" + "0:\n" + "push {r3,r5}\n" // save counter + worktable + "blx r10\n" + "pop {r3,r5}\n" + "sub r3, r3, #1\n" + "cmp r0, #0\n" + "cmpne r3, #0\n" + "bne 0b\n" + "pop {r10,lr}\n" + + FUNCTION_MARKER + ::"g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN&65535), + "g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN>>16) + ); +#else + __asm__ __volatile__( + FUNCTION_MARKER + "push {r10,lr}\n" + + "movw r10, 0xdead\n" + "movt r10, 0xbeef\n" + "0:\n" + "push {r3,r5}\n" // save worktable (r3 just for alignment) + "blx r10\n" + "pop {r3,r5}\n" + "cmp r0, #0\n" + "bne 0b\n" + "pop {r10,lr}\n" + + "0:\n" + FUNCTION_MARKER + ); +#endif +} + + +__attribute__((naked)) void nseel_asm_band(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "cmp r0, #0\n" + "beq 0f\n" + "movw r3, 0xdead\n" + "movt r3, 0xbeef\n" + "push {r3, lr}\n" + "blx r3\n" + "pop {r3, lr}\n" + "0:\n" + FUNCTION_MARKER + :: ); +} + +__attribute__((naked)) void nseel_asm_bor(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "cmp r0, #0\n" + "bne 0f\n" + "movw r3, 0xdead\n" + "movt r3, 0xbeef\n" + "push {r3, lr}\n" + "blx r3\n" + "pop {r3, lr}\n" + "0:\n" + FUNCTION_MARKER + :: ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_equal(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d2, [r6]\n" + "fsubd d0, d0, d1\n" + "fabsd d0, d0\n" + "fcmpd d2, d0\n" + "fmstat\n" + "movlt r0, #0\n" + "movge r0, #1\n" + FUNCTION_MARKER + :: + ); +} +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_equal_exact(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmpd d0, d1\n" + "fmstat\n" + "movne r0, #0\n" + "moveq r0, #1\n" + FUNCTION_MARKER + :: + ); +} +// +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_notequal_exact(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmpd d0, d1\n" + "fmstat\n" + "moveq r0, #0\n" + "movne r0, #1\n" + FUNCTION_MARKER + :: + ); +} +// +// +// +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_notequal(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d2, [r6]\n" + "fsubd d0, d0, d1\n" + "fabsd d0, d0\n" + "fcmpd d2, d0\n" + "fmstat\n" + "movlt r0, #1\n" + "movge r0, #0\n" + FUNCTION_MARKER + :: + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_below(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmpd d1, d0\n" + "fmstat\n" + "movlt r0, #1\n" + "movge r0, #0\n" + FUNCTION_MARKER + :: + ); +} + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_beloweq(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmpd d1, d0\n" + "fmstat\n" + "movle r0, #1\n" + "movgt r0, #0\n" + FUNCTION_MARKER + :: + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +__attribute__((naked)) void nseel_asm_above(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmpd d1, d0\n" + "fmstat\n" + "movgt r0, #1\n" + "movle r0, #0\n" + FUNCTION_MARKER + :: + ); +} + +__attribute__((naked)) void nseel_asm_aboveeq(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmpd d1, d0\n" + "fmstat\n" + "movge r0, #1\n" + "movlt r0, #0\n" + FUNCTION_MARKER + :: + ); +} + + + +__attribute__((naked)) void nseel_asm_min(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d0, [r0]\n" + "fldd d1, [r1]\n" + "fcmpd d1, d0\n" + "fmstat\n" + "movlt r0, r1\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_max(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d0, [r0]\n" + "fldd d1, [r1]\n" + "fcmpd d1, d0\n" + "fmstat\n" + "movge r0, r1\n" + FUNCTION_MARKER + ); +} + + + +__attribute__((naked)) void nseel_asm_min_fp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmpd d1, d0\n" + "fmstat\n" + "fcpydlt d0, d1\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_max_fp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fcmpd d1, d0\n" + "fmstat\n" + "fcpydge d0, d1\n" + FUNCTION_MARKER + ); +} + + + + + + + +__attribute__((naked)) void _asm_generic3parm(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "push {r4, lr}\n" // input: r0 last, r1=second to last, r2=third to last + // output: r0=context, r1=r2, r2=r1, r3=r0 + "mov r3, r0\n" // r0 (last parameter) -> r3 + "mov r4, r1\n" // r1 (second to last parameter) r1->r4->r2 + + "movw r0, 0xdead\n" // r0 is first parm (context) + "movt r0, 0xbeef\n" + + "mov r1, r2\n" // r2->r1 + "mov r2, r4\n" // r1->r2 + + "movw r4, 0xdead\n" + "movt r4, 0xbeef\n" + + "blx r4\n" + "pop {r4, lr}\n" + FUNCTION_MARKER + :: + ); +} + +__attribute__((naked)) void _asm_generic3parm_retd(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "push {r4, lr}\n" // input: r0 last, r1=second to last, r2=third to last + // output: r0=context, r1=r2, r2=r1, r3=r0 + "mov r3, r0\n" // r0 (last parameter) -> r3 + "mov r4, r1\n" // r1 (second to last parameter) r1->r4->r2 + + "movw r0, 0xdead\n" // r0 is first parm (context) + "movt r0, 0xbeef\n" + + "mov r1, r2\n" // r2->r1 + "mov r2, r4\n" // r1->r2 + + "movw r4, 0xdead\n" + "movt r4, 0xbeef\n" + + "blx r4\n" + "pop {r4, lr}\n" + FUNCTION_MARKER + :: + ); +} + + +__attribute__((naked)) void _asm_generic2parm(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str lr, [sp, #-8]!\n" + "mov r2, r0\n" // r0 -> r2, r1-r1, + "movw r0, 0xdead\n" // r0 is first parm + "movt r0, 0xbeef\n" + "movw r3, 0xdead\n" + "movt r3, 0xbeef\n" + "blx r3\n" + "ldr lr, [sp], #8\n" + FUNCTION_MARKER + :: + ); +} + + +__attribute__((naked)) void _asm_generic2parm_retd(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str lr, [sp, #-8]!\n" + "mov r2, r0\n" // r0 -> r2, r1-r1, + "movw r0, 0xdead\n" // r0 is first parm + "movt r0, 0xbeef\n" + "movw r3, 0xdead\n" + "movt r3, 0xbeef\n" + "blx r3\n" + "ldr lr, [sp], #8\n" + FUNCTION_MARKER + :: + ); +} + + +__attribute__((naked)) void _asm_generic2xparm_retd(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "push {r4, lr}\n" + "mov r3, r0\n" // r0 is last parameter + "mov r2, r1\n" // + "movw r0, 0xdead\n" // r0 is ctx + "movt r0, 0xbeef\n" + "movw r1, 0xdead\n" // r1 is second ctx + "movt r1, 0xbeef\n" + "movw r4, 0xdead\n" + "movt r4, 0xbeef\n" + "blx r4\n" + "pop {r4, lr}\n" + FUNCTION_MARKER + :: + ); +} + +__attribute__((naked)) void _asm_generic1parm(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str lr, [sp, #-8]!\n" + "mov r1, r0\n" // r0 -> r1 + "movw r0, 0xdead\n" // r0 is first parm + "movt r0, 0xbeef\n" + "movw r3, 0xdead\n" + "movt r3, 0xbeef\n" + "blx r3\n" + "ldr lr, [sp], #8\n" + FUNCTION_MARKER + :: + ); +} + + + +__attribute__((naked)) void _asm_generic1parm_retd(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "str lr, [sp, #-8]!\n" + "mov r1, r0\n" // r0 -> r1 + "movw r0, 0xdead\n" // r0 is first parm + "movt r0, 0xbeef\n" + "movw r3, 0xdead\n" + "movt r3, 0xbeef\n" + "blx r3\n" + "ldr lr, [sp], #8\n" + FUNCTION_MARKER + :: + ); +} + + + + +__attribute__((naked)) void _asm_megabuf(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r7, #-8]\n" + "faddd d0, d0, d1\n" + "ftouizd s0, d0\n" + "fmrs r3, s0\n" // r3 is slot index + "mov r2, r3, asr %0\n" + "bic r2, r2, #3\n" // r2 is page index*4 + "cmp r2, %1\n" + "bhs 0f\n" + + "add r2, r2, r7\n" + "ldr r2, [r2]\n" + "cmp r2, #0\n" + "beq 0f\n" + + "movw r0, %2\n" + "and r3, r3, r0\n" // r3 mask item in slot + "add r0, r2, r3, asl #3\n" // set result + "b 1f\n" + "0:\n" + + // failed, call stub function + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "str lr, [sp, #-8]!\n" + "mov r0, r7\n" // first parameter: blocks + "mov r1, r3\n" // second parameter: slot index + "blx r2\n" + "ldr lr, [sp], #8\n" + + "1:\n" + + FUNCTION_MARKER + :: + "i" (NSEEL_RAM_ITEMSPERBLOCK_LOG2 - 2/*log2(sizeof(void*))*/), + "i" (NSEEL_RAM_BLOCKS*4 /*(sizeof(void*))*/), + "i" (NSEEL_RAM_ITEMSPERBLOCK-1) + ); +} + + +__attribute__((naked)) void _asm_gmegabuf(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r0, 0xdead\n" + "movt r0, 0xbeef\n" + + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + + "fldd d1, [r7, #-8]\n" + "faddd d0, d0, d1\n" + "ftouizd s0, d0\n" + "fmrs r1, s0\n" // r1 is slot index + + "push {r4, lr}\n" + + "blx r2\n" + + "pop {r4, lr}\n" + + FUNCTION_MARKER + :: + ); +} + + +__attribute__((naked)) void nseel_asm_fcall(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r0, 0xdead\n" + "movt r0, 0xbeef\n" + "push {r4, lr}\n" + "blx r0\n" + "pop {r4, lr}\n" + FUNCTION_MARKER + ); +} + + + +__attribute__((naked)) void nseel_asm_stack_push(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d0, [r0]\n" + + "movw r3, 0xdead\n" // r3 is stack + "movt r3, 0xbeef\n" + "ldr r0, [r3]\n" + + "add r0, r0, #8\n" + + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "and r0, r0, r2\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "orr r0, r0, r2\n" + + "str r0, [r3]\n" + "fstd d0, [r0]\n" + + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_stack_pop(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, 0xdead\n" // r3 is stack + "movt r3, 0xbeef\n" + "ldr r1, [r3]\n" + "fldd d0, [r1]\n" + "sub r1, r1, #8\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "fstd d0, [r0]\n" + "and r1, r1, r2\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "orr r1, r1, r2\n" + + "str r1, [r3]\n" + FUNCTION_MARKER + ); +} + + + +__attribute__((naked)) void nseel_asm_stack_pop_fast(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, 0xdead\n" // r3 is stack + "movt r3, 0xbeef\n" + "ldr r1, [r3]\n" + "mov r0, r1\n" + "sub r1, r1, #8\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "and r1, r1, r2\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "orr r1, r1, r2\n" + "str r1, [r3]\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_stack_peek(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, 0xdead\n" // r3 is stack + "movt r3, 0xbeef\n" + + "ftosizd s0, d0\n" + "fmrs r2, s0\n" // r2 is index in stack + + "ldr r1, [r3]\n" + "sub r1, r1, r2, asl #3\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "and r1, r1, r2\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "orr r0, r1, r2\n" + FUNCTION_MARKER + ); +} + + +__attribute__((naked)) void nseel_asm_stack_peek_top(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, 0xdead\n" // r3 is stack + "movt r3, 0xbeef\n" + "ldr r0, [r3]\n" + FUNCTION_MARKER + ); +} + + +__attribute__((naked)) void nseel_asm_stack_peek_int(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, 0xdead\n" // r3 is stack + "movt r3, 0xbeef\n" + + "movw r2, 0xdead\n" // r3 is stack + "movt r2, 0xbeef\n" + + "ldr r1, [r3]\n" + "sub r1, r1, r2\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "and r1, r1, r2\n" + "movw r2, 0xdead\n" + "movt r2, 0xbeef\n" + "orr r0, r1, r2\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_stack_exch(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "movw r3, 0xdead\n" // r3 is stack + "movt r3, 0xbeef\n" + "ldr r1, [r3]\n" + "fldd d0, [r0]\n" + "fldd d1, [r1]\n" + "fstd d0, [r1]\n" + "fstd d1, [r0]\n" + FUNCTION_MARKER + ); +} + + +__attribute__((naked)) void nseel_asm_booltofp(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "cmp r0, #0\n" + "flddne d0, [r6, #16]\n" + "flddeq d0, [r6, #8]\n" + FUNCTION_MARKER + ); +} + +__attribute__((naked)) void nseel_asm_fptobool(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r6]\n" + "fabsd d0, d0\n" + "fcmpd d0, d1\n" + "fmstat\n" + "movgt r0, #1\n" + "movlt r0, #0\n" + FUNCTION_MARKER + :: + ); +} + +__attribute__((naked)) void nseel_asm_fptobool_rev(void) +{ + __asm__ __volatile__( + FUNCTION_MARKER + "fldd d1, [r6]\n" + "fabsd d0, d0\n" + "fcmpd d0, d1\n" + "fmstat\n" + "movgt r0, #0\n" + "movlt r0, #1\n" + FUNCTION_MARKER + :: + ); +} + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-ppc-gcc.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-ppc-gcc.c new file mode 100644 index 000000000..11aa25e4d --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-ppc-gcc.c @@ -0,0 +1,1436 @@ +#define FUNCTION_MARKER "mr r0, r0\n" \ + "mr r1, r1\n" \ + "mr r2, r2\n" + +#if EEL_F_SIZE == 8 + +void nseel_asm_1pdd(void) +{ + + __asm__( + FUNCTION_MARKER + "addis r5, 0, 0xdead\n" + "ori r5, r5, 0xbeef\n" + "mtctr r5\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: ); +} + +void nseel_asm_2pdd(void) +{ + + __asm__( + FUNCTION_MARKER + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + "fmr f2, f1\n" + "lfd f1, 0(r14)\n" + "mtctr r7\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: ); +}; + +void nseel_asm_2pdds(void) +{ + __asm__( + FUNCTION_MARKER + "addis r5, 0, 0xdead\n" + "ori r5, r5, 0xbeef\n" + "fmr f2, f1\n" + "lfd f1, 0(r14)\n" + "mtctr r5\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + "stfd f1, 0(r14)\n" + "mr r3, r14\n" + FUNCTION_MARKER + :: ); +} + +#else // 32 bit floating point calls + +#error no 32 bit float support + +#endif + +//--------------------------------------------------------------------------------------------------------------- + + +void nseel_asm_invsqrt(void) +{ + __asm__( + FUNCTION_MARKER + "frsqrte f1, f1\n" // less accurate than our x86 equivilent, but invsqrt() is inherently inaccurate anyway + FUNCTION_MARKER + ); +} + +void nseel_asm_dbg_getstackptr(void) +{ + __asm__( + FUNCTION_MARKER + "addis r11, 0, 0x4330\n" + "xoris r10, r1, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_sqr(void) +{ + __asm__( + FUNCTION_MARKER + "fmul f1, f1, f1\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_abs(void) +{ + __asm__( + FUNCTION_MARKER + "fabs f1, f1\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_assign(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f1, 0(r3)\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} +// +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_assign_fromfp(void) +{ + __asm__( + FUNCTION_MARKER + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_assign_fast(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f1, 0(r3)\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} +// +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_assign_fast_fromfp(void) +{ + __asm__( + FUNCTION_MARKER + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_add(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fadd f1, f1, f2\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_add_op(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fadd f1, f1, f2\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_add_op_fast(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fadd f1, f1, f2\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_sub(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fsub f1, f2, f1\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_sub_op(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fsub f1, f2, f1\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_sub_op_fast(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fsub f1, f2, f1\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_mul(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fmul f1, f2, f1\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_mul_op(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fmul f1, f2, f1\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_mul_op_fast(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fmul f1, f2, f1\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_div(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fdiv f1, f2, f1\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_div_op(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fdiv f1, f2, f1\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_div_op_fast(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fdiv f1, f2, f1\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_mod(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fabs f1, f1\n" + "fabs f2, f2\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + + "divw r12, r11, r10\n" + "mullw r12, r12, r10\n" + "subf r10, r12, r11\n" + + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_shl(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + "slw r10, r11, r10\n" // r10 has the result + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_shr(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + "sraw r10, r11, r10\n" // r10 has the result + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_mod_op(void) +{ + + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fabs f1, f1\n" + "fabs f2, f2\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + + "divw r12, r11, r10\n" + "mullw r12, r12, r10\n" + "subf r10, r12, r11\n" + + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); + +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_or(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + "or r10, r10, r11\n" // r10 has the result + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_or0(void) +{ + __asm__( + FUNCTION_MARKER + "fctiwz f1, f1\n" + "addis r11, 0, 0x4330\n" + "stfd f1, -8(r1)\n" + "lwz r10, -4(r1)\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_or_op(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + "or r10, r10, r11\n" // r10 has the result + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_xor(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + "xor r10, r10, r11\n" // r10 has the result + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_xor_op(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + "xor r10, r10, r11\n" // r10 has the result + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_and(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + "and r10, r10, r11\n" // r10 has the result + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + FUNCTION_MARKER + );} + +void nseel_asm_and_op(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fctiwz f1, f1\n" + "fctiwz f2, f2\n" + "stfd f1, -8(r1)\n" + "stfd f2, -16(r1)\n" + "lwz r10, -4(r1)\n" + "lwz r11, -12(r1)\n" //r11 and r12 have the integers + "and r10, r10, r11\n" // r10 has the result + "addis r11, 0, 0x4330\n" + "xoris r10, r10, 0x8000\n" + "stw r11, -8(r1)\n" // 0x43300000 + "stw r10, -4(r1)\n" // our integer sign flipped + "lfd f1, -8(r1)\n" + "fsub f1, f1, f30\n" + "mr r3, r14\n" + "stfd f1, 0(r14)\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_uminus(void) +{ + __asm__( + FUNCTION_MARKER + "fneg f1, f1\n" + FUNCTION_MARKER + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_sign(void) +{ + __asm__( + FUNCTION_MARKER + "li r9, 0\n" + "stw r9, -4(r1)\n" + "lis r9, 0xbf80\n" // -1 in float + "lfs f2, -4(r1)\n" + + "fcmpu cr7, f1, f2\n" + "blt- cr7, 0f\n" + "ble- cr7, 1f\n" + " lis r9, 0x3f80\n" // 1 in float + "0:\n" + " stw r9, -4(r1)\n" + " lfs f1, -4(r1)\n" + "1:\n" + FUNCTION_MARKER + :: + ); +} + + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_bnot(void) +{ + __asm__( + FUNCTION_MARKER + "cmpwi cr0, r3, 0\n" + "addis r3, 0, 0\n" + "bne cr0, 0f\n" + "addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_if(void) +{ + __asm__( + FUNCTION_MARKER + "cmpwi cr0, r3, 0\n" + "beq cr0, 0f\n" + " addis r6, 0, 0xdead\n" + " ori r6, r6, 0xbeef\n" + " mtctr r6\n" + " bctrl\n" + "b 1f\n" + "0:\n" + " addis r6, 0, 0xdead\n" + " ori r6, r6, 0xbeef\n" + " mtctr r6\n" + " bctrl\n" + "1:\n" + FUNCTION_MARKER + :: ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_repeat(void) +{ +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + __asm__( + FUNCTION_MARKER + "fctiwz f1, f1\n" + "stfd f1, -8(r1)\n" + "lwz r5, -4(r1)\n" // r5 has count now + "cmpwi cr0, r5, 0\n" + "ble cr0, 1f\n" // skip the loop + + "addis r7, 0, ha16(%0)\n" + "addi r7, r7, lo16(%0)\n" + + "stwu r16, -16(r1)\n" // set up the stack for the loop, save r16 + + "cmpw cr0, r7, r5\n" + "bge cr0, 0f\n" + "mr r5, r7\n" // set r5 to max if we have to +"0:\n" + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" + + "addi r5, r5, -1\n" + "stw r5, 4(r1)\n" + + "mtctr r6\n" + "bctrl\n" + + "lwz r16, 0(r1)\n" + "lwz r5, 4(r1)\n" + + "cmpwi cr0, r5, 0\n" + "bgt cr0, 0b\n" + + "addi r1, r1, 16\n" // restore old stack + + "1:\n" + FUNCTION_MARKER + ::"g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN) + ); +#else + __asm__( + FUNCTION_MARKER + "fctiwz f1, f1\n" + "stfd f1, -8(r1)\n" + "lwz r5, -4(r1)\n" // r5 has count now + "cmpwi cr0, r5, 0\n" + "ble cr0, 1f\n" // skip the loop + + "stwu r16, -16(r1)\n" // set up the stack for the loop, save r16 + +"0:\n" + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" + + "addi r5, r5, -1\n" + "stw r5, 4(r1)\n" + + "mtctr r6\n" + "bctrl\n" + + "lwz r16, 0(r1)\n" + "lwz r5, 4(r1)\n" + + "cmpwi cr0, r5, 0\n" + "bgt cr0, 0b\n" + + "addi r1, r1, 16\n" // restore old stack + + "1:\n" + FUNCTION_MARKER + :: + ); +#endif +} + +void nseel_asm_repeatwhile(void) +{ +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + __asm__( + FUNCTION_MARKER + "stwu r16, -16(r1)\n" // save r16 to stack, update stack + "addis r5, 0, ha16(%0)\n" + "addi r5, r5, lo16(%0)\n" +"0:\n" + + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" + "stw r5, 4(r1)\n" // save maxcnt + + "mtctr r6\n" + "bctrl\n" + + "lwz r16, 0(r1)\n" // restore r16 + "lwz r5, 4(r1)\n" // restore, check maxcnt + + "cmpwi cr7, r3, 0\n" // check return value + "addi r5, r5, -1\n" + + "beq cr7, 1f\n" + + "cmpwi cr0, r5, 0\n" + "bgt cr0, 0b\n" + "1:\n" + "addi r1, r1, 16\n" // restore stack + FUNCTION_MARKER + ::"g" (NSEEL_LOOPFUNC_SUPPORT_MAXLEN) + ); +#else + __asm__( + FUNCTION_MARKER + "stwu r16, -16(r1)\n" // save r16 to stack, update stack +"0:\n" + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" + + "mtctr r6\n" + "bctrl\n" + + "lwz r16, 0(r1)\n" // restore r16 + + "cmpwi cr7, r3, 0\n" // check return value + "bne cr7, 0b\n" + "addi r1, r1, 16\n" // restore stack + FUNCTION_MARKER + :: + ); + +#endif +} + + +void nseel_asm_band(void) +{ + __asm__( + FUNCTION_MARKER + "cmpwi cr7, r3, 0\n" + "beq cr7, 0f\n" + " addis r6, 0, 0xdead\n" + " ori r6, r6, 0xbeef\n" + " mtctr r6\n" + " bctrl\n" + "0:\n" + FUNCTION_MARKER + :: ); +} + +void nseel_asm_bor(void) +{ + __asm__( + FUNCTION_MARKER + "cmpwi cr7, r3, 0\n" + "bne cr7, 0f\n" + " addis r6, 0, 0xdead\n" + " ori r6, r6, 0xbeef\n" + " mtctr r6\n" + " bctrl\n" + "0:\n" + FUNCTION_MARKER + :: ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_equal(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fsub f1, f1, f2\n" + "fabs f1, f1\n" + "fcmpu cr7, f1, f31\n" + "addis r3, 0, 0\n" + "bge cr7, 0f\n" + "addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_equal_exact(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fcmpu cr7, f1, f2\n" + "addis r3, 0, 0\n" + "bne cr7, 0f\n" + "addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} +// +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_notequal_exact(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fcmpu cr7, f1, f2\n" + "addis r3, 0, 0\n" + "beq cr7, 0f\n" + "addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} +// +// +// +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_notequal(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fsub f1, f1, f2\n" + "fabs f1, f1\n" + "fcmpu cr7, f1, f31\n" + "addis r3, 0, 0\n" + "blt cr7, 0f\n" + " addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_below(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fcmpu cr7, f1, f2\n" + "addis r3, 0, 0\n" + "ble cr7, 0f\n" + "addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_beloweq(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fcmpu cr7, f1, f2\n" + "addis r3, 0, 0\n" + "blt cr7, 0f\n" + " addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} + + +//--------------------------------------------------------------------------------------------------------------- +void nseel_asm_above(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fcmpu cr7, f1, f2\n" + "addis r3, 0, 0\n" + "bge cr7, 0f\n" + "addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} + +void nseel_asm_aboveeq(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fcmpu cr7, f1, f2\n" + "addis r3, 0, 0\n" + "bgt cr7, 0f\n" + "addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} + + + +void nseel_asm_min(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f1, 0(r3)\n" + "lfd f2, 0(r14)\n" + "fcmpu cr7, f2, f1\n" + "bgt cr7, 0f\n" + "mr r3, r14\n" + "0:\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_max(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f1, 0(r3)\n" + "lfd f2, 0(r14)\n" + "fcmpu cr7, f2, f1\n" + "blt cr7, 0f\n" + "mr r3, r14\n" + "0:\n" + FUNCTION_MARKER + ); +} + + + +void nseel_asm_min_fp(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fcmpu cr7, f2, f1\n" + "bgt cr7, 0f\n" + "fmr f1, f2\n" + "0:\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_max_fp(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, 0(r14)\n" + "fcmpu cr7, f2, f1\n" + "blt cr7, 0f\n" + "fmr f1, f2\n" + "0:\n" + FUNCTION_MARKER + ); +} + + + + + + + +void _asm_generic3parm(void) +{ + __asm__( + FUNCTION_MARKER + "mr r6, r3\n" + "addis r3, 0, 0xdead\n" + "ori r3, r3, 0xbeef\n" + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + "mr r4, r15\n" + "mr r5, r14\n" + "mtctr r7\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: + ); +} + +void _asm_generic3parm_retd(void) +{ + __asm__( + FUNCTION_MARKER + "mr r6, r3\n" + "addis r3, 0, 0xdead\n" + "ori r3, r3, 0xbeef\n" + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + "mr r4, r15\n" + "mr r5, r14\n" + "mtctr r7\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: + ); +} + + +void _asm_generic2parm(void) // this prob neds to be fixed for ppc +{ + __asm__( + FUNCTION_MARKER + "mr r5, r3\n" + "addis r3, 0, 0xdead\n" + "ori r3, r3, 0xbeef\n" + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + "mr r4, r14\n" + "mtctr r7\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: + ); +} + + +void _asm_generic2parm_retd(void) +{ + __asm__( + FUNCTION_MARKER + "mr r5, r3\n" + "addis r3, 0, 0xdead\n" + "ori r3, r3, 0xbeef\n" + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + "mr r4, r14\n" + "mtctr r7\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: + ); +} + +void _asm_generic2xparm_retd(void) +{ + __asm__( + FUNCTION_MARKER + "mr r6, r3\n" + "addis r3, 0, 0xdead\n" + "ori r3, r3, 0xbeef\n" + "addis r4, 0, 0xdead\n" + "ori r4, r4, 0xbeef\n" + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + "mr r5, r14\n" + "mtctr r7\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: + ); +} + +void _asm_generic1parm(void) // this prob neds to be fixed for ppc +{ + __asm__( + FUNCTION_MARKER + "mr r4, r3\n" + "addis r3, 0, 0xdead\n" + "ori r3, r3, 0xbeef\n" + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + "mtctr r7\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: + ); +} + + + +void _asm_generic1parm_retd(void) +{ + __asm__( + FUNCTION_MARKER + "mr r4, r3\n" + "addis r3, 0, 0xdead\n" + "ori r3, r3, 0xbeef\n" + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + "mtctr r7\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: + ); +} + + + + +void _asm_megabuf(void) +{ + __asm__( + FUNCTION_MARKER + "lfd f2, -8(r13)\n" + "mr r3, r13\n" + + "fadd f1, f2, f1\n" + + // f1 has (float) index of array, r3 has EEL_F ** + "fctiwz f1, f1\n" + "stfd f1, -8(r1)\n" + "lwz r4, -4(r1)\n" // r4 is index of array + + "andis. r15, r4, %0\n" // check to see if it has any bits in 0xFF800000, which is 0xFFFFFFFF - (NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK - 1) + "bne cr0, 0f\n" // out of range, jump to error + + // shr 14 (16 for NSEEL_RAM_ITEMSPERBLOCK, minus two for pointer size), which is rotate 18 + // mask 7 bits (NSEEL_RAM_BLOCKS), but leave two empty bits (pointer size) + "rlwinm r15, r4, %1, %2, 29\n" + "lwzx r15, r3, r15\n" // r15 = (r3+r15) + "cmpi cr0, r15, 0\n" + "bne cr0, 1f\n" // if nonzero, jump to final calculation + + "0:\n" + // set up function call + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + "mtctr r7\n" + "subi r1, r1, 64\n" + "bctrl\n" + "addi r1, r1, 64\n" + "b 2f\n" + "1:\n" + // good news: we can do a direct addr return + // bad news: more rlwinm ugliness! + // shift left by 3 (sizeof(EEL_F)), mask off lower 3 bits, only allow 16 bits (NSEEL_RAM_ITEMSPERBLOCK) through + "rlwinm r3, r4, 3, %3, 28\n" + + // add offset of loaded block + "add r3, r3, r15\n" + + "2:\n" + FUNCTION_MARKER + :: + "i" ((0xFFFFFFFF - (NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK - 1))>>16), + "i" (32 - NSEEL_RAM_ITEMSPERBLOCK_LOG2 + 2), + "i" (30 - NSEEL_RAM_BLOCKS_LOG2), + "i" (28 - NSEEL_RAM_ITEMSPERBLOCK_LOG2 + 1) + ); +} + + +void _asm_gmegabuf(void) +{ + __asm__( + FUNCTION_MARKER + "fadd f1, f31, f1\n" + "addis r3, 0, 0xdead\n" // set up context pointer + "ori r3, r3, 0xbeef\n" + + "fctiwz f1, f1\n" + "subi r1, r1, 64\n" + + "addis r7, 0, 0xdead\n" + "ori r7, r7, 0xbeef\n" + + "stfd f1, 8(r1)\n" + "mtctr r7\n" + + "lwz r4, 12(r1)\n" + + "bctrl\n" + "addi r1, r1, 64\n" + FUNCTION_MARKER + :: + ); +} + + +void nseel_asm_fcall(void) +{ + __asm__( + FUNCTION_MARKER + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" + "mtctr r6\n" + "bctrl\n" + FUNCTION_MARKER + ); +} + + + +void nseel_asm_stack_push(void) +{ + __asm__( + FUNCTION_MARKER + + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" // r6 is stack + + "lfd f1, 0(r3)\n" // f1 is value to copy to stack + "lwz r3, 0(r6)\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "addi r3, r3, 0x8\n" + + "and r3, r3, r14\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "or r3, r3, r14\n" + + "stfd f1, 0(r3)\n" // copy parameter to stack + + "stw r3, 0(r6)\n" // update stack state + FUNCTION_MARKER + ); +} + +void nseel_asm_stack_pop(void) +{ + __asm__( + FUNCTION_MARKER + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" // r6 is stack + "lwz r15, 0(r6)\n" // return the old stack pointer + + "lfd f1, 0(r15)\n" + "subi r15, r15, 0x8\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "and r15, r15, r14\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "or r15, r15, r14\n" + "stw r15, 0(r6)\n" + + "stfd f1, 0(r3)\n" + FUNCTION_MARKER + ); +} + + + +void nseel_asm_stack_pop_fast(void) +{ + __asm__( + FUNCTION_MARKER + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" // r6 is stack + "lwz r3, 0(r6)\n" // return the old stack pointer + + "mr r15, r3\n" // update stack pointer + "subi r15, r15, 0x8\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "and r15, r15, r14\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "or r15, r15, r14\n" + "stw r15, 0(r6)\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_stack_peek(void) +{ + __asm__( + FUNCTION_MARKER + "fctiwz f1, f1\n" + "stfd f1, -8(r1)\n" + + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" // r6 is stack + + "lwz r14, -4(r1)\n" + "rlwinm r14, r14, 3, 0, 28\n" // slwi r14, r14, 3 -- 3 is log2(sizeof(EEL_F)) -- 28 represents 31-3 + "lwz r3, 0(r6)\n" // return the old stack pointer + + "sub r3, r3, r14\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "and r3, r3, r14\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "or r3, r3, r14\n" + FUNCTION_MARKER + ); +} + + +void nseel_asm_stack_peek_top(void) +{ + __asm__( + FUNCTION_MARKER + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" // r6 is stack + "lwz r3, 0(r6)\n" // return the old stack pointer + FUNCTION_MARKER + ); +} + + +void nseel_asm_stack_peek_int(void) +{ + __asm__( + FUNCTION_MARKER + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" // r6 is stack + "lwz r3, 0(r6)\n" // return the old stack pointer + + "addis r14, 0, 0xdead\n" // add manual offset + "ori r14, r14, 0xbeef\n" + "sub r3, r3, r14\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "and r3, r3, r14\n" + + "addis r14, 0, 0xdead\n" + "ori r14, r14, 0xbeef\n" + "or r3, r3, r14\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_stack_exch(void) +{ + __asm__( + FUNCTION_MARKER + "addis r6, 0, 0xdead\n" + "ori r6, r6, 0xbeef\n" // r6 is stack + "lfd f1, 0(r3)\n" + "lwz r14, 0(r6)\n" + "lfd f2, 0(r14)\n" + + "stfd f1, 0(r14)\n" + "stfd f2, 0(r3)\n" + FUNCTION_MARKER + ); +} + + +void nseel_asm_booltofp(void) +{ + __asm__( + FUNCTION_MARKER + "cmpwi cr7, r3, 0\n" + "li r14, 0\n" + "beq cr7, 0f\n" + "addis r14, 0, 0x3f80\n" + "0:\n" + "stw r14, -8(r1)\n" + "lfs f1, -8(r1)\n" + FUNCTION_MARKER + ); +} + +void nseel_asm_fptobool(void) +{ + __asm__( + FUNCTION_MARKER + "fabs f1, f1\n" + "fcmpu cr7, f1, f31\n" + "addis r3, 0, 1\n" + "bge cr7, 0f\n" + " addis r3, 0, 0\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} + +void nseel_asm_fptobool_rev(void) +{ + __asm__( + FUNCTION_MARKER + "fabs f1, f1\n" + "fcmpu cr7, f1, f31\n" + "addis r3, 0, 0\n" + "bge cr7, 0f\n" + " addis r3, 0, 1\n" + "0:\n" + FUNCTION_MARKER + :: + ); +} + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-x64-sse.asm b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-x64-sse.asm new file mode 100644 index 000000000..2a71c1c97 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/asm-nseel-x64-sse.asm @@ -0,0 +1,1357 @@ +; these must be synced with any changes in ns-eel-int.h +%define NSEEL_RAM_BLOCKS_DEFAULTMAX 128 +%define NSEEL_RAM_BLOCKS_LOG2 9 +%define NSEEL_RAM_ITEMSPERBLOCK_LOG2 16 +%define NSEEL_RAM_BLOCKS (1 << NSEEL_RAM_BLOCKS_LOG2) +%define NSEEL_RAM_ITEMSPERBLOCK (1< +#include + +#define YY_USER_ACTION yylloc->first_line = yylineno; + +#define YY_FATAL_ERROR(msg) { ((struct yyguts_t*)yyscanner)->yyextra_r->errVar=1; } +#define YY_INPUT(buf,result,max_size) { (result) = nseel_gets(yyextra,(buf),max_size); } + +#define YY_EXTRA_TYPE compileContext * + +#undef YY_BUF_SIZE +#define YY_BUF_SIZE (NSEEL_MAX_VARIABLE_NAMELEN*2) + +#undef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE (NSEEL_MAX_VARIABLE_NAMELEN) + +#include "y.tab.h" + +#ifdef _WIN32 +#define YY_NO_UNISTD_H +#endif + +#include "ns-eel-int.h" + +int nseel_gets(compileContext *ctx, char *buf, size_t sz); + +#define PARSENUM *yylval = nseel_translate(yyextra,yytext, 0); return VALUE; +#define EEL_ACTION(x) return x; + +#ifdef stdin +#undef stdin +#endif +#define stdin (0) + +#ifdef stdout +#undef stdout +#endif +#define stdout (0) + +static int g_fake_errno; +#ifdef errno +#undef errno +#endif + +#define errno g_fake_errno + +static void comment(yyscan_t yyscanner); + +%} + +%% + +[0-9]+\.?[0-9]* PARSENUM; +\.[0-9]+ PARSENUM; +0[xX][0-9a-fA-F]* PARSENUM; +\$[xX][0-9a-fA-F]* PARSENUM; +\$\~[0-9]* PARSENUM; +\$[Ee] PARSENUM; +\$[Pp][Ii] PARSENUM; +\$[Pp][Hh][Ii] PARSENUM; +\$\'.\' PARSENUM; +\#[a-zA-Z0-9\._]* *yylval = nseel_translate(yyextra,yytext, 0); return STRING_IDENTIFIER; +\<\< return TOKEN_SHL; +\>\> return TOKEN_SHR; +\<= return TOKEN_LTE; +\>= return TOKEN_GTE; +== return TOKEN_EQ; +=== return TOKEN_EQ_EXACT; +\!= return TOKEN_NE; +\!== return TOKEN_NE_EXACT; +\&\& return TOKEN_LOGICAL_AND; +\|\| return TOKEN_LOGICAL_OR; +\+= return TOKEN_ADD_OP; +-= return TOKEN_SUB_OP; +%= return TOKEN_MOD_OP; +\|= return TOKEN_OR_OP; +\&= return TOKEN_AND_OP; +\~= return TOKEN_XOR_OP; +\/= return TOKEN_DIV_OP; +\*= return TOKEN_MUL_OP; +\^= return TOKEN_POW_OP; + +[a-zA-Z_][a-zA-Z0-9\._]* &yylval = nseel_createCompiledValuePtr((compileContext *)yyextra, NULL, yytext); return IDENTIFIER; + +[ \t\r\n]+ /* whitespace */ +\/\/.*$ /* comment */ +"/*" { comment(yyscanner); } + +. return (int)yytext[0]; + +%% + +static void comment(yyscan_t yyscanner) +{ + int c,lc=0; + + while (0 != (c = input(yyscanner))) + { + if (c == '/' && lc == '*') return; + lc = c; + } + // end of file, ignore for now +} diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel2.y b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel2.y new file mode 100644 index 000000000..c7bf561bb --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel2.y @@ -0,0 +1,376 @@ +%pure-parser +%name-prefix="nseel" +%parse-param { compileContext* context } +%lex-param { void* scanner } + + +/* this will prevent y.tab.c from ever calling yydestruct(), since we do not use it and it is a waste */ +%destructor { + #define yydestruct(a,b,c,d,e) +} VALUE + + +%{ +#ifdef _WIN32 +#include +#endif +#include +#include +#include +#include + +#include "y.tab.h" +#include "ns-eel-int.h" + +#define scanner context->scanner +#define YY_(x) ("") + +%} + +%token VALUE IDENTIFIER TOKEN_SHL TOKEN_SHR +%token TOKEN_LTE TOKEN_GTE TOKEN_EQ TOKEN_EQ_EXACT TOKEN_NE TOKEN_NE_EXACT TOKEN_LOGICAL_AND TOKEN_LOGICAL_OR +%token TOKEN_ADD_OP TOKEN_SUB_OP TOKEN_MOD_OP TOKEN_OR_OP TOKEN_AND_OP TOKEN_XOR_OP TOKEN_DIV_OP TOKEN_MUL_OP TOKEN_POW_OP +%token STRING_LITERAL STRING_IDENTIFIER +%expect 75 +%start program + +%% + +more_params: + expression + | expression ',' more_params + { + $$ = nseel_createMoreParametersOpcode(context,$1,$3); + } + ; + +string: + STRING_LITERAL + | STRING_LITERAL string + { + ((struct eelStringSegmentRec *)$1)->_next = (struct eelStringSegmentRec *)$2; + $$ = $1; + } + ; + +assignable_value: + IDENTIFIER + { + if (!($$ = nseel_resolve_named_symbol(context, $1, -1, NULL))) /* convert from purely named to namespace-relative, etc */ + { + yyerror(&yyloc, context, ""); + YYERROR; + } + } + /* we used to have VALUE in here rather than rvalue, to allow 1=1 1+=2 etc, but silly to, + though this breaks Vmorph, which does 1=1 for a nop, and Jonas DrumReaplacer, which does x = 0 = y = 0 */ + | '(' expression ')' + { + $$ = $2; + } + | IDENTIFIER '(' expression ')' '(' expression ')' + { + int err; + if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, 0, 0, $6, &err))) + { + if (err == -1) yyerror(&yylsp[-2], context, ""); + else if (err == 0) yyerror(&yylsp[-6], context, ""); + else yyerror(&yylsp[-3], context, ""); // parameter count wrong + + YYERROR; + } + } + | IDENTIFIER '(' expression ')' + { + int err; + if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, 0, 0, 0, &err))) + { + if (err == 0) yyerror(&yylsp[-3], context, ""); + else yyerror(&yylsp[0], context, ""); // parameter count wrong + YYERROR; + } + } + | IDENTIFIER '(' ')' + { + int err; + if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, nseel_createCompiledValue(context,0.0), 0, 0, 0,&err))) + { + if (err == 0) yyerror(&yylsp[-2], context, ""); // function not found + else yyerror(&yylsp[0], context, ""); // parameter count wrong + YYERROR; + } + } + | IDENTIFIER '(' expression ',' expression ')' + { + int err; + if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, $5, 0, 0,&err))) + { + if (err == 0) yyerror(&yylsp[-5], context, ""); + else if (err == 2) yyerror(&yylsp[0], context, ""); // needs more than 2 parameters + else yyerror(&yylsp[-2], context, ""); // less than 2 + YYERROR; + } + } + | IDENTIFIER '(' expression ',' expression ',' more_params ')' + { + int err; + if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, $5, $7, 0, &err))) + { + if (err == 0) yyerror(&yylsp[-7], context, ""); + else if (err==2) yyerror(&yylsp[0], context, ""); // needs more parameters + else if (err==4) yyerror(&yylsp[-4], context, ""); // needs single parameter + else yyerror(&yylsp[-2], context, ""); // less parm + YYERROR; + } + } + | rvalue '[' ']' + { + $$ = nseel_createMemoryAccess(context,$1,0); + } + | rvalue '[' expression ']' + { + $$ = nseel_createMemoryAccess(context,$1,$3); + } + ; + +rvalue: + VALUE + | STRING_IDENTIFIER + | string + { + $$ = nseel_eelMakeOpcodeFromStringSegments(context,(struct eelStringSegmentRec *)$1); + } + | assignable_value + ; + + +assignment: + rvalue + | assignable_value '=' if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_ASSIGN,2,$1,$3); + } + | assignable_value TOKEN_ADD_OP if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_ADD_OP,2,$1,$3); + } + | assignable_value TOKEN_SUB_OP if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_SUB_OP,2,$1,$3); + } + | assignable_value TOKEN_MOD_OP if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_MOD_OP,2,$1,$3); + } + | assignable_value TOKEN_OR_OP if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_OR_OP,2,$1,$3); + } + | assignable_value TOKEN_AND_OP if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_AND_OP,2,$1,$3); + } + | assignable_value TOKEN_XOR_OP if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_XOR_OP,2,$1,$3); + } + | assignable_value TOKEN_DIV_OP if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_DIV_OP,2,$1,$3); + } + | assignable_value TOKEN_MUL_OP if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_MUL_OP,2,$1,$3); + } + | assignable_value TOKEN_POW_OP if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_POW_OP,2,$1,$3); + } + | STRING_IDENTIFIER '=' if_else_expr + { + $$ = nseel_createFunctionByName(context,"strcpy",2,$1,$3,NULL); + } + | STRING_IDENTIFIER TOKEN_ADD_OP if_else_expr + { + $$ = nseel_createFunctionByName(context,"strcat",2,$1,$3,NULL); + } + ; + +unary_expr: + assignment + | '+' unary_expr + { + $$ = $2; + } + | '-' unary_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_UMINUS,1,$2,0); + } + | '!' unary_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_NOT,1,$2,0); + } + ; + +pow_expr: + unary_expr + | pow_expr '^' unary_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_POW,2,$1,$3); + } + ; + +mod_expr: + pow_expr + | mod_expr '%' pow_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_MOD,2,$1,$3); + } + | mod_expr TOKEN_SHL pow_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_SHL,2,$1,$3); + } + | mod_expr TOKEN_SHR pow_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_SHR,2,$1,$3); + } + ; + +div_expr: + mod_expr + | div_expr '/' mod_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_DIVIDE,2,$1,$3); + } + ; + + +mul_expr: + div_expr + | mul_expr '*' div_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_MULTIPLY,2,$1,$3); + } + ; + + +sub_expr: + mul_expr + | sub_expr '-' mul_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_SUB,2,$1,$3); + } + ; + +add_expr: + sub_expr + | add_expr '+' sub_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_ADD,2,$1,$3); + } + ; + +andor_expr: + add_expr + | andor_expr '&' add_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_AND,2,$1,$3); + } + | andor_expr '|' add_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_OR,2,$1,$3); + } + | andor_expr '~' add_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_XOR,2,$1,$3); + } + ; + +cmp_expr: + andor_expr + | cmp_expr '<' andor_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_LT,2,$1,$3); + } + | cmp_expr '>' andor_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_GT,2,$1,$3); + } + | cmp_expr TOKEN_LTE andor_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_LTE,2,$1,$3); + } + | cmp_expr TOKEN_GTE andor_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_GTE,2,$1,$3); + } + | cmp_expr TOKEN_EQ andor_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_EQ,2,$1,$3); + } + | cmp_expr TOKEN_EQ_EXACT andor_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_EQ_EXACT,2,$1,$3); + } + | cmp_expr TOKEN_NE andor_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_NE,2,$1,$3); + } + | cmp_expr TOKEN_NE_EXACT andor_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_NE_EXACT,2,$1,$3); + } + ; + +logical_and_or_expr: + cmp_expr + | logical_and_or_expr TOKEN_LOGICAL_AND cmp_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_AND,2,$1,$3); + } + | logical_and_or_expr TOKEN_LOGICAL_OR cmp_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_OR,2,$1,$3); + } + ; + +if_else_expr: + logical_and_or_expr + | logical_and_or_expr '?' if_else_expr ':' if_else_expr + { + $$ = nseel_createIfElse(context, $1, $3, $5); + } + | logical_and_or_expr '?' ':' if_else_expr + { + $$ = nseel_createIfElse(context, $1, 0, $4); + } + | logical_and_or_expr '?' if_else_expr + { + $$ = nseel_createIfElse(context, $1, $3, 0); + } + ; + + +expression: + if_else_expr + | expression ';' if_else_expr + { + $$ = nseel_createSimpleCompiledFunction(context,FN_JOIN_STATEMENTS,2,$1,$3); + } + | expression ';' + { + $$ = $1; + } + ; + + +program: + expression + { + if (@1.first_line) { } + context->result = $1; + } + ; + + +%% diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_atomic.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_atomic.h new file mode 100644 index 000000000..f71d1c2e4 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_atomic.h @@ -0,0 +1,71 @@ +#ifndef __EEL_ATOMIC_H__ +#define __EEL_ATOMIC_H__ + +// requires these to be defined +//#define EEL_ATOMIC_SET_SCOPE(opaque) WDL_Mutex *mutex = (opaque?&((effectProcessor *)opaque)->m_atomic_mutex:&atomic_mutex); +//#define EEL_ATOMIC_ENTER mutex->Enter() +//#define EEL_ATOMIC_LEAVE mutex->Leave() + +static EEL_F NSEEL_CGEN_CALL atomic_setifeq(void *opaque, EEL_F *a, EEL_F *cmp, EEL_F *nd) +{ + EEL_F ret; + EEL_ATOMIC_SET_SCOPE(opaque) + EEL_ATOMIC_ENTER; + ret = *a; + if (fabs(ret - *cmp) < NSEEL_CLOSEFACTOR) *a = *nd; + EEL_ATOMIC_LEAVE; + return ret; +} + +static EEL_F NSEEL_CGEN_CALL atomic_exch(void *opaque, EEL_F *a, EEL_F *b) +{ + EEL_F tmp; + EEL_ATOMIC_SET_SCOPE(opaque) + EEL_ATOMIC_ENTER; + tmp = *b; + *b = *a; + *a = tmp; + EEL_ATOMIC_LEAVE; + return tmp; +} + +static EEL_F NSEEL_CGEN_CALL atomic_add(void *opaque, EEL_F *a, EEL_F *b) +{ + EEL_F tmp; + EEL_ATOMIC_SET_SCOPE(opaque) + EEL_ATOMIC_ENTER; + tmp = (*a += *b); + EEL_ATOMIC_LEAVE; + return tmp; +} + +static EEL_F NSEEL_CGEN_CALL atomic_set(void *opaque, EEL_F *a, EEL_F *b) +{ + EEL_F tmp; + EEL_ATOMIC_SET_SCOPE(opaque) + EEL_ATOMIC_ENTER; + tmp = *a = *b; + EEL_ATOMIC_LEAVE; + return tmp; +} + +static EEL_F NSEEL_CGEN_CALL atomic_get(void *opaque, EEL_F *a) +{ + EEL_F tmp; + EEL_ATOMIC_SET_SCOPE(opaque) + EEL_ATOMIC_ENTER; + tmp = *a; + EEL_ATOMIC_LEAVE; + return tmp; +} + +static void EEL_atomic_register() +{ + NSEEL_addfunc_retval("atomic_setifequal",3, NSEEL_PProc_THIS, &atomic_setifeq); + NSEEL_addfunc_retval("atomic_exch",2, NSEEL_PProc_THIS, &atomic_exch); + NSEEL_addfunc_retval("atomic_add",2, NSEEL_PProc_THIS, &atomic_add); + NSEEL_addfunc_retval("atomic_set",2, NSEEL_PProc_THIS, &atomic_set); + NSEEL_addfunc_retval("atomic_get",1, NSEEL_PProc_THIS, &atomic_get); +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_eval.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_eval.h new file mode 100644 index 000000000..8c04e96e0 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_eval.h @@ -0,0 +1,81 @@ +#ifndef _EEL_EVAL_H_ +#define _EEL_EVAL_H_ + +#ifndef EEL_EVAL_GET_CACHED +#define EEL_EVAL_GET_CACHED(str, ch) (NULL) +#endif + +#ifndef EEL_EVAL_SET_CACHED +#define EEL_EVAL_SET_CACHED(sv, ch) { NSEEL_code_free(ch); free(sv); } +#endif + +#ifndef EEL_EVAL_SCOPE_ENTER +#define EEL_EVAL_SCOPE_ENTER 1 +#define EEL_EVAL_SCOPE_LEAVE +#endif + +static EEL_F NSEEL_CGEN_CALL _eel_eval(void *opaque, EEL_F *s) +{ + NSEEL_VMCTX r = EEL_EVAL_GET_VMCTX(opaque); + NSEEL_CODEHANDLE ch = NULL; + char *sv=NULL; + if (r) + { + EEL_STRING_MUTEXLOCK_SCOPE + const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL); +#ifdef EEL_STRING_DEBUGOUT + if (!str) + { + EEL_STRING_DEBUGOUT("eval() passed invalid string handle %f",*s); + } +#endif + if (str && *str) + { + sv=EEL_EVAL_GET_CACHED(str,ch); + if (!sv) sv=strdup(str); + } + } + if (sv) + { + if (!ch) ch = NSEEL_code_compile(r,sv,0); + if (ch) + { + if (EEL_EVAL_SCOPE_ENTER) + { + NSEEL_code_execute(ch); + EEL_EVAL_SCOPE_LEAVE + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("eval() reentrancy limit reached"); +#endif + } + + EEL_EVAL_SET_CACHED(sv,ch); + return 1.0; + } + else + { +#ifdef EEL_STRING_DEBUGOUT + const char *err=NSEEL_code_getcodeerror(r); + if (err) EEL_STRING_DEBUGOUT("eval() error: %s",err); +#endif + } + free(sv); + } + return 0.0; +} + +void EEL_eval_register() +{ + NSEEL_addfunc_retval("eval",1,NSEEL_PProc_THIS,&_eel_eval); +} + +#ifdef EEL_WANT_DOCUMENTATION +static const char *eel_eval_function_reference = + "eval\t\"code\"\tExecutes code passed in. Code can use functions, but functions created in code can't be used elsewhere.\0" +; +#endif + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_fft.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_fft.h new file mode 100644 index 000000000..6b64d03dc --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_fft.h @@ -0,0 +1,396 @@ +#ifndef __EEL_FFT_H_ +#define __EEL_FFT_H_ + +#include "../fft.h" +#if WDL_FFT_REALSIZE != EEL_F_SIZE +#error WDL_FFT_REALSIZE -- EEL_F_SIZE size mismatch +#endif + +#ifndef EEL_FFT_MINBITLEN +#define EEL_FFT_MINBITLEN 4 +#endif + +#ifndef EEL_FFT_MAXBITLEN +#define EEL_FFT_MAXBITLEN 15 +#endif + +#ifndef EEL_FFT_MINBITLEN_REORDER +#define EEL_FFT_MINBITLEN_REORDER (EEL_FFT_MINBITLEN-1) +#endif + +//#define EEL_SUPER_FAST_FFT_REORDERING // quite a bit faster (50-100%) than "normal", but uses a 256kb lookup +//#define EEL_SLOW_FFT_REORDERING // 20%-80% slower than normal, alloca() use, no reason to ever use this + +#ifdef EEL_SUPER_FAST_FFT_REORDERING +static int *fft_reorder_table_for_bitsize(int bitsz) +{ + static int s_tab[ (2 << EEL_FFT_MAXBITLEN) + 24*(EEL_FFT_MAXBITLEN-EEL_FFT_MINBITLEN_REORDER+1) ]; // big 256kb table, ugh + if (bitsz<=EEL_FFT_MINBITLEN_REORDER) return s_tab; + return s_tab + (1<= 4 && dir < 8) + { + if (dir == 4 || dir == 5) + { + //timingEnter(0); +#if defined(EEL_SUPER_FAST_FFT_REORDERING) || !defined(EEL_SLOW_FFT_REORDERING) + fft_reorder_buffer(sizebits,(WDL_FFT_COMPLEX*)data,dir==4); +#else + // old blech + const int flen=1<= 0 && dir < 2) + { + WDL_fft((WDL_FFT_COMPLEX*)data,1<= 2 && dir < 4) + { + WDL_real_fft((WDL_FFT_REAL*)data,1<1 && bitl < EEL_FFT_MAXBITLEN) + { + bitl++; + l>>=1; + } + if (bitl < ((dir&4) ? EEL_FFT_MINBITLEN_REORDER : EEL_FFT_MINBITLEN)) // smallest FFT is 16 item, smallest reorder is 8 item + { + return start; + } + ilen=1< NSEEL_RAM_ITEMSPERBLOCK || dest_offs < 0 || src_offs < 0 || + dest_offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK || src_offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) return dest; + if ((dest_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)) + len > NSEEL_RAM_ITEMSPERBLOCK) return dest; + if ((src_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)) + len > NSEEL_RAM_ITEMSPERBLOCK) return dest; + + srcptr = __NSEEL_RAMAlloc(blocks,src_offs); + if (!srcptr || srcptr==&nseel_ramalloc_onfail) return dest; + destptr = __NSEEL_RAMAlloc(blocks,dest_offs); + if (!destptr || destptr==&nseel_ramalloc_onfail) return dest; + + + WDL_fft_complexmul((WDL_FFT_COMPLEX*)destptr,(WDL_FFT_COMPLEX*)srcptr,(len/2)&~1); + + return dest; +} + +void EEL_fft_register() +{ + WDL_fft_init(); +#if defined(EEL_SUPER_FAST_FFT_REORDERING) + if (!fft_reorder_table_for_bitsize(EEL_FFT_MINBITLEN_REORDER)[0]) + { + int x; + for (x=EEL_FFT_MINBITLEN_REORDER;x<=EEL_FFT_MAXBITLEN;x++) fft_make_reorder_table(x,fft_reorder_table_for_bitsize(x)); + } +#endif + NSEEL_addfunc_retptr("convolve_c",3,NSEEL_PProc_RAM,&eel_convolve_c); + NSEEL_addfunc_retptr("fft",2,NSEEL_PProc_RAM,&eel_fft); + NSEEL_addfunc_retptr("ifft",2,NSEEL_PProc_RAM,&eel_ifft); + NSEEL_addfunc_retptr("fft_real",2,NSEEL_PProc_RAM,&eel_fft_real); + NSEEL_addfunc_retptr("ifft_real",2,NSEEL_PProc_RAM,&eel_ifft_real); + NSEEL_addfunc_retptr("fft_permute",2,NSEEL_PProc_RAM,&eel_fft_permute); + NSEEL_addfunc_retptr("fft_ipermute",2,NSEEL_PProc_RAM,&eel_ifft_permute); +} + +#ifdef EEL_WANT_DOCUMENTATION +static const char *eel_fft_function_reference = +"convolve_c\tdest,src,size\tMultiplies each of size complex pairs in dest by the complex pairs in src. Often used for convolution.\0" +"fft\tbuffer,size\tPerforms a FFT on the data in the local memory buffer at the offset specified by the first parameter. The size of the FFT is specified " + "by the second parameter, which must be 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, or 32768. The outputs are permuted, so if " + "you plan to use them in-order, call fft_permute(buffer, size) before and fft_ipermute(buffer,size) after your in-order use. Your inputs or " + "outputs will need to be scaled down by 1/size, if used.\n" + "Note that fft()/ifft() require real / imaginary input pairs, so a 256 point FFT actually works with 512 items.\n" + "Note that fft()/ifft() must NOT cross a 65,536 item boundary, so be sure to specify the offset accordingly.\0" +"ifft\tbuffer,size\tPerform an inverse FFT. For more information see fft().\0" +"fft_real\tbuffer,size\tPerforms an FFT, but takes size input samples and produces size/2 complex output pairs. Usually used along with fft_permute(size/2). Inputs/outputs will need to be scaled by 0.5/size.\0" +"ifft_real\tbuffer,size\tPerforms an inverse FFT, but takes size/2 complex input pairs and produces size real output values. Usually used along with fft_ipermute(size/2).\0" +"fft_permute\tbuffer,size\tPermute the output of fft() to have bands in-order. See fft() for more information.\0" +"fft_ipermute\tbuffer,size\tPermute the input for ifft(), taking bands from in-order to the order ifft() requires. See fft() for more information.\0" +; +#endif + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_files.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_files.h new file mode 100644 index 000000000..7b5dac245 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_files.h @@ -0,0 +1,262 @@ +#ifndef __EEL_FILES_H__ +#define __EEL_FILES_H__ + +// should include eel_strings.h before this, probably +//#define EEL_FILE_OPEN(fn,mode) ((instance)opaque)->OpenFile(fn,mode) +//#define EEL_FILE_GETFP(fp) ((instance)opaque)->GetFileFP(fp) +//#define EEL_FILE_CLOSE(fpindex) ((instance)opaque)->CloseFile(fpindex) + +static EEL_F NSEEL_CGEN_CALL _eel_fopen(void *opaque, EEL_F *fn_index, EEL_F *mode_index) +{ + EEL_STRING_MUTEXLOCK_SCOPE + const char *fn = EEL_STRING_GET_FOR_INDEX(*fn_index,NULL); + const char *mode = EEL_STRING_GET_FOR_INDEX(*mode_index,NULL); + if (!fn || !mode) return 0; + return (EEL_F) EEL_FILE_OPEN(fn,mode); +} +static EEL_F NSEEL_CGEN_CALL _eel_fclose(void *opaque, EEL_F *fpp) +{ + EEL_F ret=EEL_FILE_CLOSE((int)*fpp); +#ifdef EEL_STRING_DEBUGOUT + if (ret < 0) EEL_STRING_DEBUGOUT("fclose(): file handle %f not valid",*fpp); +#endif + return ret; +} +static EEL_F NSEEL_CGEN_CALL _eel_fgetc(void *opaque, EEL_F *fpp) +{ + EEL_STRING_MUTEXLOCK_SCOPE + FILE *fp = EEL_FILE_GETFP((int)*fpp); + if (fp) return (EEL_F)fgetc(fp); +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fgetc(): file handle %f not valid",*fpp); +#endif + return -1.0; +} + +static EEL_F NSEEL_CGEN_CALL _eel_ftell(void *opaque, EEL_F *fpp) +{ + EEL_STRING_MUTEXLOCK_SCOPE + FILE *fp = EEL_FILE_GETFP((int)*fpp); + if (fp) return (EEL_F)ftell(fp); +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("ftell(): file handle %f not valid",*fpp); +#endif + return -1.0; +} +static EEL_F NSEEL_CGEN_CALL _eel_fflush(void *opaque, EEL_F *fpp) +{ + EEL_STRING_MUTEXLOCK_SCOPE + FILE *fp = EEL_FILE_GETFP((int)*fpp); + if (fp) { fflush(fp); return 0.0; } +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fflush(): file handle %f not valid",*fpp); +#endif + return -1.0; +} + +static EEL_F NSEEL_CGEN_CALL _eel_feof(void *opaque, EEL_F *fpp) +{ + EEL_STRING_MUTEXLOCK_SCOPE + FILE *fp = EEL_FILE_GETFP((int)*fpp); + if (fp) return feof(fp) ? 1.0 : 0.0; +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("feof(): file handle %f not valid",*fpp); +#endif + return -1.0; +} +static EEL_F NSEEL_CGEN_CALL _eel_fseek(void *opaque, EEL_F *fpp, EEL_F *offset, EEL_F *wh) +{ + EEL_STRING_MUTEXLOCK_SCOPE + FILE *fp = EEL_FILE_GETFP((int)*fpp); + if (fp) return fseek(fp, (int) *offset, *wh<0 ? SEEK_SET : *wh > 0 ? SEEK_END : SEEK_CUR); +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fseek(): file handle %f not valid",*fpp); +#endif + return -1.0; +} +static EEL_F NSEEL_CGEN_CALL _eel_fgets(void *opaque, EEL_F *fpp, EEL_F *strOut) +{ + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(*strOut, &wr); + + FILE *fp = EEL_FILE_GETFP((int)*fpp); + if (!fp) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fgets(): file handle %f not valid",*fpp); +#endif + if (wr) wr->Set(""); + return 0.0; + } + char buf[16384]; + buf[0]=0; + fgets(buf,sizeof(buf),fp); + if (wr) + { + wr->Set(buf); + return (EEL_F)wr->GetLength(); + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fgets: bad destination specifier passed %f, throwing away %d bytes",*strOut, (int)strlen(buf)); +#endif + return (int)strlen(buf); + } +} +static EEL_F NSEEL_CGEN_CALL _eel_fread(void *opaque, EEL_F *fpp, EEL_F *strOut, EEL_F *flen) +{ + int use_len = (int) *flen; + if (use_len < 1) return 0.0; + + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(*strOut, &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fread: bad destination specifier passed %f, not reading %d bytes",*strOut, use_len); +#endif + return -1; + } + + FILE *fp = EEL_FILE_GETFP((int)*fpp); + if (!fp) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fread(): file handle %f not valid",*fpp); +#endif + if (wr) wr->Set(""); + return 0.0; + } + + wr->SetLen(use_len); + if (wr->GetLength() != use_len) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fread: error allocating storage for read of %d bytes", use_len); +#endif + return -1.0; + } + use_len = (int)fread((char *)wr->Get(),1,use_len,fp); + wr->SetLen(use_len > 0 ? use_len : 0, true); + return (EEL_F) use_len; +} + +static EEL_F NSEEL_CGEN_CALL _eel_fwrite(void *opaque, EEL_F *fpp, EEL_F *strOut, EEL_F *flen) +{ + EEL_STRING_MUTEXLOCK_SCOPE + int use_len = (int) *flen; + + EEL_STRING_STORAGECLASS *wr=NULL; + const char *str=EEL_STRING_GET_FOR_INDEX(*strOut, &wr); + if (!wr && !str) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fwrite: bad source specifier passed %f, not writing %d bytes",*strOut, use_len); +#endif + return -1.0; + } + if (!wr) + { + const int ssl = (int)strlen(str); + if (use_len < 1 || use_len > ssl) use_len = ssl; + } + else + { + if (use_len < 1 || use_len > wr->GetLength()) use_len = wr->GetLength(); + } + + if (use_len < 1) return 0.0; + + FILE *fp = EEL_FILE_GETFP((int)*fpp); + if (!fp) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fwrite(): file handle %f not valid",*fpp); +#endif + return 0.0; + } + + return (EEL_F) fwrite(str,1,use_len,fp); +} + +static EEL_F NSEEL_CGEN_CALL _eel_fprintf(void *opaque, INT_PTR nparam, EEL_F **parm) +{ + if (opaque && nparam > 1) + { + EEL_STRING_MUTEXLOCK_SCOPE + FILE *fp = EEL_FILE_GETFP((int)*(parm[0])); + if (!fp) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fprintf(): file handle %f not valid",parm[0][0]); +#endif + return 0.0; + } + + EEL_STRING_STORAGECLASS *wr_src=NULL; + const char *fmt = EEL_STRING_GET_FOR_INDEX(*(parm[1]),&wr_src); + if (fmt) + { + char buf[16384]; + const int len = eel_format_strings(opaque,fmt,wr_src?(fmt+wr_src->GetLength()) : NULL, buf,(int)sizeof(buf),(int)nparam-2,parm+2); + + if (len >= 0) + { + return (EEL_F) fwrite(buf,1,len,fp); + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fprintf: bad format string %s",fmt); +#endif + } + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("fprintf: bad format specifier passed %f",*(parm[1])); +#endif + } + } + return 0.0; +} + +void EEL_file_register() +{ + NSEEL_addfunc_retval("fopen",2,NSEEL_PProc_THIS,&_eel_fopen); + + NSEEL_addfunc_retval("fread",3,NSEEL_PProc_THIS,&_eel_fread); + NSEEL_addfunc_retval("fgets",2,NSEEL_PProc_THIS,&_eel_fgets); + NSEEL_addfunc_retval("fgetc",1,NSEEL_PProc_THIS,&_eel_fgetc); + + NSEEL_addfunc_retval("fwrite",3,NSEEL_PProc_THIS,&_eel_fwrite); + NSEEL_addfunc_varparm("fprintf",2,NSEEL_PProc_THIS,&_eel_fprintf); + + NSEEL_addfunc_retval("fseek",3,NSEEL_PProc_THIS,&_eel_fseek); + NSEEL_addfunc_retval("ftell",1,NSEEL_PProc_THIS,&_eel_ftell); + NSEEL_addfunc_retval("feof",1,NSEEL_PProc_THIS,&_eel_feof); + NSEEL_addfunc_retval("fflush",1,NSEEL_PProc_THIS,&_eel_fflush); + NSEEL_addfunc_retval("fclose",1,NSEEL_PProc_THIS,&_eel_fclose); +} + +#ifdef EEL_WANT_DOCUMENTATION +static const char *eel_file_function_reference = +"fopen\t\"fn\",\"mode\"\tOpens a file \"fn\" with mode \"mode\". For read, use \"r\" or \"rb\", write \"w\" or \"wb\". Returns a positive integer on success.\0" +"fclose\tfp\tCloses a file previously opened with fopen().\0" +"fread\tfp,#str,length\tReads from file fp into #str, up to length bytes. Returns actual length read, or negative if error.\0" +"fgets\tfp,#str\tReads a line from file fp into #str. Returns length of #str read.\0" +"fgetc\tfp\tReads a character from file fp, returns -1 if EOF.\0" +"fwrite\tfp,#str,len\tWrites up to len characters of #str to file fp. If len is less than 1, the full contents of #str will be written. Returns the number of bytes written to file.\0" +"fprintf\tfp,\"format\"[,...]\tFormats a string and writes it to file fp. For more information on format specifiers, see sprintf(). Returns bytes written to file.\0" +"fseek\tfp,offset,whence\tSeeks file fp, offset bytes from whence reference. Whence negative specifies start of file, positive whence specifies end of file, and zero whence specifies current file position.\0" +"ftell\tfp\tRetunrs the current file position.\0" +"feof\tfp\tReturns nonzero if the file fp is at the end of file.\0" +"fflush\tfp\tIf file fp is open for writing, flushes out any buffered data to disk.\0" +; +#endif + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_import.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_import.h new file mode 100644 index 000000000..46e2176ab --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_import.h @@ -0,0 +1,104 @@ +/******************************************************************************************* + * imported EEL + ******************************************************************************************/ + +void (*NSEEL_addfunc_ret_type)(const char *name, int np, int ret_type, NSEEL_PPPROC pproc, void *fptr, eel_function_table *destination); // ret_type=-1 for bool, 1 for value, 0 for ptr +void (*NSEEL_addfunc_varparm_ex)(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination); + + +NSEEL_VMCTX (*NSEEL_VM_alloc)(); // return a handle +void (*NSEEL_VM_SetGRAM)(NSEEL_VMCTX, void **); +void (*NSEEL_VM_free)(NSEEL_VMCTX ctx); // free when done with a VM and ALL of its code have been freed, as well +void (*NSEEL_VM_SetFunctionTable)(NSEEL_VMCTX, eel_function_table *tab); // use NULL to use default (global) table +EEL_F *(*NSEEL_VM_regvar)(NSEEL_VMCTX ctx, const char *name); // register a variable (before compilation) + +void (*NSEEL_VM_SetCustomFuncThis)(NSEEL_VMCTX ctx, void *thisptr); +NSEEL_CODEHANDLE (*NSEEL_code_compile_ex)(NSEEL_VMCTX ctx, const char *code, int lineoffs, int flags); +void (*NSEEL_VM_set_var_resolver)(NSEEL_VMCTX ctx, EEL_F *(*res)(void *userctx, const char *name), void *userctx); +char *(*NSEEL_code_getcodeerror)(NSEEL_VMCTX ctx); +void (*NSEEL_code_execute)(NSEEL_CODEHANDLE code); +void (*NSEEL_code_free)(NSEEL_CODEHANDLE code); +EEL_F *(*nseel_int_register_var)(compileContext *ctx, const char *name, int isReg, const char **namePtrOut); +void (*NSEEL_VM_enumallvars)(NSEEL_VMCTX ctx, int (*func)(const char *name, EEL_F *val, void *ctx), void *userctx); +EEL_F *(*NSEEL_VM_getramptr)(NSEEL_VMCTX ctx, unsigned int offs, int *validAmt); +void ** (*eel_gmem_attach)(const char *nm, bool is_alloc); +void (*eel_fft_register)(eel_function_table*); + +struct eelStringSegmentRec { + struct eelStringSegmentRec *_next; + const char *str_start; // escaped characters, including opening/trailing characters + int str_len; +}; +void (*NSEEL_VM_SetStringFunc)(NSEEL_VMCTX ctx, + EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list), + EEL_F (*onNamedString)(void *caller_this, const char *name)); + +// call with NULL to calculate size, or non-null to generate to buffer (returning size used -- will not null terminate, caller responsibility) +int (*nseel_stringsegments_tobuf)(char *bufOut, int bufout_sz, struct eelStringSegmentRec *list); + +void *(*NSEEL_PProc_RAM)(void *data, int data_size, struct _compileContext *ctx); +void *(*NSEEL_PProc_THIS)(void *data, int data_size, struct _compileContext *ctx); + +void (*eel_enterfp)(int s[2]); +void (*eel_leavefp)(int s[2]); + + +eel_function_table g_eel_function_table; +#define NSEEL_ADDFUNC_DESTINATION (&g_eel_function_table) + +// +// adds a function that returns a value (EEL_F) +#define NSEEL_addfunc_retval(name,np,pproc,fptr) \ + NSEEL_addfunc_ret_type(name,np,1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) + +// adds a function that returns a pointer (EEL_F*) +#define NSEEL_addfunc_retptr(name,np,pproc,fptr) \ + NSEEL_addfunc_ret_type(name,np,0,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) + +// adds a void or bool function +#define NSEEL_addfunc_retbool(name,np,pproc,fptr) \ + NSEEL_addfunc_ret_type(name,np,-1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) + +// adds a function that takes min_np or more parameters (func sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms) +#define NSEEL_addfunc_varparm(name, min_np, pproc, fptr) \ + NSEEL_addfunc_varparm_ex(name,min_np,0,pproc,fptr,NSEEL_ADDFUNC_DESTINATION) + +// adds a function that takes np parameters via func: sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms) +#define NSEEL_addfunc_exparms(name, np, pproc, fptr) \ + NSEEL_addfunc_varparm_ex(name,np,1,pproc,fptr,NSEEL_ADDFUNC_DESTINATION) + +class eel_string_context_state; + +#define __NS_EELINT_H__ + +#define EEL_IMPORT_ALL(IMPORT_FUNC) \ + IMPORT_FUNC(NSEEL_addfunc_ret_type) \ + IMPORT_FUNC(NSEEL_addfunc_varparm_ex) \ + IMPORT_FUNC(NSEEL_VM_free) \ + IMPORT_FUNC(NSEEL_VM_SetFunctionTable) \ + IMPORT_FUNC(NSEEL_VM_regvar) \ + IMPORT_FUNC(NSEEL_VM_SetCustomFuncThis) \ + IMPORT_FUNC(NSEEL_code_compile_ex) \ + IMPORT_FUNC(NSEEL_code_getcodeerror) \ + IMPORT_FUNC(NSEEL_code_execute) \ + IMPORT_FUNC(NSEEL_code_free) \ + IMPORT_FUNC(NSEEL_PProc_THIS) \ + IMPORT_FUNC(NSEEL_PProc_RAM) \ + IMPORT_FUNC(NSEEL_VM_SetStringFunc) \ + IMPORT_FUNC(NSEEL_VM_enumallvars) \ + IMPORT_FUNC(NSEEL_VM_getramptr) \ + IMPORT_FUNC(NSEEL_VM_SetGRAM) \ + IMPORT_FUNC(eel_gmem_attach) \ + IMPORT_FUNC(eel_fft_register) \ + IMPORT_FUNC(nseel_stringsegments_tobuf) \ + IMPORT_FUNC(nseel_int_register_var) \ + IMPORT_FUNC(eel_leavefp) \ + IMPORT_FUNC(eel_enterfp) \ + IMPORT_FUNC(NSEEL_VM_set_var_resolver) \ + IMPORT_FUNC(NSEEL_VM_alloc) /* keep NSEEL_VM_alloc last */ + + + +/******************************************************************************************* + * END of imported EEL + ******************************************************************************************/ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_lice.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_lice.h new file mode 100644 index 000000000..ba96dfe74 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_lice.h @@ -0,0 +1,3055 @@ +#ifndef _EEL_LICE_H_ +#define _EEL_LICE_H_ + +// #define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) (((sInst*)opaque)->GetFilenameForParameter(idx,fs,p)) +// #define EEL_LICE_GET_CONTEXT(opaque) (((opaque) ? (((sInst *)opaque)->m_gfx_state) : NULL) + + +void eel_lice_register(); + +#ifdef DYNAMIC_LICE + #define LICE_IBitmap LICE_IBitmap_disabledAPI + #include "../lice/lice.h" + + #undef LICE_IBitmap + typedef void LICE_IBitmap; // prevent us from using LICE api directly, in case it ever changes + +class LICE_IFont; + +#ifdef EEL_LICE_API_ONLY +#define EEL_LICE_FUNCDEF extern +#else +#define EEL_LICE_FUNCDEF +#endif + +#define LICE_FUNCTION_VALID(x) (x) + +EEL_LICE_FUNCDEF LICE_IBitmap *(*__LICE_CreateBitmap)(int, int, int); +EEL_LICE_FUNCDEF void (*__LICE_Clear)(LICE_IBitmap *dest, LICE_pixel color); +EEL_LICE_FUNCDEF void (*__LICE_Line)(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, LICE_pixel color, float alpha, int mode, bool aa); +EEL_LICE_FUNCDEF bool (*__LICE_ClipLine)(int* pX1, int* pY1, int* pX2, int* pY2, int xLo, int yLo, int xHi, int yHi); +EEL_LICE_FUNCDEF void (*__LICE_DrawText)(LICE_IBitmap *bm, int x, int y, const char *string, + LICE_pixel color, float alpha, int mode); +EEL_LICE_FUNCDEF void (*__LICE_DrawChar)(LICE_IBitmap *bm, int x, int y, char c, + LICE_pixel color, float alpha, int mode); +EEL_LICE_FUNCDEF void (*__LICE_MeasureText)(const char *string, int *w, int *h); +EEL_LICE_FUNCDEF void (*__LICE_PutPixel)(LICE_IBitmap *bm, int x, int y, LICE_pixel color, float alpha, int mode); +EEL_LICE_FUNCDEF LICE_pixel (*__LICE_GetPixel)(LICE_IBitmap *bm, int x, int y); +EEL_LICE_FUNCDEF void (*__LICE_FillRect)(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha, int mode); +EEL_LICE_FUNCDEF void (*__LICE_DrawRect)(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha, int mode); +EEL_LICE_FUNCDEF LICE_IBitmap *(*__LICE_LoadImage)(const char* filename, LICE_IBitmap* bmp, bool tryIgnoreExtension); +EEL_LICE_FUNCDEF void (*__LICE_Blur)(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int srcx, int srcy, int srcw, int srch); // src and dest can overlap, however it may look fudgy if they do +EEL_LICE_FUNCDEF void (*__LICE_ScaledBlit)(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int dstw, int dsth, + float srcx, float srcy, float srcw, float srch, float alpha, int mode); +EEL_LICE_FUNCDEF void (*__LICE_Circle)(LICE_IBitmap* dest, float cx, float cy, float r, LICE_pixel color, float alpha, int mode, bool aa); +EEL_LICE_FUNCDEF void (*__LICE_FillCircle)(LICE_IBitmap* dest, float cx, float cy, float r, LICE_pixel color, float alpha, int mode, bool aa); +EEL_LICE_FUNCDEF void (*__LICE_FillTriangle)(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int x3, int y3, LICE_pixel color, float alpha, int mode); +EEL_LICE_FUNCDEF void (*__LICE_FillConvexPolygon)(LICE_IBitmap* dest, const int* x, const int* y, int npoints, LICE_pixel color, float alpha, int mode); +EEL_LICE_FUNCDEF void (*__LICE_RoundRect)(LICE_IBitmap *drawbm, float xpos, float ypos, float w, float h, int cornerradius, LICE_pixel col, float alpha, int mode, bool aa); +EEL_LICE_FUNCDEF void (*__LICE_Arc)(LICE_IBitmap* dest, float cx, float cy, float r, float minAngle, float maxAngle, LICE_pixel color, float alpha, int mode, bool aa); + +// if cliptosourcerect is false, then areas outside the source rect can get in (otherwise they are not drawn) +EEL_LICE_FUNCDEF void (*__LICE_RotatedBlit)(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + float srcx, float srcy, float srcw, float srch, + float angle, + bool cliptosourcerect, float alpha, int mode, + float rotxcent, float rotycent); // these coordinates are offset from the center of the image, in source pixel coordinates + + +EEL_LICE_FUNCDEF void (*__LICE_MultiplyAddRect)(LICE_IBitmap *dest, int x, int y, int w, int h, + float rsc, float gsc, float bsc, float asc, // 0-1, or -100 .. +100 if you really are insane + float radd, float gadd, float badd, float aadd); // 0-255 is the normal range on these.. of course its clamped + +EEL_LICE_FUNCDEF void (*__LICE_GradRect)(LICE_IBitmap *dest, int dstx, int dsty, int dstw, int dsth, + float ir, float ig, float ib, float ia, + float drdx, float dgdx, float dbdx, float dadx, + float drdy, float dgdy, float dbdy, float dady, + int mode); + +EEL_LICE_FUNCDEF void (*__LICE_TransformBlit2)(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + double *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates + float alpha, int mode); + +EEL_LICE_FUNCDEF void (*__LICE_DeltaBlit)(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + float srcx, float srcy, float srcw, float srch, + double dsdx, double dtdx, double dsdy, double dtdy, + double dsdxdy, double dtdxdy, + bool cliptosourcerect, float alpha, int mode); + + +#define LICE_Blur __LICE_Blur +#define LICE_Clear __LICE_Clear +#define LICE_Line __LICE_Line +#define LICE_ClipLine __LICE_ClipLine +#define LICE_FillRect __LICE_FillRect +#define LICE_DrawRect __LICE_DrawRect +#define LICE_PutPixel __LICE_PutPixel +#define LICE_GetPixel __LICE_GetPixel +#define LICE_DrawText __LICE_DrawText +#define LICE_DrawChar __LICE_DrawChar +#define LICE_MeasureText __LICE_MeasureText +#define LICE_LoadImage __LICE_LoadImage +#define LICE_RotatedBlit __LICE_RotatedBlit +#define LICE_ScaledBlit __LICE_ScaledBlit +#define LICE_MultiplyAddRect __LICE_MultiplyAddRect +#define LICE_GradRect __LICE_GradRect +#define LICE_TransformBlit2 __LICE_TransformBlit2 +#define LICE_DeltaBlit __LICE_DeltaBlit +#define LICE_Circle __LICE_Circle +#define LICE_FillCircle __LICE_FillCircle +#define LICE_FillTriangle __LICE_FillTriangle +#define LICE_FillConvexPolygon __LICE_FillConvexPolygon +#define LICE_RoundRect __LICE_RoundRect +#define LICE_Arc __LICE_Arc + +EEL_LICE_FUNCDEF HDC (*LICE__GetDC)(LICE_IBitmap *bm); +EEL_LICE_FUNCDEF int (*LICE__GetWidth)(LICE_IBitmap *bm); +EEL_LICE_FUNCDEF int (*LICE__GetHeight)(LICE_IBitmap *bm); +EEL_LICE_FUNCDEF void (*LICE__Destroy)(LICE_IBitmap *bm); +EEL_LICE_FUNCDEF bool (*LICE__resize)(LICE_IBitmap *bm, int w, int h); + +EEL_LICE_FUNCDEF void (*LICE__DestroyFont)(LICE_IFont* font); +EEL_LICE_FUNCDEF LICE_IFont *(*LICE_CreateFont)(); +EEL_LICE_FUNCDEF void (*LICE__SetFromHFont)(LICE_IFont* ifont, HFONT font, int flags); +EEL_LICE_FUNCDEF LICE_pixel (*LICE__SetTextColor)(LICE_IFont* ifont, LICE_pixel color); +EEL_LICE_FUNCDEF void (*LICE__SetTextCombineMode)(LICE_IFont* ifont, int mode, float alpha); +EEL_LICE_FUNCDEF int (*LICE__DrawText)(LICE_IFont* ifont, LICE_IBitmap *bm, const char *str, int strcnt, RECT *rect, UINT dtFlags); + +#else + +#include "../lice/lice.h" +#include "../lice/lice_text.h" + +#define LICE_FUNCTION_VALID(x) (sizeof(int) > 0) + +static HDC LICE__GetDC(LICE_IBitmap *bm) +{ + return bm->getDC(); +} +static int LICE__GetWidth(LICE_IBitmap *bm) +{ + return bm->getWidth(); +} +static int LICE__GetHeight(LICE_IBitmap *bm) +{ + return bm->getHeight(); +} +static void LICE__Destroy(LICE_IBitmap *bm) +{ + delete bm; +} +static void LICE__SetFromHFont(LICE_IFont * ifont, HFONT font, int flags) +{ + if (ifont) ifont->SetFromHFont(font,flags); +} +static LICE_pixel LICE__SetTextColor(LICE_IFont* ifont, LICE_pixel color) +{ + if (ifont) return ifont->SetTextColor(color); + return 0; +} +static void LICE__SetTextCombineMode(LICE_IFont* ifont, int mode, float alpha) +{ + if (ifont) ifont->SetCombineMode(mode, alpha); +} +static int LICE__DrawText(LICE_IFont* ifont, LICE_IBitmap *bm, const char *str, int strcnt, RECT *rect, UINT dtFlags) +{ + if (ifont) return ifont->DrawText(bm, str, strcnt, rect, dtFlags); + return 0; +} + + +static LICE_IFont *LICE_CreateFont() +{ + return new LICE_CachedFont(); +} +static void LICE__DestroyFont(LICE_IFont *bm) +{ + delete bm; +} +static bool LICE__resize(LICE_IBitmap *bm, int w, int h) +{ + return bm->resize(w,h); +} + +static LICE_IBitmap *__LICE_CreateBitmap(int mode, int w, int h) +{ + if (mode==1) return new LICE_SysBitmap(w,h); + return new LICE_MemBitmap(w,h); +} + + +#endif + +#include "../wdlutf8.h" + + +class eel_lice_state +{ +public: + + eel_lice_state(NSEEL_VMCTX vm, void *ctx, int image_slots, int font_slots); + ~eel_lice_state(); + + void resetVarsToStock() + { + if (m_gfx_a&&m_gfx_r&&m_gfx_g&&m_gfx_b) *m_gfx_r=*m_gfx_g=*m_gfx_b=*m_gfx_a=1.0; + if (m_gfx_a2) *m_gfx_a2=1.0; + if (m_gfx_dest) *m_gfx_dest=-1.0; + if (m_mouse_wheel) *m_mouse_wheel=0.0; + if (m_mouse_hwheel) *m_mouse_hwheel=0.0; + // todo: reset others? + } + + LICE_IBitmap *m_framebuffer, *m_framebuffer_extra; + int m_framebuffer_dirty; + WDL_TypedBuf m_gfx_images; + struct gfxFontStruct { + LICE_IFont *font; + char last_fontname[128]; + char actual_fontname[128]; + int last_fontsize; + int last_fontflag; + + int use_fonth; + }; + WDL_TypedBuf m_gfx_fonts; + enum { + EELFONT_FLAG_BOLD = (1<<24), + EELFONT_FLAG_ITALIC = (2<<24), + EELFONT_FLAG_UNDERLINE = (4<<24), + EELFONT_FLAG_MASK = EELFONT_FLAG_BOLD|EELFONT_FLAG_ITALIC|EELFONT_FLAG_UNDERLINE + }; + + int m_gfx_font_active; // -1 for default, otherwise index into gfx_fonts (NOTE: this differs from the exposed API, which defines 0 as default, 1-n) + LICE_IFont *GetActiveFont() { return m_gfx_font_active>=0&&m_gfx_font_active-2.0) + { + if (idx < 0.0) return m_framebuffer; + + const int a = (int)idx; + if (a >= 0 && a < m_gfx_images.GetSize()) return m_gfx_images.Get()[a]; + } + return NULL; + }; + + void SetImageDirty(LICE_IBitmap *bm) + { + if (bm == m_framebuffer && !m_framebuffer_dirty) + { + if (m_gfx_clear && *m_gfx_clear > -1.0) + { + const int a=(int)*m_gfx_clear; + if (LICE_FUNCTION_VALID(LICE_Clear)) LICE_Clear(m_framebuffer,LICE_RGBA((a&0xff),((a>>8)&0xff),((a>>16)&0xff),0)); + } + m_framebuffer_dirty=1; + } + } + + // R, G, B, A, w, h, x, y, mode(1=add,0=copy) + EEL_F *m_gfx_r, *m_gfx_g, *m_gfx_b, *m_gfx_w, *m_gfx_h, *m_gfx_a, *m_gfx_x, *m_gfx_y, *m_gfx_mode, *m_gfx_clear, *m_gfx_texth,*m_gfx_dest, *m_gfx_a2; + EEL_F *m_mouse_x, *m_mouse_y, *m_mouse_cap, *m_mouse_wheel, *m_mouse_hwheel; + EEL_F *m_gfx_ext_retina; + + NSEEL_VMCTX m_vmref; + void *m_user_ctx; + + int setup_frame(HWND hwnd, RECT r, int _mouse_x=0, int _mouse_y=0, int has_dpi=0); // mouse_x/y used only if hwnd is NULL + void finish_draw(); + + void gfx_lineto(EEL_F xpos, EEL_F ypos, EEL_F aaflag); + void gfx_rectto(EEL_F xpos, EEL_F ypos); + void gfx_line(int np, EEL_F **parms); + void gfx_rect(int np, EEL_F **parms); + void gfx_roundrect(int np, EEL_F **parms); + void gfx_arc(int np, EEL_F **parms); + void gfx_set(int np, EEL_F **parms); + void gfx_grad_or_muladd_rect(int mode, int np, EEL_F **parms); + void gfx_setpixel(EEL_F r, EEL_F g, EEL_F b); + void gfx_getpixel(EEL_F *r, EEL_F *g, EEL_F *b); + void gfx_drawnumber(EEL_F n, EEL_F ndigits); + void gfx_drawchar(EEL_F ch); + void gfx_getimgdim(EEL_F img, EEL_F *w, EEL_F *h); + EEL_F gfx_setimgdim(int img, EEL_F *w, EEL_F *h); + void gfx_blurto(EEL_F x, EEL_F y); + void gfx_blit(EEL_F img, EEL_F scale, EEL_F rotate); + void gfx_blitext(EEL_F img, EEL_F *coords, EEL_F angle); + void gfx_blitext2(int np, EEL_F **parms, int mode); // 0=blit, 1=deltablit + void gfx_transformblit(EEL_F **parms, int div_w, int div_h, EEL_F *tab); // parms[0]=src, 1-4=x,y,w,h + void gfx_circle(float x, float y, float r, bool fill, bool aaflag); + void gfx_triangle(EEL_F** parms, int nparms); + void gfx_drawstr(void *opaque, EEL_F **parms, int nparms, int formatmode); // formatmode=1 for format, 2 for purely measure no format, 3 for measure char + EEL_F gfx_loadimg(void *opaque, int img, EEL_F loadFrom); + EEL_F gfx_setfont(void *opaque, int np, EEL_F **parms); + EEL_F gfx_getfont(void *opaque, int np, EEL_F **parms); + EEL_F gfx_getdropfile(void *opaque, int np, EEL_F **parms); + + LICE_pixel getCurColor(); + int getCurMode(); + int getCurModeForBlit(bool isFBsrc); + +#ifdef EEL_LICE_WANT_STANDALONE + HWND create_wnd(HWND par, int isChild); + HWND hwnd_standalone; + int hwnd_standalone_kb_state[32]; // pressed keys, if any + + // these have to be **parms because of the hack for getting string from parm index + EEL_F gfx_showmenu(void* opaque, EEL_F** parms, int nparms); + EEL_F gfx_setcursor(void* opaque, EEL_F** parms, int nparms); + + int m_kb_queue[64]; + unsigned char m_kb_queue_valid; + unsigned char m_kb_queue_pos; + HCURSOR m_cursor; + int m_cursor_resid; +#ifdef EEL_LICE_LOADTHEMECURSOR + char m_cursor_name[128]; +#endif + +#ifndef EEL_LICE_STANDALONE_NOINITQUIT + RECT m_last_undocked_r; +#endif + +#endif + int m_has_cap; // high 16 bits are current capture state, low 16 bits are temporary flags from mousedown + bool m_has_had_getch; // set on first gfx_getchar(), makes mouse_cap updated with modifiers even when no mouse click is down + + WDL_PtrList m_ddrop_files; +}; + + +#ifndef EEL_LICE_API_ONLY + +eel_lice_state::eel_lice_state(NSEEL_VMCTX vm, void *ctx, int image_slots, int font_slots) +{ +#ifdef EEL_LICE_WANT_STANDALONE + hwnd_standalone=NULL; + memset(hwnd_standalone_kb_state,0,sizeof(hwnd_standalone_kb_state)); + m_kb_queue_valid=0; + m_cursor_resid=0; + m_cursor = NULL; +#ifndef EEL_LICE_STANDALONE_NOINITQUIT + memset(&m_last_undocked_r,0,sizeof(m_last_undocked_r)); +#endif + +#ifdef EEL_LICE_LOADTHEMECURSOR + m_cursor_name[0]=0; +#endif +#endif + m_user_ctx=ctx; + m_vmref= vm; + m_gfx_font_active=-1; + m_gfx_fonts.Resize(font_slots); + memset(m_gfx_fonts.Get(),0,m_gfx_fonts.GetSize()*sizeof(m_gfx_fonts.Get()[0])); + + m_gfx_images.Resize(image_slots); + memset(m_gfx_images.Get(),0,m_gfx_images.GetSize()*sizeof(m_gfx_images.Get()[0])); + m_framebuffer=m_framebuffer_extra=0; + m_framebuffer_dirty=0; + + m_gfx_r = NSEEL_VM_regvar(vm,"gfx_r"); + m_gfx_g = NSEEL_VM_regvar(vm,"gfx_g"); + m_gfx_b = NSEEL_VM_regvar(vm,"gfx_b"); + m_gfx_a = NSEEL_VM_regvar(vm,"gfx_a"); + m_gfx_a2 = NSEEL_VM_regvar(vm,"gfx_a2"); + + m_gfx_w = NSEEL_VM_regvar(vm,"gfx_w"); + m_gfx_h = NSEEL_VM_regvar(vm,"gfx_h"); + m_gfx_x = NSEEL_VM_regvar(vm,"gfx_x"); + m_gfx_y = NSEEL_VM_regvar(vm,"gfx_y"); + m_gfx_mode = NSEEL_VM_regvar(vm,"gfx_mode"); + m_gfx_clear = NSEEL_VM_regvar(vm,"gfx_clear"); + m_gfx_texth = NSEEL_VM_regvar(vm,"gfx_texth"); + m_gfx_dest = NSEEL_VM_regvar(vm,"gfx_dest"); + m_gfx_ext_retina = NSEEL_VM_regvar(vm,"gfx_ext_retina"); + + m_mouse_x = NSEEL_VM_regvar(vm,"mouse_x"); + m_mouse_y = NSEEL_VM_regvar(vm,"mouse_y"); + m_mouse_cap = NSEEL_VM_regvar(vm,"mouse_cap"); + m_mouse_wheel=NSEEL_VM_regvar(vm,"mouse_wheel"); + m_mouse_hwheel=NSEEL_VM_regvar(vm,"mouse_hwheel"); + + if (m_gfx_texth) *m_gfx_texth=8; + + m_has_cap=0; + m_has_had_getch=false; +} +eel_lice_state::~eel_lice_state() +{ +#ifdef EEL_LICE_WANT_STANDALONE + if (hwnd_standalone) DestroyWindow(hwnd_standalone); +#endif + if (LICE_FUNCTION_VALID(LICE__Destroy)) + { + LICE__Destroy(m_framebuffer_extra); + LICE__Destroy(m_framebuffer); + int x; + for (x=0;x>4)&0xf; + if (sm > LICE_BLIT_MODE_COPY && sm <= LICE_BLIT_MODE_HSVADJ) return sm; + + return (gmode&1) ? LICE_BLIT_MODE_ADD : LICE_BLIT_MODE_COPY; +} +int eel_lice_state::getCurModeForBlit(bool isFBsrc) +{ + const int gmode = (int) (*m_gfx_mode); + + const int sm=(gmode>>4)&0xf; + + int mode; + if (sm > LICE_BLIT_MODE_COPY && sm <= LICE_BLIT_MODE_HSVADJ) mode=sm; + else mode=((gmode&1) ? LICE_BLIT_MODE_ADD : LICE_BLIT_MODE_COPY); + + + if (!isFBsrc && !(gmode&2)) mode|=LICE_BLIT_USE_ALPHA; + if (!(gmode&4)) mode|=LICE_BLIT_FILTER_BILINEAR; + + return mode; +} +LICE_pixel eel_lice_state::getCurColor() +{ + int red=(int) (*m_gfx_r*255.0); + int green=(int) (*m_gfx_g*255.0); + int blue=(int) (*m_gfx_b*255.0); + int a2=(int) (*m_gfx_a2*255.0); + if (red<0) red=0;else if (red>255)red=255; + if (green<0) green=0;else if (green>255)green=255; + if (blue<0) blue=0; else if (blue>255) blue=255; + if (a2<0) a2=0; else if (a2>255) a2=255; + return LICE_RGBA(red,green,blue,a2); +} + + +static EEL_F * NSEEL_CGEN_CALL _gfx_lineto(void *opaque, EEL_F *xpos, EEL_F *ypos, EEL_F *useaa) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_lineto(*xpos, *ypos, *useaa); + return xpos; +} +static EEL_F * NSEEL_CGEN_CALL _gfx_lineto2(void *opaque, EEL_F *xpos, EEL_F *ypos) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_lineto(*xpos, *ypos, 1.0f); + return xpos; +} + +static EEL_F * NSEEL_CGEN_CALL _gfx_rectto(void *opaque, EEL_F *xpos, EEL_F *ypos) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_rectto(*xpos, *ypos); + return xpos; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_line(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_line((int)np,parms); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_rect(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_rect((int)np,parms); + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL _gfx_roundrect(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_roundrect((int)np,parms); + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL _gfx_arc(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_arc((int)np,parms); + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL _gfx_set(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_set((int)np,parms); + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL _gfx_gradrect(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_grad_or_muladd_rect(0,(int)np,parms); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_muladdrect(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_grad_or_muladd_rect(1,(int)np,parms); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_deltablit(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_blitext2((int)np,parms,1); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_transformblit(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { +#ifndef EEL_LICE_NO_RAM + const int divw = (int) (parms[5][0]+0.5); + const int divh = (int) (parms[6][0]+0.5); + if (divw < 1 || divh < 1) return 0.0; + const int sz = divw*divh*2; + +#ifdef EEL_LICE_RAMFUNC + EEL_F *d = EEL_LICE_RAMFUNC(opaque,7,sz); + if (!d) return 0.0; +#else + EEL_F **blocks = ctx->m_vmref ? ((compileContext*)ctx->m_vmref)->ram_state->blocks : 0; + if (!blocks || np < 8) return 0.0; + + const int addr1= (int) (parms[7][0]+0.5); + EEL_F *d=__NSEEL_RAMAlloc(blocks,addr1); + if (sz>NSEEL_RAM_ITEMSPERBLOCK) + { + int x; + for(x=NSEEL_RAM_ITEMSPERBLOCK;xgfx_transformblit(parms,divw,divh,d); +#endif + } + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_circle(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + bool aa = true, fill = false; + if (np>3) fill = parms[3][0] > 0.5; + if (np>4) aa = parms[4][0] > 0.5; + if (ctx) ctx->gfx_circle((float)parms[0][0], (float)parms[1][0], (float)parms[2][0], fill, aa); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_triangle(void* opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_triangle(parms, (int)np); + return 0.0; +} + +static EEL_F * NSEEL_CGEN_CALL _gfx_drawnumber(void *opaque, EEL_F *n, EEL_F *nd) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_drawnumber(*n, *nd); + return n; +} + +static EEL_F * NSEEL_CGEN_CALL _gfx_drawchar(void *opaque, EEL_F *n) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_drawchar(*n); + return n; +} + +static EEL_F * NSEEL_CGEN_CALL _gfx_measurestr(void *opaque, EEL_F *str, EEL_F *xOut, EEL_F *yOut) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + EEL_F *p[3]={str,xOut,yOut}; + ctx->gfx_drawstr(opaque,p,3,2); + } + return str; +} +static EEL_F * NSEEL_CGEN_CALL _gfx_measurechar(void *opaque, EEL_F *str, EEL_F *xOut, EEL_F *yOut) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + EEL_F *p[3]={str,xOut,yOut}; + ctx->gfx_drawstr(opaque,p,3,3); + } + return str; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_drawstr(void *opaque, INT_PTR nparms, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_drawstr(opaque,parms,(int)nparms,0); + return parms[0][0]; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_printf(void *opaque, INT_PTR nparms, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx && nparms>0) + { + EEL_F v= **parms; + ctx->gfx_drawstr(opaque,parms,(int)nparms,1); + return v; + } + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_showmenu(void* opaque, INT_PTR nparms, EEL_F **parms) +{ + eel_lice_state* ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) return ctx->gfx_showmenu(opaque, parms, (int)nparms); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_setcursor(void* opaque, INT_PTR nparms, EEL_F **parms) +{ + eel_lice_state* ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) return ctx->gfx_setcursor(opaque, parms, (int)nparms); + return 0.0; +} + +static EEL_F * NSEEL_CGEN_CALL _gfx_setpixel(void *opaque, EEL_F *r, EEL_F *g, EEL_F *b) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_setpixel(*r, *g, *b); + return r; +} + +static EEL_F * NSEEL_CGEN_CALL _gfx_getpixel(void *opaque, EEL_F *r, EEL_F *g, EEL_F *b) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_getpixel(r, g, b); + return r; +} + +static EEL_F * NSEEL_CGEN_CALL _gfx_blit(void *opaque, EEL_F *img, EEL_F *scale, EEL_F *rotate) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_blit(*img,*scale,*rotate); + return img; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_setfont(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) return ctx->gfx_setfont(opaque,(int)np,parms); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_getfont(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + const int idx=ctx->m_gfx_font_active; + if (idx>=0 && idx < ctx->m_gfx_fonts.GetSize()) + { + eel_lice_state::gfxFontStruct* f=ctx->m_gfx_fonts.Get()+idx; + + EEL_STRING_MUTEXLOCK_SCOPE + +#ifdef NOT_EEL_STRING_UPDATE_STRING + NOT_EEL_STRING_UPDATE_STRING(parms[0][0],f->actual_fontname); +#else + WDL_FastString *fs=NULL; + EEL_STRING_GET_FOR_WRITE(parms[0][0],&fs); + if (fs) fs->Set(f->actual_fontname); +#endif + } + return idx; + } + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_blit2(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx && np>=3) + { + ctx->gfx_blitext2((int)np,parms,0); + return *(parms[0]); + } + return 0.0; +} + +static EEL_F * NSEEL_CGEN_CALL _gfx_blitext(void *opaque, EEL_F *img, EEL_F *coordidx, EEL_F *rotate) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { +#ifndef EEL_LICE_NO_RAM +#ifdef EEL_LICE_RAMFUNC + EEL_F *buf = EEL_LICE_RAMFUNC(opaque,1,10); + if (!buf) return img; +#else + EEL_F fc = *coordidx; + if (fc < -0.5 || fc >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) return img; + int a=(int)fc; + if (a<0) return img; + + EEL_F buf[10]; + int x; + EEL_F **blocks = ctx->m_vmref ? ((compileContext*)ctx->m_vmref)->ram_state->blocks : 0; + if (!blocks) return img; + for (x = 0;x < 10; x ++) + { + EEL_F *d=__NSEEL_RAMAlloc(blocks,a++); + if (!d || d==&nseel_ramalloc_onfail) return img; + buf[x]=*d; + } +#endif + // read megabuf + ctx->gfx_blitext(*img,buf,*rotate); +#endif + } + return img; +} + + +static EEL_F * NSEEL_CGEN_CALL _gfx_blurto(void *opaque, EEL_F *x, EEL_F *y) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_blurto(*x,*y); + return x; +} + +static EEL_F * NSEEL_CGEN_CALL _gfx_getimgdim(void *opaque, EEL_F *img, EEL_F *w, EEL_F *h) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) ctx->gfx_getimgdim(*img,w,h); + return img; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_loadimg(void *opaque, EEL_F *img, EEL_F *fr) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) return ctx->gfx_loadimg(opaque,(int)*img,*fr); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_getdropfile(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) return ctx->gfx_getdropfile(opaque,(int) np, parms); + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL _gfx_setimgdim(void *opaque, EEL_F *img, EEL_F *w, EEL_F *h) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) return ctx->gfx_setimgdim((int)*img,w,h); + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_getsyscol(void* ctxe, INT_PTR np, EEL_F **parms) +{ + return (EEL_F)LICE_RGBA_FROMNATIVE(GetSysColor(COLOR_3DFACE)); +} + +void eel_lice_state::gfx_lineto(EEL_F xpos, EEL_F ypos, EEL_F aaflag) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_lineto"); + if (!dest) return; + + int x1=(int)floor(xpos),y1=(int)floor(ypos),x2=(int)floor(*m_gfx_x), y2=(int)floor(*m_gfx_y); + if (LICE_FUNCTION_VALID(LICE__GetWidth) && LICE_FUNCTION_VALID(LICE__GetHeight) && LICE_FUNCTION_VALID(LICE_Line) && + LICE_FUNCTION_VALID(LICE_ClipLine) && + LICE_ClipLine(&x1,&y1,&x2,&y2,0,0,LICE__GetWidth(dest),LICE__GetHeight(dest))) + { + SetImageDirty(dest); + LICE_Line(dest,x1,y1,x2,y2,getCurColor(),(float) *m_gfx_a,getCurMode(),aaflag > 0.5); + } + *m_gfx_x = xpos; + *m_gfx_y = ypos; + +} + +void eel_lice_state::gfx_circle(float x, float y, float r, bool fill, bool aaflag) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_circle"); + if (!dest) return; + + if (LICE_FUNCTION_VALID(LICE_Circle) && LICE_FUNCTION_VALID(LICE_FillCircle)) + { + SetImageDirty(dest); + if(fill) + LICE_FillCircle(dest, x, y, r, getCurColor(), (float) *m_gfx_a, getCurMode(), aaflag); + else + LICE_Circle(dest, x, y, r, getCurColor(), (float) *m_gfx_a, getCurMode(), aaflag); + } +} + +void eel_lice_state::gfx_triangle(EEL_F** parms, int np) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest, "gfx_triangle"); + if (np >= 6) + { + np &= ~1; + SetImageDirty(dest); + if (np == 6) + { + if (!LICE_FUNCTION_VALID(LICE_FillTriangle)) return; + + LICE_FillTriangle(dest, (int)parms[0][0], (int)parms[1][0], (int)parms[2][0], (int)parms[3][0], + (int)parms[4][0], (int)parms[5][0], getCurColor(), (float)*m_gfx_a, getCurMode()); + } + else + { + if (!LICE_FUNCTION_VALID(LICE_FillConvexPolygon)) return; + + const int maxpt = 512; + const int n = wdl_min(np/2, maxpt); + int i, rdi=0; + int x[maxpt], y[maxpt]; + for (i=0; i < n; i++) + { + x[i]=(int)parms[rdi++][0]; + y[i]=(int)parms[rdi++][0]; + } + + LICE_FillConvexPolygon(dest, x, y, n, getCurColor(), (float)*m_gfx_a, getCurMode()); + } + } +} + +void eel_lice_state::gfx_rectto(EEL_F xpos, EEL_F ypos) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_rectto"); + if (!dest) return; + + EEL_F x1=xpos,y1=ypos,x2=*m_gfx_x, y2=*m_gfx_y; + if (x2 0.5 && y2-y1 > 0.5) + { + SetImageDirty(dest); + LICE_FillRect(dest,(int)x1,(int)y1,(int)(x2-x1),(int)(y2-y1),getCurColor(),(float)*m_gfx_a,getCurMode()); + } + *m_gfx_x = xpos; + *m_gfx_y = ypos; +} + + +void eel_lice_state::gfx_line(int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_line"); + if (!dest) return; + + int x1=(int)floor(parms[0][0]),y1=(int)floor(parms[1][0]),x2=(int)floor(parms[2][0]), y2=(int)floor(parms[3][0]); + if (LICE_FUNCTION_VALID(LICE__GetWidth) && + LICE_FUNCTION_VALID(LICE__GetHeight) && + LICE_FUNCTION_VALID(LICE_Line) && + LICE_FUNCTION_VALID(LICE_ClipLine) && LICE_ClipLine(&x1,&y1,&x2,&y2,0,0,LICE__GetWidth(dest),LICE__GetHeight(dest))) + { + SetImageDirty(dest); + LICE_Line(dest,x1,y1,x2,y2,getCurColor(),(float)*m_gfx_a,getCurMode(),np< 5 || parms[4][0] > 0.5); + } +} + +void eel_lice_state::gfx_rect(int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_rect"); + if (!dest) return; + + int x1=(int)floor(parms[0][0]),y1=(int)floor(parms[1][0]),w=(int)floor(parms[2][0]),h=(int)floor(parms[3][0]); + int filled=(np < 5 || parms[4][0] > 0.5); + + if (LICE_FUNCTION_VALID(LICE_FillRect) && LICE_FUNCTION_VALID(LICE_DrawRect) && w>0 && h>0) + { + SetImageDirty(dest); + if (filled) LICE_FillRect(dest,x1,y1,w,h,getCurColor(),(float)*m_gfx_a,getCurMode()); + else LICE_DrawRect(dest, x1, y1, w-1, h-1, getCurColor(), (float)*m_gfx_a, getCurMode()); + } +} + +void eel_lice_state::gfx_roundrect(int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_roundrect"); + if (!dest) return; + + const bool aa = np <= 5 || parms[5][0]>0.5; + + if (LICE_FUNCTION_VALID(LICE_RoundRect) && parms[2][0]>0 && parms[3][0]>0) + { + SetImageDirty(dest); + LICE_RoundRect(dest, (float)parms[0][0], (float)parms[1][0], (float)parms[2][0], (float)parms[3][0], (int)parms[4][0], getCurColor(), (float)*m_gfx_a, getCurMode(), aa); + } +} + +void eel_lice_state::gfx_arc(int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_arc"); + if (!dest) return; + + const bool aa = np <= 5 || parms[5][0]>0.5; + + if (LICE_FUNCTION_VALID(LICE_Arc)) + { + SetImageDirty(dest); + LICE_Arc(dest, (float)parms[0][0], (float)parms[1][0], (float)parms[2][0], (float)parms[3][0], (float)parms[4][0], getCurColor(), (float)*m_gfx_a, getCurMode(), aa); + } +} + +void eel_lice_state::gfx_grad_or_muladd_rect(int whichmode, int np, EEL_F **parms) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,whichmode==0?"gfx_gradrect":"gfx_muladdrect"); + if (!dest) return; + + const int x1=(int)floor(parms[0][0]),y1=(int)floor(parms[1][0]),w=(int)floor(parms[2][0]), h=(int)floor(parms[3][0]); + + if (w>0 && h>0) + { + SetImageDirty(dest); + if (whichmode==0 && LICE_FUNCTION_VALID(LICE_GradRect) && np > 7) + { + LICE_GradRect(dest,x1,y1,w,h,(float)parms[4][0],(float)parms[5][0],(float)parms[6][0],(float)parms[7][0], + np > 8 ? (float)parms[8][0]:0.0f, np > 9 ? (float)parms[9][0]:0.0f, np > 10 ? (float)parms[10][0]:0.0f, np > 11 ? (float)parms[11][0]:0.0f, + np > 12 ? (float)parms[12][0]:0.0f, np > 13 ? (float)parms[13][0]:0.0f, np > 14 ? (float)parms[14][0]:0.0f, np > 15 ? (float)parms[15][0]:0.0f, + getCurMode()); + } + else if (whichmode==1 && LICE_FUNCTION_VALID(LICE_MultiplyAddRect) && np > 6) + { + const double sc = 255.0; + LICE_MultiplyAddRect(dest,x1,y1,w,h,(float)parms[4][0],(float)parms[5][0],(float)parms[6][0],np>7 ? (float)parms[7][0]:1.0f, + (float)(np > 8 ? sc*parms[8][0]:0.0), (float)(np > 9 ? sc*parms[9][0]:0.0), (float)(np > 10 ? sc*parms[10][0]:0.0), (float)(np > 11 ? sc*parms[11][0]:0.0)); + } + } +} + + + +void eel_lice_state::gfx_setpixel(EEL_F r, EEL_F g, EEL_F b) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_setpixel"); + if (!dest) return; + + int red=(int) (r*255.0); + int green=(int) (g*255.0); + int blue=(int) (b*255.0); + if (red<0) red=0;else if (red>255)red=255; + if (green<0) green=0;else if (green>255)green=255; + if (blue<0) blue=0; else if (blue>255) blue=255; + + if (LICE_FUNCTION_VALID(LICE_PutPixel)) + { + SetImageDirty(dest); + LICE_PutPixel(dest,(int)*m_gfx_x, (int)*m_gfx_y,LICE_RGBA(red,green,blue,255), (float)*m_gfx_a,getCurMode()); + } +} + +void eel_lice_state::gfx_getimgdim(EEL_F img, EEL_F *w, EEL_F *h) +{ + *w=*h=0; +#ifdef DYNAMIC_LICE + if (!LICE__GetWidth || !LICE__GetHeight) return; +#endif + + LICE_IBitmap *bm=GetImageForIndex(img,"gfx_getimgdim"); + if (bm) + { + *w=LICE__GetWidth(bm); + *h=LICE__GetHeight(bm); + } +} + +EEL_F eel_lice_state::gfx_getdropfile(void *opaque, int np, EEL_F **parms) +{ + const int idx = (int) parms[0][0]; + if (idx<0) m_ddrop_files.Empty(true,free); + if (idx < 0 || idx >= m_ddrop_files.GetSize()) return 0.0; + +#ifdef NOT_EEL_STRING_UPDATE_STRING + NOT_EEL_STRING_UPDATE_STRING(parms[1][0],m_ddrop_files.Get(idx)); +#else + if (np > 1) + { + EEL_STRING_MUTEXLOCK_SCOPE + WDL_FastString *fs=NULL; + EEL_STRING_GET_FOR_WRITE(parms[1][0], &fs); + if (fs) fs->Set(m_ddrop_files.Get(idx)); + } +#endif + return 1.0; +} + +EEL_F eel_lice_state::gfx_loadimg(void *opaque, int img, EEL_F loadFrom) +{ +#ifdef DYNAMIC_LICE + if (!__LICE_LoadImage || !LICE__Destroy) return 0.0; +#endif + + if (img >= 0 && img < m_gfx_images.GetSize()) + { + WDL_FastString fs; + bool ok = EEL_LICE_GET_FILENAME_FOR_STRING(loadFrom,&fs,0); + + if (ok && fs.GetLength()) + { + LICE_IBitmap *bm = LICE_LoadImage(fs.Get(),NULL,false); + if (bm) + { + LICE__Destroy(m_gfx_images.Get()[img]); + m_gfx_images.Get()[img]=bm; + return img; + } + } + } + return -1.0; + +} + +EEL_F eel_lice_state::gfx_setimgdim(int img, EEL_F *w, EEL_F *h) +{ + int rv=0; +#ifdef DYNAMIC_LICE + if (!LICE__resize ||!LICE__GetWidth || !LICE__GetHeight||!__LICE_CreateBitmap) return 0.0; +#endif + + int use_w = (int)*w; + int use_h = (int)*h; + if (use_w<1 || use_h < 1) use_w=use_h=0; + if (use_w > 8192) use_w=8192; + if (use_h > 8192) use_h=8192; + + LICE_IBitmap *bm=NULL; + if (img >= 0 && img < m_gfx_images.GetSize()) + { + bm=m_gfx_images.Get()[img]; + if (!bm) + { + m_gfx_images.Get()[img] = bm = __LICE_CreateBitmap(1,use_w,use_h); + rv=!!bm; + } + else + { + rv=LICE__resize(bm,use_w,use_h); + } + } + + return rv?1.0:0.0; +} + +void eel_lice_state::gfx_blurto(EEL_F x, EEL_F y) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_blurto"); + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_Blur +#endif + ) return; + + SetImageDirty(dest); + + int srcx = (int)x; + int srcy = (int)y; + int srcw=(int) (*m_gfx_x-x); + int srch=(int) (*m_gfx_y-y); + if (srch < 0) { srch=-srch; srcy = (int)*m_gfx_y; } + if (srcw < 0) { srcw=-srcw; srcx = (int)*m_gfx_x; } + LICE_Blur(dest,dest,srcx,srcy,srcx,srcy,srcw,srch); + *m_gfx_x = x; + *m_gfx_y = y; +} + +static bool CoordsSrcDestOverlap(EEL_F *coords) +{ + if (coords[0]+coords[2] < coords[4]) return false; + if (coords[0] > coords[4] + coords[6]) return false; + if (coords[1]+coords[3] < coords[5]) return false; + if (coords[1] > coords[5] + coords[7]) return false; + return true; +} + +void eel_lice_state::gfx_transformblit(EEL_F **parms, int div_w, int div_h, EEL_F *tab) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_transformblit"); + + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_ScaledBlit || !LICE_TransformBlit2 ||!LICE__GetWidth||!LICE__GetHeight +#endif + ) return; + + LICE_IBitmap *bm=GetImageForIndex(parms[0][0],"gfx_transformblit:src"); + if (!bm) return; + + const int bmw=LICE__GetWidth(bm); + const int bmh=LICE__GetHeight(bm); + + const bool isFromFB = bm==m_framebuffer; + + SetImageDirty(dest); + + if (bm == dest) + { + if (!m_framebuffer_extra && LICE_FUNCTION_VALID(__LICE_CreateBitmap)) m_framebuffer_extra=__LICE_CreateBitmap(0,bmw,bmh); + if (m_framebuffer_extra) + { + + LICE__resize(bm=m_framebuffer_extra,bmw,bmh); + LICE_ScaledBlit(bm,dest, // copy the entire image + 0,0,bmw,bmh, + 0.0f,0.0f,(float)bmw,(float)bmh, + 1.0f,LICE_BLIT_MODE_COPY); + } + } + LICE_TransformBlit2(dest,bm,(int)floor(parms[1][0]),(int)floor(parms[2][0]),(int)floor(parms[3][0]),(int)floor(parms[4][0]),tab,div_w,div_h, (float)*m_gfx_a,getCurModeForBlit(isFromFB)); +} + +EEL_F eel_lice_state::gfx_setfont(void *opaque, int np, EEL_F **parms) +{ + int a = np>0 ? ((int)floor(parms[0][0]))-1 : -1; + + if (a>=0 && a < m_gfx_fonts.GetSize()) + { + gfxFontStruct *s = m_gfx_fonts.Get()+a; + if (np>1 && LICE_FUNCTION_VALID(LICE_CreateFont) && LICE_FUNCTION_VALID(LICE__SetFromHFont)) + { + const int sz=np>2 ? (int)parms[2][0] : 10; + + bool doCreate=false; + int fontflag=0; + if (!s->font) s->actual_fontname[0]=0; + + { + EEL_STRING_MUTEXLOCK_SCOPE + + const char *face=EEL_STRING_GET_FOR_INDEX(parms[1][0],NULL); + #ifdef EEL_STRING_DEBUGOUT + if (!face) EEL_STRING_DEBUGOUT("gfx_setfont: invalid string identifier %f",parms[1][0]); + #endif + if (!face || !*face) face="Arial"; + + { + unsigned int c = np > 3 ? (unsigned int) parms[3][0] : 0; + while (c) + { + switch (toupper(c&0xff)) + { + case 'B': fontflag|=EELFONT_FLAG_BOLD; break; + case 'I': fontflag|=EELFONT_FLAG_ITALIC; break; + case 'U': fontflag|=EELFONT_FLAG_UNDERLINE; break; + case 'R': fontflag|=16; break; //LICE_FONT_FLAG_FX_BLUR + case 'V': fontflag|=32; break; //LICE_FONT_FLAG_FX_INVERT + case 'M': fontflag|=64; break; //LICE_FONT_FLAG_FX_MONO + case 'S': fontflag|=128; break; //LICE_FONT_FLAG_FX_SHADOW + case 'O': fontflag|=256; break; //LICE_FONT_FLAG_FX_OUTLINE + case 'Z': fontflag|=1; break; //LICE_FONT_FLAG_VERTICAL + case 'Y': fontflag|=1|2; break; //LICE_FONT_FLAG_VERTICAL|LICE_FONT_FLAG_VERTICAL_BOTTOMUP + } + c>>=8; + } + } + + + if (fontflag != s->last_fontflag || sz!=s->last_fontsize || strncmp(s->last_fontname,face,sizeof(s->last_fontname)-1)) + { + lstrcpyn_safe(s->last_fontname,face,sizeof(s->last_fontname)); + s->last_fontsize=sz; + s->last_fontflag=fontflag; + doCreate=1; + } + } + + if (doCreate) + { + s->actual_fontname[0]=0; + if (!s->font) s->font=LICE_CreateFont(); + if (s->font) + { + const int fw = (fontflag&EELFONT_FLAG_BOLD) ? FW_BOLD : FW_NORMAL; + const bool italic = !!(fontflag&EELFONT_FLAG_ITALIC); + const bool underline = !!(fontflag&EELFONT_FLAG_UNDERLINE); + HFONT hf=NULL; +#if defined(_WIN32) && !defined(WDL_NO_SUPPORT_UTF8) + WCHAR wf[256]; + if (WDL_DetectUTF8(s->last_fontname)>0 && + GetVersion()<0x80000000 && + MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,s->last_fontname,-1,wf,256)) + { + hf = CreateFontW(sz,0,0,0,fw,italic,underline,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,wf); + } +#endif + if (!hf) hf = CreateFont(sz,0,0,0,fw,italic,underline,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,s->last_fontname); + + if (!hf) + { + s->use_fonth=0; // disable this font + } + else + { + TEXTMETRIC tm; + tm.tmHeight = sz; + + if (!m_framebuffer && LICE_FUNCTION_VALID(__LICE_CreateBitmap)) m_framebuffer=__LICE_CreateBitmap(1,64,64); + + if (m_framebuffer && LICE_FUNCTION_VALID(LICE__GetDC)) + { + HGDIOBJ oldFont = 0; + HDC hdc=LICE__GetDC(m_framebuffer); + if (hdc) + { + oldFont = SelectObject(hdc,hf); + GetTextMetrics(hdc,&tm); + +#if defined(_WIN32) && !defined(WDL_NO_SUPPORT_UTF8) + if (GetVersion()<0x80000000 && + GetTextFaceW(hdc,sizeof(wf)/sizeof(wf[0]),wf) && + WideCharToMultiByte(CP_UTF8,0,wf,-1,s->actual_fontname,sizeof(s->actual_fontname),NULL,NULL)) + { + s->actual_fontname[sizeof(s->actual_fontname)-1]=0; + } + else +#endif + GetTextFace(hdc, sizeof(s->actual_fontname), s->actual_fontname); + SelectObject(hdc,oldFont); + } + } + + s->use_fonth=wdl_max(tm.tmHeight,1); + LICE__SetFromHFont(s->font,hf, (fontflag & ~EELFONT_FLAG_MASK) | 512 /*LICE_FONT_FLAG_OWNS_HFONT*/); + } + } + } + } + + + if (s->font && s->use_fonth) + { + m_gfx_font_active=a; + if (m_gfx_texth) *m_gfx_texth=s->use_fonth; + return 1.0; + } + // try to init this font + } + #ifdef EEL_STRING_DEBUGOUT + if (a >= m_gfx_fonts.GetSize()) EEL_STRING_DEBUGOUT("gfx_setfont: invalid font %d specified",a); + #endif + + if (a<0||a>=m_gfx_fonts.GetSize()||!m_gfx_fonts.Get()[a].font) + { + m_gfx_font_active=-1; + if (m_gfx_texth) *m_gfx_texth=8; + return 1.0; + } + return 0.0; +} + +void eel_lice_state::gfx_blitext2(int np, EEL_F **parms, int blitmode) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_blitext2"); + + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_ScaledBlit || !LICE_RotatedBlit||!LICE__GetWidth||!LICE__GetHeight +#endif + ) return; + + LICE_IBitmap *bm=GetImageForIndex(parms[0][0],"gfx_blitext2:src"); + if (!bm) return; + + const int bmw=LICE__GetWidth(bm); + const int bmh=LICE__GetHeight(bm); + + // 0=img, 1=scale, 2=rotate + double coords[8]; + const double sc = blitmode==0 ? parms[1][0] : 1.0, + angle = blitmode==0 ? parms[2][0] : 0.0; + if (blitmode==0) + { + parms+=2; + np -= 2; + } + + coords[0]=np > 1 ? parms[1][0] : 0.0f; + coords[1]=np > 2 ? parms[2][0] : 0.0f; + coords[2]=np > 3 ? parms[3][0] : bmw; + coords[3]=np > 4 ? parms[4][0] : bmh; + coords[4]=np > 5 ? parms[5][0] : *m_gfx_x; + coords[5]=np > 6 ? parms[6][0] : *m_gfx_y; + coords[6]=np > 7 ? parms[7][0] : coords[2]*sc; + coords[7]=np > 8 ? parms[8][0] : coords[3]*sc; + + const bool isFromFB = bm == m_framebuffer; + SetImageDirty(dest); + + if (bm == dest && CoordsSrcDestOverlap(coords)) + { + if (!m_framebuffer_extra && LICE_FUNCTION_VALID(__LICE_CreateBitmap)) m_framebuffer_extra=__LICE_CreateBitmap(0,bmw,bmh); + if (m_framebuffer_extra) + { + + LICE__resize(bm=m_framebuffer_extra,bmw,bmh); + LICE_ScaledBlit(bm,dest, // copy the source portion + (int)coords[0],(int)coords[1],(int)coords[2],(int)coords[3], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], + 1.0f,LICE_BLIT_MODE_COPY); + } + } + + if (blitmode==1) + { + if (LICE_FUNCTION_VALID(LICE_DeltaBlit)) + LICE_DeltaBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], + np > 9 ? (float)parms[9][0]:1.0f, // dsdx + np > 10 ? (float)parms[10][0]:0.0f, // dtdx + np > 11 ? (float)parms[11][0]:0.0f, // dsdy + np > 12 ? (float)parms[12][0]:1.0f, // dtdy + np > 13 ? (float)parms[13][0]:0.0f, // dsdxdy + np > 14 ? (float)parms[14][0]:0.0f, // dtdxdy + np <= 15 || parms[15][0] > 0.5, (float)*m_gfx_a,getCurModeForBlit(isFromFB)); + } + else if (fabs(angle)>0.000000001) + { + LICE_RotatedBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], + (float)angle,true, (float)*m_gfx_a,getCurModeForBlit(isFromFB), + np > 9 ? (float)parms[9][0] : 0.0f, + np > 10 ? (float)parms[10][0] : 0.0f); + } + else + { + LICE_ScaledBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], (float)*m_gfx_a,getCurModeForBlit(isFromFB)); + } +} + +void eel_lice_state::gfx_blitext(EEL_F img, EEL_F *coords, EEL_F angle) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_blitext"); + + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_ScaledBlit || !LICE_RotatedBlit||!LICE__GetWidth||!LICE__GetHeight +#endif + ) return; + + LICE_IBitmap *bm=GetImageForIndex(img,"gfx_blitext:src"); + if (!bm) return; + + SetImageDirty(dest); + const bool isFromFB = bm == m_framebuffer; + + int bmw=LICE__GetWidth(bm); + int bmh=LICE__GetHeight(bm); + + if (bm == dest && CoordsSrcDestOverlap(coords)) + { + if (!m_framebuffer_extra && LICE_FUNCTION_VALID(__LICE_CreateBitmap)) m_framebuffer_extra=__LICE_CreateBitmap(0,bmw,bmh); + if ( m_framebuffer_extra) + { + + LICE__resize(bm=m_framebuffer_extra,bmw,bmh); + LICE_ScaledBlit(bm,dest, // copy the source portion + (int)coords[0],(int)coords[1],(int)coords[2],(int)coords[3], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], + 1.0f,LICE_BLIT_MODE_COPY); + } + } + + if (fabs(angle)>0.000000001) + { + LICE_RotatedBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3],(float)angle, + true, (float)*m_gfx_a,getCurModeForBlit(isFromFB), + (float)coords[8],(float)coords[9]); + } + else + { + LICE_ScaledBlit(dest,bm,(int)coords[4],(int)coords[5],(int)coords[6],(int)coords[7], + (float)coords[0],(float)coords[1],(float)coords[2],(float)coords[3], (float)*m_gfx_a,getCurModeForBlit(isFromFB)); + } +} + +void eel_lice_state::gfx_blit(EEL_F img, EEL_F scale, EEL_F rotate) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_blit"); + if (!dest +#ifdef DYNAMIC_LICE + ||!LICE_ScaledBlit || !LICE_RotatedBlit||!LICE__GetWidth||!LICE__GetHeight +#endif + ) return; + + LICE_IBitmap *bm=GetImageForIndex(img,"gfx_blit:src"); + + if (!bm) return; + + SetImageDirty(dest); + const bool isFromFB = bm == m_framebuffer; + + int bmw=LICE__GetWidth(bm); + int bmh=LICE__GetHeight(bm); + if (fabs(rotate)>0.000000001) + { + LICE_RotatedBlit(dest,bm,(int)*m_gfx_x,(int)*m_gfx_y,(int) (bmw*scale),(int) (bmh*scale),0.0f,0.0f,(float)bmw,(float)bmh,(float)rotate,true, (float)*m_gfx_a,getCurModeForBlit(isFromFB), + 0.0f,0.0f); + } + else + { + LICE_ScaledBlit(dest,bm,(int)*m_gfx_x,(int)*m_gfx_y,(int) (bmw*scale),(int) (bmh*scale),0.0f,0.0f,(float)bmw,(float)bmh, (float)*m_gfx_a,getCurModeForBlit(isFromFB)); + } +} + +void eel_lice_state::gfx_set(int np, EEL_F **parms) +{ + if (np < 1) return; + if (m_gfx_r) *m_gfx_r = parms[0][0]; + if (m_gfx_g) *m_gfx_g = np > 1 ? parms[1][0] : parms[0][0]; + if (m_gfx_b) *m_gfx_b = np > 2 ? parms[2][0] : parms[0][0]; + if (m_gfx_a) *m_gfx_a = np > 3 ? parms[3][0] : 1.0; + if (m_gfx_mode) *m_gfx_mode = np > 4 ? parms[4][0] : 0; + if (np > 5 && m_gfx_dest) *m_gfx_dest = parms[5][0]; + if (m_gfx_a2) *m_gfx_a2 = np > 6 ? parms[6][0] : 1.0; +} + +void eel_lice_state::gfx_getpixel(EEL_F *r, EEL_F *g, EEL_F *b) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_getpixel"); + if (!dest) return; + + int ret=LICE_FUNCTION_VALID(LICE_GetPixel)?LICE_GetPixel(dest,(int)*m_gfx_x, (int)*m_gfx_y):0; + + *r=LICE_GETR(ret)/255.0; + *g=LICE_GETG(ret)/255.0; + *b=LICE_GETB(ret)/255.0; + +} + + +static int __drawTextWithFont(LICE_IBitmap *dest, const RECT *rect, LICE_IFont *font, const char *buf, int buflen, + int fg, int mode, float alpha, int flags, EEL_F *wantYoutput, EEL_F **measureOnly) +{ + if (font && LICE_FUNCTION_VALID(LICE__DrawText)) + { + RECT tr=*rect; + LICE__SetTextColor(font,fg); + LICE__SetTextCombineMode(font,mode,alpha); + + int maxx=0; + RECT r={0,0,tr.left,0}; + while (buflen>0) + { + int thislen = 0; + while (thislen < buflen && buf[thislen] != '\n') thislen++; + memset(&r,0,sizeof(r)); + int lineh = LICE__DrawText(font,dest,buf,thislen?thislen:1,&r,DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT); + if (!measureOnly) + { + r.right += tr.left; + lineh = LICE__DrawText(font,dest,buf,thislen?thislen:1,&tr,DT_SINGLELINE|DT_NOPREFIX|flags); + if (wantYoutput) *wantYoutput = tr.top; + } + else + { + if (r.right > maxx) maxx=r.right; + } + tr.top += lineh; + + buflen -= thislen+1; + buf += thislen+1; + } + if (measureOnly) + { + measureOnly[0][0] = maxx; + measureOnly[1][0] = tr.top; + } + return r.right; + } + else + { + int xpos=rect->left, ypos=rect->top; + int x; + int maxx=0,maxy=0; + + LICE_SubBitmap sbm( +#ifdef DYNAMIC_LICE + (LICE_IBitmap_disabledAPI*) +#endif + dest,rect->left,rect->top,rect->right-rect->left,rect->bottom-rect->top); + + if (!measureOnly) + { + if (!(flags & DT_NOCLIP)) + { + if (rect->right <= rect->left || rect->bottom <= rect->top) return 0; // invalid clip rect hm + + xpos = ypos = 0; + dest = &sbm; + } + if (flags & (DT_RIGHT|DT_BOTTOM|DT_CENTER|DT_VCENTER)) + { + EEL_F w=0.0,h=0.0; + EEL_F *mo[2] = { &w,&h}; + RECT tr={0,}; + __drawTextWithFont(dest,&tr,NULL,buf,buflen,0,0,0.0f,0,NULL,mo); + + if (flags & DT_RIGHT) xpos += (rect->right-rect->left) - (int)floor(w); + else if (flags & DT_CENTER) xpos += (rect->right-rect->left)/2 - (int)floor(w*.5); + + if (flags & DT_BOTTOM) ypos += (rect->bottom-rect->top) - (int)floor(h); + else if (flags & DT_VCENTER) ypos += (rect->bottom-rect->top)/2 - (int)floor(h*.5); + } + } + const int sxpos = xpos; + + if (LICE_FUNCTION_VALID(LICE_DrawChar)) for(x=0;x maxx) maxx=xpos; + maxy = ypos + 8; + break; + } + } + if (measureOnly) + { + measureOnly[0][0]=maxx; + measureOnly[1][0]=maxy; + } + else + { + if (wantYoutput) *wantYoutput=ypos; + } + return xpos; + } +} + +static HMENU PopulateMenuFromStr(const char** str, int* startid) +{ + HMENU hm=CreatePopupMenu(); + int pos=0; + int id=*startid; + + char buf[1024]; + const char* p=*str; + const char* sep=strchr(p, '|'); + while (sep || *p) + { + int len = (int)(sep ? sep-p : strlen(p)); + int destlen=wdl_min(len, (int)sizeof(buf)-1); + lstrcpyn(buf, p, destlen+1); + p += len; + if (sep) sep=strchr(++p, '|'); + + const char* q=buf; + HMENU subm=NULL; + bool done=false; + int flags=MF_BYPOSITION|MF_STRING; + while (strspn(q, ">#!<")) + { + if (*q == '>' && !subm) + { + subm=PopulateMenuFromStr(&p, &id); + sep=strchr(p, '|'); + } + if (*q == '#') flags |= MF_GRAYED; + if (*q == '!') flags |= MF_CHECKED; + if (*q == '<') done=true; + ++q; + } + if (subm) flags |= MF_POPUP; + if (*q) InsertMenu(hm, pos++, flags, (subm ? (INT_PTR)subm : (INT_PTR)id++), q); + else if (!done) InsertMenu(hm, pos++, MF_BYPOSITION|MF_SEPARATOR, 0, NULL); + if (done) break; + } + + *str=p; + *startid=id; + + if (!pos) + { + DestroyMenu(hm); + return NULL; + } + return hm; +} + +EEL_F eel_lice_state::gfx_showmenu(void* opaque, EEL_F** parms, int nparms) +{ + const char* p=EEL_STRING_GET_FOR_INDEX(parms[0][0], NULL); + if (!p || !p[0]) return 0.0; + + int id=1; + HMENU hm=PopulateMenuFromStr(&p, &id); + + int ret=0; + if (hm) + { + POINT pt; + if (hwnd_standalone) + { +#ifdef __APPLE__ + if (*m_gfx_ext_retina > 1.0) + { + pt.x = (short)(*m_gfx_x * .5); + pt.y = (short)(*m_gfx_y * .5); + } + else +#endif + { + pt.x = (short)*m_gfx_x; + pt.y = (short)*m_gfx_y; + } + ClientToScreen(hwnd_standalone, &pt); + } + else + GetCursorPos(&pt); + ret=TrackPopupMenu(hm, TPM_NONOTIFY|TPM_RETURNCMD, pt.x, pt.y, 0, hwnd_standalone, NULL); + DestroyMenu(hm); + } + return (EEL_F)ret; +} + +EEL_F eel_lice_state::gfx_setcursor(void* opaque, EEL_F** parms, int nparms) +{ + if (!hwnd_standalone) return 0.0; + + bool chg = false; + const int nc = (int)parms[0][0]; + if (m_cursor_resid != nc) + { + m_cursor_resid = nc; + chg = true; + } + + const char *p = NULL; +#ifdef EEL_LICE_LOADTHEMECURSOR + if (nparms > 1) p=EEL_STRING_GET_FOR_INDEX(parms[1][0], NULL); + + if (strncmp(p?p:"",m_cursor_name,sizeof(m_cursor_name)-1)) + { + lstrcpyn(m_cursor_name, p?p:"", sizeof(m_cursor_name)); + chg = true; + } +#endif + + if (chg) + { + m_cursor = NULL; + if (m_cursor_resid > 0) + { + if (!p || !*p) m_cursor = LoadCursor(NULL, MAKEINTRESOURCE(m_cursor_resid)); +#ifdef EEL_LICE_LOADTHEMECURSOR + else m_cursor = EEL_LICE_LOADTHEMECURSOR(m_cursor_resid, p); +#endif + } + + bool do_set = GetCapture() == hwnd_standalone; + if (!do_set && GetFocus() == hwnd_standalone) + { + POINT pt; + RECT r; + GetCursorPos(&pt); + ScreenToClient(hwnd_standalone,&pt); + GetClientRect(hwnd_standalone,&r); + do_set = PtInRect(&r,pt)!=0; + } + + if (do_set) + { + SetCursor(m_cursor ? m_cursor : LoadCursor(NULL,IDC_ARROW)); + } + } + + return 1.0; +} + + +void eel_lice_state::gfx_drawstr(void *opaque, EEL_F **parms, int nparms, int formatmode)// formatmode=1 for format, 2 for purely measure no format +{ + int nfmtparms = nparms-1; + EEL_F **fmtparms = parms+1; + const char *funcname = formatmode==1?"gfx_printf": + formatmode==2?"gfx_measurestr": + formatmode==3?"gfx_measurechar" : "gfx_drawstr"; + + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,funcname); + if (!dest) return; + +#ifdef DYNAMIC_LICE + if (!LICE__GetWidth || !LICE__GetHeight) return; +#endif + + EEL_STRING_MUTEXLOCK_SCOPE + + WDL_FastString *fs=NULL; + char buf[4096]; + int s_len=0; + + const char *s; + if (formatmode==3) + { + s_len = WDL_MakeUTFChar(buf, (int)parms[0][0], sizeof(buf)); + s=buf; + } + else + { + s=EEL_STRING_GET_FOR_INDEX(parms[0][0],&fs); + #ifdef EEL_STRING_DEBUGOUT + if (!s) EEL_STRING_DEBUGOUT("gfx_%s: invalid string identifier %f",funcname,parms[0][0]); + #endif + if (!s) + { + s=""; + s_len = 12; + } + else if (formatmode==1) + { + extern int eel_format_strings(void *, const char *s, const char *ep, char *, int, int, EEL_F **); + s_len = eel_format_strings(opaque,s,fs?(s+fs->GetLength()):NULL,buf,sizeof(buf),nfmtparms,fmtparms); + if (s_len<1) return; + s=buf; + } + else + { + s_len = fs?fs->GetLength():(int)strlen(s); + } + } + + if (s_len) + { + SetImageDirty(dest); + if (formatmode>=2) + { + if (nfmtparms==2) + { + RECT r={0,0,0,0}; + __drawTextWithFont(dest,&r,GetActiveFont(),s,s_len, + getCurColor(),getCurMode(),(float)*m_gfx_a,0,NULL,fmtparms); + } + } + else + { + RECT r={(int)floor(*m_gfx_x),(int)floor(*m_gfx_y),0,0}; + int flags=DT_NOCLIP; + if (formatmode == 0 && nparms >= 4) + { + flags=(int)*parms[1]; + flags &= (DT_CENTER|DT_RIGHT|DT_VCENTER|DT_BOTTOM|DT_NOCLIP); + r.right=(int)*parms[2]; + r.bottom=(int)*parms[3]; + } + *m_gfx_x=__drawTextWithFont(dest,&r,GetActiveFont(),s,s_len, + getCurColor(),getCurMode(),(float)*m_gfx_a,flags,m_gfx_y,NULL); + } + } +} + +void eel_lice_state::gfx_drawchar(EEL_F ch) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_drawchar"); + if (!dest) return; + + SetImageDirty(dest); + + int a=(int)(ch+0.5); + if (a == '\r' || a=='\n') a=' '; + + char buf[32]; + const int buflen = WDL_MakeUTFChar(buf, a, sizeof(buf)); + + RECT r={(int)floor(*m_gfx_x),(int)floor(*m_gfx_y),0,0}; + *m_gfx_x = __drawTextWithFont(dest,&r, + GetActiveFont(),buf,buflen, + getCurColor(),getCurMode(),(float)*m_gfx_a,DT_NOCLIP,NULL,NULL); + +} + + +void eel_lice_state::gfx_drawnumber(EEL_F n, EEL_F ndigits) +{ + LICE_IBitmap *dest = GetImageForIndex(*m_gfx_dest,"gfx_drawnumber"); + if (!dest) return; + + SetImageDirty(dest); + + char buf[512]; + int a=(int)(ndigits+0.5); + if (a <0)a=0; + else if (a > 16) a=16; + snprintf(buf,sizeof(buf),"%.*f",a,n); + + RECT r={(int)floor(*m_gfx_x),(int)floor(*m_gfx_y),0,0}; + *m_gfx_x = __drawTextWithFont(dest,&r, + GetActiveFont(),buf,(int)strlen(buf), + getCurColor(),getCurMode(),(float)*m_gfx_a,DT_NOCLIP,NULL,NULL); +} + +int eel_lice_state::setup_frame(HWND hwnd, RECT r, int _mouse_x, int _mouse_y, int has_dpi) +{ + int use_w = r.right - r.left; + int use_h = r.bottom - r.top; + + POINT pt = { _mouse_x, _mouse_y }; + if (hwnd) + { + GetCursorPos(&pt); + ScreenToClient(hwnd,&pt); + } + + *m_mouse_x=pt.x-r.left; + *m_mouse_y=pt.y-r.top; + + if (has_dpi>0 && *m_gfx_ext_retina > 0.0) + { + *m_gfx_ext_retina = has_dpi/256.0; + } + else if (*m_gfx_ext_retina > 0.0) + { +#ifdef __APPLE__ + *m_gfx_ext_retina = (hwnd && SWELL_IsRetinaHWND(hwnd)) ? 2.0 : 1.0; + if (*m_gfx_ext_retina > 1.0) + { + *m_mouse_x *= 2.0; + *m_mouse_y *= 2.0; + use_w*=2; + use_h*=2; + } +#else + *m_gfx_ext_retina = 1.0; + #ifdef _WIN32 + static UINT (WINAPI *__GetDpiForWindow)(HWND); + if (!__GetDpiForWindow) + { + HINSTANCE h = LoadLibrary("user32.dll"); + if (h) *(void **)&__GetDpiForWindow = GetProcAddress(h,"GetDpiForWindow"); + if (!__GetDpiForWindow) + *(void **)&__GetDpiForWindow = (void*)(INT_PTR)1; + } + if (hwnd && (UINT_PTR)__GetDpiForWindow > (UINT_PTR)1) + { + int dpi = __GetDpiForWindow(hwnd); + if (dpi != 96) + *m_gfx_ext_retina = dpi / 96.0; + } + #else + const int rsc = SWELL_GetScaling256(); + if (rsc > 256) *m_gfx_ext_retina = rsc/256.0; + #endif +#endif + } + int dr=0; + if (!m_framebuffer && LICE_FUNCTION_VALID(__LICE_CreateBitmap)) + { + m_framebuffer=__LICE_CreateBitmap(1,use_w,use_h); + dr=1; + } + + if (!m_framebuffer || !LICE_FUNCTION_VALID(LICE__GetHeight) || !LICE_FUNCTION_VALID(LICE__GetWidth)) return -1; + + if (use_w != LICE__GetWidth(m_framebuffer) || use_h != LICE__GetHeight(m_framebuffer)) + { + LICE__resize(m_framebuffer,use_w,use_h); + dr=1; + } + *m_gfx_w = use_w; + *m_gfx_h = use_h; + + if (*m_gfx_clear > -1.0 && dr) + { + const int a=(int)*m_gfx_clear; + if (LICE_FUNCTION_VALID(LICE_Clear)) LICE_Clear(m_framebuffer,LICE_RGBA((a&0xff),((a>>8)&0xff),((a>>16)&0xff),0)); + } + m_framebuffer_dirty = dr; + + int vflags=0; + + if (m_has_cap) + { + bool swap = false; +#ifdef _WIN32 + swap = !!GetSystemMetrics(SM_SWAPBUTTON); +#endif + vflags|=m_has_cap&0xffff; + if (GetAsyncKeyState(VK_LBUTTON)&0x8000) vflags|=swap?2:1; + if (GetAsyncKeyState(VK_RBUTTON)&0x8000) vflags|=swap?1:2; + if (GetAsyncKeyState(VK_MBUTTON)&0x8000) vflags|=64; + } + if (m_has_cap || (m_has_had_getch && hwnd && GetFocus()==hwnd)) + { + if (GetAsyncKeyState(VK_CONTROL)&0x8000) vflags|=4; + if (GetAsyncKeyState(VK_SHIFT)&0x8000) vflags|=8; + if (GetAsyncKeyState(VK_MENU)&0x8000) vflags|=16; + if (GetAsyncKeyState(VK_LWIN)&0x8000) vflags|=32; + } + m_has_cap &= 0xf0000; + + *m_mouse_cap=(EEL_F)vflags; + + *m_gfx_dest = -1.0; // m_framebuffer + *m_gfx_a2 = *m_gfx_a = 1.0; // default to full alpha every call + int fh; + if (m_gfx_font_active>=0&&m_gfx_font_active0) + *m_gfx_texth=fh; + else + *m_gfx_texth = 8; + + return dr; +} + +void eel_lice_state::finish_draw() +{ + if (hwnd_standalone && m_framebuffer_dirty) + { +#ifdef __APPLE__ + void *p = SWELL_InitAutoRelease(); +#endif + + InvalidateRect(hwnd_standalone,NULL,FALSE); + UpdateWindow(hwnd_standalone); + +#ifdef __APPLE__ + SWELL_QuitAutoRelease(p); +#endif + m_framebuffer_dirty = 0; + } +} + +#ifndef EEL_LICE_NO_REGISTER +void eel_lice_register() +{ + NSEEL_addfunc_retptr("gfx_lineto",3,NSEEL_PProc_THIS,&_gfx_lineto); + NSEEL_addfunc_retptr("gfx_lineto",2,NSEEL_PProc_THIS,&_gfx_lineto2); + NSEEL_addfunc_retptr("gfx_rectto",2,NSEEL_PProc_THIS,&_gfx_rectto); + NSEEL_addfunc_varparm("gfx_rect",4,NSEEL_PProc_THIS,&_gfx_rect); + NSEEL_addfunc_varparm("gfx_line",4,NSEEL_PProc_THIS,&_gfx_line); // 5th param is optionally AA + NSEEL_addfunc_varparm("gfx_gradrect",8,NSEEL_PProc_THIS,&_gfx_gradrect); + NSEEL_addfunc_varparm("gfx_muladdrect",7,NSEEL_PProc_THIS,&_gfx_muladdrect); + NSEEL_addfunc_varparm("gfx_deltablit",9,NSEEL_PProc_THIS,&_gfx_deltablit); + NSEEL_addfunc_exparms("gfx_transformblit",8,NSEEL_PProc_THIS,&_gfx_transformblit); + NSEEL_addfunc_varparm("gfx_circle",3,NSEEL_PProc_THIS,&_gfx_circle); + NSEEL_addfunc_varparm("gfx_triangle", 6, NSEEL_PProc_THIS, &_gfx_triangle); + NSEEL_addfunc_varparm("gfx_roundrect",5,NSEEL_PProc_THIS,&_gfx_roundrect); + NSEEL_addfunc_varparm("gfx_arc",5,NSEEL_PProc_THIS,&_gfx_arc); + NSEEL_addfunc_retptr("gfx_blurto",2,NSEEL_PProc_THIS,&_gfx_blurto); + NSEEL_addfunc_exparms("gfx_showmenu",1,NSEEL_PProc_THIS,&_gfx_showmenu); + NSEEL_addfunc_varparm("gfx_setcursor",1, NSEEL_PProc_THIS, &_gfx_setcursor); + NSEEL_addfunc_retptr("gfx_drawnumber",2,NSEEL_PProc_THIS,&_gfx_drawnumber); + NSEEL_addfunc_retptr("gfx_drawchar",1,NSEEL_PProc_THIS,&_gfx_drawchar); + NSEEL_addfunc_varparm("gfx_drawstr",1,NSEEL_PProc_THIS,&_gfx_drawstr); + NSEEL_addfunc_retptr("gfx_measurestr",3,NSEEL_PProc_THIS,&_gfx_measurestr); + NSEEL_addfunc_retptr("gfx_measurechar",3,NSEEL_PProc_THIS,&_gfx_measurechar); + NSEEL_addfunc_varparm("gfx_printf",1,NSEEL_PProc_THIS,&_gfx_printf); + NSEEL_addfunc_retptr("gfx_setpixel",3,NSEEL_PProc_THIS,&_gfx_setpixel); + NSEEL_addfunc_retptr("gfx_getpixel",3,NSEEL_PProc_THIS,&_gfx_getpixel); + NSEEL_addfunc_retptr("gfx_getimgdim",3,NSEEL_PProc_THIS,&_gfx_getimgdim); + NSEEL_addfunc_retval("gfx_setimgdim",3,NSEEL_PProc_THIS,&_gfx_setimgdim); + NSEEL_addfunc_retval("gfx_loadimg",2,NSEEL_PProc_THIS,&_gfx_loadimg); + NSEEL_addfunc_retptr("gfx_blit",3,NSEEL_PProc_THIS,&_gfx_blit); + NSEEL_addfunc_retptr("gfx_blitext",3,NSEEL_PProc_THIS,&_gfx_blitext); + NSEEL_addfunc_varparm("gfx_blit",4,NSEEL_PProc_THIS,&_gfx_blit2); + NSEEL_addfunc_varparm("gfx_setfont",1,NSEEL_PProc_THIS,&_gfx_setfont); + NSEEL_addfunc_varparm("gfx_getfont",1,NSEEL_PProc_THIS,&_gfx_getfont); + NSEEL_addfunc_varparm("gfx_set",1,NSEEL_PProc_THIS,&_gfx_set); + NSEEL_addfunc_varparm("gfx_getdropfile",1,NSEEL_PProc_THIS,&_gfx_getdropfile); + NSEEL_addfunc_varparm("gfx_getsyscol",0,NSEEL_PProc_THIS,&_gfx_getsyscol); +} +#endif + +#ifdef EEL_LICE_WANT_STANDALONE + +#ifdef _WIN32 +static HINSTANCE eel_lice_hinstance; +#endif +static const char *eel_lice_standalone_classname; + +#ifdef EEL_LICE_WANT_STANDALONE_UPDATE +static EEL_F * NSEEL_CGEN_CALL _gfx_update(void *opaque, EEL_F *n) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + ctx->m_ddrop_files.Empty(true,free); + if (ctx->hwnd_standalone) + { +#ifndef EEL_LICE_WANT_STANDALONE_UPDATE_NO_SETUPFRAME + ctx->finish_draw(); +#endif + + // run message pump +#ifndef EEL_LICE_WANT_STANDALONE_UPDATE_NO_MSGPUMP + +#ifdef _WIN32 + MSG msg; + while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +#else + void SWELL_RunEvents(); + SWELL_RunEvents(); +#endif +#endif +#ifndef EEL_LICE_WANT_STANDALONE_UPDATE_NO_SETUPFRAME + RECT r; + GetClientRect(ctx->hwnd_standalone,&r); + ctx->setup_frame(ctx->hwnd_standalone,r); +#endif + } + } + return n; +} +#endif + + + +static EEL_F NSEEL_CGEN_CALL _gfx_getchar(void *opaque, EEL_F *p) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + ctx->m_has_had_getch=true; + if (*p >= 2.0) + { + if (*p == 65536.0) + { + int rv = 1; + if (ctx->hwnd_standalone) + { + if (ctx->hwnd_standalone==GetFocus()) rv|=2; + if (IsWindowVisible(ctx->hwnd_standalone)) rv|=4; + } + return rv; + } + int x; + const int n = sizeof(ctx->hwnd_standalone_kb_state) / sizeof(ctx->hwnd_standalone_kb_state[0]); + int *st = ctx->hwnd_standalone_kb_state; + int a = (int)*p; + for (x=0;xhwnd_standalone) return -1.0; + + if (ctx->m_kb_queue_valid) + { + const int qsize = sizeof(ctx->m_kb_queue)/sizeof(ctx->m_kb_queue[0]); + const int a = ctx->m_kb_queue[ctx->m_kb_queue_pos & (qsize-1)]; + ctx->m_kb_queue_pos++; + ctx->m_kb_queue_valid--; + return a; + } + } + return 0.0; +} + +static int eel_lice_key_xlate(int msg, int wParam, int lParam, bool *isAltOut) +{ +#define EEL_MB_C(a) (sizeof(a)<=2 ? a[0] : \ + sizeof(a)==3 ? (((a[0])<<8)+(a[1])) : \ + sizeof(a)==4 ? (((a[0])<<16)+((a[1])<<8)+(a[2])) : \ + (((a[0])<<24)+((a[1])<<16)+((a[2])<<8)+(a[3]))) + + if (msg != WM_CHAR) + { +#ifndef _WIN32 + if (lParam & FVIRTKEY) +#endif + switch (wParam) + { + case VK_HOME: return EEL_MB_C("home"); + case VK_UP: return EEL_MB_C("up"); + case VK_PRIOR: return EEL_MB_C("pgup"); + case VK_LEFT: return EEL_MB_C("left"); + case VK_RIGHT: return EEL_MB_C("rght"); + case VK_END: return EEL_MB_C("end"); + case VK_DOWN: return EEL_MB_C("down"); + case VK_NEXT: return EEL_MB_C("pgdn"); + case VK_INSERT: return EEL_MB_C("ins"); + case VK_DELETE: return EEL_MB_C("del"); + case VK_F1: return EEL_MB_C("f1"); + case VK_F2: return EEL_MB_C("f2"); + case VK_F3: return EEL_MB_C("f3"); + case VK_F4: return EEL_MB_C("f4"); + case VK_F5: return EEL_MB_C("f5"); + case VK_F6: return EEL_MB_C("f6"); + case VK_F7: return EEL_MB_C("f7"); + case VK_F8: return EEL_MB_C("f8"); + case VK_F9: return EEL_MB_C("f9"); + case VK_F10: return EEL_MB_C("f10"); + case VK_F11: return EEL_MB_C("f11"); + case VK_F12: return EEL_MB_C("f12"); +#ifndef _WIN32 + case VK_SUBTRACT: return '-'; // numpad - + case VK_ADD: return '+'; + case VK_MULTIPLY: return '*'; + case VK_DIVIDE: return '/'; + case VK_DECIMAL: return '.'; + case VK_NUMPAD0: return '0'; + case VK_NUMPAD1: return '1'; + case VK_NUMPAD2: return '2'; + case VK_NUMPAD3: return '3'; + case VK_NUMPAD4: return '4'; + case VK_NUMPAD5: return '5'; + case VK_NUMPAD6: return '6'; + case VK_NUMPAD7: return '7'; + case VK_NUMPAD8: return '8'; + case VK_NUMPAD9: return '9'; + case (32768|VK_RETURN): return VK_RETURN; +#endif + } + + switch (wParam) + { + case VK_RETURN: + case VK_BACK: + case VK_TAB: + case VK_ESCAPE: + return wParam; + + case VK_CONTROL: break; + + default: + { + const bool isctrl = !!(GetAsyncKeyState(VK_CONTROL)&0x8000); + const bool isalt = !!(GetAsyncKeyState(VK_MENU)&0x8000); + if(isctrl || isalt) + { + if (wParam>='a' && wParam<='z') + { + if (isctrl) wParam += 1-'a'; + if (isalt) wParam += 256; + *isAltOut=isalt; + return wParam; + } + if (wParam>='A' && wParam<='Z') + { + if (isctrl) wParam += 1-'A'; + if (isalt) wParam += 256; + *isAltOut=isalt; + return wParam; + } + } + + if (isctrl) + { + if ((wParam&~0x80) == '[') return 27; + if ((wParam&~0x80) == ']') return 29; + } + } + break; + } + } + + if(wParam>=32) + { + #ifdef _WIN32 + if (msg == WM_CHAR) return wParam; + #else + if (!(GetAsyncKeyState(VK_SHIFT)&0x8000)) + { + if (wParam>='A' && wParam<='Z') + { + if ((GetAsyncKeyState(VK_LWIN)&0x8000)) wParam -= 'A'-1; + else + wParam += 'a'-'A'; + } + } + return wParam; + #endif + } + return 0; +} +#undef EEL_MB_C + +static LRESULT WINAPI eel_lice_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +#ifdef __APPLE__ +extern "C" +{ + void *objc_getClass(const char *p); +#ifndef _OBJC_OBJC_H_ + void *sel_getUid(const char *p); +#endif + void objc_msgSend(void); +}; +#endif + + +HWND eel_lice_state::create_wnd(HWND par, int isChild) +{ + if (hwnd_standalone) return hwnd_standalone; +#ifdef _WIN32 + return CreateWindowEx(WS_EX_ACCEPTFILES,eel_lice_standalone_classname,"", + isChild ? (WS_CHILD|WS_TABSTOP) : (WS_POPUP|WS_CAPTION|WS_THICKFRAME|WS_SYSMENU),CW_USEDEFAULT,CW_USEDEFAULT,100,100,par,NULL,eel_lice_hinstance,this); +#else + HWND h = SWELL_CreateDialog(NULL,isChild ? NULL : ((const char *)(INT_PTR)0x400001),par,(DLGPROC)eel_lice_wndproc,(LPARAM)this); + if (h) + { + SWELL_SetClassName(h,eel_lice_standalone_classname); + SWELL_EnableMetal(h,1); + } + return h; +#endif +} + +#ifdef EEL_LICE_WANTDOCK +#ifndef ID_DOCKWINDOW +#define ID_DOCKWINDOW 40269 +#endif + +static EEL_F NSEEL_CGEN_CALL _gfx_dock(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + if (np > 0 && parms[0][0] >= 0.0 && ctx->hwnd_standalone) EEL_LICE_WANTDOCK(ctx,(int)parms[0][0]); + + if (np > 1 && parms[1]) parms[1][0] = ctx->m_last_undocked_r.left; + if (np > 2 && parms[2]) parms[2][0] = ctx->m_last_undocked_r.top; + if (np > 3 && parms[3]) parms[3][0] = ctx->m_last_undocked_r.right; + if (np > 4 && parms[4]) parms[4][0] = ctx->m_last_undocked_r.bottom; + +#ifdef EEL_LICE_ISDOCKED + return EEL_LICE_ISDOCKED(ctx); +#endif + } + return 0.0; +} + +#endif //EEL_LICE_WANTDOCK + + +#ifndef EEL_LICE_STANDALONE_NOINITQUIT + +static EEL_F * NSEEL_CGEN_CALL _gfx_quit(void *opaque, EEL_F *n) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx) + { + if (ctx->hwnd_standalone) + { + DestroyWindow(ctx->hwnd_standalone); + } + ctx->hwnd_standalone=0; + } + return n; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_init(void *opaque, INT_PTR np, EEL_F **parms) +{ +#ifdef EEL_LICE_GET_CONTEXT_INIT + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT_INIT(opaque); +#else + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); +#endif + if (ctx) + { + bool wantShow=false, wantResize=true; + int sug_w = np > 1 ? (int)parms[1][0] : 640; + int sug_h = np > 2 ? (int)parms[2][0] : 480; + if (sug_w <1 && sug_h < 1 && ctx->hwnd_standalone) + { + RECT r; + GetClientRect(ctx->hwnd_standalone,&r); + sug_w = r.right; + sug_h = r.bottom; + } + #ifdef EEL_LICE_WANTDOCK + const int pos_offs = 4; + #else + const int pos_offs = 3; + #endif + + if (sug_w < 16) sug_w=16; + else if (sug_w > 8192) sug_w=8192; + if (sug_h < 16) sug_h=16; + else if (sug_h > 8192) sug_h=8192; + + if (!ctx->hwnd_standalone) + { + #ifdef __APPLE__ + void *(*send_msg)(void *, void *) = (void *(*)(void *, void *))objc_msgSend; + void (*send_msg_longparm)(void *, void *, long) = (void (*)(void *, void *, long))objc_msgSend; // long = NSInteger + + void *nsapp=send_msg( objc_getClass("NSApplication"), sel_getUid("sharedApplication")); + send_msg_longparm(nsapp,sel_getUid("setActivationPolicy:"), 0); + send_msg_longparm(nsapp,sel_getUid("activateIgnoringOtherApps:"), 1); + + #endif + + #ifdef EEL_LICE_STANDALONE_PARENT + HWND par = EEL_LICE_STANDALONE_PARENT(opaque); + #elif defined(_WIN32) + HWND par=GetDesktopWindow(); + #else + HWND par=NULL; + #endif + + ctx->create_wnd(par,0); + // resize client + + if (ctx->hwnd_standalone) + { + int px=0,py=0; + if (np >= pos_offs+2) + { + px = (int) floor(parms[pos_offs][0] + 0.5); + py = (int) floor(parms[pos_offs+1][0] + 0.5); +#ifdef EEL_LICE_VALIDATE_RECT_ON_SCREEN + RECT r = {px,py,px+sug_w,py+sug_h}; + EEL_LICE_VALIDATE_RECT_ON_SCREEN(r); + px=r.left; py=r.top; sug_w = r.right-r.left; sug_h = r.bottom-r.top; +#endif + ctx->m_last_undocked_r.left = px; + ctx->m_last_undocked_r.top = py; + ctx->m_last_undocked_r.right = sug_w; + ctx->m_last_undocked_r.bottom = sug_h; + } + + RECT r1,r2; + GetWindowRect(ctx->hwnd_standalone,&r1); + GetClientRect(ctx->hwnd_standalone,&r2); + sug_w += (r1.right-r1.left) - r2.right; + sug_h += abs(r1.bottom-r1.top) - r2.bottom; + + SetWindowPos(ctx->hwnd_standalone,NULL,px,py,sug_w,sug_h,(np >= pos_offs+2 ? 0:SWP_NOMOVE)|SWP_NOZORDER|SWP_NOACTIVATE); + + wantShow=true; + #ifdef EEL_LICE_WANTDOCK + if (np > 3) EEL_LICE_WANTDOCK(ctx,parms[3][0]); + #endif + #ifdef EEL_LICE_WANT_STANDALONE_UPDATE + { + RECT r; + GetClientRect(ctx->hwnd_standalone,&r); + ctx->setup_frame(ctx->hwnd_standalone,r); + } + #endif + } + wantResize=false; + } + if (!ctx->hwnd_standalone) return 0; + + if (np>0) + { + EEL_STRING_MUTEXLOCK_SCOPE + const char *title=EEL_STRING_GET_FOR_INDEX(parms[0][0],NULL); + #ifdef EEL_STRING_DEBUGOUT + if (!title) EEL_STRING_DEBUGOUT("gfx_init: invalid string identifier %f",parms[0][0]); + #endif + if (title&&*title) + { + SetWindowText(ctx->hwnd_standalone,title); + wantResize=false; // ignore resize if we're setting title + } + } + if (wantShow) + ShowWindow(ctx->hwnd_standalone,SW_SHOW); + if (wantResize && np>2 && !(GetWindowLong(ctx->hwnd_standalone,GWL_STYLE)&WS_CHILD)) + { + RECT r1,r2; + GetWindowRect(ctx->hwnd_standalone,&r1); + GetClientRect(ctx->hwnd_standalone,&r2); + const bool do_size = sug_w != r2.right || sug_h != r2.bottom; + + sug_w += (r1.right-r1.left) - r2.right; + sug_h += abs(r1.bottom-r1.top) - r2.bottom; + + int px=0,py=0; + const bool do_move=(np >= pos_offs+2); + if (do_move) + { + px = (int) floor(parms[pos_offs][0] + 0.5); + py = (int) floor(parms[pos_offs+1][0] + 0.5); +#ifdef EEL_LICE_VALIDATE_RECT_ON_SCREEN + RECT r = {px,py,px+sug_w,py+sug_h}; + EEL_LICE_VALIDATE_RECT_ON_SCREEN(r); + px=r.left; py=r.top; sug_w = r.right-r.left; sug_h = r.bottom-r.top; +#endif + } + if (do_size || do_move) + SetWindowPos(ctx->hwnd_standalone,NULL,px,py,sug_w,sug_h, + (do_size ? 0 : SWP_NOSIZE)|(do_move? 0:SWP_NOMOVE)|SWP_NOZORDER|SWP_NOACTIVATE); + } + return 1; + } + return 0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_screentoclient(void *opaque, EEL_F *x, EEL_F *y) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx && ctx->hwnd_standalone) + { + POINT pt={(int) *x, (int) *y}; + ScreenToClient(ctx->hwnd_standalone,&pt); + *x = pt.x; + *y = pt.y; + return 1.0; + } + return 0.0; +} + +static EEL_F NSEEL_CGEN_CALL _gfx_clienttoscreen(void *opaque, EEL_F *x, EEL_F *y) +{ + eel_lice_state *ctx=EEL_LICE_GET_CONTEXT(opaque); + if (ctx && ctx->hwnd_standalone) + { + POINT pt={(int) *x, (int) *y}; + ClientToScreen(ctx->hwnd_standalone,&pt); + *x = pt.x; + *y = pt.y; + return 1.0; + } + return 0.0; +} + +#endif // !EEL_LICE_STANDALONE_NOINITQUIT + + +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x20A +#endif +#ifndef WM_MOUSEHWHEEL +#define WM_MOUSEHWHEEL 0x20E +#endif + +LRESULT WINAPI eel_lice_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ +#ifdef WIN32 + static UINT Scroll_Message; + static bool sm_init; + if (!sm_init) + { + sm_init=true; + Scroll_Message = RegisterWindowMessage("MSWHEEL_ROLLMSG"); + } + if (Scroll_Message && uMsg == Scroll_Message) + { + uMsg=WM_MOUSEWHEEL; + wParam<<=16; + } +#endif + + switch (uMsg) + { + case WM_CREATE: + { +#ifdef _WIN32 + LPCREATESTRUCT lpcs= (LPCREATESTRUCT )lParam; + eel_lice_state *ctx=(eel_lice_state*)lpcs->lpCreateParams; + SetWindowLongPtr(hwnd,GWLP_USERDATA,(LPARAM)lpcs->lpCreateParams); +#else + eel_lice_state *ctx=(eel_lice_state*)lParam; + SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam); + SetWindowLong(hwnd,GWL_EXSTYLE, GetWindowLong(hwnd,GWL_EXSTYLE) | WS_EX_ACCEPTFILES); +#endif + ctx->m_kb_queue_valid=0; + ctx->hwnd_standalone=hwnd; + } + return 0; +#ifndef _WIN32 + case WM_CLOSE: + DestroyWindow(hwnd); + return 0; +#endif + case WM_DESTROY: + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (ctx) + { +#ifdef EEL_LICE_WANTDOCK + EEL_LICE_WANTDOCK(ctx,0); +#endif + ctx->hwnd_standalone=NULL; + } + } + return 0; + case WM_ACTIVATE: + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (ctx) memset(&ctx->hwnd_standalone_kb_state,0,sizeof(ctx->hwnd_standalone_kb_state)); + } + break; + case WM_SETCURSOR: + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (ctx && ctx->m_cursor) + { + POINT p; + GetCursorPos(&p); + ScreenToClient(hwnd, &p); + RECT r; + GetClientRect(hwnd, &r); + if (PtInRect(&r,p)) + { + SetCursor(ctx->m_cursor); + return TRUE; + } + } + } + break; +#ifdef EEL_LICE_WANTDOCK + case WM_CONTEXTMENU: + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (ctx) + { + char title[512], buf[1024]; + GetWindowText(hwnd, title, sizeof(title)-1); + if (!title[0]) strcpy(title, "ReaScript"); + + HMENU hm=CreatePopupMenu(); + int pos=0; + + int flag=((EEL_LICE_ISDOCKED(ctx)&1) ? MF_CHECKED : 0); + snprintf(buf, sizeof(buf), "Dock %s window in Docker", title); + InsertMenu(hm, pos++, MF_BYPOSITION|MF_STRING|flag, ID_DOCKWINDOW, buf); + snprintf(buf, sizeof(buf), "Close %s window", title); + InsertMenu(hm, pos++, MF_BYPOSITION|MF_STRING, IDCANCEL, buf); + + POINT pt; + GetCursorPos(&pt); + TrackPopupMenu(hm, 0, pt.x, pt.y, 0, hwnd, NULL); + DestroyMenu(hm); + } + } + return 0; +#endif + case WM_COMMAND: + switch (LOWORD(wParam)) + { +#ifdef EEL_LICE_WANTDOCK + case ID_DOCKWINDOW: + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (ctx) EEL_LICE_WANTDOCK(ctx, EEL_LICE_ISDOCKED(ctx)^1); + } + return 0; +#endif + case IDCANCEL: + DestroyWindow(hwnd); + return 0; + } + break; + case WM_DROPFILES: + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (ctx && wParam) + { + ctx->m_ddrop_files.Empty(true,free); + + HDROP hDrop = (HDROP) wParam; + const int n=DragQueryFile(hDrop,-1,NULL,0); + for (int x=0;xm_ddrop_files.Add(strdup(buf)); + } + DragFinish(hDrop); + } + } + return 0; + case WM_MOUSEHWHEEL: + case WM_MOUSEWHEEL: + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (ctx) + { + EEL_F *p= uMsg==WM_MOUSEHWHEEL ? ctx->m_mouse_hwheel : ctx->m_mouse_wheel; + if (p) *p += (EEL_F) (short)HIWORD(wParam); + } + } + return -1; +#ifdef _WIN32 + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: +#endif + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + + bool hadAltAdj=false; + int a=eel_lice_key_xlate(uMsg,(int)wParam,(int)lParam, &hadAltAdj); +#ifdef _WIN32 + if (!a && (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP) && wParam >= 'A' && wParam <= 'Z') a=(int)wParam + 'a' - 'A'; +#endif + const int mask = hadAltAdj ? ~256 : ~0; + +#ifdef _WIN32 + if (!a && (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)) + { + // not ideal, doesn't properly support all modifiers but better than nothing + a = (int)MapVirtualKey((UINT)wParam,2/*MAPVK_VK_TO_CHAR*/); + } +#endif + + if (a & mask) + { + int a_no_alt = (a&mask); + const int lowera = a_no_alt >= 1 && a_no_alt < 27 ? (a_no_alt+'a'-1) : a_no_alt >= 'A' && a_no_alt <= 'Z' ? a_no_alt+'a'-'A' : a_no_alt; + + int *st = ctx->hwnd_standalone_kb_state; + + const int n = sizeof(ctx->hwnd_standalone_kb_state) / sizeof(ctx->hwnd_standalone_kb_state[0]); + int zp=n-1,x; + + for (x=0;xm_kb_queue)/sizeof(ctx->m_kb_queue[0]); + if (ctx->m_kb_queue_valid>=qsize) // queue full, dump an old event! + { + ctx->m_kb_queue_valid--; + ctx->m_kb_queue_pos++; + } + ctx->m_kb_queue[(ctx->m_kb_queue_pos + ctx->m_kb_queue_valid++) & (qsize-1)] = a; + } + + } + return 0; + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_LBUTTONDOWN: + { + POINT p = { (short)LOWORD(lParam), (short)HIWORD(lParam) }; + RECT r; + GetClientRect(hwnd, &r); + if (p.x >= r.left && p.x < r.right && p.y >= r.top && p.y < r.bottom) + { + if (GetCapture()!=hwnd) SetFocus(hwnd); + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (ctx) + { + if (GetCapture()!=hwnd) SetCapture(hwnd); + int f = 0; + if (uMsg == WM_LBUTTONDBLCLK || uMsg == WM_LBUTTONDOWN) f=0x10001; + else if (uMsg == WM_RBUTTONDBLCLK || uMsg == WM_RBUTTONDOWN) f=0x20002; + else if (uMsg == WM_MBUTTONDBLCLK || uMsg == WM_MBUTTONDOWN) f=0x40040; + + if (GetAsyncKeyState(VK_CONTROL)&0x8000) f|=4; + if (GetAsyncKeyState(VK_SHIFT)&0x8000) f|=8; + if (GetAsyncKeyState(VK_MENU)&0x8000) f|=16; + if (GetAsyncKeyState(VK_LWIN)&0x8000) f|=32; + + ctx->m_has_cap|=f; + } + } + } + return 1; + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_CAPTURECHANGED: + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (ctx) + { + if (uMsg == WM_CAPTURECHANGED) + { + ctx->m_has_cap &= 0xffff; + } + else + { + if (uMsg == WM_LBUTTONUP) ctx->m_has_cap &= ~0x10000; + else if (uMsg == WM_RBUTTONUP) ctx->m_has_cap &= ~0x20000; + else if (uMsg == WM_MBUTTONUP) ctx->m_has_cap &= ~0x40000; + + if (!(ctx->m_has_cap & 0xf0000)) + { + ReleaseCapture(); + } + } + } + } + return 1; +#ifdef _WIN32 + case WM_GETDLGCODE: + if (GetWindowLong(hwnd,GWL_STYLE)&WS_CHILD) return DLGC_WANTALLKEYS; + break; + case 0x02E0: //WM_DPICHANGED + if (!(GetWindowLong(hwnd,GWL_STYLE)&WS_CHILD)) + { + RECT *prcNewWindow = (RECT*)lParam; + SetWindowPos(hwnd, + NULL, + prcNewWindow ->left, + prcNewWindow ->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, + SWP_NOZORDER | SWP_NOACTIVATE); + } + break; +#endif + case WM_SIZE: + // fall through +#ifndef EEL_LICE_STANDALONE_NOINITQUIT + case WM_MOVE: + if (uMsg != WM_SIZE || wParam != SIZE_MINIMIZED) + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (ctx +#ifdef EEL_LICE_ISDOCKED + && !(GetWindowLong(hwnd,GWL_STYLE)&WS_CHILD) +#endif + ) + { + RECT r; + GetWindowRect(hwnd,&ctx->m_last_undocked_r); + GetClientRect(hwnd,&r); + if (ctx->m_last_undocked_r.bottom < ctx->m_last_undocked_r.top) ctx->m_last_undocked_r.top = ctx->m_last_undocked_r.bottom; + ctx->m_last_undocked_r.right = r.right; + ctx->m_last_undocked_r.bottom = r.bottom; + } + + } +#endif + + break; + + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + eel_lice_state *ctx=(eel_lice_state*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (ctx && ctx->m_framebuffer && + LICE_FUNCTION_VALID(LICE__GetDC) && LICE_FUNCTION_VALID(LICE__GetWidth) && LICE_FUNCTION_VALID(LICE__GetHeight)) + { + int w = LICE__GetWidth(ctx->m_framebuffer); + int h = LICE__GetHeight(ctx->m_framebuffer); +#ifdef __APPLE__ + if (*ctx->m_gfx_ext_retina > 1.0) + { + StretchBlt(ps.hdc,0,0,w/2,h/2,LICE__GetDC(ctx->m_framebuffer),0,0,w,h,SRCCOPY); + } + else +#endif + BitBlt(ps.hdc,0,0,w,h,LICE__GetDC(ctx->m_framebuffer),0,0,SRCCOPY); + } + EndPaint(hwnd,&ps); + } + } + return 0; + case WM_GETMINMAXINFO: + { + LPMINMAXINFO p=(LPMINMAXINFO)lParam; + if (p->ptMinTrackSize.x > 10) p->ptMinTrackSize.x = 10; + if (p->ptMinTrackSize.y > 10) p->ptMinTrackSize.y = 10; + } + return 0; + } + + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + + + +void eel_lice_register_standalone(HINSTANCE hInstance, const char *classname, HWND hwndPar, HICON icon) +{ + eel_lice_standalone_classname=classname && *classname ? classname : "EEL_LICE_gfx_standalone"; +#ifdef _WIN32 + static bool reg; + if (!reg) + { + eel_lice_hinstance=hInstance; + WNDCLASS wc={CS_DBLCLKS,eel_lice_wndproc,0,0,hInstance,icon,LoadCursor(NULL,IDC_ARROW), NULL, NULL,eel_lice_standalone_classname}; + RegisterClass(&wc); + reg = true; + } +#endif + +#ifndef EEL_LICE_NO_REGISTER + // gfx_init(title[, w,h, flags]) +#ifndef EEL_LICE_STANDALONE_NOINITQUIT + NSEEL_addfunc_varparm("gfx_init",1,NSEEL_PProc_THIS,&_gfx_init); + NSEEL_addfunc_retptr("gfx_quit",1,NSEEL_PProc_THIS,&_gfx_quit); + + NSEEL_addfunc_retval("gfx_screentoclient",2,NSEEL_PProc_THIS,&_gfx_screentoclient); + NSEEL_addfunc_retval("gfx_clienttoscreen",2,NSEEL_PProc_THIS,&_gfx_clienttoscreen); + +#endif +#ifdef EEL_LICE_WANTDOCK + NSEEL_addfunc_varparm("gfx_dock",1,NSEEL_PProc_THIS,&_gfx_dock); +#endif + +#ifdef EEL_LICE_WANT_STANDALONE_UPDATE + NSEEL_addfunc_retptr("gfx_update",1,NSEEL_PProc_THIS,&_gfx_update); +#endif + + NSEEL_addfunc_retval("gfx_getchar",1,NSEEL_PProc_THIS,&_gfx_getchar); +#endif +} + + +#endif + +#endif//!EEL_LICE_API_ONLY + + + + +#ifdef DYNAMIC_LICE +static WDL_STATICFUNC_UNUSED void eel_lice_initfuncs(void *(*getFunc)(const char *name)) +{ + if (!getFunc) return; + + *(void **)&__LICE_CreateBitmap = getFunc("LICE_CreateBitmap"); + *(void **)&LICE_Clear = getFunc("LICE_Clear"); + *(void **)&LICE_Line = getFunc("LICE_LineInt"); + *(void **)&LICE_ClipLine = getFunc("LICE_ClipLine"); + *(void **)&LICE_FillRect = getFunc("LICE_FillRect"); + *(void **)&LICE_DrawRect = getFunc("LICE_DrawRect"); + *(void **)&LICE_PutPixel = getFunc("LICE_PutPixel"); + *(void **)&LICE_GetPixel = getFunc("LICE_GetPixel"); + *(void **)&LICE_DrawText = getFunc("LICE_DrawText"); + *(void **)&LICE_DrawChar = getFunc("LICE_DrawChar"); + *(void **)&LICE_MeasureText = getFunc("LICE_MeasureText"); + *(void **)&LICE_LoadImage = getFunc("LICE_LoadImage"); + *(void **)&LICE__GetDC = getFunc("LICE__GetDC"); + *(void **)&LICE__Destroy = getFunc("LICE__Destroy"); + *(void **)&LICE__GetWidth = getFunc("LICE__GetWidth"); + *(void **)&LICE__GetHeight = getFunc("LICE__GetHeight"); + *(void **)&LICE__resize = getFunc("LICE__resize"); + *(void **)&LICE_Blur = getFunc("LICE_Blur"); + *(void **)&LICE_RotatedBlit = getFunc("LICE_RotatedBlit"); + *(void **)&LICE_ScaledBlit = getFunc("LICE_ScaledBlit"); + *(void **)&LICE_Circle = getFunc("LICE_Circle"); + *(void **)&LICE_FillCircle = getFunc("LICE_FillCircle"); + *(void **)&LICE_FillTriangle=getFunc("LICE_FillTriangle"); + *(void **)&LICE_FillConvexPolygon=getFunc("LICE_FillConvexPolygon"); + *(void **)&LICE_RoundRect = getFunc("LICE_RoundRect"); + *(void **)&LICE_Arc = getFunc("LICE_Arc"); + + *(void **)&LICE_MultiplyAddRect = getFunc("LICE_MultiplyAddRect"); + *(void **)&LICE_GradRect = getFunc("LICE_GradRect"); + *(void **)&LICE_TransformBlit2 = getFunc("LICE_TransformBlit2"); + *(void **)&LICE_DeltaBlit = getFunc("LICE_DeltaBlit"); + + *(void **)&LICE__DestroyFont = getFunc("LICE__DestroyFont"); + *(void **)&LICE_CreateFont = getFunc("LICE_CreateFont"); + *(void **)&LICE__SetFromHFont = getFunc("LICE__SetFromHFont2"); + + *(void **)&LICE__SetTextColor = getFunc("LICE__SetTextColor"); + *(void **)&LICE__SetTextCombineMode = getFunc("LICE__SetTextCombineMode"); + *(void **)&LICE__DrawText = getFunc("LICE__DrawText"); +} +#endif + +#ifdef EEL_WANT_DOCUMENTATION + +#ifdef EELSCRIPT_LICE_MAX_IMAGES +#define MKSTR2(x) #x +#define MKSTR(x) MKSTR2(x) +#define EEL_LICE_DOC_MAXHANDLE MKSTR(EELSCRIPT_LICE_MAX_IMAGES-1) +#else +#define EEL_LICE_DOC_MAXHANDLE "127" +#endif + + +static const char *eel_lice_function_reference = +#ifdef EEL_LICE_WANT_STANDALONE +#ifndef EEL_LICE_STANDALONE_NOINITQUIT +#ifdef EEL_LICE_WANTDOCK + "gfx_init\t\"name\"[,width,height,dockstate,xpos,ypos]\tInitializes the graphics window with title name. Suggested width and height can be specified. If window is already open, a non-empty name will re-title window, or an empty title will resize window. \n\n" +#else + "gfx_init\t\"name\"[,width,height,xpos,ypos]\tInitializes the graphics window with title name. Suggested width and height can be specified. If window is already open, a non-empty name will re-title window, or an empty title will resize window.\n\n" +#endif + "Once the graphics window is open, gfx_update() should be called periodically. \0" + "gfx_quit\t\tCloses the graphics window.\0" +#endif +#ifdef EEL_LICE_WANT_STANDALONE_UPDATE + "gfx_update\t\tUpdates the graphics display, if opened\0" +#endif +#endif +#ifdef EEL_LICE_WANTDOCK + "gfx_dock\tv[,wx,wy,ww,wh]\tCall with v=-1 to query docked state, otherwise v>=0 to set docked state. State is &1 if docked, second byte is docker index (or last docker index if undocked). If wx-wh are specified, they will be filled with the undocked window position/size\0" +#endif + "gfx_aaaaa\t\t" + "The following global variables are special and will be used by the graphics system:\n\n\3" + // we depend on the formatting here -- following gfx_aaaaa, search for \4[gfx_*|mouse_*]- for syntax highlight etc + "\4gfx_r - current red component (0..1) used by drawing operations.\n" + "\4gfx_g - current green component (0..1) used by drawing operations.\n" + "\4gfx_b - current blue component (0..1) used by drawing operations.\n" + "\4gfx_a2 - current alpha component (0..1) used by drawing operations when writing solid colors (normally ignored but useful when creating transparent images).\n" + "\4gfx_a - alpha for drawing (1=normal).\n" + "\4gfx_mode - blend mode for drawing. Set mode to 0 for default options. Add 1.0 for additive blend mode (if you wish to do subtractive, set gfx_a to negative and use gfx_mode as additive). Add 2.0 to disable source alpha for gfx_blit(). Add 4.0 to disable filtering for gfx_blit(). \n" + "\4gfx_w - width of the UI framebuffer. \n" + "\4gfx_h - height of the UI framebuffer. \n" + "\4gfx_x - current graphics position X. Some drawing functions use as start position and update. \n" + "\4gfx_y - current graphics position Y. Some drawing functions use as start position and update. \n" + "\4gfx_clear - if greater than -1.0, framebuffer will be cleared to that color. the color for this one is packed RGB (0..255), i.e. red+green*256+blue*65536. The default is 0 (black). \n" + "\4gfx_dest - destination for drawing operations, -1 is main framebuffer, set to 0.." EEL_LICE_DOC_MAXHANDLE " to have drawing operations go to an offscreen buffer (or loaded image).\n" + "\4gfx_texth - the (READ-ONLY) height of a line of text in the current font. Do not modify this variable.\n" + "\4gfx_ext_retina - to support hidpi/retina, callers should set to 1.0 on initialization, this value will be updated to value greater than 1.0 (such as 2.0) if retina/hidpi. On macOS gfx_w/gfx_h/etc will be doubled, but on other systems gfx_w/gfx_h will remain the same and gfx_ext_retina is a scaling hint for drawing.\n" + "\4mouse_x - current X coordinate of the mouse relative to the graphics window.\n" + "\4mouse_y - current Y coordinate of the mouse relative to the graphics window.\n" + "\4mouse_wheel - wheel position, will change typically by 120 or a multiple thereof, the caller should clear the state to 0 after reading it.\n" + "\4mouse_hwheel - horizontal wheel positions, will change typically by 120 or a multiple thereof, the caller should clear the state to 0 after reading it.\n" + "\4mouse_cap - a bitfield of mouse and keyboard modifier state. Note that a script must call gfx_getchar() at least once in order to get modifier state when the mouse is not captured by the window. Bitfield bits:\3" + "\4" "1: left mouse button\n" + "\4" "2: right mouse button\n" +#ifdef __APPLE__ + "\4" "4: Command key\n" + "\4" "8: Shift key\n" + "\4" "16: Option key\n" + "\4" "32: Control key\n" +#else + "\4" "4: Control key\n" + "\4" "8: Shift key\n" + "\4" "16: Alt key\n" + "\4" "32: Windows key\n" +#endif + "\4" "64: middle mouse button\n" + "\2" + "\2\0" + +"gfx_getchar\t[char]\tIf char is 0 or omitted, returns a character from the keyboard queue, or 0 if no character is available, or -1 if the graphics window is not open. " + "If char is specified and nonzero, that character's status will be checked, and the function will return greater than 0 if it is pressed. Note that calling gfx_getchar() at least once causes mouse_cap to reflect keyboard modifiers even when the mouse is not captured.\n\n" + "Common values are standard ASCII, such as 'a', 'A', '=' and '1', but for many keys multi-byte values are used, including 'home', 'up', 'down', 'left', 'rght', 'f1'.. 'f12', 'pgup', 'pgdn', 'ins', and 'del'. \n\n" + "Modified and special keys can also be returned, including:\3\n" + "\4Ctrl/Cmd+A..Ctrl+Z as 1..26\n" + "\4Ctrl/Cmd+Alt+A..Z as 257..282\n" + "\4Alt+A..Z as 'A'+256..'Z'+256\n" + "\4" "27 for ESC\n" + "\4" "13 for Enter\n" + "\4' ' for space\n" + "\4" "65536 for query of special flags, returns: &1 (supported), &2=window has focus, &4=window is visible\n" + "\2\0" + + "gfx_showmenu\t\"str\"\tShows a popup menu at gfx_x,gfx_y. str is a list of fields separated by | characters. " + "Each field represents a menu item.\nFields can start with special characters:\n\n" + "# : grayed out\n" + "! : checked\n" + "> : this menu item shows a submenu\n" + "< : last item in the current submenu\n\n" + "An empty field will appear as a separator in the menu. " + "gfx_showmenu returns 0 if the user selected nothing from the menu, 1 if the first field is selected, etc.\nExample:\n\n" + "gfx_showmenu(\"first item, followed by separator||!second item, checked|>third item which spawns a submenu|#first item in submenu, grayed out| +#endif +#include +#include +#include + +#define EEL_DCT_MINBITLEN 5 +#define EEL_DCT_MAXBITLEN 12 + + +#define PI 3.1415926535897932384626433832795 + +typedef struct { + int n; + int log2n; + EEL_F *trig; + int *bitrev; + EEL_F scale; + EEL_F *window; +} mdct_lookup; + +static void mdct(EEL_F *in, EEL_F *out, int len) +{ + int k; + EEL_F pioverlen = PI * 0.5 / (EEL_F)len; + for (k = 0; k < len / 2; k ++) + { + int i; + EEL_F d = 0.0; + for (i = 0; i < len; i ++) + { + d += in[i] * cos(pioverlen * (2.0 * i + 1.0 + len * 0.5) * (2.0 * k + 1.0)); + } + out[k] = (EEL_F)d; + } +} + +static void imdct(EEL_F *in, EEL_F *out, int len) +{ + int k; + EEL_F fourovern = 4.0 / (EEL_F)len; + EEL_F pioverlen = PI * 0.5 / (EEL_F)len; + for (k = 0; k < len; k ++) + { + int i; + EEL_F d = 0.0; + for (i = 0; i < len / 2; i ++) + { + d += in[i] * cos(pioverlen * (2.0 * k + 1.0 + len * 0.5) * (2 * i + 1.0)); + } + out[k] = (EEL_F)(d * fourovern); + } +} + + +// MDCT/iMDCT borrowed from Vorbis, thanks xiph! + + +#define cPI3_8 .38268343236508977175 +#define cPI2_8 .70710678118654752441 +#define cPI1_8 .92387953251128675613 + +#define FLOAT_CONV(x) ((EEL_F) ( x )) +#define MULT_NORM(x) (x) +#define HALVE(x) ((x)*.5f) + + + +/* 8 point butterfly (in place, 4 register) */ +static void mdct_butterfly_8(EEL_F *x) { + EEL_F r0 = x[6] + x[2]; + EEL_F r1 = x[6] - x[2]; + EEL_F r2 = x[4] + x[0]; + EEL_F r3 = x[4] - x[0]; + + x[6] = r0 + r2; + x[4] = r0 - r2; + + r0 = x[5] - x[1]; + r2 = x[7] - x[3]; + x[0] = r1 + r0; + x[2] = r1 - r0; + + r0 = x[5] + x[1]; + r1 = x[7] + x[3]; + x[3] = r2 + r3; + x[1] = r2 - r3; + x[7] = r1 + r0; + x[5] = r1 - r0; + +} + +/* 16 point butterfly (in place, 4 register) */ +static void mdct_butterfly_16(EEL_F *x) { + EEL_F r0 = x[1] - x[9]; + EEL_F r1 = x[0] - x[8]; + + x[8] += x[0]; + x[9] += x[1]; + x[0] = MULT_NORM((r0 + r1) * cPI2_8); + x[1] = MULT_NORM((r0 - r1) * cPI2_8); + + r0 = x[3] - x[11]; + r1 = x[10] - x[2]; + x[10] += x[2]; + x[11] += x[3]; + x[2] = r0; + x[3] = r1; + + r0 = x[12] - x[4]; + r1 = x[13] - x[5]; + x[12] += x[4]; + x[13] += x[5]; + x[4] = MULT_NORM((r0 - r1) * cPI2_8); + x[5] = MULT_NORM((r0 + r1) * cPI2_8); + + r0 = x[14] - x[6]; + r1 = x[15] - x[7]; + x[14] += x[6]; + x[15] += x[7]; + x[6] = r0; + x[7] = r1; + + mdct_butterfly_8(x); + mdct_butterfly_8(x + 8); +} + +/* 32 point butterfly (in place, 4 register) */ +static void mdct_butterfly_32(EEL_F *x) { + EEL_F r0 = x[30] - x[14]; + EEL_F r1 = x[31] - x[15]; + + x[30] += x[14]; + x[31] += x[15]; + x[14] = r0; + x[15] = r1; + + r0 = x[28] - x[12]; + r1 = x[29] - x[13]; + x[28] += x[12]; + x[29] += x[13]; + x[12] = MULT_NORM( r0 * cPI1_8 - r1 * cPI3_8 ); + x[13] = MULT_NORM( r0 * cPI3_8 + r1 * cPI1_8 ); + + r0 = x[26] - x[10]; + r1 = x[27] - x[11]; + x[26] += x[10]; + x[27] += x[11]; + x[10] = MULT_NORM(( r0 - r1 ) * cPI2_8); + x[11] = MULT_NORM(( r0 + r1 ) * cPI2_8); + + r0 = x[24] - x[8]; + r1 = x[25] - x[9]; + x[24] += x[8]; + x[25] += x[9]; + x[8] = MULT_NORM( r0 * cPI3_8 - r1 * cPI1_8 ); + x[9] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); + + r0 = x[22] - x[6]; + r1 = x[7] - x[23]; + x[22] += x[6]; + x[23] += x[7]; + x[6] = r1; + x[7] = r0; + + r0 = x[4] - x[20]; + r1 = x[5] - x[21]; + x[20] += x[4]; + x[21] += x[5]; + x[4] = MULT_NORM( r1 * cPI1_8 + r0 * cPI3_8 ); + x[5] = MULT_NORM( r1 * cPI3_8 - r0 * cPI1_8 ); + + r0 = x[2] - x[18]; + r1 = x[3] - x[19]; + x[18] += x[2]; + x[19] += x[3]; + x[2] = MULT_NORM(( r1 + r0 ) * cPI2_8); + x[3] = MULT_NORM(( r1 - r0 ) * cPI2_8); + + r0 = x[0] - x[16]; + r1 = x[1] - x[17]; + x[16] += x[0]; + x[17] += x[1]; + x[0] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); + x[1] = MULT_NORM( r1 * cPI1_8 - r0 * cPI3_8 ); + + mdct_butterfly_16(x); + mdct_butterfly_16(x + 16); + +} + +/* N point first stage butterfly (in place, 2 register) */ +static void mdct_butterfly_first(EEL_F *T, + EEL_F *x, + int points) { + + EEL_F *x1 = x + points - 8; + EEL_F *x2 = x + (points >> 1) - 8; + EEL_F r0; + EEL_F r1; + + do { + + r0 = x1[6] - x2[6]; + r1 = x1[7] - x2[7]; + x1[6] += x2[6]; + x1[7] += x2[7]; + x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + r0 = x1[4] - x2[4]; + r1 = x1[5] - x2[5]; + x1[4] += x2[4]; + x1[5] += x2[5]; + x2[4] = MULT_NORM(r1 * T[5] + r0 * T[4]); + x2[5] = MULT_NORM(r1 * T[4] - r0 * T[5]); + + r0 = x1[2] - x2[2]; + r1 = x1[3] - x2[3]; + x1[2] += x2[2]; + x1[3] += x2[3]; + x2[2] = MULT_NORM(r1 * T[9] + r0 * T[8]); + x2[3] = MULT_NORM(r1 * T[8] - r0 * T[9]); + + r0 = x1[0] - x2[0]; + r1 = x1[1] - x2[1]; + x1[0] += x2[0]; + x1[1] += x2[1]; + x2[0] = MULT_NORM(r1 * T[13] + r0 * T[12]); + x2[1] = MULT_NORM(r1 * T[12] - r0 * T[13]); + + x1 -= 8; + x2 -= 8; + T += 16; + + } while(x2 >= x); +} + +/* N/stage point generic N stage butterfly (in place, 2 register) */ +static void mdct_butterfly_generic(EEL_F *T, + EEL_F *x, + int points, + int trigint) { + + EEL_F *x1 = x + points - 8; + EEL_F *x2 = x + (points >> 1) - 8; + EEL_F r0; + EEL_F r1; + + do { + + r0 = x1[6] - x2[6]; + r1 = x1[7] - x2[7]; + x1[6] += x2[6]; + x1[7] += x2[7]; + x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T += trigint; + + r0 = x1[4] - x2[4]; + r1 = x1[5] - x2[5]; + x1[4] += x2[4]; + x1[5] += x2[5]; + x2[4] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[5] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T += trigint; + + r0 = x1[2] - x2[2]; + r1 = x1[3] - x2[3]; + x1[2] += x2[2]; + x1[3] += x2[3]; + x2[2] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[3] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T += trigint; + + r0 = x1[0] - x2[0]; + r1 = x1[1] - x2[1]; + x1[0] += x2[0]; + x1[1] += x2[1]; + x2[0] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[1] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T += trigint; + x1 -= 8; + x2 -= 8; + + } while(x2 >= x); +} + +static void mdct_butterflies(mdct_lookup *init, + EEL_F *x, + int points) { + + EEL_F *T = init->trig; + int stages = init->log2n - 5; + int i, j; + + if(--stages > 0) { + mdct_butterfly_first(T, x, points); + } + + for(i = 1; --stages > 0; i++) { + for(j = 0; j < (1 << i); j++) + mdct_butterfly_generic(T, x + (points >> i)*j, points >> i, 4 << i); + } + + for(j = 0; j < points; j += 32) + mdct_butterfly_32(x + j); + +} + +static void mdct_bitreverse(mdct_lookup *init, + EEL_F *x) { + int n = init->n; + int *bit = init->bitrev; + EEL_F *w0 = x; + EEL_F *w1 = x = w0 + (n >> 1); + EEL_F *T = init->trig + n; + + do { + EEL_F *x0 = x + bit[0]; + EEL_F *x1 = x + bit[1]; + + EEL_F r0 = x0[1] - x1[1]; + EEL_F r1 = x0[0] + x1[0]; + EEL_F r2 = MULT_NORM(r1 * T[0] + r0 * T[1]); + EEL_F r3 = MULT_NORM(r1 * T[1] - r0 * T[0]); + + w1 -= 4; + + r0 = HALVE(x0[1] + x1[1]); + r1 = HALVE(x0[0] - x1[0]); + + w0[0] = r0 + r2; + w1[2] = r0 - r2; + w0[1] = r1 + r3; + w1[3] = r3 - r1; + + x0 = x + bit[2]; + x1 = x + bit[3]; + + r0 = x0[1] - x1[1]; + r1 = x0[0] + x1[0]; + r2 = MULT_NORM(r1 * T[2] + r0 * T[3]); + r3 = MULT_NORM(r1 * T[3] - r0 * T[2]); + + r0 = HALVE(x0[1] + x1[1]); + r1 = HALVE(x0[0] - x1[0]); + + w0[2] = r0 + r2; + w1[0] = r0 - r2; + w0[3] = r1 + r3; + w1[1] = r3 - r1; + + T += 4; + bit += 4; + w0 += 4; + + } while(w0 < w1); +} + +static void megabuf_mdct_apply_window(void *init, EEL_F *inbuf, EEL_F *outbuf) +{ + mdct_lookup *p = (mdct_lookup *)init; + EEL_F *w; + int cnt; + if (!p) return; + + w = p->window; + if (!w) return; + + cnt = p->n / 2; + while (cnt--) *outbuf++ = *inbuf++ * *w++; + cnt = p->n / 2; + while (cnt--) *outbuf++ = *inbuf++ * *--w; +} + + + +static void *megabuf_mdct_init(int n) { + mdct_lookup *lookup = (mdct_lookup *)calloc(sizeof(mdct_lookup), 1); + int i; + EEL_F c = (PI / (EEL_F) n); + int *bitrev; + EEL_F *T; + int n2, log2n; + if (!lookup) return 0; + + lookup->n = n; + lookup->window = (EEL_F *)calloc(sizeof(EEL_F), n / 2); + if (!lookup->window) return lookup; + + for (i = 0; i < n / 2; i ++) + { + lookup->window[i] = sin(c * (i + 0.5)); + } + + if (n <= 32) return lookup; + bitrev = (int*)calloc(sizeof(int), (n / 4)); + lookup->bitrev = bitrev; + if (!bitrev) return lookup; + + T = (EEL_F*)calloc(sizeof(EEL_F), (n + n / 4)); + lookup->trig = T; + if (!T) return lookup; + + n2 = n >> 1; + log2n = lookup->log2n = (int)(log((double)n) / log(2.0) + 0.5); + + /* trig lookups... */ + + for(i = 0; i < n / 4; i++) { + T[i * 2] = FLOAT_CONV(cos((PI / n) * (4 * i))); + T[i * 2 + 1] = FLOAT_CONV(-sin((PI / n) * (4 * i))); + T[n2 + i * 2] = FLOAT_CONV(cos((PI / (2 * n)) * (2 * i + 1))); + T[n2 + i * 2 + 1] = FLOAT_CONV(sin((PI / (2 * n)) * (2 * i + 1))); + } + for(i = 0; i < n / 8; i++) { + T[n + i * 2] = FLOAT_CONV(cos((PI / n) * (4 * i + 2)) * .5); + T[n + i * 2 + 1] = FLOAT_CONV(-sin((PI / n) * (4 * i + 2)) * .5); + } + + /* bitreverse lookup... */ + + { + int mask = (1 << (log2n - 1)) - 1, j; + int msb = 1 << (log2n - 2); + for(i = 0; i < n / 8; i++) { + int acc = 0; + for(j = 0; msb >> j; j++) + if((msb >> j)&i)acc |= 1 << j; + bitrev[i * 2] = ((~acc)&mask) - 1; + bitrev[i * 2 + 1] = acc; + + } + } + lookup->scale = FLOAT_CONV(4.f / n); + return lookup; +} + +static void megabuf_mdct_backward(void *init, EEL_F *in, EEL_F *out) { + mdct_lookup *lookup = (mdct_lookup *)init; + int n, n2, n4; + EEL_F *iX, *oX, *T; + if (!lookup) return; + n = lookup->n; + if (n <= 32 || !lookup->bitrev || !lookup->trig) + { + imdct(in, out, n); + return; + } + n2 = n >> 1; + n4 = n >> 2; + + /* rotate */ + + iX = in + n2 - 7; + oX = out + n2 + n4; + T = lookup->trig + n4; + + do { + oX -= 4; + oX[0] = MULT_NORM(-iX[2] * T[3] - iX[0] * T[2]); + oX[1] = MULT_NORM (iX[0] * T[3] - iX[2] * T[2]); + oX[2] = MULT_NORM(-iX[6] * T[1] - iX[4] * T[0]); + oX[3] = MULT_NORM (iX[4] * T[1] - iX[6] * T[0]); + iX -= 8; + T += 4; + } while(iX >= in); + + iX = in + n2 - 8; + oX = out + n2 + n4; + T = lookup->trig + n4; + + do { + T -= 4; + oX[0] = MULT_NORM (iX[4] * T[3] + iX[6] * T[2]); + oX[1] = MULT_NORM (iX[4] * T[2] - iX[6] * T[3]); + oX[2] = MULT_NORM (iX[0] * T[1] + iX[2] * T[0]); + oX[3] = MULT_NORM (iX[0] * T[0] - iX[2] * T[1]); + iX -= 8; + oX += 4; + } while(iX >= in); + + mdct_butterflies(lookup, out + n2, n2); + mdct_bitreverse(lookup, out); + + /* roatate + window */ + + { + EEL_F *oX1 = out + n2 + n4; + EEL_F *oX2 = out + n2 + n4; + iX = out; + T = lookup->trig + n2; + + do { + oX1 -= 4; + + oX1[3] = MULT_NORM (iX[0] * T[1] - iX[1] * T[0]); + oX2[0] = -MULT_NORM (iX[0] * T[0] + iX[1] * T[1]); + + oX1[2] = MULT_NORM (iX[2] * T[3] - iX[3] * T[2]); + oX2[1] = -MULT_NORM (iX[2] * T[2] + iX[3] * T[3]); + + oX1[1] = MULT_NORM (iX[4] * T[5] - iX[5] * T[4]); + oX2[2] = -MULT_NORM (iX[4] * T[4] + iX[5] * T[5]); + + oX1[0] = MULT_NORM (iX[6] * T[7] - iX[7] * T[6]); + oX2[3] = -MULT_NORM (iX[6] * T[6] + iX[7] * T[7]); + + oX2 += 4; + iX += 8; + T += 8; + } while(iX < oX1); + + iX = out + n2 + n4; + oX1 = out + n4; + oX2 = oX1; + + do { + oX1 -= 4; + iX -= 4; + + oX2[0] = -(oX1[3] = iX[3]); + oX2[1] = -(oX1[2] = iX[2]); + oX2[2] = -(oX1[1] = iX[1]); + oX2[3] = -(oX1[0] = iX[0]); + + oX2 += 4; + } while(oX2 < iX); + + iX = out + n2 + n4; + oX1 = out + n2 + n4; + oX2 = out + n2; + do { + oX1 -= 4; + oX1[0] = iX[3]; + oX1[1] = iX[2]; + oX1[2] = iX[1]; + oX1[3] = iX[0]; + iX += 4; + } while(oX1 > oX2); + } +} + + +static void megabuf_mdct_forward(void *init, EEL_F *in, EEL_F *out) { + mdct_lookup *lookup = (mdct_lookup *)init; + int n, n2, n4, n8; + EEL_F *w, *w2; + if (!lookup) return; + + n = lookup->n; + if (n <= 32 || !lookup->bitrev || !lookup->trig) + { + mdct(in, out, n); + return; + } + n2 = n >> 1; + n4 = n >> 2; + n8 = n >> 3; + EEL_F oldw[1<trig + n2; + + int i = 0; + + for(i = 0; i < n8; i += 2) { + x0 -= 4; + T -= 2; + r0 = x0[2] + x1[0]; + r1 = x0[0] + x1[2]; + w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]); + w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]); + x1 += 4; + } + + x1 = in + 1; + + for(; i < n2 - n8; i += 2) { + T -= 2; + x0 -= 4; + r0 = x0[2] - x1[0]; + r1 = x0[0] - x1[2]; + w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]); + w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]); + x1 += 4; + } + + x0 = in + n; + + for(; i < n2; i += 2) { + T -= 2; + x0 -= 4; + r0 = -x0[2] - x1[0]; + r1 = -x0[0] - x1[2]; + w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]); + w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]); + x1 += 4; + } + + + mdct_butterflies(lookup, w + n2, n2); + mdct_bitreverse(lookup, w); + + /* roatate + window */ + + T = lookup->trig + n2; + x0 = out + n2; + + for(i = 0; i < n4; i++) { + x0--; + out[i] = MULT_NORM((w[0] * T[0] + w[1] * T[1]) * lookup->scale); + x0[0] = MULT_NORM((w[0] * T[1] - w[1] * T[0]) * lookup->scale); + w += 2; + T += 2; + } + } +} + +#if 0 + +static void dct(EEL_F *in, EEL_F *out, int len) +{ + int k; + EEL_F wk = sqrt(2.0 / len); + EEL_F overtwolen = 0.5 / (EEL_F)len; + for (k = 0; k < len; k ++) + { + int n; + EEL_F d = 0.0; + for (n = 0; n < len; n ++) + { + int an = n + 1; + d += in[n] * cos(PI * (2.0 * n + 1.0) * (EEL_F)k * overtwolen); + } + if (!k) d /= sqrt(len); + else d *= wk; + out[k] = (EEL_F)d; + } +} + + +static void idct(EEL_F *in, EEL_F *out, int len) +{ + int n; + EEL_F dd0 = 1.0 / sqrt(len); + EEL_F dd1 = sqrt(2.0 / len); + EEL_F overtwolen = 0.5 / len; + for (n = 0; n < len; n ++) + { + int k; + EEL_F d = 0.0; + for (k = 0; k < len; k ++) + { + EEL_F dd; + if (!k) dd = dd0 * in[k]; + else dd = dd1 * in[k]; + d += dd * cos(PI * (2.0 * n + 1.0) * k * overtwolen); + } + out[n] = (EEL_F)d; + } +} +#endif + + +// 0 is megabuf blocks +// 1 is need_free flag + + +static EEL_F * NSEEL_CGEN_CALL mdct_func(int dir, EEL_F **blocks, EEL_F *start, EEL_F *length) +{ + int l = (int)(*length + 0.0001); + int offs = (int)(*start + 0.0001); + int bitl = 0; + int ilen; + int bidx; + EEL_F *ptr; + while (l > 1 && bitl < EEL_DCT_MAXBITLEN) + { + bitl++; + l >>= 1; + } + if (bitl < EEL_DCT_MINBITLEN) + { + return start; + } + ilen = 1 << bitl; + + bidx = bitl - EEL_DCT_MINBITLEN; + + + // check to make sure we don't cross a boundary + if (offs / NSEEL_RAM_ITEMSPERBLOCK != (offs + ilen * 2 - 1) / NSEEL_RAM_ITEMSPERBLOCK) + { + return start; + } + + ptr = __NSEEL_RAMAlloc(blocks, offs); + if (!ptr || ptr == &nseel_ramalloc_onfail) + { + return start; + } + + if (ilen > 1) + { + static void *mdct_ctxs[1 + EEL_DCT_MAXBITLEN - EEL_DCT_MINBITLEN]; + + if (!mdct_ctxs[bidx]) + { + NSEEL_HOSTSTUB_EnterMutex(); + if (!mdct_ctxs[bidx]) + mdct_ctxs[bidx] = megabuf_mdct_init(ilen); + NSEEL_HOSTSTUB_LeaveMutex(); + } + + if (mdct_ctxs[bidx]) + { + EEL_F buf[1 << EEL_DCT_MAXBITLEN]; + if (dir < 0) + { + megabuf_mdct_backward(mdct_ctxs[bidx], ptr, buf); + megabuf_mdct_apply_window(mdct_ctxs[bidx], buf, ptr); + } + else + { + megabuf_mdct_apply_window(mdct_ctxs[bidx], ptr, buf); + megabuf_mdct_forward(mdct_ctxs[bidx], buf, ptr); + } + } + } + return start; +} + + + +static EEL_F * NSEEL_CGEN_CALL megabuf_mdct(EEL_F **blocks, EEL_F *start, EEL_F *length) +{ + return mdct_func(0, blocks, start, length); +} + +static EEL_F * NSEEL_CGEN_CALL megabuf_imdct(EEL_F **blocks, EEL_F *start, EEL_F *length) +{ + return mdct_func(-1, blocks, start, length); +} + +void EEL_mdct_register() +{ + NSEEL_addfunc_retptr("mdct", 2, NSEEL_PProc_RAM, &megabuf_mdct); + NSEEL_addfunc_retptr("imdct", 2, NSEEL_PProc_RAM, &megabuf_imdct); +} + +#ifdef EEL_WANT_DOCUMENTATION +static const char *eel_mdct_function_reference = +"mdct\tbuffer,length\tPerforms a windowed modified DCT, taking length inputs and producing length/2 outputs. buffer must not cross a 65,536 item boundary, and length must be 64, 128, 256, 512, 2048 or 4096.\0" +"imdct\tbuffer,length\tPerforms a windowed inverse modified DCT, taking length/2 inputs and producing length outputs. buffer must not cross a 65,536 item boundary, and length must be 64, 128, 256, 512, 2048 or 4096.\0" +; +#endif + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_misc.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_misc.h new file mode 100644 index 000000000..a819a8ff5 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_misc.h @@ -0,0 +1,73 @@ +#ifndef _EEL_MISC_H_ +#define _EEL_MISC_H_ + + +#ifndef _WIN32 +#include +#endif +#include +// some generic EEL functions for things like time + +#ifndef EEL_MISC_NO_SLEEP +static EEL_F NSEEL_CGEN_CALL _eel_sleep(void *opaque, EEL_F *amt) +{ + if (*amt >= 0.0) + { + #ifdef _WIN32 + if (*amt > 30000000.0) Sleep(30000000); + else Sleep((DWORD)(*amt+0.5)); + #else + if (*amt > 30000000.0) usleep(((useconds_t)30000000)*1000); + else usleep((useconds_t)(*amt*1000.0+0.5)); + #endif + } + return 0.0; +} +#endif + +static EEL_F * NSEEL_CGEN_CALL _eel_time(void *opaque, EEL_F *v) +{ + *v = (EEL_F) time(NULL); + return v; +} + +static EEL_F * NSEEL_CGEN_CALL _eel_time_precise(void *opaque, EEL_F *v) +{ +#ifdef _WIN32 + LARGE_INTEGER freq,now; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&now); + *v = (double)now.QuadPart / (double)freq.QuadPart; + // *v = (EEL_F)timeGetTime() * 0.001; +#else + struct timeval tm={0,}; + gettimeofday(&tm,NULL); + *v = tm.tv_sec + tm.tv_usec*0.000001; +#endif + return v; +} + + +void EEL_misc_register() +{ +#ifndef EEL_MISC_NO_SLEEP + NSEEL_addfunc_retval("sleep",1,NSEEL_PProc_THIS,&_eel_sleep); +#endif + NSEEL_addfunc_retptr("time",1,NSEEL_PProc_THIS,&_eel_time); + NSEEL_addfunc_retptr("time_precise",1,NSEEL_PProc_THIS,&_eel_time_precise); +} + +#ifdef EEL_WANT_DOCUMENTATION +static const char *eel_misc_function_reference = +#ifndef EEL_MISC_NO_SLEEP + "sleep\tms\tYields the CPU for the millisecond count specified, calling Sleep() on Windows or usleep() on other platforms.\0" +#endif + "time\t[&val]\tSets the parameter (or a temporary buffer if omitted) to the number of seconds since January 1, 1970, and returns a reference to that value. " + "The granularity of the value returned is 1 second.\0" + "time_precise\t[&val]\tSets the parameter (or a temporary buffer if omitted) to a system-local timestamp in seconds, and returns a reference to that value. " + "The granularity of the value returned is system defined (but generally significantly smaller than one second).\0" +; +#endif + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_net.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_net.h new file mode 100644 index 000000000..a796223c3 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_net.h @@ -0,0 +1,564 @@ +#ifndef _EEL_NET_H_ +#define _EEL_NET_H_ + +// x = tcp_listen(port[,interface, connected_ip_out]) poll this, returns connection id > 0, or <0 on error, or 0 if no new connect -- interface only valid on first call (or after tcp_listen_end(port)) +// tcp_listen_end(port); + +// connection = tcp_connect(host, port[, block]) // connection id > 0 on ok +// tcp_set_block(connection, block?) +// tcp_close(connection) + +// tcp_send(connection, string[, length]) // can return 0 if block, -1 if error, otherwise returns length sent +// tcp_recv(connection, string[, maxlength]) // 0 on nothing, -1 on error, otherwise returns length recv'd + + +// need: +// #define EEL_NET_GET_CONTEXT(opaque) (((sInst *)opaque)->m_net_state) + +// you must pass a JNL_AsyncDNS object to eel_net_state to support nonblocking connect with DNS resolution, otherwise DNS will block +// #define EEL_NET_NO_SYNC_DNS -- never ever call gethostbyname() synchronously, may disable DNS for blocking connect, or if a JNL_IAsyncDNS is not provided. + +#ifndef EEL_NET_MAXSEND +#define EEL_NET_MAXSEND (EEL_STRING_MAXUSERSTRING_LENGTH_HINT+4096) +#endif + + +#include "../jnetlib/netinc.h" +#define JNL_NO_IMPLEMENTATION +#include "../jnetlib/asyncdns.h" + +class eel_net_state +{ + public: + enum { STATE_FREE=0, STATE_RESOLVING, STATE_CONNECTED, STATE_ERR }; + enum { CONNECTION_ID_BASE=0x110000 }; + + eel_net_state(int max_con, JNL_IAsyncDNS *dns); + ~eel_net_state(); + + struct connection_state { + char *hostname; // set during resolve only + SOCKET sock; + int state; // STATE_RESOLVING... + int port; + bool blockmode; + }; + WDL_TypedBuf m_cons; + WDL_IntKeyedArray m_listens; + JNL_IAsyncDNS *m_dns; + + EEL_F onConnect(char *hostNameOwned, int port, int block); + EEL_F onClose(void *opaque, EEL_F handle); + EEL_F set_block(void *opaque, EEL_F handle, bool block); + EEL_F onListen(void *opaque, EEL_F handle, int mode, EEL_F *ifStr, EEL_F *ipOut); + + int __run_connect(connection_state *cs, unsigned int ip); + int __run(connection_state *cs); + int do_send(void *opaque, EEL_F h, const char *src, int len); + int do_recv(void *opaque, EEL_F h, char *buf, int maxlen); + #ifdef _WIN32 + bool m_had_socketlib_init; + #endif +}; + +eel_net_state::eel_net_state(int max_con, JNL_IAsyncDNS *dns) +{ + #ifdef _WIN32 + m_had_socketlib_init=false; + #endif + + m_cons.Resize(max_con); + int x; + for (x=0;xstate == STATE_FREE) + { + unsigned int ip=inet_addr(hostNameOwned); + if (m_dns && ip == INADDR_NONE && !block) + { + const int r=m_dns->resolve(hostNameOwned,&ip); + if (r<0) break; // error! + + if (r>0) ip = INADDR_NONE; + } +#ifndef EEL_NET_NO_SYNC_DNS + else if (ip == INADDR_NONE) + { + struct hostent *he = gethostbyname(hostNameOwned); + if (he) ip = *(int *)he->h_addr; + } +#endif + if (hostNameOwned || ip != INADDR_NONE) + { + if (ip != INADDR_NONE) + { + free(hostNameOwned); + hostNameOwned=NULL; + } + + s->state = STATE_RESOLVING; + s->hostname = hostNameOwned; + s->blockmode = !!block; + s->port = port; + if (hostNameOwned || __run_connect(s,ip)) return x + CONNECTION_ID_BASE; + + s->state=STATE_FREE; + s->hostname=NULL; + } + break; + } + } + free(hostNameOwned); + return -1; +} + +EEL_F eel_net_state::onListen(void *opaque, EEL_F handle, int mode, EEL_F *ifStr, EEL_F *ipOut) +{ + const int port = (int) handle; + if (port < 1 || port > 65535) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("tcp_listen(%d): invalid port specified, will never succeed",port); +#endif + return 0.0; + } +#ifdef _WIN32 + if (!m_had_socketlib_init) + { + m_had_socketlib_init=1; + WSADATA wsaData; + WSAStartup(MAKEWORD(1, 1), &wsaData); + } +#endif + SOCKET *sockptr = m_listens.GetPtr(port); + if (mode<0) + { + if (!sockptr) return -1.0; + + SOCKET ss=*sockptr; + m_listens.Delete(port); + if (ss != INVALID_SOCKET) + { + shutdown(ss, SHUT_RDWR); + closesocket(ss); + } + return 0.0; + } + if (!sockptr) + { + struct sockaddr_in sin; + memset((char *) &sin, 0,sizeof(sin)); + if (ifStr) + { + EEL_STRING_MUTEXLOCK_SCOPE + const char *fn = EEL_STRING_GET_FOR_INDEX(*ifStr,NULL); +#ifdef EEL_STRING_DEBUGOUT + if (!fn) EEL_STRING_DEBUGOUT("tcp_listen(%d): bad string identifier %f for second parameter (interface)",port,*ifStr); +#endif + if (fn && *fn) sin.sin_addr.s_addr=inet_addr(fn); + + } + if (!sin.sin_addr.s_addr || sin.sin_addr.s_addr==INADDR_NONE) sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_family = AF_INET; + sin.sin_port = htons( (short) port ); + SOCKET sock = socket(AF_INET,SOCK_STREAM,0); + if (sock != INVALID_SOCKET) + { + SET_SOCK_DEFAULTS(sock); + SET_SOCK_BLOCK(sock,0); + if (bind(sock,(struct sockaddr *)&sin,sizeof(sin)) || listen(sock,8)==-1) + { + shutdown(sock, SHUT_RDWR); + closesocket(sock); + sock=INVALID_SOCKET; + } + } +#ifdef EEL_STRING_DEBUGOUT + //if (sock == INVALID_SOCKET) EEL_STRING_DEBUGOUT("tcp_listen(%d): failed listening on port",port); + // we report -1 to the caller, no need to error message +#endif + m_listens.Insert(port,sock); + sockptr = m_listens.GetPtr(port); + } + if (!sockptr || *sockptr == INVALID_SOCKET) return -1; + + struct sockaddr_in saddr; + socklen_t length = sizeof(struct sockaddr_in); + SOCKET newsock = accept(*sockptr, (struct sockaddr *) &saddr, &length); + if (newsock == INVALID_SOCKET) + { + return 0; // nothing to report here + } + SET_SOCK_DEFAULTS(newsock); + + int x; + for(x=0;xstate == STATE_FREE) + { + cs->state=STATE_CONNECTED; + free(cs->hostname); + cs->hostname=NULL; + cs->sock = newsock; + cs->blockmode=true; + cs->port=0; + if (ipOut) + { + EEL_STRING_MUTEXLOCK_SCOPE + WDL_FastString *ws=NULL; + EEL_STRING_GET_FOR_WRITE(*ipOut,&ws); + if (ws) + { + const unsigned int a = ntohl(saddr.sin_addr.s_addr); + ws->SetFormatted(128,"%d.%d.%d.%d",(a>>24)&0xff,(a>>16)&0xff,(a>>8)&0xff,a&0xff); + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("tcp_listen(%d): bad string identifier %f for third parameter (IP-out)",port,*ipOut); +#endif + } + } + return x + CONNECTION_ID_BASE; + } + } + shutdown(newsock, SHUT_RDWR); + closesocket(newsock); + return -1; + + +} + +int eel_net_state::__run_connect(connection_state *cs, unsigned int ip) +{ + SOCKET s=socket(AF_INET,SOCK_STREAM,0); + if (s == INVALID_SOCKET) return 0; + SET_SOCK_DEFAULTS(s); + + if (!cs->blockmode) SET_SOCK_BLOCK(s,0); + + struct sockaddr_in sa={0,}; + sa.sin_family=AF_INET; + sa.sin_addr.s_addr = ip; + sa.sin_port = htons(cs->port); + if (!connect(s,(struct sockaddr *)&sa,16) || (!cs->blockmode && JNL_ERRNO == JNL_EINPROGRESS)) + { + cs->state = STATE_CONNECTED; + cs->sock = s; + return 1; + } + shutdown(s, SHUT_RDWR); + closesocket(s); + return 0; +} + +int eel_net_state::__run(connection_state *cs) +{ + if (cs->sock != INVALID_SOCKET) return 0; + + if (!cs->hostname) return -1; + + unsigned int ip=INADDR_NONE; + const int r=m_dns ? m_dns->resolve(cs->hostname,&ip) : -1; + if (r>0) return 0; + + free(cs->hostname); + cs->hostname=NULL; + + if (r<0 || !__run_connect(cs,ip)) + { + cs->state = STATE_ERR; + return -1; + } + + return 0; +} + +int eel_net_state::do_recv(void *opaque, EEL_F h, char *buf, int maxlen) +{ + const int idx=(int)h-CONNECTION_ID_BASE; + if (idx>=0 && idxsock == INVALID_SOCKET && !s->hostname) + EEL_STRING_DEBUGOUT("tcp_recv: connection identifier %f is not open",h); +#endif + if (__run(s) || s->sock == INVALID_SOCKET) return s->state == STATE_ERR ? -1 : 0; + + if (maxlen == 0) return 0; + + const int rv=(int)recv(s->sock,buf,maxlen,0); + if (rv < 0 && !s->blockmode && (JNL_ERRNO == JNL_EWOULDBLOCK || JNL_ERRNO == JNL_ENOTCONN)) return 0; + + if (!rv) return -1; // TCP, 0=connection terminated + + return rv; + } +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("tcp_recv: connection identifier %f is out of range",h); +#endif + return -1; +} + +int eel_net_state::do_send(void *opaque, EEL_F h, const char *src, int len) +{ + const int idx=(int)h-CONNECTION_ID_BASE; + if (idx>=0 && idxsock == INVALID_SOCKET && !s->hostname) + EEL_STRING_DEBUGOUT("tcp_send: connection identifier %f is not open",h); +#endif + if (__run(s) || s->sock == INVALID_SOCKET) return s->state == STATE_ERR ? -1 : 0; + const int rv=(int)send(s->sock,src,len,0); + if (rv < 0 && !s->blockmode && (JNL_ERRNO == JNL_EWOULDBLOCK || JNL_ERRNO == JNL_ENOTCONN)) return 0; + return rv; + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("tcp_send: connection identifier %f out of range",h); +#endif + } + return -1; +} + +EEL_F eel_net_state::set_block(void *opaque, EEL_F handle, bool block) +{ + int idx=(int)handle-CONNECTION_ID_BASE; + if (idx>=0 && idxblockmode != block) + { + s->blockmode=block; + if (s->sock != INVALID_SOCKET) + { + SET_SOCK_BLOCK(s->sock,(block?1:0)); + } + else + { +#ifdef EEL_STRING_DEBUGOUT + if (!s->hostname) EEL_STRING_DEBUGOUT("tcp_set_block: connection identifier %f is not open",handle); +#endif + } + return 1; + } + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("tcp_set_block: connection identifier %f out of range",handle); +#endif + } + return 0; +} + +EEL_F eel_net_state::onClose(void *opaque, EEL_F handle) +{ + int idx=(int)handle-CONNECTION_ID_BASE; + if (idx>=0 && idxhostname; + free(s->hostname); + s->hostname = NULL; + s->state = STATE_ERR; + if (s->sock != INVALID_SOCKET) + { + shutdown(s->sock,SHUT_RDWR); + closesocket(s->sock); + s->sock = INVALID_SOCKET; + return 1.0; + } + else if (!hadhn) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("tcp_close: connection identifier %f is not open",handle); +#endif + } + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("tcp_close: connection identifier %f is out of range",handle); +#endif + } + return 0.0; +} + + +static EEL_F NSEEL_CGEN_CALL _eel_tcp_connect(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_net_state *ctx; + if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) + { + char *dest=NULL; + { + EEL_STRING_MUTEXLOCK_SCOPE + const char *fn = EEL_STRING_GET_FOR_INDEX(parms[0][0],NULL); + if (fn) dest=strdup(fn); + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("tcp_connect(): host string identifier %f invalid",parms[0][0]); +#endif + } + } + if (dest) return ctx->onConnect(dest, (int) (parms[1][0]+0.5), np < 3 || parms[2][0] >= 0.5); + } + return -1.0; +} + +static EEL_F NSEEL_CGEN_CALL _eel_tcp_set_block(void *opaque, EEL_F *handle, EEL_F *bl) +{ + eel_net_state *ctx; + if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->set_block(opaque,*handle, *bl >= 0.5); + return 0; +} + +static EEL_F NSEEL_CGEN_CALL _eel_tcp_close(void *opaque, EEL_F *handle) +{ + eel_net_state *ctx; + if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onClose(opaque,*handle); + return 0; +} + +static EEL_F NSEEL_CGEN_CALL _eel_tcp_recv(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_net_state *ctx; + if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) + { + char buf[EEL_STRING_MAXUSERSTRING_LENGTH_HINT]; + int ml = np > 2 ? (int)parms[2][0] : 4096; + if (ml < 0 || ml > EEL_STRING_MAXUSERSTRING_LENGTH_HINT) ml = EEL_STRING_MAXUSERSTRING_LENGTH_HINT; + + ml=ctx->do_recv(opaque,parms[0][0],buf,ml); + + { + EEL_STRING_MUTEXLOCK_SCOPE + WDL_FastString *ws=NULL; + EEL_STRING_GET_FOR_WRITE(parms[1][0],&ws); + if (ws) + { + if (ml<=0) ws->Set(""); + else ws->SetRaw(buf,ml); + } + } + return ml; + } + return -1; +} + +static EEL_F NSEEL_CGEN_CALL _eel_tcp_send(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_net_state *ctx; + if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) + { + char buf[EEL_NET_MAXSEND]; + + int l; + { + EEL_STRING_MUTEXLOCK_SCOPE + WDL_FastString *ws=NULL; + const char *fn = EEL_STRING_GET_FOR_INDEX(parms[1][0],&ws); + l = ws ? ws->GetLength() : (int) strlen(fn); + if (np > 2) + { + int al=(int)parms[2][0]; + if (al<0) al=0; + if (al 0) memcpy(buf,fn,l); + } + if (l>0) return ctx->do_send(opaque,parms[0][0],buf,l); + return 0; + } + return -1; +} + +static EEL_F NSEEL_CGEN_CALL _eel_tcp_listen(void *opaque, INT_PTR np, EEL_F **parms) +{ + eel_net_state *ctx; + if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onListen(opaque,parms[0][0],1,np>1?parms[1]:NULL,np>2?parms[2]:NULL); + return 0; +} + +static EEL_F NSEEL_CGEN_CALL _eel_tcp_listen_end(void *opaque, EEL_F *handle) +{ + eel_net_state *ctx; + if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onListen(opaque,*handle,-1,NULL,NULL); + return 0; +} + + +void EEL_tcp_register() +{ + NSEEL_addfunc_varparm("tcp_listen",1,NSEEL_PProc_THIS,&_eel_tcp_listen); + NSEEL_addfunc_retval("tcp_listen_end",1,NSEEL_PProc_THIS,&_eel_tcp_listen_end); + + NSEEL_addfunc_varparm("tcp_connect",2,NSEEL_PProc_THIS,&_eel_tcp_connect); + NSEEL_addfunc_varparm("tcp_send",2,NSEEL_PProc_THIS,&_eel_tcp_send); + NSEEL_addfunc_varparm("tcp_recv",2,NSEEL_PProc_THIS,&_eel_tcp_recv); + + NSEEL_addfunc_retval("tcp_set_block",2,NSEEL_PProc_THIS,&_eel_tcp_set_block); + NSEEL_addfunc_retval("tcp_close",1,NSEEL_PProc_THIS,&_eel_tcp_close); +} + +#ifdef EEL_WANT_DOCUMENTATION +const char *eel_net_function_reference = + "tcp_listen\tport[,\"interface\",#ip_out]\tListens on port specified. Returns less than 0 if could not listen, 0 if no new connection available, or greater than 0 (as a TCP connection ID) if a new connection was made. If a connection made and #ip_out specified, it will be set to the remote IP. interface can be empty for all interfaces, otherwise an interface IP as a string.\0" + "tcp_listen_end\tport\tEnds listening on port specified.\0" + "tcp_connect\t\"address\",port[,block]\tCreate a new TCP connection to address:port. If block is specified and 0, connection will be made nonblocking. Returns TCP connection ID greater than 0 on success.\0" + "tcp_send\tconnection,\"str\"[,len]\tSends a string to connection. Returns -1 on error, 0 if connection is non-blocking and would block, otherwise returns length sent. If len is specified and not less than 1, only the first len bytes of the string parameter will be sent.\0" + "tcp_recv\tconnection,#str[,maxlen]\tReceives data from a connection to #str. If maxlen is specified, no more than maxlen bytes will be received. If non-blocking, 0 will be returned if would block. Returns less than 0 if error.\0" + "tcp_set_block\tconnection,block\tSets whether a connection blocks.\0" + "tcp_close\tconnection\tCloses a TCP connection created by tcp_listen() or tcp_connect().\0" +; +#endif + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_strings.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_strings.h new file mode 100644 index 000000000..7991721f1 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eel_strings.h @@ -0,0 +1,1668 @@ +#ifndef __EEL__STRINGS_H__ +#define __EEL__STRINGS_H__ + +#include "ns-eel-int.h" +#include "../wdlcstring.h" +#include "../wdlstring.h" + +// required for context +// #define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((sInst *)opaque)->m_eel_string_state) + + +/* + // writeable user-strings are 0..1023 (EEL_STRING_MAX_USER_STRINGS-1), and can be up to about EEL_STRING_MAXUSERSTRING_LENGTH_HINT bytes long + + printf("string %d blah"); -- output to log, allows %d %u %f etc, if host implements formats [1] + sprintf(str,"string %d blah"); -- output to str [1] + strlen(str); -- returns string length + match("*test*", "this is a test") -- search for first parameter regex-style in second parameter + matchi("*test*", "this is a test") -- search for first parameter regex-style in second parameter (case insensitive) + // %s means 1 or more chars + // %0s means 0 or more chars + // %5s means exactly 5 chars + // %5-s means 5 or more chars + // %-10s means 1-10 chars + // %3-5s means 3-5 chars. + // %0-5s means 0-5 chars. + // %S (uppercase) indicates lazy match (%D, %F, %X, etc) + + strcpy(str, srcstr); -- replaces str with srcstr + strcat(str, srcstr); -- appends srcstr to str + strcmp(str, str2) -- compares strings + stricmp(str, str2) -- compares strings (ignoring case) + strncmp(str, str2, maxlen) -- compares strings up to maxlen bytes + strnicmp(str, str2, maxlen) -- compares strings (ignoring case) up to maxlen bytes + strncpy(str, srcstr, maxlen); -- replaces str with srcstr, up to maxlen (-1 for unlimited) + strncat(str, srcstr, maxlen); -- appends up to maxlen of srcstr to str (-1 for unlimited) + strcpy_from(str,srcstr, offset); -- copies srcstr to str, but starts reading srcstr at offset offset + strcpy_substr(str, srcstr, offs, ml) -- php-style (start at offs, offs<0 means from end, ml for maxlen, ml<0 = reduce length by this amt) + str_getchar(str, offset[, type]); -- returns value at offset offset, type can be omitted or 0 or 'c', 's', 'S', 'i', 'I', 'f', 'F', 'd', 'D', 'uc', 'us', 'US', 'ui', 'UI' + -- negative offset is offset from end of string + str_setchar(str, offset, val[, type]); - sets value at offset offset, type optional. offset must be [0,length], if length it can lengthen string, if >length then call fails + -- negative offset is offset from end of string + + str_setlen(str, len); -- sets length of string (if increasing, will be space-padded) + str_delsub(str, pos, len); -- deletes len chars at pos + str_insert(str, srcstr, pos); -- inserts srcstr at pos + + [1]: note: printf/sprintf are NOT binary safe when using %s with modifiers (such as %100s etc) -- the source string being formatted + will terminate at the first NULL character + ); + + + + */ + +// define this to allow modifying literal strings via code, i.e. foobar = strcat("foo","bar"); +// disabled by default, so literals can be pooled/reused/etc +// #define EEL_STRINGS_MUTABLE_LITERALS + +#ifndef EEL_STRING_MAXUSERSTRING_LENGTH_HINT +#define EEL_STRING_MAXUSERSTRING_LENGTH_HINT 16384 +#endif + +#ifndef EEL_STRING_MAX_USER_STRINGS +// strings 0...x-1 +#define EEL_STRING_MAX_USER_STRINGS 1024 +#endif + +#ifndef EEL_STRING_LITERAL_BASE +// strings defined by "xyz" +#define EEL_STRING_LITERAL_BASE 10000 +#endif + +// base for named mutable strings (#xyz) +#ifndef EEL_STRING_NAMED_BASE +#define EEL_STRING_NAMED_BASE 90000 +#endif + +// base for unnamed mutable strings (#) +#ifndef EEL_STRING_UNNAMED_BASE +#define EEL_STRING_UNNAMED_BASE 190000 +#endif + +// define EEL_STRING_MUTEXLOCK_SCOPE for custom, otherwise EEL_STRING_WANT_MUTEX for builtin locking +#ifndef EEL_STRING_MUTEXLOCK_SCOPE + #ifdef EEL_STRING_WANT_MUTEX + #include "../mutex.h" + #define EEL_STRING_MUTEXLOCK_SCOPE WDL_MutexLock __lock(&(EEL_STRING_GET_CONTEXT_POINTER(opaque)->m_mutex)); + #else + #define EEL_STRING_MUTEXLOCK_SCOPE + #endif +#endif + +// allow overriding behavior +#ifndef EEL_STRING_GET_FOR_INDEX +#define EEL_STRING_GET_FOR_INDEX(x, wr) (EEL_STRING_GET_CONTEXT_POINTER(opaque)->GetStringForIndex(x, wr, false)) +#endif + +#ifndef EEL_STRING_GET_FOR_WRITE +#define EEL_STRING_GET_FOR_WRITE(x, wr) (EEL_STRING_GET_CONTEXT_POINTER(opaque)->GetStringForIndex(x, wr, true)) +#endif + +#ifndef EEL_STRING_GETFMTVAR +#define EEL_STRING_GETFMTVAR(x) (EEL_STRING_GET_CONTEXT_POINTER(opaque)->GetVarForFormat(x)) +#endif + +#ifndef EEL_STRING_GETNAMEDVAR +#define EEL_STRING_GETNAMEDVAR(x,createOK,altOut) (EEL_STRING_GET_CONTEXT_POINTER(opaque)->GetNamedVar(x,createOK,altOut)) +#endif + + + + + +#ifndef EEL_STRING_STORAGECLASS +#define EEL_STRING_STORAGECLASS WDL_FastString +#endif + + + +class eel_string_context_state +{ + public: + static int cmpistr(const char **a, const char **b) { return stricmp(*a,*b); } + + eel_string_context_state() : m_named_strings_names(false), m_varname_cache(cmpistr) + { + m_vm=0; + memset(m_user_strings,0,sizeof(m_user_strings)); + } + ~eel_string_context_state() + { + clear_state(true); + } + + void clear_state(bool full) + { + if (full) + { + int x; + for (x=0;x=0 && idx < EEL_STRING_MAX_USER_STRINGS) + { + if (stringContainerOut) + { + if (!m_user_strings[idx]) m_user_strings[idx] = new EEL_STRING_STORAGECLASS; + *stringContainerOut = m_user_strings[idx]; + } + return m_user_strings[idx]?m_user_strings[idx]->Get():""; + } + EEL_STRING_STORAGECLASS *s; + s = m_unnamed_strings.Get(idx - EEL_STRING_UNNAMED_BASE); + if (!s) s= m_named_strings.Get(idx - EEL_STRING_NAMED_BASE); + + if (s) + { + // mutable string + if (stringContainerOut) *stringContainerOut=s; + } + else + { + s = m_literal_strings.Get(idx - EEL_STRING_LITERAL_BASE); + #ifdef EEL_STRINGS_MUTABLE_LITERALS + if (stringContainerOut) *stringContainerOut=s; + #else + if (stringContainerOut) *stringContainerOut=is_for_write ? NULL : s; + #endif + } + return s ? s->Get() : NULL; + } + + WDL_PtrList m_literal_strings; // "this kind", normally immutable + WDL_PtrList m_unnamed_strings; // # + WDL_PtrList m_named_strings; // #xyz by index, but stringkeyed below for names + WDL_StringKeyedArray m_named_strings_names; // #xyz->index + + EEL_STRING_STORAGECLASS *m_user_strings[EEL_STRING_MAX_USER_STRINGS]; // indices 0-1023 (etc) + WDL_AssocArray m_varname_cache; // cached pointers when using %{xyz}s, %{#xyz}s bypasses + + NSEEL_VMCTX m_vm; +#ifdef EEL_STRING_WANT_MUTEX + WDL_Mutex m_mutex; +#endif + + static EEL_F addNamedStringCallback(void *opaque, const char *name) + { + if (!opaque) return -1.0; + eel_string_context_state *_this = EEL_STRING_GET_CONTEXT_POINTER(opaque); + if (!_this) return -1.0; +#ifdef EEL_STRING_NAMEDSTRINGCALLBACK_HOOK + EEL_STRING_NAMEDSTRINGCALLBACK_HOOK +#endif + + EEL_STRING_MUTEXLOCK_SCOPE + if (!name || !name[0]) + { + _this->m_unnamed_strings.Add(new EEL_STRING_STORAGECLASS); + return (EEL_F) (_this->m_unnamed_strings.GetSize()-1 + EEL_STRING_UNNAMED_BASE); + } + + int a = _this->m_named_strings_names.Get(name); + if (a) return (EEL_F)a; + + a = _this->m_named_strings.GetSize() + EEL_STRING_NAMED_BASE; + _this->m_named_strings.Add(new EEL_STRING_STORAGECLASS); + _this->m_named_strings_names.Insert(name,a); + + return (EEL_F)a; + } + + static EEL_F addStringCallback(void *opaque, struct eelStringSegmentRec *list) + { + if (!opaque) return -1.0; + + eel_string_context_state *_this = EEL_STRING_GET_CONTEXT_POINTER(opaque); + if (!_this) return -1.0; + + EEL_STRING_STORAGECLASS *ns = new EEL_STRING_STORAGECLASS; + // could probably do a faster implementation using AddRaw() etc but this should also be OK + int sz=nseel_stringsegments_tobuf(NULL,0,list); + ns->SetLen(sz+32); + sz=nseel_stringsegments_tobuf((char *)ns->Get(),sz,list); + ns->SetLen(sz); + EEL_STRING_MUTEXLOCK_SCOPE + return (EEL_F)_this->AddString(ns); + } + int AddString(EEL_STRING_STORAGECLASS *ns) + { +#ifdef EEL_STRINGS_MUTABLE_LITERALS + m_literal_strings.Add(ns); + return m_literal_strings.GetSize()-1+EEL_STRING_LITERAL_BASE; +#else + const int l = ns->GetLength(); + const int sz=m_literal_strings.GetSize(); + int x; + for (x=0;xGetLength() == l && !strcmp(s->Get(),ns->Get())) break; + } + if (xm_varname_cache.AddUnsorted(name,val); + return 1; + } + +}; + + + +static int eel_validate_format_specifier(const char *fmt_in, char *typeOut, + char *fmtOut, int fmtOut_sz, + char *varOut, int varOut_sz, + int *varOut_used + ) +{ + const char *fmt = fmt_in+1; + int state=0; + if (fmt_in[0] != '%') return 0; // ugh passed a non-specifier + + *varOut_used = 0; + *varOut = 0; + + if (fmtOut_sz-- < 2) return 0; + *fmtOut++ = '%'; + + while (*fmt) + { + const char c = *fmt++; + if (fmtOut_sz < 2) return 0; + + if (c == 'f'|| c=='e' || c=='E' || c=='g' || c=='G' || c == 'd' || c == 'u' || + c == 'x' || c == 'X' || c == 'c' || c == 'C' || c =='s' || c=='S' || c=='i') + { + *typeOut = c; + fmtOut[0] = c; + fmtOut[1] = 0; + return (int) (fmt - fmt_in); + } + else if (c == '.') + { + *fmtOut++ = c; fmtOut_sz--; + if (state&(2)) break; + state |= 2; + } + else if (c == '+') + { + *fmtOut++ = c; fmtOut_sz--; + if (state&(32|16|8|4)) break; + state |= 8; + } + else if (c == '-' || c == ' ') + { + *fmtOut++ = c; fmtOut_sz--; + if (state&(32|16|8|4)) break; + state |= 16; + } + else if (c >= '0' && c <= '9') + { + *fmtOut++ = c; fmtOut_sz--; + state|=4; + } + else if (c == '{') + { + if (state & 64) break; + state|=64; + if (*fmt == '.' || (*fmt >= '0' && *fmt <= '9')) return 0; // symbol name can't start with 0-9 or . + + while (*fmt != '}') + { + if ((*fmt >= 'a' && *fmt <= 'z') || + (*fmt >= 'A' && *fmt <= 'Z') || + (*fmt >= '0' && *fmt <= '9') || + *fmt == '_' || *fmt == '.' || *fmt == '#') + { + if (varOut_sz < 2) return 0; + *varOut++ = *fmt++; + varOut_sz -- ; + } + else + { + return 0; // bad character in variable name + } + } + fmt++; + *varOut = 0; + *varOut_used=1; + } + else + { + break; + } + } + return 0; +} + +int eel_format_strings(void *opaque, const char *fmt, const char *fmt_end, char *buf, int buf_sz, int num_fmt_parms, EEL_F **fmt_parms) +{ + int fmt_parmpos = 0; + char *op = buf; + while ((fmt_end ? fmt < fmt_end : *fmt) && op < buf+buf_sz-128) + { + if (fmt[0] == '%' && fmt[1] == '%') + { + *op++ = '%'; + fmt+=2; + } + else if (fmt[0] == '%') + { + char ct=0; + char fs[128]; + char varname[128]; + int varname_used=0; + const int l=eel_validate_format_specifier(fmt,&ct,fs,sizeof(fs),varname,sizeof(varname),&varname_used); + if (!l || !ct) + { + *op=0; + return -1; + } + + EEL_F vv=0.0; + const EEL_F *varptr = NULL; + if (varname_used) + { +#ifdef EEL_STRING_GETNAMEDVAR + if (varname[0]) varptr=EEL_STRING_GETNAMEDVAR(varname,0,&vv); +#endif + } + else + { + if (fmt_parmpos < num_fmt_parms) varptr = fmt_parms[fmt_parmpos]; +#ifdef EEL_STRING_GETFMTVAR + if (!varptr) varptr = EEL_STRING_GETFMTVAR(fmt_parmpos); +#endif + fmt_parmpos++; + } + double v = varptr ? (double)*varptr : 0.0; + + if (ct == 's' || ct=='S') + { + EEL_STRING_STORAGECLASS *wr=NULL; + const char *str = EEL_STRING_GET_FOR_INDEX(v,&wr); + const int maxl=(int) (buf+buf_sz - 2 - op); + if (wr && !fs[2]) // %s or %S -- todo: implement padding modes for binary compat too? + { + int wl = wr->GetLength(); + if (wl > maxl) wl=maxl; + memcpy(op,wr->Get(),wl); + op += wl; + *op=0; + } + else + { + snprintf(op,maxl,fs,str ? str : ""); + } + } + else + { + if (varptr == &vv) // passed %{#str}d etc, convert to float + { + const char *str = EEL_STRING_GET_FOR_INDEX(v,NULL); + v = str ? atof(str) : 0.0; + } + + if (ct == 'x' || ct == 'X' || ct == 'd' || ct == 'u' || ct=='i') + { + snprintf(op,64,fs,(int) (v)); + } + else if (ct == 'c') + { + *op++=(char) (int)v; + *op=0; + } + else if (ct == 'C') + { + const unsigned int iv = (unsigned int) v; + int bs = 0; + if (iv & 0xff000000) bs=24; + else if (iv & 0x00ff0000) bs=16; + else if (iv & 0x0000ff00) bs=8; + while (bs>=0) + { + const char c=(char) (iv>>bs); + *op++=c?c:' '; + bs-=8; + } + *op=0; + } + else + { + snprintf(op,64,fs,v); + } + } + + while (*op) op++; + + fmt += l; + } + else + { + *op++ = *fmt++; + } + + } + *op=0; + return (int) (op - buf); +} + + + +static int eel_string_match(void *opaque, const char *fmt, const char *msg, int match_fmt_pos, int ignorecase, const char *fmt_endptr, const char *msg_endptr, int num_fmt_parms, EEL_F **fmt_parms) +{ + // check for match, updating EEL_STRING_GETFMTVAR(*) as necessary + // %d=12345 + // %f=12345[.678] + // %c=any nonzero char, ascii value + // %x=12354ab + // %*, %?, %+, %% literals + // * ? + match minimal groups of 0+,1, or 1+ chars + for (;;) + { + if (fmt>=fmt_endptr) + { + if (msg>=msg_endptr) return 1; + return 0; // format ends before matching string + } + + // if string ends and format is not on a wildcard, early-out to 0 + if (msg>=msg_endptr && *fmt != '*' && *fmt != '%') return 0; + + switch (*fmt) + { + case '*': + case '+': + // if last char of search pattern, we're done! + if (fmt+1>=fmt_endptr || (fmt[1] == '?' && fmt+2>=fmt_endptr)) return *fmt == '*' || msg= 0 && !eel_string_match(opaque,fmt, msg+len,match_fmt_pos,ignorecase,fmt_endptr, msg_endptr,num_fmt_parms,fmt_parms)) len--; + return len >= 0; + } + break; + case '?': + fmt++; + msg++; + break; + case '%': + { + fmt++; + unsigned short fmt_minlen = 1, fmt_maxlen = 0; + if (*fmt >= '0' && *fmt <= '9') + { + fmt_minlen = *fmt++ - '0'; + while (*fmt >= '0' && *fmt <= '9') fmt_minlen = fmt_minlen * 10 + (*fmt++ - '0'); + fmt_maxlen = fmt_minlen; + } + if (*fmt == '-') + { + fmt++; + fmt_maxlen = 0; + while (*fmt >= '0' && *fmt <= '9') fmt_maxlen = fmt_maxlen * 10 + (*fmt++ - '0'); + } + const char *dest_varname=NULL; + if (*fmt == '{') + { + dest_varname=++fmt; + while (*fmt && fmt < fmt_endptr && *fmt != '}') fmt++; + if (fmt >= fmt_endptr-1 || *fmt != '}') return 0; // malformed %{var}s + fmt++; // skip '}' + } + + char fmt_char = *fmt++; + if (!fmt_char) return 0; // malformed + + if (fmt_char == '*' || + fmt_char == '?' || + fmt_char == '+' || + fmt_char == '%') + { + if (*msg++ != fmt_char) return 0; + } + else if (fmt_char == 'c') + { + EEL_F *varOut = NULL; + EEL_F vv=0.0; + if (!dest_varname) + { + if (match_fmt_pos < num_fmt_parms) varOut = fmt_parms[match_fmt_pos]; +#ifdef EEL_STRING_GETFMTVAR + if (!varOut) varOut = EEL_STRING_GETFMTVAR(match_fmt_pos); +#endif + match_fmt_pos++; + } + else + { +#ifdef EEL_STRING_GETNAMEDVAR + char tmp[128]; + int idx=0; + while (dest_varname < fmt_endptr && *dest_varname && *dest_varname != '}' && idx<(int)sizeof(tmp)-1) tmp[idx++] = *dest_varname++; + tmp[idx]=0; + if (idx>0) varOut = EEL_STRING_GETNAMEDVAR(tmp,1,&vv); +#endif + } + if (msg >= msg_endptr) return 0; // out of chars + + if (varOut) + { + if (varOut == &vv) // %{#foo}c + { + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(vv, &wr); + if (wr) wr->Set(msg,1); + } + else + { + *varOut = (EEL_F)*(unsigned char *)msg; + } + } + msg++; + } + else + { + int len=0; + int lazy=0; + if (fmt_char>='A'&&fmt_char<='Z') { lazy=1; fmt_char += 'a' - 'A'; } + + if (fmt_char == 's') + { + len = (int) (msg_endptr-msg); + } + else if (fmt_char == 'x') + { + while ((msg[len] >= '0' && msg[len] <= '9') || + (msg[len] >= 'A' && msg[len] <= 'F') || + (msg[len] >= 'a' && msg[len] <= 'f')) len++; + } + else if (fmt_char == 'f') + { + if (msg[len] == '-') len++; + while (msg[len] >= '0' && msg[len] <= '9') len++; + if (msg[len] == '.') + { + len++; + while (msg[len] >= '0' && msg[len] <= '9') len++; + } + } + else if (fmt_char == 'd' || fmt_char == 'u' || fmt_char == 'i') + { + if (fmt_char != 'u' && msg[len] == '-') len++; + while (msg[len] >= '0' && msg[len] <= '9') len++; + } + else + { + // bad format + return 0; + } + + if (fmt_maxlen>0 && len > fmt_maxlen) len = fmt_maxlen; + + if (!dest_varname) match_fmt_pos++; + + if (lazy) + { + if (fmt_maxlen<1 || fmt_maxlen>len) fmt_maxlen=len; + len=fmt_minlen; + while (len <= fmt_maxlen && !eel_string_match(opaque,fmt, msg+len,match_fmt_pos,ignorecase,fmt_endptr, msg_endptr,num_fmt_parms,fmt_parms)) len++; + if (len > fmt_maxlen) return 0; + } + else + { + while (len >= fmt_minlen && !eel_string_match(opaque,fmt, msg+len,match_fmt_pos,ignorecase,fmt_endptr, msg_endptr,num_fmt_parms,fmt_parms)) len--; + if (len < fmt_minlen) return 0; + } + + EEL_F vv=0.0; + EEL_F *varOut = NULL; + if (!dest_varname) + { + if (match_fmt_pos>0 && match_fmt_pos-1 < num_fmt_parms) varOut = fmt_parms[match_fmt_pos-1]; +#ifdef EEL_STRING_GETFMTVAR + if (!varOut) varOut = EEL_STRING_GETFMTVAR(match_fmt_pos-1); +#endif + } + else + { +#ifdef EEL_STRING_GETNAMEDVAR + char tmp[128]; + int idx=0; + while (dest_varname < fmt_endptr && *dest_varname && *dest_varname != '}' && idx<(int)sizeof(tmp)-1) tmp[idx++] = *dest_varname++; + tmp[idx]=0; + if (idx>0) varOut = EEL_STRING_GETNAMEDVAR(tmp,1,&vv); +#endif + } + if (varOut) + { + if (fmt_char == 's') + { + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(*varOut, &wr); + if (wr) + { + if (msg_endptr >= wr->Get() && msg_endptr <= wr->Get() + wr->GetLength()) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("match: destination specifier passed is also haystack, will not update"); +#endif + } + else if (fmt_endptr >= wr->Get() && fmt_endptr <= wr->Get() + wr->GetLength()) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("match: destination specifier passed is also format, will not update"); +#endif + } + else + { + wr->SetRaw(msg,len); + } + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("match: bad destination specifier passed as %d: %f",match_fmt_pos,*varOut); +#endif + } + } + else + { + char tmp[128]; + lstrcpyn_safe(tmp,msg,wdl_min(len+1,(int)sizeof(tmp))); + if (varOut == &vv) + { + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(vv, &wr); + if (wr) wr->Set(tmp); + } + else + { + char *bl=(char*)msg; + if (fmt_char == 'u') + *varOut = (EEL_F)strtoul(tmp,&bl,10); + else if (fmt_char == 'x') + *varOut = (EEL_F)strtoul(msg,&bl,16); + else + *varOut = (EEL_F)atof(tmp); + } + } + } + return 1; + } + } + break; + default: + if (ignorecase ? (toupper(*fmt) != toupper(*msg)) : (*fmt!= *msg)) return 0; + fmt++; + msg++; + break; + } + } +} + + + +static EEL_F NSEEL_CGEN_CALL _eel_sprintf(void *opaque, INT_PTR num_param, EEL_F **parms) +{ + if (num_param<2) return 0.0; + + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(*(parms[0]), &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("sprintf: bad destination specifier passed %f",*(parms[0])); +#endif + } + else + { + EEL_STRING_STORAGECLASS *wr_src=NULL; + const char *fmt = EEL_STRING_GET_FOR_INDEX(*(parms[1]),&wr_src); + if (fmt) + { + char buf[16384]; + const int fmt_len = eel_format_strings(opaque,fmt,wr_src?(fmt+wr_src->GetLength()):NULL,buf,(int)sizeof(buf), (int)num_param-2, parms+2); + + if (fmt_len>=0) + { + wr->SetRaw(buf,fmt_len); + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("sprintf: bad format string %s",fmt); +#endif + } + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("sprintf: bad format specifier passed %f",*(parms[1])); +#endif + } + } + } + return *(parms[0]); +} + + +static EEL_F NSEEL_CGEN_CALL _eel_strncat(void *opaque, EEL_F *strOut, EEL_F *fmt_index, EEL_F *maxlen) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL, *wr_src=NULL; + EEL_STRING_GET_FOR_WRITE(*strOut, &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str%scat: bad destination specifier passed %f",maxlen ? "n":"",*strOut); +#endif + } + else + { + const char *fmt = EEL_STRING_GET_FOR_INDEX(*fmt_index,&wr_src); + if (fmt||wr_src) + { + if (wr->GetLength() > EEL_STRING_MAXUSERSTRING_LENGTH_HINT) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str%scat: will not grow string since it is already %d bytes",maxlen ? "n":"",wr->GetLength()); +#endif + } + else + { + int ml=0; + if (maxlen && *maxlen > 0) ml = (int)*maxlen; + if (wr_src) + { + EEL_STRING_STORAGECLASS tmp; + if (wr_src == wr) *(wr_src=&tmp) = *wr; + wr->AppendRaw(wr_src->Get(), ml > 0 && ml < wr_src->GetLength() ? ml : wr_src->GetLength()); + } + else + wr->Append(fmt, ml); + } + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str%scat: bad format specifier passed %f",maxlen ? "n":"",*fmt_index); +#endif + } + } + } + return *strOut; +} + +static EEL_F NSEEL_CGEN_CALL _eel_strcpysubstr(void *opaque, INT_PTR nparm, EEL_F **parms) //EEL_F *strOut, EEL_F *fmt_index, EEL_F *offs +{ + if (opaque && nparm>=3) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL, *wr_src=NULL; + EEL_STRING_GET_FOR_WRITE(parms[0][0], &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("strcpy_substr: bad destination specifier passed %f",parms[0][0]); +#endif + } + else + { + const char *fmt = EEL_STRING_GET_FOR_INDEX(parms[1][0],&wr_src); + if (fmt) + { + const int fmt_len = wr_src ? wr_src->GetLength() : (int) strlen(fmt); + int maxlen, o = (int) parms[2][0]; + if (o < 0) + { + o = fmt_len + o; + if (o < 0) o=0; + } + maxlen = fmt_len - o; + if (nparm >= 4) + { + const int a = (int) parms[3][0]; + if (a<0) maxlen += a; + else if (a= fmt_len) + { + wr->Set(""); + } + else + { + if (wr_src==wr) + { + wr->DeleteSub(0,o); + if (wr->GetLength() > maxlen) wr->SetLen(maxlen); + } + else + { + wr->SetRaw(fmt+o,maxlen); + } + } + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("strcpy_substr: bad format specifier passed %f",parms[1][0]); +#endif + } + } + return parms[0][0]; + } + return 0.0; +} + + +static EEL_F NSEEL_CGEN_CALL _eel_strncpy(void *opaque, EEL_F *strOut, EEL_F *fmt_index, EEL_F *maxlen) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL, *wr_src=NULL; + EEL_STRING_GET_FOR_WRITE(*strOut, &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str%scpy: bad destination specifier passed %f",maxlen ? "n":"",*strOut); +#endif + } + else + { + const char *fmt = EEL_STRING_GET_FOR_INDEX(*fmt_index,&wr_src); + if (fmt) + { + int ml=-1; + if (maxlen && *maxlen >= 0) ml = (int)*maxlen; + + if (wr_src == wr) + { + if (ml>=0 && ml < wr->GetLength()) wr->SetLen(ml); // shorten string if strncpy(x,x,len) and len >=0 + return *strOut; + } + + if (wr_src) wr->SetRaw(fmt, ml>0 && ml < wr_src->GetLength() ? ml : wr_src->GetLength()); + else wr->Set(fmt,ml); + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str%scpy: bad format specifier passed %f",maxlen ? "n":"",*fmt_index); +#endif + } + } + } + return *strOut; +} + +static EEL_F _eel_strcmp_int(const char *a, int a_len, const char *b, int b_len, int ml, bool ignorecase) +{ + // binary-safe comparison (at least if a_len>=0 etc) + int pos = 0; + for (;;) + { + if (ml > 0 && pos == ml) return 0.0; + const bool a_end = a_len >= 0 ? pos == a_len : !a[pos]; + const bool b_end = b_len >= 0 ? pos == b_len : !b[pos]; + if (a_end || b_end) + { + if (!b_end) return -1.0; // b[pos] is nonzero, a[pos] is zero + if (!a_end) return 1.0; + return 0.0; + } + char av = a[pos]; + char bv = b[pos]; + if (ignorecase) + { + av=toupper(av); + bv=toupper(bv); + } + if (bv > av) return -1.0; + if (av > bv) return 1.0; + + pos++; + } +} + +static EEL_F NSEEL_CGEN_CALL _eel_strncmp(void *opaque, EEL_F *aa, EEL_F *bb, EEL_F *maxlen) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr_a=NULL,*wr_b=NULL; + const char *a = EEL_STRING_GET_FOR_INDEX(*aa,&wr_a); + const char *b = EEL_STRING_GET_FOR_INDEX(*bb,&wr_b); + if (!a || !b) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str%scmp: bad specifier(s) passed %f/%f",maxlen ? "n" : "",*aa,*bb); +#endif + } + else + { + const int ml = maxlen ? (int) *maxlen : -1; + if (!ml || a==b) return 0; // strncmp(x,y,0) == 0 + + return _eel_strcmp_int(a,wr_a ? wr_a->GetLength() : -1,b,wr_b ? wr_b->GetLength() : -1, ml, false); + } + } + return -1.0; +} + +static EEL_F NSEEL_CGEN_CALL _eel_strnicmp(void *opaque, EEL_F *aa, EEL_F *bb, EEL_F *maxlen) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr_a=NULL,*wr_b=NULL; + const char *a = EEL_STRING_GET_FOR_INDEX(*aa,&wr_a); + const char *b = EEL_STRING_GET_FOR_INDEX(*bb,&wr_b); + if (!a || !b) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str%sicmp: bad specifier(s) passed %f/%f",maxlen ? "n" : "",*aa,*bb); +#endif + } + else + { + const int ml = maxlen ? (int) *maxlen : -1; + if (!ml || a==b) return 0; // strncmp(x,y,0) == 0 + return _eel_strcmp_int(a,wr_a ? wr_a->GetLength() : -1,b,wr_b ? wr_b->GetLength() : -1, ml, true); + } + } + return -1.0; +} + + +static EEL_F NSEEL_CGEN_CALL _eel_strcat(void *opaque, EEL_F *strOut, EEL_F *fmt_index) +{ + return _eel_strncat(opaque,strOut,fmt_index,NULL); +} + +static EEL_F NSEEL_CGEN_CALL _eel_strcpy(void *opaque, EEL_F *strOut, EEL_F *fmt_index) +{ + return _eel_strncpy(opaque,strOut,fmt_index,NULL); +} + + +static EEL_F NSEEL_CGEN_CALL _eel_strcmp(void *opaque, EEL_F *strOut, EEL_F *fmt_index) +{ + return _eel_strncmp(opaque,strOut,fmt_index,NULL); +} + +static EEL_F NSEEL_CGEN_CALL _eel_stricmp(void *opaque, EEL_F *strOut, EEL_F *fmt_index) +{ + return _eel_strnicmp(opaque,strOut,fmt_index,NULL); +} + + +static EEL_F NSEEL_CGEN_CALL _eel_strgetchar(void *opaque, EEL_F *strOut, EEL_F *idx) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + const char *fmt = EEL_STRING_GET_FOR_INDEX(*strOut, &wr); + if (!fmt) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_getchar: bad specifier passed %f",*strOut); +#endif + } + else + { + const int wl=(wr?wr->GetLength():(int)strlen(fmt)); + int l = (int) *idx; + if (*idx < 0.0) l+=wl; + if (l>=0 && l < wl) return ((unsigned char *)fmt)[l]; + } + } + return 0; +} + +#define EEL_GETCHAR_FLAG_ENDIANSWAP 0x10 +#define EEL_GETCHAR_FLAG_UNSIGNED 0x20 +#define EEL_GETCHAR_FLAG_FLOAT 0x40 +static int eel_getchar_flag(int type) +{ +#ifdef __ppc__ + int ret=EEL_GETCHAR_FLAG_ENDIANSWAP; // default to LE +#else + int ret=0; +#endif + + if (toupper((type>>8)&0xff) == 'U') ret|=EEL_GETCHAR_FLAG_UNSIGNED; + else if (type>255 && toupper(type&0xff) == 'U') { ret|=EEL_GETCHAR_FLAG_UNSIGNED; type>>=8; } + type&=0xff; + + if (isupper(type)) ret^=EEL_GETCHAR_FLAG_ENDIANSWAP; + else type += 'A'-'a'; + + switch (type) + { + case 'F': return ret|4|EEL_GETCHAR_FLAG_FLOAT; + case 'D': return ret|8|EEL_GETCHAR_FLAG_FLOAT; + case 'S': return ret|2; + case 'I': return ret|4; + } + + return ret|1; +} + +static void eel_setchar_do(int flag, char *dest, EEL_F val) +{ + union + { + char buf[8]; + float asFloat; + double asDouble; + int asInt; + short asShort; + char asChar; + unsigned int asUInt; + unsigned short asUShort; + unsigned char asUChar; + } a; + const int type_sz=flag&0xf; + + if (flag & EEL_GETCHAR_FLAG_FLOAT) + { + if (type_sz==8) a.asDouble=val; + else a.asFloat=(float)val; + } + else if (flag & EEL_GETCHAR_FLAG_UNSIGNED) + { + if (type_sz==4) a.asUInt=(unsigned int)val; + else if (type_sz==2) a.asUShort=(unsigned short)val; + else a.asUChar=(unsigned char)val; + } + else if (type_sz==4) a.asInt=(int)val; + else if (type_sz==2) a.asShort=(short)val; + else a.asChar=(char)val; + + if (flag & EEL_GETCHAR_FLAG_ENDIANSWAP) + { + dest += type_sz; + int x; + for(x=0;x=3) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + const char *fmt = EEL_STRING_GET_FOR_INDEX(parms[0][0], &wr); + if (!fmt) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_getchar: bad specifier passed %f",parms[0][0]); +#endif + } + else + { + const int wl=(wr?wr->GetLength():(int)strlen(fmt)); + const int flags=eel_getchar_flag((int) parms[2][0]); + int l = (int) parms[1][0]; + if (parms[1][0] < 0.0) l+=wl; + + if (l>=0 && l <= wl-(flags&0xf)) + { + return eel_getchar_do(flags,fmt+l); + } + } + } + return 0; +} + +static EEL_F NSEEL_CGEN_CALL _eel_strsetchar2(void *opaque, INT_PTR np, EEL_F **parms) +{ + if (opaque && np>=4) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(parms[0][0], &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_setchar: bad destination specifier passed %f",parms[0][0]); +#endif + } + else + { + const int wl=wr->GetLength(); + + int l = (int) parms[1][0]; + if (parms[1][0] < 0.0) l+=wl; + if (l>=0 && l <= wl) + { + const int flags=eel_getchar_flag((int) parms[3][0]); + if (l==wl) + { + if (wl > EEL_STRING_MAXUSERSTRING_LENGTH_HINT) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_setchar: will not grow string since it is already %d bytes",wl); +#endif + } + else + { + char buf[32]; + eel_setchar_do(flags,buf,parms[2][0]); + wr->AppendRaw(buf,flags&0xf); + } + } + else + eel_setchar_do(flags,(char*)wr->Get()+l,parms[2][0]); + } + } + } + return parms[0][0]; +} + +static EEL_F NSEEL_CGEN_CALL _eel_strsetchar(void *opaque, EEL_F *strOut, EEL_F *idx, EEL_F *val) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(*strOut, &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_setchar: bad destination specifier passed %f",*strOut); +#endif + } + else + { + const int wl=wr->GetLength(); + int l = (int) *idx; + if (*idx < 0.0) l+=wl; + if (l>=0 && l <= wl) + { + const unsigned char v=((int)*val)&255; + if (l==wl) + { + if (wl > EEL_STRING_MAXUSERSTRING_LENGTH_HINT) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_setchar: will not grow string since it is already %d bytes",wl); +#endif + } + else + wr->AppendRaw((const char*)&v,1); + } + else + ((unsigned char *)wr->Get())[l]=v; // allow putting nulls in string, strlen() will still get the full size + } + } + } + return *strOut; +} + +static EEL_F NSEEL_CGEN_CALL _eel_strinsert(void *opaque, EEL_F *strOut, EEL_F *fmt_index, EEL_F *pos) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL, *wr_src=NULL; + EEL_STRING_GET_FOR_WRITE(*strOut, &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_insert: bad destination specifier passed %f",*strOut); +#endif + } + else + { + const char *fmt = EEL_STRING_GET_FOR_INDEX(*fmt_index,&wr_src); + if (fmt) + { + EEL_STRING_STORAGECLASS tmp; + if (wr_src == wr) *(wr_src=&tmp) = *wr; // insert from copy + + // if wr_src, fmt is guaranteed to be wr_src.Get() + int p = (int)*pos; + int insert_l = wr_src ? wr_src->GetLength() : (int)strlen(fmt); + if (p < 0) + { + insert_l += p; // decrease insert_l + fmt -= p; // advance fmt -- if fmt gets advanced past NULL term, insert_l will be < 0 anyway + p=0; + } + + if (insert_l>0) + { + if (wr->GetLength() > EEL_STRING_MAXUSERSTRING_LENGTH_HINT) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_insert: will not grow string since it is already %d bytes",wr->GetLength()); +#endif + } + else + { + if (wr_src) + { + wr->InsertRaw(fmt,p, insert_l); + } + else + { + wr->Insert(fmt,p); + } + } + } + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_insert: bad source specifier passed %f",*fmt_index); +#endif + } + } + } + return *strOut; +} + +static EEL_F NSEEL_CGEN_CALL _eel_strdelsub(void *opaque, EEL_F *strOut, EEL_F *pos, EEL_F *len) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(*strOut, &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_delsub: bad destination specifier passed %f",*strOut); +#endif + } + else + { + int p=(int)*pos; + int l=(int)*len; + if (p<0) + { + l+=p; + p=0; + } + if (l>0) + wr->DeleteSub(p,l); + } + } + return *strOut; +} + +static EEL_F NSEEL_CGEN_CALL _eel_strsetlen(void *opaque, EEL_F *strOut, EEL_F *newlen) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + EEL_STRING_GET_FOR_WRITE(*strOut, &wr); + if (!wr) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_setlen: bad destination specifier passed %f",*strOut); +#endif + } + else + { + int l = (int) *newlen; + if (l < 0) l=0; + if (l > EEL_STRING_MAXUSERSTRING_LENGTH_HINT) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("str_setlen: clamping requested length of %d to %d",l,EEL_STRING_MAXUSERSTRING_LENGTH_HINT); +#endif + l=EEL_STRING_MAXUSERSTRING_LENGTH_HINT; + } + wr->SetLen(l); + + } + } + return *strOut; +} + + +static EEL_F NSEEL_CGEN_CALL _eel_strlen(void *opaque, EEL_F *fmt_index) +{ + if (opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr=NULL; + const char *fmt = EEL_STRING_GET_FOR_INDEX(*fmt_index,&wr); + if (wr) return (EEL_F) wr->GetLength(); + if (fmt) return (EEL_F) strlen(fmt); + } + return 0.0; +} + + + + +static EEL_F NSEEL_CGEN_CALL _eel_printf(void *opaque, INT_PTR num_param, EEL_F **parms) +{ + if (num_param>0 && opaque) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *wr_src=NULL; + const char *fmt = EEL_STRING_GET_FOR_INDEX(*(parms[0]),&wr_src); + if (fmt) + { + char buf[16384]; + const int len = eel_format_strings(opaque,fmt,wr_src?(fmt+wr_src->GetLength()):NULL,buf,(int)sizeof(buf), (int)num_param-1, parms+1); + + if (len >= 0) + { +#ifdef EEL_STRING_STDOUT_WRITE + EEL_STRING_STDOUT_WRITE(buf,len); +#endif + return 1.0; + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("printf: bad format string %s",fmt); +#endif + } + } + else + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("printf: bad format specifier passed %f",*(parms[0])); +#endif + } + } + return 0.0; +} + + + +static EEL_F NSEEL_CGEN_CALL _eel_match(void *opaque, INT_PTR num_parms, EEL_F **parms) +{ + if (opaque && num_parms >= 2) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *fmt_wr=NULL, *msg_wr=NULL; + const char *fmt = EEL_STRING_GET_FOR_INDEX(*(parms[0]),&fmt_wr); + const char *msg = EEL_STRING_GET_FOR_INDEX(*(parms[1]),&msg_wr); + + if (fmt && msg) return eel_string_match(opaque,fmt,msg,0,0, fmt + (fmt_wr?fmt_wr->GetLength():strlen(fmt)), msg + (msg_wr?msg_wr->GetLength():strlen(msg)),(int)num_parms-2,parms+2) ? 1.0 : 0.0; + } + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL _eel_matchi(void *opaque, INT_PTR num_parms, EEL_F **parms) +{ + if (opaque && num_parms >= 2) + { + EEL_STRING_MUTEXLOCK_SCOPE + EEL_STRING_STORAGECLASS *fmt_wr=NULL, *msg_wr=NULL; + const char *fmt = EEL_STRING_GET_FOR_INDEX(*(parms[0]),&fmt_wr); + const char *msg = EEL_STRING_GET_FOR_INDEX(*(parms[1]),&msg_wr); + + if (fmt && msg) return eel_string_match(opaque,fmt,msg,0,1, fmt + (fmt_wr?fmt_wr->GetLength():strlen(fmt)), msg + (msg_wr?msg_wr->GetLength():strlen(msg)),(int)num_parms-2,parms+2) ? 1.0 : 0.0; + } + return 0.0; +} + +void EEL_string_register() +{ + NSEEL_addfunc_retval("strlen",1,NSEEL_PProc_THIS,&_eel_strlen); + + NSEEL_addfunc_retval("strcat",2,NSEEL_PProc_THIS,&_eel_strcat); + NSEEL_addfunc_retval("strcpy",2,NSEEL_PProc_THIS,&_eel_strcpy); + NSEEL_addfunc_retval("strcmp",2,NSEEL_PProc_THIS,&_eel_strcmp); + NSEEL_addfunc_retval("stricmp",2,NSEEL_PProc_THIS,&_eel_stricmp); + + NSEEL_addfunc_retval("strncat",3,NSEEL_PProc_THIS,&_eel_strncat); + NSEEL_addfunc_retval("strncpy",3,NSEEL_PProc_THIS,&_eel_strncpy); + NSEEL_addfunc_retval("strncmp",3,NSEEL_PProc_THIS,&_eel_strncmp); + NSEEL_addfunc_retval("strnicmp",3,NSEEL_PProc_THIS,&_eel_strnicmp); + NSEEL_addfunc_retval("str_setlen",2,NSEEL_PProc_THIS,&_eel_strsetlen); + NSEEL_addfunc_exparms("strcpy_from",3,NSEEL_PProc_THIS,&_eel_strcpysubstr); // alias + NSEEL_addfunc_exparms("strcpy_substr",3,NSEEL_PProc_THIS,&_eel_strcpysubstr); // only allow 3 or 4 parms + NSEEL_addfunc_exparms("strcpy_substr",4,NSEEL_PProc_THIS,&_eel_strcpysubstr); + + NSEEL_addfunc_retval("str_getchar",2,NSEEL_PProc_THIS,&_eel_strgetchar); + NSEEL_addfunc_retval("str_setchar",3,NSEEL_PProc_THIS,&_eel_strsetchar); + NSEEL_addfunc_exparms("str_getchar",3,NSEEL_PProc_THIS,&_eel_strgetchar2); + NSEEL_addfunc_exparms("str_setchar",4,NSEEL_PProc_THIS,&_eel_strsetchar2); + + NSEEL_addfunc_retval("str_insert",3,NSEEL_PProc_THIS,&_eel_strinsert); + NSEEL_addfunc_retval("str_delsub",3,NSEEL_PProc_THIS,&_eel_strdelsub); + + NSEEL_addfunc_varparm("sprintf",2,NSEEL_PProc_THIS,&_eel_sprintf); + NSEEL_addfunc_varparm("printf",1,NSEEL_PProc_THIS,&_eel_printf); + + NSEEL_addfunc_varparm("match",2,NSEEL_PProc_THIS,&_eel_match); + NSEEL_addfunc_varparm("matchi",2,NSEEL_PProc_THIS,&_eel_matchi); + +} +void eel_string_initvm(NSEEL_VMCTX vm) +{ + NSEEL_VM_SetStringFunc(vm, eel_string_context_state::addStringCallback, eel_string_context_state::addNamedStringCallback); +} + +#ifdef EEL_WANT_DOCUMENTATION +static const char *eel_strings_function_reference = +#ifdef EEL_STRING_STDOUT_WRITE +"printf\t\"format\"[, ...]\tOutput formatted string to system-specific destination, see sprintf() for more information\0" +#endif +"sprintf\t#dest,\"format\"[, ...]\tFormats a string and stores it in #dest. Format specifiers begin with %, and may include:\3\n" + "\4 %% = %\n" + "\4 %s = string from parameter\n" + "\4 %d = parameter as integer\n" + "\4 %i = parameter as integer\n" + "\4 %u = parameter as unsigned integer\n" + "\4 %x = parameter as hex (lowercase) integer\n" + "\4 %X = parameter as hex (uppercase) integer\n" + "\4 %c = parameter as character\n" + "\4 %f = parameter as floating point\n" + "\4 %e = parameter as floating point (scientific notation, lowercase)\n" + "\4 %E = parameter as floating point (scientific notation, uppercase)\n" + "\4 %g = parameter as floating point (shortest representation, lowercase)\n" + "\4 %G = parameter as floating point (shortest representation, uppercase)\n" + "\2\n" + "Many standard C printf() modifiers can be used, including:\3\n" + "\4 %.10s = string, but only print up to 10 characters\n" + "\4 %-10s = string, left justified to 10 characters\n" + "\4 %10s = string, right justified to 10 characters\n" + "\4 %+f = floating point, always show sign\n" + "\4 %.4f = floating point, minimum of 4 digits after decimal point\n" + "\4 %10d = integer, minimum of 10 digits (space padded)\n" + "\4 %010f = integer, minimum of 10 digits (zero padded)\n" + "\2\n" + "Values for format specifiers can be specified as additional parameters to sprintf, or within {} in the format specifier (such as %{varname}d, in that case a global variable is always used).\0" + + "matchi\t\"needle\",\"haystack\"[, ...]\tCase-insensitive version of match().\0" + "match\t\"needle\",\"haystack\"[, ...]\tSearches for the first parameter in the second parameter, using a simplified regular expression syntax.\3\n" + "\4* = match 0 or more characters\n" + "\4*? = match 0 or more characters, lazy\n" + "\4+ = match 1 or more characters\n" + "\4+? = match 1 or more characters, lazy\n" + "\4? = match one character\n" + "\2\n" + "You can also use format specifiers to match certain types of data, and optionally put that into a variable:\3\n" + "\4%s means 1 or more chars\n" + "\4%0s means 0 or more chars\n" + "\4%5s means exactly 5 chars\n" + "\4%5-s means 5 or more chars\n" + "\4%-10s means 1-10 chars\n" + "\4%3-5s means 3-5 chars\n" + "\4%0-5s means 0-5 chars\n" + "\4%x, %d, %u, and %f are available for use similarly\n" + "\4%c can be used, but can't take any length modifiers\n" + "\4Use uppercase (%S, %D, etc) for lazy matching\n" + "\2\n" + "See also sprintf() for other notes, including specifying direct variable references via {}.\0" + + "strlen\t\"str\"\tReturns the length of the string passed as a parameter\0" + "strcpy\t#str,\"srcstr\"\tCopies the contents of srcstr to #str, and returns #str\0" + "strcat\t#str,\"srcstr\"\tAppends srcstr to #str, and returns #str\0" + "strcmp\t\"str\",\"str2\"\tCompares strings, returning 0 if equal\0" + "stricmp\t\"str\",\"str2\"\tCompares strings ignoring case, returning 0 if equal\0" + "strncmp\t\"str\",\"str2\",maxlen\tCompares strings giving up after maxlen characters, returning 0 if equal\0" + "strnicmp\t\"str\",\"str2\",maxlen\tCompares strings giving up after maxlen characters, ignoring case, returning 0 if equal\0" + "strncpy\t#str,\"srcstr\",maxlen\tCopies srcstr to #str, stopping after maxlen characters. Returns #str.\0" + "strncat\t#str,\"srcstr\",maxlen\tAppends srcstr to #str, stopping after maxlen characters of srcstr. Returns #str.\0" + "strcpy_from\t#str,\"srcstr\",offset\tCopies srcstr to #str, but starts reading srcstr at offset offset\0" + "strcpy_substr\t#str,\"srcstr\",offs,ml)\tPHP-style (start at offs, offs<0 means from end, ml for maxlen, ml<0 = reduce length by this amt)\0" + "str_getchar\t\"str\",offset[,type]\tReturns the data at byte-offset offset of str. If offset is negative, position is relative to end of string." + "type defaults to signed char, but can be specified to read raw binary data in other formats (note the single quotes, these are single/multi-byte characters):\3\n" + "\4'c' - signed char\n" + "\4'cu' - unsigned char\n" + "\4's' - signed short\n" + "\4'S' - signed short, big endian\n" + "\4'su' - unsigned short\n" + "\4'Su' - unsigned short, big endian\n" + "\4'i' - signed int\n" + "\4'I' - signed int, big endian\n" + "\4'iu' - unsigned int\n" + "\4'Iu' - unsigned int, big endian\n" + "\4'f' - float\n" + "\4'F' - float, big endian\n" + "\4'd' - double\n" + "\4'D' - double, big endian\n" + "\2\0" + "str_setchar\t#str,offset,val[,type])\tSets value at offset offset, type optional. offset may be negative to refer to offset relative to end of string, or between 0 and length, inclusive, and if set to length it will lengthen string. See str_getchar() for more information on types.\0" + "str_setlen\t#str,len\tSets length of #str (if increasing, will be space-padded), and returns #str.\0" + "str_delsub\t#str,pos,len\tDeletes len characters at offset pos from #str, and returns #str.\0" + "str_insert\t#str,\"srcstr\",pos\tInserts srcstr into #str at offset pos. Returns #str\0" +; +#endif + + +#endif + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eelscript.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eelscript.h new file mode 100644 index 000000000..8a330eef4 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/eelscript.h @@ -0,0 +1,776 @@ +#ifndef _WIN32 +#include + #ifndef EELSCRIPT_NO_LICE + #include "../swell/swell.h" + #endif +#endif + +#include "../wdltypes.h" +#include "../ptrlist.h" +#include "../wdlstring.h" +#include "../assocarray.h" +#include "../queue.h" +#include "../win32_utf8.h" +#include "ns-eel.h" + + +#ifndef EELSCRIPT_MAX_FILE_HANDLES +#define EELSCRIPT_MAX_FILE_HANDLES 512 +#endif +#ifndef EELSCRIPT_FILE_HANDLE_INDEX_BASE +#define EELSCRIPT_FILE_HANDLE_INDEX_BASE 1000000 +#endif +#ifndef EEL_STRING_MAXUSERSTRING_LENGTH_HINT +#define EEL_STRING_MAXUSERSTRING_LENGTH_HINT (1<<16) // 64KB per string max +#endif +#ifndef EEL_STRING_MAX_USER_STRINGS +#define EEL_STRING_MAX_USER_STRINGS 32768 +#endif +#ifndef EEL_STRING_LITERAL_BASE +#define EEL_STRING_LITERAL_BASE 2000000 +#endif +#ifndef EELSCRIPT_LICE_MAX_IMAGES +#define EELSCRIPT_LICE_MAX_IMAGES 1024 +#endif + +#ifndef EELSCRIPT_LICE_MAX_FONTS +#define EELSCRIPT_LICE_MAX_FONTS 128 +#endif + +#ifndef EELSCRIPT_NET_MAXCON +#define EELSCRIPT_NET_MAXCON 4096 +#endif + +#ifndef EELSCRIPT_LICE_CLASSNAME +#define EELSCRIPT_LICE_CLASSNAME "eelscript_gfx" +#endif + + +// #define EELSCRIPT_NO_NET +// #define EELSCRIPT_NO_LICE +// #define EELSCRIPT_NO_FILE +// #define EELSCRIPT_NO_FFT +// #define EELSCRIPT_NO_MDCT +// #define EELSCRIPT_NO_EVAL + +class eel_string_context_state; +#ifndef EELSCRIPT_NO_NET +class eel_net_state; +#endif +#ifndef EELSCRIPT_NO_LICE +class eel_lice_state; +#endif + +class eelScriptInst { + public: + + static int init(); + + eelScriptInst(); + virtual ~eelScriptInst(); + + NSEEL_CODEHANDLE compile_code(const char *code, const char **err); + int runcode(const char *code, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec); + int loadfile(const char *fn, const char *callerfn, bool allowstdin); + + NSEEL_VMCTX m_vm; + + WDL_PtrList m_code_freelist; + +#ifndef EELSCRIPT_NO_FILE + FILE *m_handles[EELSCRIPT_MAX_FILE_HANDLES]; + virtual EEL_F OpenFile(const char *fn, const char *mode) + { + if (!*fn || !*mode) return 0.0; +#ifndef EELSCRIPT_NO_STDIO + if (!strcmp(fn,"stdin")) return 1; + if (!strcmp(fn,"stdout")) return 2; + if (!strcmp(fn,"stderr")) return 3; +#endif + + WDL_FastString fnstr(fn); + if (!translateFilename(&fnstr,mode)) return 0.0; + + int x; + for (x=0;x= EELSCRIPT_MAX_FILE_HANDLES) return 0.0; + + FILE *fp = fopenUTF8(fnstr.Get(),mode); + if (!fp) return 0.0; + m_handles[x]=fp; + return x + EELSCRIPT_FILE_HANDLE_INDEX_BASE; + } + virtual EEL_F CloseFile(int fp_idx) + { + fp_idx-=EELSCRIPT_FILE_HANDLE_INDEX_BASE; + if (fp_idx>=0 && fp_idx=0 && fp_idx m_eval_cache; + virtual char *evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch); + virtual void evalCacheDispose(char *key, NSEEL_CODEHANDLE ch); + WDL_Queue m_defer_eval, m_atexit_eval; + void runCodeQ(WDL_Queue *q, const char *fname); + void runAtExitCode() + { + runCodeQ(&m_atexit_eval,"atexit"); + m_atexit_eval.Clear(); // make sure nothing gets added in atexit(), in case the user called runAtExitCode before destroying + } +#endif + virtual bool run_deferred(); // requires eval support to be useful + virtual bool has_deferred(); + + + WDL_StringKeyedArray m_loaded_fnlist; // imported file list (to avoid repeats) +}; + +//#define EEL_STRINGS_MUTABLE_LITERALS +//#define EEL_STRING_WANT_MUTEX + + +#define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((eelScriptInst *)opaque)->m_string_context) +#ifndef EEL_STRING_STDOUT_WRITE + #ifndef EELSCRIPT_NO_STDIO + #define EEL_STRING_STDOUT_WRITE(x,len) { fwrite(x,len,1,stdout); fflush(stdout); } + #endif +#endif +#include "eel_strings.h" + +#include "eel_misc.h" + + +#ifndef EELSCRIPT_NO_FILE + #define EEL_FILE_OPEN(fn,mode) ((eelScriptInst*)opaque)->OpenFile(fn,mode) + #define EEL_FILE_GETFP(fp) ((eelScriptInst*)opaque)->GetFileFP(fp) + #define EEL_FILE_CLOSE(fpindex) ((eelScriptInst*)opaque)->CloseFile(fpindex) + + #include "eel_files.h" +#endif + +#ifndef EELSCRIPT_NO_FFT + #include "eel_fft.h" +#endif + +#ifndef EELSCRIPT_NO_MDCT + #include "eel_mdct.h" +#endif + +#ifndef EELSCRIPT_NO_NET + #define EEL_NET_GET_CONTEXT(opaque) (((eelScriptInst *)opaque)->m_net_state) + #include "eel_net.h" +#endif + +#ifndef EELSCRIPT_NO_LICE + #ifndef EEL_LICE_WANT_STANDALONE + #define EEL_LICE_WANT_STANDALONE + #endif + #ifndef EELSCRIPT_LICE_NOUPDATE + #define EEL_LICE_WANT_STANDALONE_UPDATE // gfx_update() which runs message pump and updates screen etc + #endif + + #define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) (((eelScriptInst*)opaque)->GetFilenameForParameter(idx,fs,p)) + #define EEL_LICE_GET_CONTEXT(opaque) ((opaque) ? (((eelScriptInst *)opaque)->m_gfx_state) : NULL) + #include "eel_lice.h" +#endif + +#ifndef EELSCRIPT_NO_EVAL + #define EEL_EVAL_GET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheGet(str,&(ch)) + #define EEL_EVAL_SET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheDispose(str,ch) + #define EEL_EVAL_GET_VMCTX(opaque) (((eelScriptInst *)opaque)->m_vm) + #define EEL_EVAL_SCOPE_ENTER (((eelScriptInst *)opaque)->m_eval_depth < 3 ? \ + ++((eelScriptInst *)opaque)->m_eval_depth : 0) + #define EEL_EVAL_SCOPE_LEAVE ((eelScriptInst *)opaque)->m_eval_depth--; + #include "eel_eval.h" + +static EEL_F NSEEL_CGEN_CALL _eel_defer(void *opaque, EEL_F *s) +{ + EEL_STRING_MUTEXLOCK_SCOPE + const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL); + if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow defer(0) etc + { + eelScriptInst *inst = (eelScriptInst *)opaque; + if (inst->m_defer_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT) + { + inst->m_defer_eval.Add(str,strlen(str)+1); + return 1.0; + } +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("defer(): too much defer() code already added, ignoring"); +#endif + } +#ifdef EEL_STRING_DEBUGOUT + else if (!str) + { + EEL_STRING_DEBUGOUT("defer(): invalid string identifier specified %f",*s); + } + else if (*s < EEL_STRING_MAX_USER_STRINGS) + { + EEL_STRING_DEBUGOUT("defer(): user string identifier %f specified but not allowed",*s); + } +#endif + return 0.0; +} +static EEL_F NSEEL_CGEN_CALL _eel_atexit(void *opaque, EEL_F *s) +{ + EEL_STRING_MUTEXLOCK_SCOPE + const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL); + if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow atexit(0) etc + { + eelScriptInst *inst = (eelScriptInst *)opaque; + if (inst->m_atexit_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT) + { + inst->m_atexit_eval.Add(str,strlen(str)+1); + return 1.0; + } +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("atexit(): too much atexit() code already added, ignoring"); +#endif + } +#ifdef EEL_STRING_DEBUGOUT + else if (!str) + { + EEL_STRING_DEBUGOUT("atexit(): invalid string identifier specified %f",*s); + } + else if (*s < EEL_STRING_MAX_USER_STRINGS) + { + EEL_STRING_DEBUGOUT("atexit(): user string identifier %f specified but not allowed",*s); + } +#endif + return 0.0; +} +#endif + + +#define opaque ((void *)this) + +eelScriptInst::eelScriptInst() : m_loaded_fnlist(false) +{ +#ifndef EELSCRIPT_NO_FILE + memset(m_handles,0,sizeof(m_handles)); +#endif + m_vm = NSEEL_VM_alloc(); +#ifdef EEL_STRING_DEBUGOUT + if (!m_vm) EEL_STRING_DEBUGOUT("NSEEL_VM_alloc(): failed"); +#endif + NSEEL_VM_SetCustomFuncThis(m_vm,this); +#ifdef NSEEL_ADDFUNC_DESTINATION + NSEEL_VM_SetFunctionTable(m_vm,NSEEL_ADDFUNC_DESTINATION); +#endif + + m_string_context = new eel_string_context_state; + eel_string_initvm(m_vm); +#ifndef EELSCRIPT_NO_NET + m_net_state = new eel_net_state(EELSCRIPT_NET_MAXCON,NULL); +#endif +#ifndef EELSCRIPT_NO_LICE + m_gfx_state = new eel_lice_state(m_vm,this,EELSCRIPT_LICE_MAX_IMAGES,EELSCRIPT_LICE_MAX_FONTS); + + m_gfx_state->resetVarsToStock(); +#endif +#ifndef EELSCRIPT_NO_EVAL + m_eval_depth=0; +#endif +} + +eelScriptInst::~eelScriptInst() +{ +#ifndef EELSCRIPT_NO_EVAL + if (m_atexit_eval.GetSize()>0) runAtExitCode(); +#endif + int x; + m_code_freelist.Empty((void (*)(void *))NSEEL_code_free); +#ifndef EELSCRIPT_NO_EVAL + for (x=0;xSet(fmt); + return translateFilename(fs,iswrite?"w":"r"); +} + +NSEEL_CODEHANDLE eelScriptInst::compile_code(const char *code, const char **err) +{ + if (!m_vm) + { + *err = "EEL VM not initialized"; + return NULL; + } + NSEEL_CODEHANDLE ch = NSEEL_code_compile_ex(m_vm, code, 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS); + if (ch) + { + m_string_context->update_named_vars(m_vm); + m_code_freelist.Add((void*)ch); + return ch; + } + *err = NSEEL_code_getcodeerror(m_vm); + return NULL; +} + +int eelScriptInst::runcode(const char *codeptr, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec) +{ + if (m_vm) + { + NSEEL_CODEHANDLE code = NSEEL_code_compile_ex(m_vm,codeptr,0,canfree ? 0 : NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS); + if (code) m_string_context->update_named_vars(m_vm); + + char *err; + if (!code && (err=NSEEL_code_getcodeerror(m_vm))) + { + if (!ignoreEndOfInputChk && (NSEEL_code_geterror_flag(m_vm)&1)) return 1; + if (showerr) + { +#ifdef EEL_STRING_DEBUGOUT + if (showerr==2) + { + EEL_STRING_DEBUGOUT("Warning: %s:%s",WDL_get_filepart(showerrfn),err); + } + else + { + EEL_STRING_DEBUGOUT("%s:%s",WDL_get_filepart(showerrfn),err); + } +#endif + } + return -1; + } + else + { + if (code) + { +#ifdef EELSCRIPT_DO_DISASSEMBLE + codeHandleType *p = (codeHandleType*)code; + + char buf[512]; + buf[0]=0; +#ifdef _WIN32 + GetTempPath(sizeof(buf)-64,buf); + lstrcatn(buf,"jsfx-out",sizeof(buf)); +#else + lstrcpyn_safe(buf,"/tmp/jsfx-out",sizeof(buf)); +#endif + FILE *fp = fopenUTF8(buf,"wb"); + if (fp) + { + fwrite(p->code,1,p->code_size,fp); + fclose(fp); + char buf2[2048]; +#ifdef _WIN32 + snprintf(buf2,sizeof(buf2),"disasm \"%s\"",buf); +#else + #ifdef __aarch64__ + snprintf(buf2,sizeof(buf2), "objdump -D -b binary -maarch64 \"%s\"",buf); + #elif defined(__arm__) + snprintf(buf2,sizeof(buf2), "objdump -D -b binary -m arm \"%s\"",buf); + #elif defined(__LP64__) + #ifdef __APPLE__ + snprintf(buf2,sizeof(buf2),"distorm3 --b64 \"%s\"",buf); + #else + snprintf(buf2,sizeof(buf2),"objdump -D -b binary -m i386:x86-64 \"%s\"",buf); + #endif + #else + snprintf(buf2,sizeof(buf2),"distorm3 --b32 \"%s\"",buf); + #endif +#endif + system(buf2); + } +#endif + + if (doExec) NSEEL_code_execute(code); + if (canfree) NSEEL_code_free(code); + else m_code_freelist.Add((void*)code); + } + return 0; + } + } + return -1; +} + + +FILE *eelscript_resolvePath(WDL_FastString &usefn, const char *fn, const char *callerfn) +{ + // resolve path relative to current + int x; + bool had_abs=false; + for (x=0;x<2; x ++) + { +#ifdef _WIN32 + if (!x && ((fn[0] == '\\' && fn[1] == '\\') || (fn[0] && fn[1] == ':'))) +#else + if (!x && fn[0] == '/') +#endif + { + usefn.Set(fn); + had_abs=true; + } + else + { + const char *fnu = fn; + if (x) + { + while (*fnu) fnu++; + while (fnu >= fn && *fnu != '\\' && *fnu != '/') fnu--; + if (fnu < fn) break; + fnu++; + } + + usefn.Set(callerfn); + int l=usefn.GetLength(); + while (l > 0 && usefn.Get()[l-1] != '\\' && usefn.Get()[l-1] != '/') l--; + if (l > 0) + { + usefn.SetLen(l); + usefn.Append(fnu); + } + else + { + usefn.Set(fnu); + } + int last_slash_pos=-1; + for (l = 0; l < usefn.GetLength(); l ++) + { + if (usefn.Get()[l] == '/' || usefn.Get()[l] == '\\') + { + if (usefn.Get()[l+1] == '.' && usefn.Get()[l+2] == '.' && + (usefn.Get()[l+3] == '/' || usefn.Get()[l+3] == '\\')) + { + if (last_slash_pos >= 0) + usefn.DeleteSub(last_slash_pos, l+3-last_slash_pos); + else + usefn.DeleteSub(0,l+3+1); + } + else + { + last_slash_pos=l; + } + } + // take currentfn, remove filename part, add fnu + } + } + + FILE *fp = fopenUTF8(usefn.Get(),"r"); + if (fp) return fp; + } + if (had_abs) usefn.Set(fn); + return NULL; +} + +int eelScriptInst::loadfile(const char *fn, const char *callerfn, bool allowstdin) +{ + WDL_FastString usefn; + FILE *fp = NULL; + if (!strcmp(fn,"-")) + { + if (callerfn) + { +#ifdef EEL_STRING_DEBUGOUT + EEL_STRING_DEBUGOUT("@import: can't import \"-\" (stdin)"); +#endif + return -1; + } + if (allowstdin) + { + fp = stdin; + fn = "(stdin)"; + } + } + else if (!callerfn) + { + fp = fopenUTF8(fn,"r"); + if (fp) m_loaded_fnlist.Insert(fn,true); + } + else + { + fp = eelscript_resolvePath(usefn,fn,callerfn); + if (fp) + { + if (m_loaded_fnlist.Get(usefn.Get())) + { + fclose(fp); + return 0; + } + m_loaded_fnlist.Insert(usefn.Get(),true); + fn = usefn.Get(); + } + } + + if (!fp) + { +#ifdef EEL_STRING_DEBUGOUT + if (callerfn) + EEL_STRING_DEBUGOUT("Warning: @import could not open '%s'",fn); + else + EEL_STRING_DEBUGOUT("Error opening %s",fn); +#endif + return -1; + } + + WDL_FastString code; + char line[4096]; + for (;;) + { + line[0]=0; + fgets(line,sizeof(line),fp); + if (!line[0]) break; + if (!strnicmp(line,"@import",7) && isspace((unsigned char)line[7])) + { + char *p=line+7; + while (isspace((unsigned char)*p)) p++; + + char *ep=p; + while (*ep) ep++; + while (ep>p && isspace((unsigned char)ep[-1])) ep--; + *ep=0; + + if (*p) loadfile(p,fn,false); + } + else + { + code.Append(line); + } + } + if (fp != stdin) fclose(fp); + + return runcode(code.Get(),callerfn ? 2 : 1, fn,false,true,!callerfn); +} + +char *eelScriptInst::evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch) +{ + // should mutex protect if multiple threads access this eelScriptInst context + int x=m_eval_cache.GetSize(); + while (--x >= 0) + { + char *ret; + if (!strcmp(ret=m_eval_cache.Get()[x].str, str)) + { + *ch = m_eval_cache.Get()[x].ch; + m_eval_cache.Delete(x); + return ret; + } + } + return NULL; +} + +void eelScriptInst::evalCacheDispose(char *key, NSEEL_CODEHANDLE ch) +{ + // should mutex protect if multiple threads access this eelScriptInst context + evalCacheEnt ecc; + ecc.str= key; + ecc.ch = ch; + if (m_eval_cache.GetSize() > 1024) + { + NSEEL_code_free(m_eval_cache.Get()->ch); + free(m_eval_cache.Get()->str); + m_eval_cache.Delete(0); + } + m_eval_cache.Add(ecc); +} + +int eelScriptInst::init() +{ + EEL_string_register(); +#ifndef EELSCRIPT_NO_FILE + EEL_file_register(); +#endif +#ifndef EELSCRIPT_NO_FFT + EEL_fft_register(); +#endif +#ifndef EELSCRIPT_NO_MDCT + EEL_mdct_register(); +#endif + EEL_misc_register(); +#ifndef EELSCRIPT_NO_EVAL + EEL_eval_register(); + NSEEL_addfunc_retval("defer",1,NSEEL_PProc_THIS,&_eel_defer); + NSEEL_addfunc_retval("runloop", 1, NSEEL_PProc_THIS, &_eel_defer); + NSEEL_addfunc_retval("atexit",1,NSEEL_PProc_THIS,&_eel_atexit); +#endif +#ifndef EELSCRIPT_NO_NET + EEL_tcp_register(); +#endif +#ifndef EELSCRIPT_NO_LICE + eel_lice_register(); + #ifdef _WIN32 + eel_lice_register_standalone(GetModuleHandle(NULL),EELSCRIPT_LICE_CLASSNAME,NULL,NULL); + #else + eel_lice_register_standalone(NULL,EELSCRIPT_LICE_CLASSNAME,NULL,NULL); + #endif +#endif + return 0; +} + +bool eelScriptInst::has_deferred() +{ +#ifndef EELSCRIPT_NO_EVAL + return m_defer_eval.Available() && m_vm; +#else + return false; +#endif +} + +#ifndef EELSCRIPT_NO_EVAL +void eelScriptInst::runCodeQ(WDL_Queue *q, const char *callername) +{ + const int endptr = q->Available(); + int offs = 0; + while (offs < endptr) + { + if (q->Available() < endptr) break; // should never happen, but safety first! + + const char *ptr = (const char *)q->Get() + offs; + offs += strlen(ptr)+1; + + NSEEL_CODEHANDLE ch=NULL; + char *sv=evalCacheGet(ptr,&ch); + + if (!sv) sv=strdup(ptr); + if (!ch) ch=NSEEL_code_compile(m_vm,sv,0); + if (!ch) + { + free(sv); +#ifdef EEL_STRING_DEBUGOUT + const char *err = NSEEL_code_getcodeerror(m_vm); + if (err) EEL_STRING_DEBUGOUT("%s: error in code: %s",callername,err); +#endif + } + else + { + NSEEL_code_execute(ch); + evalCacheDispose(sv,ch); + } + } + q->Advance(endptr); +} +#endif + +bool eelScriptInst::run_deferred() +{ +#ifndef EELSCRIPT_NO_EVAL + if (!m_defer_eval.Available()||!m_vm) return false; + + runCodeQ(&m_defer_eval,"defer"); + m_defer_eval.Compact(); + return m_defer_eval.Available()>0; +#else + return false; +#endif +} + +#ifdef EEL_WANT_DOCUMENTATION +#include "ns-eel-func-ref.h" + +void EELScript_GenerateFunctionList(WDL_PtrList *fs) +{ + const char *p = nseel_builtin_function_reference; + while (*p) { fs->Add(p); p += strlen(p) + 1; } + p = eel_strings_function_reference; + while (*p) { fs->Add(p); p += strlen(p) + 1; } + p = eel_misc_function_reference; + while (*p) { fs->Add(p); p += strlen(p) + 1; } +#ifndef EELSCRIPT_NO_EVAL + fs->Add("atexit\t\"code\"\t" +#ifndef EELSCRIPT_HELP_NO_DEFER_DESC + "Adds code to be executed when the script finishes." +#endif + ); + fs->Add("defer\t\"code\"\t" +#ifndef EELSCRIPT_HELP_NO_DEFER_DESC + "Adds code which will be executed some small amount of time after the current code finishes. Identical to runloop()" +#endif + ); + fs->Add("runloop\t\"code\"\t" +#ifndef EELSCRIPT_HELP_NO_DEFER_DESC + "Adds code which will be executed some small amount of time after the current code finishes. Identical to defer()" +#endif + ); + + p = eel_eval_function_reference; + while (*p) { fs->Add(p); p += strlen(p) + 1; } +#endif +#ifndef EELSCRIPT_NO_NET + p = eel_net_function_reference; + while (*p) { fs->Add(p); p += strlen(p) + 1; } +#endif +#ifndef EELSCRIPT_NO_FFT + p = eel_fft_function_reference; + while (*p) { fs->Add(p); p += strlen(p) + 1; } +#endif +#ifndef EELSCRIPT_NO_FILE + p = eel_file_function_reference; + while (*p) { fs->Add(p); p += strlen(p) + 1; } +#endif +#ifndef EELSCRIPT_NO_MDCT + p = eel_mdct_function_reference; + while (*p) { fs->Add(p); p += strlen(p) + 1; } +#endif +#ifndef EELSCRIPT_NO_LICE + p = eel_lice_function_reference; + while (*p) { fs->Add(p); p += strlen(p) + 1; } +#endif + +} + + +#endif + +#undef opaque diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_aarch64.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_aarch64.h new file mode 100644 index 000000000..36a4d8b49 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_aarch64.h @@ -0,0 +1,399 @@ +#ifndef _NSEEL_GLUE_AARCH64_H_ +#define _NSEEL_GLUE_AARCH64_H_ + +// x0=return value, first parm, x1-x2 parms (x3-x7 more params) +// x8 return struct? +// x9-x15 temporary +// x16-x17 = PLT, linker +// x18 reserved (TLS) +// x19-x28 callee-saved +// x19 = worktable +// x20 = ramtable +// x21 = consttab +// x22 = worktable ptr +// x23-x28 spare +// x29 frame pointer +// x30 link register +// x31 SP/zero + +// x0=p1 +// x1=p2 +// x2=p3 + +// d0 is return value for fp? +// d/v/f0-7 = arguments/results +// 8-15 callee saved +// 16-31 temporary + +// v8-v15 spill registers +#define GLUE_MAX_SPILL_REGS 8 +#define GLUE_SAVE_TO_SPILL_SIZE(x) (4) +#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4) + +static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws) +{ + *(unsigned int *)b = 0x1e604101 + (ws<<5); // fmov d1, d8+ws +} +static void GLUE_SAVE_TO_SPILL(void *b, int ws) +{ + *(unsigned int *)b = 0x1e604008 + ws; // fmov d8+ws, d0 +} + + +#define GLUE_HAS_FPREG2 1 + +static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = { 0x1e604001 }; // fmov d1, d0 +static unsigned int GLUE_POP_STACK_TO_FPREG2[] = { + 0xfc4107e1 // ldr d1, [sp], #16 +}; + +#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support +#define GLUE_MAX_JMPSIZE ((1<<20) - 1024) // maximum relative jump size + +// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction. +// 0 = current instruction +static void GLUE_JMP_SET_OFFSET(void *endOfInstruction, int offset) +{ + unsigned int *a = (unsigned int*) endOfInstruction - 1; + offset += 4; + offset >>= 2; // as dwords + if ((a[0] & 0xFC000000) == 0x14000000) + { + // NC b = 0x14 + 26 bit offset + a[0] = 0x14000000 | (offset & 0x3FFFFFF); + } + else if ((a[0] & 0xFF000000) == 0x54000000) + { + // condb = 0x54 + 20 bit offset + 5 bit condition: 0=eq, 1=ne, b=lt, c=gt, d=le, a=ge + a[0] = 0x54000000 | (a[0] & 0xF) | ((offset & 0x7FFFF) << 5); + } +} + +static const unsigned int GLUE_JMP_NC[] = { 0x14000000 }; + +static const unsigned int GLUE_JMP_IF_P1_Z[]= +{ + 0x7100001f, // cmp w0, #0 + 0x54000000, // b.eq +}; +static const unsigned int GLUE_JMP_IF_P1_NZ[]= +{ + 0x7100001f, // cmp w0, #0 + 0x54000001, // b.ne +}; + +#define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 16 // wr=-2, sets d1 +#define GLUE_MOV_PX_DIRECTVALUE_SIZE 12 +static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) +{ + static const unsigned int tab[3] = { + 0xd2800000, // mov x0, #0000 (val<<5) | reg + 0xf2a00000, // movk x0, #0000, lsl 16 (val<<5) | reg + 0xf2c00000, // movk x0, #0000, lsl 32 (val<<5) | reg + }; + // 0xABAAA, B is register, A are bits of word + unsigned int *p=(unsigned int *)b; + int wvo = wv; + if (wv<0) wv=0; + p[0] = tab[0] | wv | ((v&0xFFFF)<<5); + p[1] = tab[1] | wv | (((v>>16)&0xFFFF)<<5); + p[2] = tab[2] | wv | (((v>>32)&0xFFFF)<<5); + if (wvo == -2) p[3] = 0xfd400001; // ldr d1, [x0] +} + +const static unsigned int GLUE_FUNC_ENTER[2] = { 0xa9bf7bfd, 0x910003fd }; // stp x29, x30, [sp, #-16]! ; mov x29, sp +#define GLUE_FUNC_ENTER_SIZE 4 +const static unsigned int GLUE_FUNC_LEAVE[1] = { 0 }; // let GLUE_RET pop +#define GLUE_FUNC_LEAVE_SIZE 0 +const static unsigned int GLUE_RET[]={ 0xa8c17bfd, 0xd65f03c0 }; // ldp x29,x30, [sp], #16 ; ret + +static int GLUE_RESET_WTP(unsigned char *out, void *ptr) +{ + const static unsigned int GLUE_SET_WTP_FROM_R19 = 0xaa1303f6; // mov r22, r19 + if (out) memcpy(out,&GLUE_SET_WTP_FROM_R19,sizeof(GLUE_SET_WTP_FROM_R19)); + return 4; +} + + +const static unsigned int GLUE_PUSH_P1[1]={ 0xf81f0fe0 }; // str x0, [sp, #-16]! + +#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(offs) ((offs)>=32768 ? 8 : 4) +static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) +{ + if (offs >= 32768) + { + // add x1, sp, (offs/4096) lsl 12 + *(unsigned int *)b = 0x914003e1 + ((offs>>12)<<10); + + // str x0, [x1, #offs & 4095] + offs &= 4095; + offs <<= 10-3; + offs &= 0x7FFC00; + ((unsigned int *)b)[1] = 0xf9000020 + offs; + } + else + { + // str x0, [sp, #offs] + offs <<= 10-3; + offs &= 0x7FFC00; + *(unsigned int *)b = 0xf90003e0 + offs; + } +} + +#define GLUE_MOVE_PX_STACKPTR_SIZE 4 +static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) +{ + // mov xX, sp + *(unsigned int *)b = 0x910003e0 + wv; +} + +#define GLUE_MOVE_STACK_SIZE 4 +static void GLUE_MOVE_STACK(void *b, int amt) +{ + if (amt>=0) + { + if (amt >= 4096) + *(unsigned int*)b = 0x914003ff | (((amt+4095)>>12)<<10); + else + *(unsigned int*)b = 0x910003ff | (amt << 10); + } + else + { + amt = -amt; + if (amt >= 4096) + *(unsigned int*)b = 0xd14003ff | (((amt+4095)>>12)<<10); + else + *(unsigned int*)b = 0xd10003ff | (amt << 10); + } +} + +#define GLUE_POP_PX_SIZE 4 +static void GLUE_POP_PX(void *b, int wv) +{ + ((unsigned int *)b)[0] = 0xf84107e0 | wv; // ldr x, [sp], 16 +} + +#define GLUE_SET_PX_FROM_P1_SIZE 4 +static void GLUE_SET_PX_FROM_P1(void *b, int wv) +{ + *(unsigned int *)b = 0xaa0003e0 | wv; +} + + +static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] = +{ + 0xfd400007, // ldr d7, [x0] + 0xfc1f0fe7, // str d7, [sp, #-16]! +}; + +static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) +{ + if (buf) + { + unsigned int *bufptr = (unsigned int *)buf; + *bufptr++ = 0xfc4107e7; // ldr d7, [sp], #16 + GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); + bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; + *bufptr++ = 0xfd000007; // str d7, [x0] + } + return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE; +} + +static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) +{ + if (buf) + { + unsigned int *bufptr = (unsigned int *)buf; + *bufptr++ = 0xfd400007; // ldr d7, [x0] + GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); + bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; + *bufptr++ = 0xfd000007; // str d7, [x0] + } + return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE; +} + + +#ifndef _MSC_VER +#define GLUE_CALL_CODE(bp, cp, rt) do { \ + unsigned long f; \ + if (!(h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) && \ + !((f=glue_getscr())&(1<<24))) { \ + glue_setscr(f|(1<<24)); \ + eel_callcode64(bp, cp, rt); \ + glue_setscr(f); \ + } else eel_callcode64(bp, cp, rt);\ + } while(0) + +static void eel_callcode64(INT_PTR bp, INT_PTR cp, INT_PTR rt) +{ + //fwrite((void *)cp,4,20,stdout); + //return; + static const double consttab[] = { + NSEEL_CLOSEFACTOR, + 0.0, + 1.0, + -1.0, + -0.5, // for invsqrt + 1.5, + }; + __asm__( + "mov x1, %2\n" + "mov x2, %3\n" + "mov x3, %1\n" + "mov x0, %0\n" + "stp x29, x30, [sp, #-64]!\n" + "stp x18, x20, [sp, 16]\n" + "stp x21, x19, [sp, 32]\n" + "stp x22, x23, [sp, 48]\n" + "mov x29, sp\n" + "mov x19, x3\n" + "mov x20, x1\n" + "mov x21, x2\n" + "blr x0\n" + "ldp x29, x30, [sp], 16\n" + "ldp x18, x20, [sp], 16\n" + "ldp x21, x19, [sp], 16\n" + "ldp x22, x23, [sp], 16\n" + ::"r" (cp), "r" (bp), "r" (rt), "r" (consttab) :"x0","x1","x2","x3","x4","x5","x6","x7", + "x8","x9","x10","x11","x12","x13","x14","x15", + "v8","v9","v10","v11","v12","v13","v14","v15"); + +}; +#endif + +static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) +{ + unsigned int *p=(unsigned int *)_p; +// 0xd2800000, // mov x0, #0000 (val<<5) | reg + // 0xf2a00000, // movk x0, #0000, lsl 16 (val<<5) | reg + // 0xf2c00000, // movk x0, #0000, lsl 32 (val<<5) | reg + while (((p[0]>>5)&0xffff)!=0xdead || + ((p[1]>>5)&0xffff)!=0xbeef || + ((p[2]>>5)&0xffff)!=0xbeef) p++; + + p[0] = (p[0] & 0xFFE0001F) | ((newv&0xffff)<<5); + p[1] = (p[1] & 0xFFE0001F) | (((newv>>16)&0xffff)<<5); + p[2] = (p[2] & 0xFFE0001F) | (((newv>>32)&0xffff)<<5); + + return (unsigned char *)(p+2); +} + +#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int) +static void GLUE_SET_PX_FROM_WTP(void *b, int wv) +{ + *(unsigned int *)b = 0xaa1603e0 + wv; // mov x, x22 +} + +static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) +{ + if (buf) + { + unsigned int *bufptr = (unsigned int *)buf; + GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); + bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; + + *bufptr++ = 0xfd000000; // str d0, [x0] + } + return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int); +} + +#define GLUE_POP_FPSTACK_SIZE 0 +static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack + +static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = { + 0xfc1f0fe0, // str d0, [sp, #-16]! + +}; + +static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = { + 0xfc0086c0, // str d0, [x22], #8 +}; + +#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4 +static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) +{ + *(unsigned int *)b = 0xfd400000 + (wv<<5); // ldr d0, [xX] +} + +#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE) +static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) +{ + GLUE_SET_PX_FROM_WTP(buf,wv); + memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); +}; + +static const unsigned int GLUE_SET_P1_Z[] = { 0x52800000 }; // mov w0, #0 +static const unsigned int GLUE_SET_P1_NZ[] = { 0x52800020 }; // mov w0, #1 + + +static void *GLUE_realAddress(void *fn, int *size) +{ + while ((*(int*)fn & 0xFC000000) == 0x14000000) + { + int offset = (*(int*)fn & 0x3FFFFFF); + if (offset & 0x2000000) + offset |= 0xFC000000; + + fn = (int*)fn + offset; + } + static const unsigned int sig[3] = { 0xaa0003e0, 0xaa0103e1, 0xaa0203e2 }; + unsigned char *p = (unsigned char *)fn; + + while (memcmp(p,sig,sizeof(sig))) p+=4; + p+=sizeof(sig); + fn = p; + + while (memcmp(p,sig,sizeof(sig))) p+=4; + *size = p - (unsigned char *)fn; + return fn; +} + + +static unsigned long __attribute__((unused)) glue_getscr() +{ + unsigned long rv; + asm volatile ( "mrs %0, fpcr" : "=r" (rv)); + return rv; +} +static void __attribute__((unused)) glue_setscr(unsigned long v) +{ + asm volatile ( "msr fpcr, %0" :: "r"(v)); +} + +void eel_enterfp(int _s[2]) +{ + unsigned long *s = (unsigned long*)_s; + s[0] = glue_getscr(); + glue_setscr(s[0] | (1<<24)); +} +void eel_leavefp(int _s[2]) +{ + unsigned long *s = (unsigned long*)_s; + glue_setscr(s[0]); +} + +#define GLUE_HAS_FUSE 1 +static int GLUE_FUSE(compileContext *ctx, unsigned char *code, int left_size, int right_size, int fuse_flags, int spill_reg) +{ + if (left_size>=4 && right_size == 4) + { + unsigned int instr = ((unsigned int *)code)[-1]; + if (spill_reg >= 0 && (instr & 0xfffffc1f) == 0x1e604001) // fmov d1, dX + { + const int src_reg = (instr>>5)&0x1f; + if (src_reg == spill_reg + 8) + { + instr = ((unsigned int *)code)[0]; + if ((instr & 0xffffcfff) == 0x1e600820) + { + ((unsigned int *)code)[-1] = instr + ((src_reg-1) << 5); + return -4; + } + } + } + } + return 0; +} + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_arm.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_arm.h new file mode 100644 index 000000000..ab9ec733d --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_arm.h @@ -0,0 +1,335 @@ +#ifndef _NSEEL_GLUE_ARM_H_ +#define _NSEEL_GLUE_ARM_H_ + +// r0=return value, first parm, r1-r2 parms +// r3+ should be reserved +// blx addr +// stmfd sp!, {register list, lr} +// ldmfd sp!, {register list, pc} + +// let's make r8 = worktable +// let's make r7 = ramtable +// r6 = consttab +// r5 = worktable ptr + +// r0=p1 +// r1=p2 +// r2=p3 + +// d0 is return value? + + +#define GLUE_HAS_FPREG2 1 + +static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = { + 0xeeb01b40 // fcpyd d1, d0 +}; + +static unsigned int GLUE_POP_STACK_TO_FPREG2[] = { + 0xed9d1b00,// vldr d1, [sp] + 0xe28dd008,// add sp, sp, #8 +}; + +#define GLUE_MAX_SPILL_REGS 8 +#define GLUE_SAVE_TO_SPILL_SIZE(x) (4) +#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4) + +static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws) +{ + *(unsigned int *)b = 0xeeb01b48 + ws; // fcpyd d1, d8+ws +} +static void GLUE_SAVE_TO_SPILL(void *b, int ws) +{ + *(unsigned int *)b = 0xeeb08b40 + (ws<<12); // fcpyd d8+ws, d0 +} + + +#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support +#define GLUE_MAX_JMPSIZE ((1<<25) - 1024) // maximum relative jump size + +// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction. +// TODO: verify, but offset probably from next instruction (PC is ahead) +#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (((int *)(endOfInstruction))[-1]&0xFF000000)|((((offset)>>2)-1))) + + // /=conditional=always = 0xE + // |/= 101(L), so 8+2+0 = 10 = A +static const unsigned int GLUE_JMP_NC[] = { 0xEA000000 }; + +static const unsigned int GLUE_JMP_IF_P1_Z[]= +{ + 0xe1100000, // tst r0, r0 + 0x0A000000, // branch if Z set +}; +static const unsigned int GLUE_JMP_IF_P1_NZ[]= +{ + 0xe1100000, // tst r0, r0 + 0x1A000000, // branch if Z clear +}; + +#define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 12 // wr=-2, sets d1 +#define GLUE_MOV_PX_DIRECTVALUE_SIZE 8 +static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) +{ + // requires ARMv6thumb2 or later + const unsigned int reg_add = wdl_max(wv,0) << 12; + static const unsigned int tab[2] = { + 0xe3000000, // movw r0, #0000 + 0xe3400000, // movt r0, #0000 + }; + // 0xABAAA, B is register, A are bits of word + unsigned int *p=(unsigned int *)b; + p[0] = tab[0] | reg_add | (v&0xfff) | ((v&0xf000)<<4); + p[1] = tab[1] | reg_add | ((v>>16)&0xfff) | ((v&0xf0000000)>>12); + if (wv == -2) p[2] = 0xed901b00; // fldd d1, [r0] +} + +const static unsigned int GLUE_FUNC_ENTER[1] = { 0xe92d4010 }; // push {r4, lr} +#define GLUE_FUNC_ENTER_SIZE 4 +const static unsigned int GLUE_FUNC_LEAVE[1] = { 0 }; // let GLUE_RET pop +#define GLUE_FUNC_LEAVE_SIZE 0 +const static unsigned int GLUE_RET[]={ 0xe8bd8010 }; // pop {r4, pc} + +static int GLUE_RESET_WTP(unsigned char *out, void *ptr) +{ + const static unsigned int GLUE_SET_WTP_FROM_R8 = 0xe1a05008; // mov r5, r8 + if (out) memcpy(out,&GLUE_SET_WTP_FROM_R8,sizeof(GLUE_SET_WTP_FROM_R8)); + return sizeof(GLUE_SET_WTP_FROM_R8); +} + + +const static unsigned int GLUE_PUSH_P1[1]={ 0xe52d0008 }; // push {r0}, aligned to 8 + + +static int arm_encode_constforalu(int amt) +{ + int nrot = 16; + while (amt >= 0x100 && nrot > 1) + { + // ARM encodes integers for ALU operations as rotated right by third nibble*2 + amt = (amt + 3)>>2; + nrot--; + } + return ((nrot&15) << 8) | amt; +} + + +#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) ((x)>=4096 ? 8 : 4) +static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) +{ + if (offs >= 4096) + { + // add r2, sp, (offs&~4095) + *(unsigned int *)b = 0xe28d2000 | arm_encode_constforalu(offs&~4095); + // str r0, [r2, offs&4095] + ((unsigned int *)b)[1] = 0xe5820000 + (offs&4095); + } + else + { + // str r0, [sp, #offs] + *(unsigned int *)b = 0xe58d0000 + offs; + } +} + +#define GLUE_MOVE_PX_STACKPTR_SIZE 4 +static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) +{ + // mov rX, sp + *(unsigned int *)b = 0xe1a0000d + (wv<<12); +} + +#define GLUE_MOVE_STACK_SIZE 4 +static void GLUE_MOVE_STACK(void *b, int amt) +{ + unsigned int instr = 0xe28dd000; + if (amt < 0) + { + instr = 0xe24dd000; + amt=-amt; + } + *(unsigned int*)b = instr | arm_encode_constforalu(amt); +} + +#define GLUE_POP_PX_SIZE 4 +static void GLUE_POP_PX(void *b, int wv) +{ + ((unsigned int *)b)[0] = 0xe49d0008 | (wv<<12); // pop {rX}, aligned to 8 +} + +#define GLUE_SET_PX_FROM_P1_SIZE 4 +static void GLUE_SET_PX_FROM_P1(void *b, int wv) +{ + *(unsigned int *)b = 0xe1a00000 | (wv<<12); // mov rX, r0 +} + + +static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] = +{ + 0xed907b00, // fldd d7, [r0] + 0xe24dd008, // sub sp, sp, #8 + 0xed8d7b00, // fstd d7, [sp] +}; + +static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) +{ + if (buf) + { + unsigned int *bufptr = (unsigned int *)buf; + *bufptr++ = 0xed9d7b00; // fldd d7, [sp] + *bufptr++ = 0xe28dd008; // add sp, sp, #8 + GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); + bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; + *bufptr++ = 0xed807b00; // fstd d7, [r0] + } + return 3*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE; +} + +static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) +{ + if (buf) + { + unsigned int *bufptr = (unsigned int *)buf; + *bufptr++ = 0xed907b00; // fldd d7, [r0] + GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); + bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; + *bufptr++ = 0xed807b00; // fstd d7, [r0] + } + return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE; +} + + +#ifndef _MSC_VER +#define GLUE_CALL_CODE(bp, cp, rt) do { \ + unsigned int f; \ + if (!(h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) && \ + !((f=glue_getscr())&(1<<24))) { \ + glue_setscr(f|(1<<24)); \ + eel_callcode32(bp, cp, rt); \ + glue_setscr(f); \ + } else eel_callcode32(bp, cp, rt);\ + } while(0) + +static const double __consttab[] = { + NSEEL_CLOSEFACTOR, + 0.0, + 1.0, + -1.0, + -0.5, // for invsqrt + 1.5, + }; + +static void eel_callcode32(INT_PTR bp, INT_PTR cp, INT_PTR rt) +{ + __asm__ volatile( + "mov r7, %2\n" + "mov r6, %3\n" + "mov r8, %1\n" + "mov r0, %0\n" + "mov r1, sp\n" + "bic sp, sp, #7\n" + "push {r1, lr}\n" + "blx r0\n" + "pop {r1, lr}\n" + "mov sp, r1\n" + ::"r" (cp), "r" (bp), "r" (rt), "r" (__consttab) : + "r5", "r6", "r7", "r8", "r10", + "d8","d9","d10","d11","d12","d13","d14","d15"); +}; +#endif + +static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) +{ + unsigned int *p=(unsigned int *)_p; + while ((p[0]&0x000F0FFF) != 0x000d0ead && + (p[1]&0x000F0FFF) != 0x000b0eef) p++; + p[0] = (p[0]&0xFFF0F000) | (newv&0xFFF) | ((newv << 4) & 0xF0000); + p[1] = (p[1]&0xFFF0F000) | ((newv>>16)&0xFFF) | ((newv >> 12)&0xF0000); + + return (unsigned char *)(p+1); +} + +#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int) +static void GLUE_SET_PX_FROM_WTP(void *b, int wv) +{ + *(unsigned int *)b = 0xe1a00005 + (wv<<12); // mov rX, r5 +} + +static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) +{ + if (buf) + { + unsigned int *bufptr = (unsigned int *)buf; + GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); + bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; + + *bufptr++ = 0xed800b00; // fstd d0, [r0] + } + return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int); +} + +#define GLUE_POP_FPSTACK_SIZE 0 +static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack + +static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = { + 0xe24dd008, // sub sp, sp, #8 + 0xed8d0b00, // fstd d0, [sp] +}; + +static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = { + 0xed850b00, // fstd d0, [r5] + 0xe2855008, // add r5, r5, #8 +}; + +#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4 +static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) +{ + *(unsigned int *)b = 0xed900b00 + (wv<<16); // fldd d0, [rX] +} + +#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE) +static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) +{ + GLUE_SET_PX_FROM_WTP(buf,wv); + memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); +}; + +static const unsigned int GLUE_SET_P1_Z[] = { 0xe3a00000 }; // mov r0, #0 +static const unsigned int GLUE_SET_P1_NZ[] = { 0xe3a00001 }; // mov r0, #1 + + +static void *GLUE_realAddress(void *fn, int *size) +{ + static const unsigned int sig[3] = { 0xe1a00000, 0xe1a01001, 0xe1a02002 }; + unsigned char *p = (unsigned char *)fn; + + while (memcmp(p,sig,sizeof(sig))) p+=4; + p+=sizeof(sig); + fn = p; + + while (memcmp(p,sig,sizeof(sig))) p+=4; + *size = p - (unsigned char *)fn; + return fn; +} + +static unsigned int __attribute__((unused)) glue_getscr() +{ + unsigned int rv; + asm volatile ( "fmrx %0, fpscr" : "=r" (rv)); + return rv; +} +static void __attribute__((unused)) glue_setscr(unsigned int v) +{ + asm volatile ( "fmxr fpscr, %0" :: "r"(v)); +} + +void eel_enterfp(int s[2]) +{ + s[0] = glue_getscr(); + glue_setscr(s[0] | (1<<24)); // could also do 3<<22 for RTZ +} +void eel_leavefp(int s[2]) +{ + glue_setscr(s[0]); +} + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_port.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_port.h new file mode 100644 index 000000000..4d27def18 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_port.h @@ -0,0 +1,1028 @@ +#ifndef _EEL_GLUE_PORTABLE_H_ +#define _EEL_GLUE_PORTABLE_H_ + + +#define DECL_ASMFUNC(x) +#define GLUE_JMP_TYPE int +#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((GLUE_JMP_TYPE *)(endOfInstruction))[-1] = (offset)) + +#define GLUE_MAX_FPSTACK_SIZE 64 +#define BIF_FPSTACKUSE(x) (0) // fp stack is not used within functions +#define BIF_GETFPSTACKUSE(x) (1) + +enum { + EEL_BC_NOP=1, + EEL_BC_RET, + EEL_BC_JMP_NC, // followed by GLUE_JMP_TYPE + EEL_BC_JMP_IF_P1_Z, + EEL_BC_JMP_IF_P1_NZ, + + EEL_BC_MOV_FPTOP_DV, + EEL_BC_MOV_P1_DV, // followed by INT_PTR ptr + EEL_BC_MOV_P2_DV, + EEL_BC_MOV_P3_DV, + EEL_BC__RESET_WTP, + + EEL_BC_PUSH_P1, + EEL_BC_PUSH_P1PTR_AS_VALUE, + EEL_BC_POP_P1, + EEL_BC_POP_P2, + EEL_BC_POP_P3, + EEL_BC_POP_VALUE_TO_ADDR, + + EEL_BC_MOVE_STACK, + EEL_BC_STORE_P1_TO_STACK_AT_OFFS, + EEL_BC_MOVE_STACKPTR_TO_P1, + EEL_BC_MOVE_STACKPTR_TO_P2, + EEL_BC_MOVE_STACKPTR_TO_P3, + + EEL_BC_SET_P2_FROM_P1, + EEL_BC_SET_P3_FROM_P1, + EEL_BC_COPY_VALUE_AT_P1_TO_ADDR, + EEL_BC_SET_P1_FROM_WTP, + EEL_BC_SET_P2_FROM_WTP, + EEL_BC_SET_P3_FROM_WTP, + + EEL_BC_POP_FPSTACK_TO_PTR, + EEL_BC_POP_FPSTACK_TOSTACK, + + EEL_BC_PUSH_VAL_AT_P1_TO_FPSTACK, + EEL_BC_PUSH_VAL_AT_P2_TO_FPSTACK, + EEL_BC_PUSH_VAL_AT_P3_TO_FPSTACK, + EEL_BC_POP_FPSTACK_TO_WTP, + EEL_BC_SET_P1_Z, + EEL_BC_SET_P1_NZ, + + + EEL_BC_LOOP_LOADCNT, + EEL_BC_LOOP_END, + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + EEL_BC_WHILE_SETUP, +#endif + + EEL_BC_WHILE_BEGIN, + EEL_BC_WHILE_END, + EEL_BC_WHILE_CHECK_RV, + + + + EEL_BC_BNOT, + EEL_BC_EQUAL, + EEL_BC_EQUAL_EXACT, + EEL_BC_NOTEQUAL, + EEL_BC_NOTEQUAL_EXACT, + EEL_BC_ABOVE, + EEL_BC_BELOWEQ, + + + EEL_BC_ADD, + EEL_BC_SUB, + EEL_BC_MUL, + EEL_BC_DIV, + EEL_BC_AND, + EEL_BC_OR, + EEL_BC_OR0, + EEL_BC_XOR, + + EEL_BC_ADD_OP, + EEL_BC_SUB_OP, + EEL_BC_ADD_OP_FAST, + EEL_BC_SUB_OP_FAST, + EEL_BC_MUL_OP, + EEL_BC_DIV_OP, + EEL_BC_MUL_OP_FAST, + EEL_BC_DIV_OP_FAST, + EEL_BC_AND_OP, + EEL_BC_OR_OP, + EEL_BC_XOR_OP, + + EEL_BC_UMINUS, + + EEL_BC_ASSIGN, + EEL_BC_ASSIGN_FAST, + EEL_BC_ASSIGN_FAST_FROMFP, + EEL_BC_ASSIGN_FROMFP, + EEL_BC_MOD, + EEL_BC_MOD_OP, + EEL_BC_SHR, + EEL_BC_SHL, + + EEL_BC_SQR, + EEL_BC_MIN, + EEL_BC_MAX, + EEL_BC_MIN_FP, + EEL_BC_MAX_FP, + EEL_BC_ABS, + EEL_BC_SIGN, + EEL_BC_INVSQRT, + + EEL_BC_FXCH, + EEL_BC_POP_FPSTACK, + + EEL_BC_FCALL, + EEL_BC_BOOLTOFP, + EEL_BC_FPTOBOOL, + EEL_BC_FPTOBOOL_REV, + + EEL_BC_CFUNC_1PDD, + EEL_BC_CFUNC_2PDD, + EEL_BC_CFUNC_2PDDS, + + EEL_BC_MEGABUF, + EEL_BC_GMEGABUF, + + EEL_BC_GENERIC1PARM, + EEL_BC_GENERIC2PARM, + EEL_BC_GENERIC3PARM, + EEL_BC_GENERIC1PARM_RETD, + EEL_BC_GENERIC2PARM_RETD, + EEL_BC_GENERIC2XPARM_RETD, + EEL_BC_GENERIC3PARM_RETD, + + EEL_BC_USERSTACK_PUSH, + EEL_BC_USERSTACK_POP, + EEL_BC_USERSTACK_POPFAST, + EEL_BC_USERSTACK_PEEK, + EEL_BC_USERSTACK_PEEK_INT, + EEL_BC_USERSTACK_PEEK_TOP, + EEL_BC_USERSTACK_EXCH, + + EEL_BC_DBG_GETSTACKPTR, + +}; + +#define BC_DECL(x) static const EEL_BC_TYPE GLUE_##x[] = { EEL_BC_##x }; +#define BC_DECL_JMP(x) static const EEL_BC_TYPE GLUE_##x[1 + sizeof(GLUE_JMP_TYPE) / sizeof(EEL_BC_TYPE)] = { EEL_BC_##x }; +BC_DECL_JMP(JMP_NC) +BC_DECL_JMP(JMP_IF_P1_Z) +BC_DECL_JMP(JMP_IF_P1_NZ) +BC_DECL(RET) +BC_DECL(FXCH) +BC_DECL(POP_FPSTACK) +#define GLUE_POP_FPSTACK_SIZE sizeof(EEL_BC_TYPE) +BC_DECL(PUSH_P1) +BC_DECL(PUSH_P1PTR_AS_VALUE) +BC_DECL(POP_FPSTACK_TOSTACK) +BC_DECL(POP_FPSTACK_TO_WTP) +BC_DECL(SET_P1_Z) +BC_DECL(SET_P1_NZ) +BC_DECL_JMP(LOOP_LOADCNT) + +BC_DECL_JMP(LOOP_END) + +#define GLUE_LOOP_BEGIN_SIZE 0 +#define GLUE_LOOP_BEGIN ((void*)"") +#define GLUE_LOOP_CLAMPCNT_SIZE 0 +#define GLUE_LOOP_CLAMPCNT ((void*)"") + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + BC_DECL(WHILE_SETUP) + #define GLUE_WHILE_SETUP_SIZE sizeof(GLUE_WHILE_SETUP) + BC_DECL_JMP(WHILE_END) +#else + #define GLUE_WHILE_SETUP_SIZE 0 + #define GLUE_WHILE_SETUP ((void *)"") + #define GLUE_WHILE_END_NOJUMP + BC_DECL(WHILE_END) +#endif + +BC_DECL(WHILE_BEGIN); +BC_DECL_JMP(WHILE_CHECK_RV) + +#define GLUE_MOV_PX_DIRECTVALUE_SIZE (sizeof(EEL_BC_TYPE) + sizeof(INT_PTR)) +#define GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE GLUE_MOV_PX_DIRECTVALUE_SIZE +static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) +{ + static const EEL_BC_TYPE tab[] = { + EEL_BC_MOV_FPTOP_DV, + EEL_BC_MOV_P1_DV, + EEL_BC_MOV_P2_DV, + EEL_BC_MOV_P3_DV, + }; + *(EEL_BC_TYPE *)b = tab[wv+1]; + *(INT_PTR *) ((char *)b + sizeof(EEL_BC_TYPE)) = v; +} + +#define GLUE_FUNC_ENTER_SIZE 0 +#define GLUE_FUNC_LEAVE_SIZE 0 +static const EEL_BC_TYPE GLUE_FUNC_ENTER[1]={-1}; +static const EEL_BC_TYPE GLUE_FUNC_LEAVE[1]={-1}; + +static int GLUE_RESET_WTP(unsigned char *out, void *ptr) +{ + BC_DECL(_RESET_WTP) + if (out) memcpy(out,&GLUE__RESET_WTP,sizeof(GLUE__RESET_WTP)); + if (out) *(void **) (out+sizeof(GLUE__RESET_WTP)) = ptr; + return sizeof(GLUE__RESET_WTP) + sizeof(void *); +} + +#define GLUE_POP_PX_SIZE sizeof(EEL_BC_TYPE) +static void GLUE_POP_PX(void *b, int wv) +{ + static const EEL_BC_TYPE tab[3] ={ + EEL_BC_POP_P1, + EEL_BC_POP_P2, + EEL_BC_POP_P3, + }; + *(EEL_BC_TYPE *)b = tab[wv]; +} + +#define GLUE_SET_PX_FROM_P1_SIZE sizeof(EEL_BC_TYPE) +static void GLUE_SET_PX_FROM_P1(void *b, int wv) +{ + static const unsigned int tab[3]={ + EEL_BC_NOP, + EEL_BC_SET_P2_FROM_P1, + EEL_BC_SET_P3_FROM_P1, + }; + *(EEL_BC_TYPE *)b = tab[wv]; +} + +#define GLUE_MOVE_STACK_SIZE (sizeof(EEL_BC_TYPE) + sizeof(int)) +static void GLUE_MOVE_STACK(void *b, int amt) +{ + *(EEL_BC_TYPE *)b = EEL_BC_MOVE_STACK; + *(int *)(((EEL_BC_TYPE *)b)+1) = amt; +} +#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) (sizeof(EEL_BC_TYPE) + sizeof(int)) +static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) +{ + *(EEL_BC_TYPE *)b = EEL_BC_STORE_P1_TO_STACK_AT_OFFS; + *(int *)(((EEL_BC_TYPE *)b)+1) = offs; +} + +#define GLUE_MOVE_PX_STACKPTR_SIZE sizeof(EEL_BC_TYPE) +static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) +{ + static const EEL_BC_TYPE tab[3] = { + EEL_BC_MOVE_STACKPTR_TO_P1, + EEL_BC_MOVE_STACKPTR_TO_P2, + EEL_BC_MOVE_STACKPTR_TO_P3 + }; + *(EEL_BC_TYPE *)b = tab[wv]; +} + + +static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) +{ + if (buf) + { + *(EEL_BC_TYPE *)buf = EEL_BC_POP_VALUE_TO_ADDR; + *(void **) (buf+sizeof(EEL_BC_TYPE)) = destptr; + } + return sizeof(EEL_BC_TYPE) + sizeof(void *); +} + +static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) +{ + if (buf) + { + *(EEL_BC_TYPE *)buf = EEL_BC_COPY_VALUE_AT_P1_TO_ADDR; + *(void **) (buf+sizeof(EEL_BC_TYPE)) = destptr; + } + return sizeof(EEL_BC_TYPE) + sizeof(void *); +} + + + + +static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) +{ + int mv=5; + char *p=(char*)_p; + p+=sizeof(EEL_BC_TYPE); + while (*(INT_PTR*)p && mv-- > 0) p++; + if (!mv) return (unsigned char *)p; + + *(INT_PTR *)p = newv; + return (unsigned char *) p + sizeof(INT_PTR) - sizeof(EEL_BC_TYPE); +} + +#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(EEL_BC_TYPE) +static void GLUE_SET_PX_FROM_WTP(void *b, int wv) +{ + static const EEL_BC_TYPE tab[3]={ + EEL_BC_SET_P1_FROM_WTP, + EEL_BC_SET_P2_FROM_WTP, + EEL_BC_SET_P3_FROM_WTP, + }; + *(EEL_BC_TYPE *)b = tab[wv]; +} + +static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) +{ + if (buf) + { + *(EEL_BC_TYPE *)buf = EEL_BC_POP_FPSTACK_TO_PTR; + *(void **) (buf+sizeof(EEL_BC_TYPE)) = destptr; + } + return sizeof(EEL_BC_TYPE) + sizeof(void *); +} + + #define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE sizeof(EEL_BC_TYPE) + static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) + { + static const EEL_BC_TYPE tab[3] = { + EEL_BC_PUSH_VAL_AT_P1_TO_FPSTACK, + EEL_BC_PUSH_VAL_AT_P2_TO_FPSTACK, + EEL_BC_PUSH_VAL_AT_P3_TO_FPSTACK, + }; + *(EEL_BC_TYPE *)b = tab[wv]; + } + +#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE) +static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) +{ + GLUE_SET_PX_FROM_WTP(buf,wv); + memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); +}; + +static unsigned char GLUE_POP_STACK_TO_FPSTACK[1] = { 0 }; // todo + +#define GLUE_INLINE_LOOPS + +// end of bytecode glue, now for stubbage + + +#define BC_DECL_OPCODESZ(n) (1 + ((n)*sizeof(INT_PTR))/sizeof(EEL_BC_TYPE)) +#define BC_DECLASM_N(x,y,n) static EEL_BC_TYPE nseel_asm_##x[1 + BC_DECL_OPCODESZ(n)]={BC_DECL_OPCODESZ(n),EEL_BC_##y }; +#define BC_DECLASM_N2(x,y,n) static EEL_BC_TYPE _asm_##x[1 + BC_DECL_OPCODESZ(n)]={BC_DECL_OPCODESZ(n),EEL_BC_##y }; +#define BC_DECLASM_N_EXPORT(x,y,n) EEL_BC_TYPE _asm_##x[1 + BC_DECL_OPCODESZ(n)]={BC_DECL_OPCODESZ(n),EEL_BC_##y }; +#define BC_DECLASM(x,y) BC_DECLASM_N(x,y,0) + +BC_DECLASM(band,NOP) +BC_DECLASM(bor,NOP) + +BC_DECLASM(bnot,BNOT) +BC_DECLASM(equal,EQUAL) +BC_DECLASM(equal_exact,EQUAL_EXACT) +BC_DECLASM(notequal_exact,NOTEQUAL_EXACT) +BC_DECLASM(notequal,NOTEQUAL) +BC_DECLASM(above,ABOVE) +BC_DECLASM(beloweq,BELOWEQ) + +BC_DECLASM(add,ADD) +BC_DECLASM(sub,SUB) +BC_DECLASM(mul,MUL) +BC_DECLASM(div,DIV) +BC_DECLASM(and,AND) +BC_DECLASM(or,OR) +BC_DECLASM(or0,OR0) +BC_DECLASM(xor,XOR) + +BC_DECLASM(add_op,ADD_OP) +BC_DECLASM(sub_op,SUB_OP) +BC_DECLASM(add_op_fast,ADD_OP_FAST) +BC_DECLASM(sub_op_fast,SUB_OP_FAST) +BC_DECLASM(mul_op,MUL_OP) +BC_DECLASM(div_op,DIV_OP) +BC_DECLASM(mul_op_fast,MUL_OP_FAST) +BC_DECLASM(div_op_fast,DIV_OP_FAST) +BC_DECLASM(and_op,AND_OP) +BC_DECLASM(or_op,OR_OP) +BC_DECLASM(xor_op,XOR_OP) + +BC_DECLASM(uminus,UMINUS) + +BC_DECLASM(assign,ASSIGN) +BC_DECLASM(assign_fast,ASSIGN_FAST) +BC_DECLASM(assign_fast_fromfp,ASSIGN_FAST_FROMFP) +BC_DECLASM(assign_fromfp,ASSIGN_FROMFP) +BC_DECLASM(mod,MOD) +BC_DECLASM(mod_op,MOD_OP) +BC_DECLASM(shr,SHR) +BC_DECLASM(shl,SHL) +BC_DECLASM(sqr,SQR) + +BC_DECLASM(min,MIN) +BC_DECLASM(max,MAX) +BC_DECLASM(min_fp,MIN_FP) +BC_DECLASM(max_fp,MAX_FP) +BC_DECLASM(abs,ABS) +BC_DECLASM(sign,SIGN) +BC_DECLASM(invsqrt,INVSQRT) +BC_DECLASM(dbg_getstackptr,DBG_GETSTACKPTR) + +BC_DECLASM(booltofp,BOOLTOFP) +BC_DECLASM(fptobool,FPTOBOOL) +BC_DECLASM(fptobool_rev,FPTOBOOL_REV) + +BC_DECLASM_N(stack_push,USERSTACK_PUSH,3) +BC_DECLASM_N(stack_pop,USERSTACK_POP,3) +BC_DECLASM_N(stack_pop_fast,USERSTACK_POPFAST,3) +BC_DECLASM_N(stack_peek,USERSTACK_PEEK,3) + +BC_DECLASM_N(stack_peek_int,USERSTACK_PEEK_INT,4) + +BC_DECLASM_N(stack_peek_top,USERSTACK_PEEK_TOP,1) +BC_DECLASM_N(stack_exch,USERSTACK_EXCH,1) + +BC_DECLASM_N(fcall,FCALL,1) + +BC_DECLASM_N(1pdd,CFUNC_1PDD,1) +BC_DECLASM_N(2pdd,CFUNC_2PDD,1) +BC_DECLASM_N(2pdds,CFUNC_2PDDS,1) + +BC_DECLASM_N2(megabuf,MEGABUF,0) +BC_DECLASM_N2(gmegabuf,GMEGABUF,2) + +BC_DECLASM_N_EXPORT(generic1parm,GENERIC1PARM,2) +BC_DECLASM_N_EXPORT(generic2parm,GENERIC2PARM,2) +BC_DECLASM_N_EXPORT(generic3parm,GENERIC3PARM,2) +BC_DECLASM_N_EXPORT(generic1parm_retd,GENERIC1PARM_RETD,2) +BC_DECLASM_N_EXPORT(generic2parm_retd,GENERIC2PARM_RETD,2) +BC_DECLASM_N_EXPORT(generic2xparm_retd,GENERIC2XPARM_RETD,3) +BC_DECLASM_N_EXPORT(generic3parm_retd,GENERIC3PARM_RETD,2) + + + +static void *GLUE_realAddress(void *fn, int *size) +{ + EEL_BC_TYPE *rd = (EEL_BC_TYPE *)fn; + *size = rd[0]*sizeof(EEL_BC_TYPE); + return rd+1; +} + +#define EEL_BC_STACKSIZE (65536) + +// todo: check for stack overflows! we could determine if this is possible at compile time. +#define EEL_BC_STACK_POP_SIZE 8 +#define EEL_BC_STACK_PUSH(type, val) (*(type *)(stackptr -= EEL_BC_STACK_POP_SIZE)) = (val) +#define EEL_BC_STACK_POP() (stackptr += EEL_BC_STACK_POP_SIZE) + +#define EEL_BC_TRUE ((EEL_F*)(INT_PTR)1) + + + + +static void GLUE_CALL_CODE(INT_PTR bp, INT_PTR cp, INT_PTR rt) +{ + char __stack[EEL_BC_STACKSIZE]; + char *iptr = (char*)cp; + char *stackptr=__stack + EEL_BC_STACKSIZE; + EEL_F *p1 = NULL, *p2 = NULL, *p3 = NULL, *wtp = (EEL_F*)bp; +#define fp_top (_fpstacktop[0]) +#define fp_top2 (_fpstacktop[-1]) +#define fp_push(x) *++_fpstacktop=(x) +#define fp_pop() (*_fpstacktop--) +#define fp_rewind(x) (_fpstacktop -= (x)) + + EEL_F fpstack[GLUE_MAX_FPSTACK_SIZE]; + EEL_F *_fpstacktop=fpstack-1; + for (;;) + { + EEL_BC_TYPE inst = *(EEL_BC_TYPE *)iptr; + iptr += sizeof(EEL_BC_TYPE); + switch (inst) + { + case EEL_BC_FXCH: + { + EEL_F a = fp_top; + fp_top=fp_top2; + fp_top2=a; + } + break; + case EEL_BC_POP_FPSTACK: fp_rewind(1); break; + case EEL_BC_NOP: break; + case EEL_BC_RET: + if (EEL_BC_STACK_POP() > __stack+EEL_BC_STACKSIZE) + { + return; + } + iptr = *(void **)(stackptr - EEL_BC_STACK_POP_SIZE); + break; + case EEL_BC_JMP_NC: + iptr += sizeof(GLUE_JMP_TYPE)+*(GLUE_JMP_TYPE *)iptr; + break; + case EEL_BC_JMP_IF_P1_Z: + iptr += p1 ? sizeof(GLUE_JMP_TYPE) : sizeof(GLUE_JMP_TYPE)+*(GLUE_JMP_TYPE *)iptr; + break; + case EEL_BC_JMP_IF_P1_NZ: + iptr += p1 ? sizeof(GLUE_JMP_TYPE)+*(GLUE_JMP_TYPE *)iptr : sizeof(GLUE_JMP_TYPE); + break; + case EEL_BC_MOV_FPTOP_DV: + fp_push(**(EEL_F **)iptr); + iptr += sizeof(void*); + break; + case EEL_BC_MOV_P1_DV: + p1 = *(void **)iptr; + iptr += sizeof(void*); + break; + case EEL_BC_MOV_P2_DV: + p2 = *(void **)iptr; + iptr += sizeof(void*); + break; + case EEL_BC_MOV_P3_DV: + p3 = *(void **)iptr; + iptr += sizeof(void*); + break; + case EEL_BC__RESET_WTP: + wtp = *(void **)iptr; + iptr += sizeof(void*); + break; + case EEL_BC_PUSH_P1: + EEL_BC_STACK_PUSH(void *, p1); + break; + case EEL_BC_PUSH_P1PTR_AS_VALUE: + EEL_BC_STACK_PUSH(EEL_F, *p1); + break; + case EEL_BC_POP_P1: + p1 = *(EEL_F **) stackptr; + EEL_BC_STACK_POP(); + break; + case EEL_BC_POP_P2: + p2 = *(EEL_F **) stackptr; + EEL_BC_STACK_POP(); + break; + case EEL_BC_POP_P3: + p3 = *(EEL_F **) stackptr; + EEL_BC_STACK_POP(); + break; + case EEL_BC_POP_VALUE_TO_ADDR: + **(EEL_F**)iptr = *(EEL_F *)stackptr; + EEL_BC_STACK_POP(); + iptr += sizeof(void*); + break; + case EEL_BC_MOVE_STACK: + stackptr += *(int *)iptr; + iptr += sizeof(int); + break; + case EEL_BC_STORE_P1_TO_STACK_AT_OFFS: + *(void **) (stackptr + *(int *)iptr) = p1; + iptr += sizeof(int); + break; + case EEL_BC_MOVE_STACKPTR_TO_P1: + p1 = (double *)stackptr; + break; + case EEL_BC_MOVE_STACKPTR_TO_P2: + p2 = (double *)stackptr; + break; + case EEL_BC_MOVE_STACKPTR_TO_P3: + p3 = (double *)stackptr; + break; + case EEL_BC_SET_P2_FROM_P1: + p2=p1; + break; + case EEL_BC_SET_P3_FROM_P1: + p3=p1; + break; + case EEL_BC_COPY_VALUE_AT_P1_TO_ADDR: + **(EEL_F **)iptr = *p1; + iptr += sizeof(void*); + break; + case EEL_BC_SET_P1_FROM_WTP: + p1 = wtp; + break; + case EEL_BC_SET_P2_FROM_WTP: + p2 = wtp; + break; + case EEL_BC_SET_P3_FROM_WTP: + p3 = wtp; + break; + case EEL_BC_POP_FPSTACK_TO_PTR: + **((EEL_F **)iptr) = fp_pop(); + iptr += sizeof(void *); + break; + case EEL_BC_POP_FPSTACK_TOSTACK: + EEL_BC_STACK_PUSH(EEL_F, fp_pop()); + break; + case EEL_BC_PUSH_VAL_AT_P1_TO_FPSTACK: + fp_push(*p1); + break; + case EEL_BC_PUSH_VAL_AT_P2_TO_FPSTACK: + fp_push(*p2); + break; + case EEL_BC_PUSH_VAL_AT_P3_TO_FPSTACK: + fp_push(*p3); + break; + case EEL_BC_POP_FPSTACK_TO_WTP: + *wtp++ = fp_pop(); + break; + case EEL_BC_SET_P1_Z: + p1=NULL; + break; + case EEL_BC_SET_P1_NZ: + p1 = EEL_BC_TRUE; + break; + + case EEL_BC_LOOP_LOADCNT: + if ((EEL_BC_STACK_PUSH(int, (int)fp_pop())) < 1) + { + EEL_BC_STACK_POP(); + iptr+= sizeof(GLUE_JMP_TYPE)+*(GLUE_JMP_TYPE *)iptr; + } + else + { + iptr += sizeof(GLUE_JMP_TYPE); +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + if ((*(int *)stackptr) > NSEEL_LOOPFUNC_SUPPORT_MAXLEN) (*(int *)stackptr) = NSEEL_LOOPFUNC_SUPPORT_MAXLEN; +#endif + EEL_BC_STACK_PUSH(void *, wtp); + } + break; + case EEL_BC_LOOP_END: + wtp = *(void **) (stackptr); + if (--(*(int *)(stackptr+EEL_BC_STACK_POP_SIZE)) <= 0) + { + stackptr += EEL_BC_STACK_POP_SIZE*2; + iptr += sizeof(GLUE_JMP_TYPE); + } + else + { + iptr += sizeof(GLUE_JMP_TYPE)+*(GLUE_JMP_TYPE *)iptr; // back to the start! + } + break; + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + case EEL_BC_WHILE_SETUP: + EEL_BC_STACK_PUSH(int,NSEEL_LOOPFUNC_SUPPORT_MAXLEN); + break; +#endif + case EEL_BC_WHILE_BEGIN: + EEL_BC_STACK_PUSH(void *, wtp); + break; + case EEL_BC_WHILE_END: + wtp = *(EEL_F **) stackptr; + EEL_BC_STACK_POP(); + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + if (--(*(int *)stackptr) <= 0) + { + EEL_BC_STACK_POP(); + iptr += sizeof(GLUE_JMP_TYPE)+*(GLUE_JMP_TYPE *)iptr; // endpt + } + else + { + iptr += sizeof(GLUE_JMP_TYPE); + } +#endif + break; + case EEL_BC_WHILE_CHECK_RV: + if (p1) + { + iptr += sizeof(GLUE_JMP_TYPE)+*(GLUE_JMP_TYPE *)iptr; // loop + } + else + { + // done +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + EEL_BC_STACK_POP(); +#endif + iptr += sizeof(GLUE_JMP_TYPE); + } + break; + case EEL_BC_BNOT: + p1 = p1 ? NULL : EEL_BC_TRUE; + break; + case EEL_BC_EQUAL: + p1 = fabs(fp_top - fp_top2) < NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; + fp_rewind(2); + break; + case EEL_BC_EQUAL_EXACT: + p1 = fp_top == fp_top2 ? EEL_BC_TRUE : NULL; + fp_rewind(2); + break; + case EEL_BC_NOTEQUAL: + p1 = fabs(fp_top - fp_top2) >= NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; + fp_rewind(2); + break; + case EEL_BC_NOTEQUAL_EXACT: + p1 = fp_top != fp_top2 ? EEL_BC_TRUE : NULL; + fp_rewind(2); + break; + case EEL_BC_ABOVE: + p1 = fp_top < fp_top2 ? EEL_BC_TRUE : NULL; + fp_rewind(2); + break; + case EEL_BC_BELOWEQ: + p1 = fp_top >= fp_top2 ? EEL_BC_TRUE : NULL; + fp_rewind(2); + break; + + case EEL_BC_ADD: + fp_top2 += fp_top; + fp_rewind(1); + break; + case EEL_BC_SUB: + fp_top2 -= fp_top; + fp_rewind(1); + break; + case EEL_BC_MUL: + fp_top2 *= fp_top; + fp_rewind(1); + break; + case EEL_BC_DIV: + fp_top2 /= fp_top; + fp_rewind(1); + break; + case EEL_BC_AND: + fp_top2 = (EEL_F) (((WDL_INT64)fp_top) & (WDL_INT64)(fp_top2)); + fp_rewind(1); + break; + case EEL_BC_OR: + fp_top2 = (EEL_F) (((WDL_INT64)fp_top) | (WDL_INT64)(fp_top2)); + fp_rewind(1); + break; + case EEL_BC_OR0: + fp_top = (EEL_F) ((WDL_INT64)(fp_top)); + break; + case EEL_BC_XOR: + fp_top2 = (EEL_F) (((WDL_INT64)fp_top) ^ (WDL_INT64)(fp_top2)); + fp_rewind(1); + break; + + case EEL_BC_ADD_OP: + *(p1 = p2) = denormal_filter_double2(*p2 + fp_pop()); + break; + case EEL_BC_SUB_OP: + *(p1 = p2) = denormal_filter_double2(*p2 - fp_pop()); + break; + case EEL_BC_ADD_OP_FAST: + *(p1 = p2) += fp_pop(); + break; + case EEL_BC_SUB_OP_FAST: + *(p1 = p2) -= fp_pop(); + break; + case EEL_BC_MUL_OP: + *(p1 = p2) = denormal_filter_double2(*p2 * fp_pop()); + break; + case EEL_BC_DIV_OP: + *(p1 = p2) = denormal_filter_double2(*p2 / fp_pop()); + break; + case EEL_BC_MUL_OP_FAST: + *(p1 = p2) *= fp_pop(); + break; + case EEL_BC_DIV_OP_FAST: + *(p1 = p2) /= fp_pop(); + break; + case EEL_BC_AND_OP: + p1 = p2; + *p2 = (EEL_F) (((WDL_INT64)*p2) & (WDL_INT64)fp_pop()); + break; + case EEL_BC_OR_OP: + p1 = p2; + *p2 = (EEL_F) (((WDL_INT64)*p2) | (WDL_INT64)fp_pop()); + break; + case EEL_BC_XOR_OP: + p1 = p2; + *p2 = (EEL_F) (((WDL_INT64)*p2) ^ (WDL_INT64)fp_pop()); + break; + case EEL_BC_UMINUS: + fp_top = -fp_top; + break; + case EEL_BC_ASSIGN: + *p2 = denormal_filter_double2(*p1); + p1 = p2; + break; + case EEL_BC_ASSIGN_FAST: + *p2 = *p1; + p1 = p2; + break; + case EEL_BC_ASSIGN_FAST_FROMFP: + *p2 = fp_pop(); + p1 = p2; + break; + case EEL_BC_ASSIGN_FROMFP: + *p2 = denormal_filter_double2(fp_pop()); + p1 = p2; + break; + case EEL_BC_MOD: + { + int a = (int) (fp_pop()); + fp_top = a ? (EEL_F) ((int)fp_top % a) : 0.0; + } + break; + case EEL_BC_MOD_OP: + { + int a = (int) (fp_pop()); + *p2 = a ? (EEL_F) ((int)*p2 % a) : 0.0; + p1=p2; + + } + break; + case EEL_BC_SHR: + fp_top2 = (EEL_F) (((int)fp_top2) >> (int)fp_top); + fp_rewind(1); + break; + case EEL_BC_SHL: + fp_top2 = (EEL_F) (((int)fp_top2) << (int)fp_top); + fp_rewind(1); + break; + case EEL_BC_SQR: + fp_top *= fp_top; + break; + case EEL_BC_MIN: + if (*p1 > *p2) p1 = p2; + break; + case EEL_BC_MAX: + if (*p1 < *p2) p1 = p2; + break; + case EEL_BC_MIN_FP: + { + EEL_F a=fp_pop(); + if (afp_top) fp_top=a; + } + break; + case EEL_BC_ABS: + fp_top = fabs(fp_top); + break; + case EEL_BC_SIGN: + if (fp_top<0.0) fp_top=-1.0; + else if (fp_top>0.0) fp_top=1.0; + break; + case EEL_BC_DBG_GETSTACKPTR: + fp_top = (int)(stackptr - __stack); + break; + case EEL_BC_INVSQRT: + { + float y = (float)fp_top; + int i = 0x5f3759df - ( (* (int *) &y) >> 1 ); + y = *(float *) &i; + fp_top = y * ( 1.5F - ( (fp_top * 0.5) * y * y ) ); + } + break; + case EEL_BC_FCALL: + { + char *newiptr = *(char **)iptr; + EEL_BC_STACK_PUSH(void *, (iptr += sizeof(void *))); + iptr = newiptr; + } + break; + case EEL_BC_BOOLTOFP: + fp_push(p1 ? 1.0 : 0.0); + break; + case EEL_BC_FPTOBOOL: + p1 = fabs(fp_pop()) >= NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; + break; + case EEL_BC_FPTOBOOL_REV: + p1 = fabs(fp_pop()) < NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; + break; + + case EEL_BC_CFUNC_1PDD: + { + double (*f)(double) = *(double (**)(double)) iptr; + fp_top = f(fp_top); + iptr += sizeof(void *); + } + break; + case EEL_BC_CFUNC_2PDD: + { + double (*f)(double,double) = *(double (**)(double,double))iptr; + fp_top2 = f(fp_top2,fp_top); + fp_rewind(1); + iptr += sizeof(void *); + } + break; + case EEL_BC_CFUNC_2PDDS: + { + double (*f)(double,double) = *(double (**)(double,double))iptr; + *p2 = f(*p2,fp_pop()); + p1 = p2; + iptr += sizeof(void *); + } + break; + + case EEL_BC_MEGABUF: + { + unsigned int idx=(unsigned int) (fp_pop() + NSEEL_CLOSEFACTOR); + EEL_F **f = (EEL_F **)rt,*f2; + p1 = (idx < NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK && (f2=f[idx/NSEEL_RAM_ITEMSPERBLOCK])) ? + (f2 + (idx&(NSEEL_RAM_ITEMSPERBLOCK-1))) : + __NSEEL_RAMAlloc((void*)rt,idx); + } + break; + case EEL_BC_GMEGABUF: + { + p1 = __NSEEL_RAMAllocGMEM(*(EEL_F ****)iptr,(int) (fp_pop() + NSEEL_CLOSEFACTOR)); + iptr += sizeof(void *)*2; // also includes ptr to __NSEEL_RAMAllocGMEM, which we ignore + } + break; + case EEL_BC_GENERIC1PARM: + { + EEL_F *(*f)(void *,EEL_F*) = *(EEL_F *(**)(void *, EEL_F *)) (iptr+sizeof(void *)); + p1 = f(*(void **)iptr,p1); + iptr += sizeof(void *)*2; + } + break; + case EEL_BC_GENERIC2PARM: + { + EEL_F *(*f)(void *,EEL_F*,EEL_F*) = *(EEL_F *(**)(void *, EEL_F *, EEL_F *)) (iptr+sizeof(void *)); + p1 = f(*(void **)iptr,p2, p1); + iptr += sizeof(void *)*2; + } + break; + case EEL_BC_GENERIC3PARM: + { + EEL_F *(*f)(void *,EEL_F*,EEL_F*,EEL_F*) = *(EEL_F *(**)(void *, EEL_F *, EEL_F *, EEL_F *)) (iptr+sizeof(void *)); + p1 = f(*(void **)iptr,p3, p2, p1); + iptr += sizeof(void *)*2; + } + break; + case EEL_BC_GENERIC1PARM_RETD: + { + EEL_F (*f)(void *,EEL_F*) = *(EEL_F (**)(void *, EEL_F *)) (iptr+sizeof(void *)); + fp_push(f(*(void **)iptr,p1)); + iptr += sizeof(void *)*2; + } + break; + case EEL_BC_GENERIC2PARM_RETD: + { + EEL_F (*f)(void *,EEL_F*,EEL_F*) = *(EEL_F (**)(void *, EEL_F *, EEL_F *)) (iptr+sizeof(void *)); + fp_push(f(*(void **)iptr,p2, p1)); + iptr += sizeof(void *)*2; + } + break; + case EEL_BC_GENERIC2XPARM_RETD: + { + EEL_F (*f)(void *,void *,EEL_F*,EEL_F*) = *(EEL_F (**)(void *, void *, EEL_F *, EEL_F *)) (iptr+2*sizeof(void *)); + fp_push(f(*(void **)iptr,((void **)iptr)[1],p2, p1)); + iptr += sizeof(void *)*3; + } + break; + case EEL_BC_GENERIC3PARM_RETD: + { + EEL_F (*f)(void *,EEL_F*,EEL_F*,EEL_F*) = *(EEL_F (**)(void *, EEL_F *, EEL_F *, EEL_F *)) (iptr+sizeof(void *)); + fp_push(f(*(void **)iptr,p3, p2, p1)); + iptr += sizeof(void *)*2; + } + break; + + case EEL_BC_USERSTACK_PUSH: + { + UINT_PTR *sptr = *(UINT_PTR **)iptr; + (*sptr) += 8; + (*sptr) &= *(UINT_PTR*)(iptr+sizeof(void *)); + (*sptr) |= *(UINT_PTR*)(iptr+2*sizeof(void *)); + *(EEL_F *)*sptr = *p1; + } + iptr += sizeof(void*)*3; + break; + case EEL_BC_USERSTACK_POP: + { + UINT_PTR *sptr = *(UINT_PTR **)iptr; + *p1 = *(EEL_F *)*sptr; + (*sptr) -= 8; + (*sptr) &= *(UINT_PTR*)(iptr+sizeof(void *)); + (*sptr) |= *(UINT_PTR*)(iptr+2*sizeof(void *)); + } + iptr += sizeof(void*)*3; + break; + case EEL_BC_USERSTACK_POPFAST: + { + UINT_PTR *sptr = *(UINT_PTR **)iptr; + p1 = (EEL_F *)*sptr; + (*sptr) -= 8; + (*sptr) &= *(UINT_PTR*)(iptr+sizeof(void *)); + (*sptr) |= *(UINT_PTR*)(iptr+2*sizeof(void *)); + } + iptr += sizeof(void*)*3; + break; + case EEL_BC_USERSTACK_PEEK: + { + UINT_PTR sptr = **(UINT_PTR **)iptr; + sptr -= sizeof(EEL_F) * (int)(fp_pop()); + sptr &= *(UINT_PTR*)(iptr+sizeof(void *)); + sptr |= *(UINT_PTR*)(iptr+2*sizeof(void *)); + p1 = (EEL_F *)sptr; + } + iptr += sizeof(void*)*3; + break; + case EEL_BC_USERSTACK_PEEK_INT: + { + UINT_PTR sptr = **(UINT_PTR **)iptr; + sptr -= *(UINT_PTR*)(iptr+sizeof(void*)); + sptr &= *(UINT_PTR*)(iptr+2*sizeof(void *)); + sptr |= *(UINT_PTR*)(iptr+3*sizeof(void *)); + p1 = (EEL_F *)sptr; + } + iptr += sizeof(void*)*4; + break; + case EEL_BC_USERSTACK_PEEK_TOP: + p1 = **(EEL_F ***)iptr; + iptr += sizeof(void*); + break; + case EEL_BC_USERSTACK_EXCH: + { + EEL_F *p=**(EEL_F ***)iptr; + EEL_F a=*p; + *p=*p1; + *p1=a; + } + iptr += sizeof(void*); + break; + } + } +#undef fp_top +#undef fp_top2 +#undef fp_pop +#undef fp_push +}; + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_port_new.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_port_new.h new file mode 100644 index 000000000..5aced73d9 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_port_new.h @@ -0,0 +1,969 @@ +#ifndef _EEL_GLUE_PORTABLE_H_ +#define _EEL_GLUE_PORTABLE_H_ + +/* slightly faster portable implementation, requires tail call elimination though bleh + */ + + +#define DECL_ASMFUNC(x) +#define GLUE_JMP_TYPE int +#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((GLUE_JMP_TYPE *)(endOfInstruction))[-1] = (offset)) + +#define GLUE_MAX_FPSTACK_SIZE 64 +#define BIF_FPSTACKUSE(x) (0) // fp stack is not used within functions +#define BIF_GETFPSTACKUSE(x) (1) + +#define BC_DECL(x) static const EEL_BC_TYPE GLUE_##x[] = { EEL_BC_##x }; +#define BC_DECL_JMP(x) static const EEL_BC_TYPE GLUE_##x[1 + sizeof(GLUE_JMP_TYPE) / sizeof(EEL_BC_TYPE)] = { EEL_BC_##x }; +#define GLUE_POP_FPSTACK_SIZE sizeof(EEL_BC_TYPE) + + +#define GLUE_LOOP_BEGIN_SIZE 0 +#define GLUE_LOOP_BEGIN ((void*)"") +#define GLUE_LOOP_CLAMPCNT_SIZE 0 +#define GLUE_LOOP_CLAMPCNT ((void*)"") + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + #define GLUE_WHILE_SETUP_SIZE sizeof(GLUE_WHILE_SETUP) +#else + #define GLUE_WHILE_SETUP_SIZE 0 + #define GLUE_WHILE_SETUP ((void *)"") + #define GLUE_WHILE_END_NOJUMP +#endif + + +#define EEL_BC_STACKSIZE (65536) + +// todo: check for stack overflows! we could determine if this is possible at compile time. +#define EEL_BC_STACK_POP_SIZE 8 +#define EEL_BC_STACK_PUSH(type, val) (*(type *)(stackptr -= EEL_BC_STACK_POP_SIZE)) = (val) +#define EEL_BC_STACK_POP() (stackptr += EEL_BC_STACK_POP_SIZE) + +#define EEL_BC_TRUE ((EEL_F*)(INT_PTR)1) + + +typedef struct { + char *stackptr; + EEL_F *_fpstacktop; + EEL_F *p1, *p2, *p3, *wtp; + EEL_F fpstack[GLUE_MAX_FPSTACK_SIZE]; + char __stack[EEL_BC_STACKSIZE]; +} EEL_BC_STATE; + + +#define GLUE_MEM_NEEDS_PPROC +static void GLUE_CALL_CODE(INT_PTR bp, INT_PTR cp, INT_PTR rt) +{ + EEL_BC_STATE state; + EEL_BC_TYPE *c = (EEL_BC_TYPE *)cp; + state.stackptr=state.__stack + EEL_BC_STACKSIZE; + state._fpstacktop=state.fpstack-1; + state.p1 = NULL; + state.p2 = NULL; + state.p3 = NULL; + state.wtp = (EEL_F*)bp; + + c[0](c+1,&state); +} + +#define fp_top (((EEL_BC_STATE*)__state)->_fpstacktop[0]) +#define fp_top2 (((EEL_BC_STATE*)__state)->_fpstacktop[-1]) +#define fp_push(x) *++((EEL_BC_STATE*)__state)->_fpstacktop=(x) +#define fp_pop() (*((EEL_BC_STATE*)__state)->_fpstacktop--) +#define fp_rewind(x) (((EEL_BC_STATE*)__state)->_fpstacktop -= (x)) +#define __stack ((EEL_BC_STATE*)__state)->__stack +#define wtp ((EEL_BC_STATE*)__state)->wtp +#define p1 ((EEL_BC_STATE*)__state)->p1 +#define p2 ((EEL_BC_STATE*)__state)->p2 +#define p3 ((EEL_BC_STATE*)__state)->p3 +#define stackptr ((EEL_BC_STATE*)__state)->stackptr + +#define EEL_BC_BEGIN(x) static void x(void *inst, void *__state) { +#define EEL_BC_END_BYTEOFFS(x) inst = (char *)inst + (x); EEL_BC_END +#define EEL_BC_END ((EEL_BC_TYPE *)inst)[0]((char*)inst + sizeof(EEL_BC_TYPE),__state); } + EEL_BC_BEGIN(EEL_BC_FXCH) + EEL_F a = fp_top; + fp_top=fp_top2; + fp_top2=a; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_POP_FPSTACK) fp_rewind(1); EEL_BC_END + EEL_BC_BEGIN(EEL_BC_NOP) EEL_BC_END + EEL_BC_BEGIN(EEL_BC_RET) + if (EEL_BC_STACK_POP() > __stack+EEL_BC_STACKSIZE) + { + return; + } + inst = *(void **)(stackptr - EEL_BC_STACK_POP_SIZE); + EEL_BC_END + + EEL_BC_BEGIN(EEL_BC_JMP_NC) + EEL_BC_END_BYTEOFFS(sizeof(GLUE_JMP_TYPE) + *(GLUE_JMP_TYPE *)inst); + EEL_BC_BEGIN(EEL_BC_JMP_IF_P1_Z) + EEL_BC_END_BYTEOFFS(sizeof(GLUE_JMP_TYPE) + (p1 ? 0 : *(GLUE_JMP_TYPE *)inst)); + EEL_BC_BEGIN(EEL_BC_JMP_IF_P1_NZ) + EEL_BC_END_BYTEOFFS(sizeof(GLUE_JMP_TYPE) + (p1 ? *(GLUE_JMP_TYPE *)inst : 0)); + EEL_BC_BEGIN(EEL_BC_MOV_FPTOP_DV) + fp_push(**(EEL_F **)inst); + EEL_BC_END_BYTEOFFS(sizeof(void*)); + EEL_BC_BEGIN(EEL_BC_MOV_P1_DV) + p1 = *(void **)inst; + EEL_BC_END_BYTEOFFS(sizeof(void*)); + EEL_BC_BEGIN(EEL_BC_MOV_P2_DV) + p2 = *(void **)inst; + EEL_BC_END_BYTEOFFS(sizeof(void*)); + EEL_BC_BEGIN(EEL_BC_MOV_P3_DV) + p3 = *(void **)inst; + EEL_BC_END_BYTEOFFS(sizeof(void*)); + EEL_BC_BEGIN(EEL_BC__RESET_WTP) + wtp = *(void **)inst; + EEL_BC_END_BYTEOFFS(sizeof(void*)); + EEL_BC_BEGIN(EEL_BC_PUSH_P1) + EEL_BC_STACK_PUSH(void *, p1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_PUSH_P1PTR_AS_VALUE) + EEL_BC_STACK_PUSH(EEL_F, *p1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_POP_P1) + p1 = *(EEL_F **) stackptr; + EEL_BC_STACK_POP(); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_POP_P2) + p2 = *(EEL_F **) stackptr; + EEL_BC_STACK_POP(); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_POP_P3) + p3 = *(EEL_F **) stackptr; + EEL_BC_STACK_POP(); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_POP_VALUE_TO_ADDR) + **(EEL_F**)inst = *(EEL_F *)stackptr; + EEL_BC_STACK_POP(); + EEL_BC_END_BYTEOFFS(sizeof(void*)); + EEL_BC_BEGIN(EEL_BC_MOVE_STACK) + stackptr += *(int *)inst; + EEL_BC_END_BYTEOFFS(sizeof(int)); + EEL_BC_BEGIN(EEL_BC_STORE_P1_TO_STACK_AT_OFFS) + *(void **) (stackptr + *(int *)inst) = p1; + EEL_BC_END_BYTEOFFS(sizeof(int)); + EEL_BC_BEGIN(EEL_BC_MOVE_STACKPTR_TO_P1) + p1 = (double *)stackptr; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MOVE_STACKPTR_TO_P2) + p2 = (double *)stackptr; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MOVE_STACKPTR_TO_P3) + p3 = (double *)stackptr; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SET_P2_FROM_P1) + p2=p1; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SET_P3_FROM_P1) + p3=p1; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_COPY_VALUE_AT_P1_TO_ADDR) + **(EEL_F **)inst = *p1; + EEL_BC_END_BYTEOFFS(sizeof(void*)); + EEL_BC_BEGIN(EEL_BC_SET_P1_FROM_WTP) + p1 = wtp; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SET_P2_FROM_WTP) + p2 = wtp; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SET_P3_FROM_WTP) + p3 = wtp; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_POP_FPSTACK_TO_PTR) + **((EEL_F **)inst) = fp_pop(); + EEL_BC_END_BYTEOFFS(sizeof(void*)); + EEL_BC_BEGIN(EEL_BC_POP_FPSTACK_TOSTACK) + EEL_BC_STACK_PUSH(EEL_F, fp_pop()); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_PUSH_VAL_AT_P1_TO_FPSTACK) + fp_push(*p1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_PUSH_VAL_AT_P2_TO_FPSTACK) + fp_push(*p2); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_PUSH_VAL_AT_P3_TO_FPSTACK) + fp_push(*p3); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_POP_FPSTACK_TO_WTP) + *wtp++ = fp_pop(); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SET_P1_Z) + p1=NULL; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SET_P1_NZ) + p1 = EEL_BC_TRUE; + EEL_BC_END + + EEL_BC_BEGIN(EEL_BC_LOOP_LOADCNT) + int ret_offs = sizeof(GLUE_JMP_TYPE); + if ((EEL_BC_STACK_PUSH(int, (int)fp_pop())) < 1) + { + EEL_BC_STACK_POP(); + ret_offs += *(GLUE_JMP_TYPE *)inst; + } + else + { +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + if ((*(int *)stackptr) > NSEEL_LOOPFUNC_SUPPORT_MAXLEN) (*(int *)stackptr) = NSEEL_LOOPFUNC_SUPPORT_MAXLEN; +#endif + EEL_BC_STACK_PUSH(void *, wtp); + } + EEL_BC_END_BYTEOFFS(ret_offs); + EEL_BC_BEGIN(EEL_BC_LOOP_END) + int ret_offs = sizeof(GLUE_JMP_TYPE); + wtp = *(void **) (stackptr); + if (--(*(int *)(stackptr+EEL_BC_STACK_POP_SIZE)) <= 0) + { + stackptr += EEL_BC_STACK_POP_SIZE*2; + } + else + { + ret_offs += *(GLUE_JMP_TYPE *)inst; // back to the start! + } + EEL_BC_END_BYTEOFFS(ret_offs); + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + EEL_BC_BEGIN(EEL_BC_WHILE_SETUP) + EEL_BC_STACK_PUSH(int,NSEEL_LOOPFUNC_SUPPORT_MAXLEN); + EEL_BC_END +#endif + EEL_BC_BEGIN(EEL_BC_WHILE_BEGIN) + EEL_BC_STACK_PUSH(void *, wtp); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_WHILE_END) + int ret_offs = sizeof(GLUE_JMP_TYPE); + wtp = *(EEL_F **) stackptr; + EEL_BC_STACK_POP(); + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + if (--(*(int *)stackptr) <= 0) + { + EEL_BC_STACK_POP(); + ret_offs += *(GLUE_JMP_TYPE *)inst; // endpt + } +#endif + EEL_BC_END_BYTEOFFS(ret_offs); + EEL_BC_BEGIN(EEL_BC_WHILE_CHECK_RV) + int ret_offs = sizeof(GLUE_JMP_TYPE); + if (p1) + { + ret_offs += *(GLUE_JMP_TYPE *)inst; // loop + } + else + { + // done +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + EEL_BC_STACK_POP(); +#endif + } + EEL_BC_END_BYTEOFFS(ret_offs); + EEL_BC_BEGIN(EEL_BC_BNOT) + p1 = p1 ? NULL : EEL_BC_TRUE; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_BNOTNOT) + p1 = p1 ? EEL_BC_TRUE : NULL; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_EQUAL) + p1 = fabs(fp_top - fp_top2) < NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; + fp_rewind(2); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_EQUAL_EXACT) + p1 = fp_top == fp_top2 ? EEL_BC_TRUE : NULL; + fp_rewind(2); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_NOTEQUAL) + p1 = fabs(fp_top - fp_top2) >= NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; + fp_rewind(2); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_NOTEQUAL_EXACT) + p1 = fp_top != fp_top2 ? EEL_BC_TRUE : NULL; + fp_rewind(2); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_ABOVE) + p1 = fp_top < fp_top2 ? EEL_BC_TRUE : NULL; + fp_rewind(2); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_BELOWEQ) + p1 = fp_top >= fp_top2 ? EEL_BC_TRUE : NULL; + fp_rewind(2); + EEL_BC_END + + EEL_BC_BEGIN(EEL_BC_ADD) + fp_top2 += fp_top; + fp_rewind(1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SUB) + fp_top2 -= fp_top; + fp_rewind(1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MUL) + fp_top2 *= fp_top; + fp_rewind(1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_DIV) + fp_top2 /= fp_top; + fp_rewind(1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_AND) + fp_top2 = (EEL_F) (((int)fp_top) & (int)(fp_top2)); + fp_rewind(1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_OR) + fp_top2 = (EEL_F) (((int)fp_top) | (int)(fp_top2)); + fp_rewind(1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_OR0) + fp_top = (EEL_F) ((int)(fp_top)); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_XOR) + fp_top2 = (EEL_F) (((int)fp_top) ^ (int)(fp_top2)); + fp_rewind(1); + EEL_BC_END + + EEL_BC_BEGIN(EEL_BC_ADD_OP) + *(p1 = p2) = denormal_filter_double2(*p2 + fp_pop()); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SUB_OP) + *(p1 = p2) = denormal_filter_double2(*p2 - fp_pop()); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_ADD_OP_FAST) + *(p1 = p2) += fp_pop(); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SUB_OP_FAST) + *(p1 = p2) -= fp_pop(); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MUL_OP) + *(p1 = p2) = denormal_filter_double2(*p2 * fp_pop()); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_DIV_OP) + *(p1 = p2) = denormal_filter_double2(*p2 / fp_pop()); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MUL_OP_FAST) + *(p1 = p2) *= fp_pop(); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_DIV_OP_FAST) + *(p1 = p2) /= fp_pop(); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_AND_OP) + p1 = p2; + *p2 = (EEL_F) (((int)*p2) & (int)fp_pop()); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_OR_OP) + p1 = p2; + *p2 = (EEL_F) (((int)*p2) | (int)fp_pop()); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_XOR_OP) + p1 = p2; + *p2 = (EEL_F) (((int)*p2) ^ (int)fp_pop()); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_UMINUS) + fp_top = -fp_top; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_ASSIGN) + *p2 = denormal_filter_double2(*p1); + p1 = p2; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_ASSIGN_FAST) + *p2 = *p1; + p1 = p2; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_ASSIGN_FAST_FROMFP) + *p2 = fp_pop(); + p1 = p2; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_ASSIGN_FROMFP) + *p2 = denormal_filter_double2(fp_pop()); + p1 = p2; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MOD) + { + int a = (int) (fp_pop()); + fp_top = a ? (EEL_F) ((int)fp_top % a) : 0.0; + } + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MOD_OP) + { + int a = (int) (fp_pop()); + *p2 = a ? (EEL_F) ((int)*p2 % a) : 0.0; + p1=p2; + + } + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SHR) + fp_top2 = (EEL_F) (((int)fp_top2) >> (int)fp_top); + fp_rewind(1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SHL) + fp_top2 = (EEL_F) (((int)fp_top2) << (int)fp_top); + fp_rewind(1); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SQR) + fp_top *= fp_top; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MIN) + if (*p1 > *p2) p1 = p2; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MAX) + if (*p1 < *p2) p1 = p2; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_MIN_FP) + { + EEL_F a=fp_pop(); + if (afp_top) fp_top=a; + } + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_ABS) + fp_top = fabs(fp_top); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_SIGN) + if (fp_top<0.0) fp_top=-1.0; + else if (fp_top>0.0) fp_top=1.0; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_DBG_GETSTACKPTR) + fp_top = (int)(stackptr - __stack); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_INVSQRT) + { + float y = (float)fp_top; + int i = 0x5f3759df - ( (* (int *) &y) >> 1 ); + y = *(float *) &i; + fp_top = y * ( 1.5F - ( (fp_top * 0.5) * y * y ) ); + } + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_FCALL) + { + char *newiptr = *(char **)inst; + EEL_BC_STACK_PUSH(void *, ((char*)inst + sizeof(void *))); + inst = (EEL_BC_TYPE *)newiptr; + } + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_BOOLTOFP) + fp_push(p1 ? 1.0 : 0.0); + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_FPTOBOOL) + p1 = fabs(fp_pop()) >= NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; + EEL_BC_END + EEL_BC_BEGIN(EEL_BC_FPTOBOOL_REV) + p1 = fabs(fp_pop()) < NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; + EEL_BC_END + + EEL_BC_BEGIN(EEL_BC_CFUNC_1PDD) + { + double (*f)(double) = *(double (**)(double)) inst; + fp_top = f(fp_top); + } + EEL_BC_END_BYTEOFFS(sizeof(void *)); + EEL_BC_BEGIN(EEL_BC_CFUNC_2PDD) + { + double (*f)(double,double) = *(double (**)(double,double))inst; + fp_top2 = f(fp_top2,fp_top); + fp_rewind(1); + } + EEL_BC_END_BYTEOFFS(sizeof(void*)); + EEL_BC_BEGIN(EEL_BC_CFUNC_2PDDS) + { + double (*f)(double,double) = *(double (**)(double,double))inst; + *p2 = f(*p2,fp_pop()); + p1 = p2; + } + EEL_BC_END_BYTEOFFS(sizeof(void*)); + + EEL_BC_BEGIN(EEL_BC_MEGABUF) + { + unsigned int idx=(unsigned int) (fp_pop() + NSEEL_CLOSEFACTOR); + EEL_F **f = *(EEL_F ***)inst,*f2; + p1 = (idx < NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK && (f2=f[idx/NSEEL_RAM_ITEMSPERBLOCK])) ? + (f2 + (idx&(NSEEL_RAM_ITEMSPERBLOCK-1))) : + __NSEEL_RAMAlloc((void*)f,idx); + } + EEL_BC_END_BYTEOFFS(sizeof(void*)) + EEL_BC_BEGIN(EEL_BC_GMEGABUF) + { + p1 = __NSEEL_RAMAllocGMEM(*(EEL_F ****)inst,(int) (fp_pop() + NSEEL_CLOSEFACTOR)); + } + EEL_BC_END_BYTEOFFS(sizeof(void *)*2); + EEL_BC_BEGIN(EEL_BC_GENERIC1PARM) + { + EEL_F *(*f)(void *,EEL_F*) = *(EEL_F *(**)(void *, EEL_F *)) ((char*)inst+sizeof(void *)); + p1 = f(*(void **)inst,p1); + } + EEL_BC_END_BYTEOFFS(sizeof(void *)*2); + EEL_BC_BEGIN(EEL_BC_GENERIC2PARM) + { + EEL_F *(*f)(void *,EEL_F*,EEL_F*) = *(EEL_F *(**)(void *, EEL_F *, EEL_F *)) ((char*)inst+sizeof(void *)); + p1 = f(*(void **)inst,p2, p1); + } + EEL_BC_END_BYTEOFFS(sizeof(void *)*2); + EEL_BC_BEGIN(EEL_BC_GENERIC3PARM) + { + EEL_F *(*f)(void *,EEL_F*,EEL_F*,EEL_F*) = *(EEL_F *(**)(void *, EEL_F *, EEL_F *, EEL_F *)) ((char*)inst+sizeof(void *)); + p1 = f(*(void **)inst,p3, p2, p1); + } + EEL_BC_END_BYTEOFFS(sizeof(void *)*2); + EEL_BC_BEGIN(EEL_BC_GENERIC1PARM_RETD) + { + EEL_F (*f)(void *,EEL_F*) = *(EEL_F (**)(void *, EEL_F *)) ((char*)inst+sizeof(void *)); + fp_push(f(*(void **)inst,p1)); + } + EEL_BC_END_BYTEOFFS(sizeof(void *)*2); + EEL_BC_BEGIN(EEL_BC_GENERIC2PARM_RETD) + { + EEL_F (*f)(void *,EEL_F*,EEL_F*) = *(EEL_F (**)(void *, EEL_F *, EEL_F *)) ((char*)inst+sizeof(void *)); + fp_push(f(*(void **)inst,p2, p1)); + } + EEL_BC_END_BYTEOFFS(sizeof(void *)*2); + EEL_BC_BEGIN(EEL_BC_GENERIC3PARM_RETD) + { + EEL_F (*f)(void *,EEL_F*,EEL_F*,EEL_F*) = *(EEL_F (**)(void *, EEL_F *, EEL_F *, EEL_F *)) ((char*)inst+sizeof(void *)); + fp_push(f(*(void **)inst,p3, p2, p1)); + } + EEL_BC_END_BYTEOFFS(sizeof(void *)*2); + + EEL_BC_BEGIN(EEL_BC_USERSTACK_PUSH) + { + char *iptr = (char *)inst; + UINT_PTR *sptr = *(UINT_PTR **)iptr; + (*sptr) += 8; + (*sptr) &= *(UINT_PTR*)(iptr+sizeof(void *)); + (*sptr) |= *(UINT_PTR*)(iptr+2*sizeof(void *)); + *(EEL_F *)*sptr = *p1; + } + EEL_BC_END_BYTEOFFS(sizeof(void*)*3); + EEL_BC_BEGIN(EEL_BC_USERSTACK_POP) + { + char *iptr = (char *)inst; + UINT_PTR *sptr = *(UINT_PTR **)iptr; + *p1 = *(EEL_F *)*sptr; + (*sptr) -= 8; + (*sptr) &= *(UINT_PTR*)(iptr+sizeof(void *)); + (*sptr) |= *(UINT_PTR*)(iptr+2*sizeof(void *)); + } + EEL_BC_END_BYTEOFFS(sizeof(void*)*3); + EEL_BC_BEGIN(EEL_BC_USERSTACK_POPFAST) + { + char *iptr = (char *)inst; + UINT_PTR *sptr = *(UINT_PTR **)iptr; + p1 = (EEL_F *)*sptr; + (*sptr) -= 8; + (*sptr) &= *(UINT_PTR*)(iptr+sizeof(void *)); + (*sptr) |= *(UINT_PTR*)(iptr+2*sizeof(void *)); + } + EEL_BC_END_BYTEOFFS(sizeof(void*)*3); + EEL_BC_BEGIN(EEL_BC_USERSTACK_PEEK) + { + char *iptr = (char*)inst; + UINT_PTR sptr = **(UINT_PTR **)iptr; + sptr -= sizeof(EEL_F) * (int)(fp_pop()); + sptr &= *(UINT_PTR*)(iptr+sizeof(void *)); + sptr |= *(UINT_PTR*)(iptr+2*sizeof(void *)); + p1 = (EEL_F *)sptr; + } + EEL_BC_END_BYTEOFFS(sizeof(void*)*3); + EEL_BC_BEGIN(EEL_BC_USERSTACK_PEEK_INT) + { + char *iptr = (char*)inst; + UINT_PTR sptr = **(UINT_PTR **)iptr; + sptr -= *(UINT_PTR*)(iptr+sizeof(void*)); + sptr &= *(UINT_PTR*)(iptr+2*sizeof(void *)); + sptr |= *(UINT_PTR*)(iptr+3*sizeof(void *)); + p1 = (EEL_F *)sptr; + } + EEL_BC_END_BYTEOFFS(sizeof(void*)*4); + EEL_BC_BEGIN(EEL_BC_USERSTACK_PEEK_TOP) + p1 = **(EEL_F ***)inst; + EEL_BC_END_BYTEOFFS(sizeof(void *)); + EEL_BC_BEGIN(EEL_BC_USERSTACK_EXCH) + { + EEL_F *p=**(EEL_F ***)inst; + EEL_F a=*p; + *p=*p1; + *p1=a; + } + EEL_BC_END_BYTEOFFS(sizeof(void *)); + +#undef fp_top +#undef fp_top2 +#undef fp_pop +#undef fp_push +#undef iptr +#undef p1 +#undef p2 +#undef p3 +#undef wtp +#undef __stack +#undef stackptr + +#define BC_DECLASM(x,y) static EEL_BC_TYPE nseel_asm_##x[1]={EEL_BC_##y}; +#define EEL_BC_ENDOF(x) (((char*)(x))+sizeof(x)) + +#define BC_DECLASM_N(x,y,n) static EEL_BC_TYPE nseel_asm_##x[1 + (n*sizeof(INT_PTR))/sizeof(EEL_BC_TYPE)]={EEL_BC_##y, }; +#define BC_DECLASM_N2(x,y,n) static EEL_BC_TYPE _asm_##x[1 + (n*sizeof(INT_PTR))/sizeof(EEL_BC_TYPE)]={EEL_BC_##y, }; + +#define BC_DECLASM_N_EXPORT(x,y,n) EEL_BC_TYPE _asm_##x[1 + (n*sizeof(INT_PTR))/sizeof(EEL_BC_TYPE)]={EEL_BC_##y, }; const void *const _asm_##x##_end = EEL_BC_ENDOF(_asm_##x); + + +BC_DECLASM(band,NOP) +BC_DECLASM(bor,NOP) + +BC_DECLASM(bnot,BNOT) +BC_DECLASM(bnotnot,BNOTNOT) +BC_DECLASM(equal,EQUAL) +BC_DECLASM(equal_exact,EQUAL_EXACT) +BC_DECLASM(notequal_exact,NOTEQUAL_EXACT) +BC_DECLASM(notequal,NOTEQUAL) +BC_DECLASM(above,ABOVE) +BC_DECLASM(beloweq,BELOWEQ) + +BC_DECLASM(add,ADD) +BC_DECLASM(sub,SUB) +BC_DECLASM(mul,MUL) +BC_DECLASM(div,DIV) +BC_DECLASM(and,AND) +BC_DECLASM(or,OR) +BC_DECLASM(or0,OR0) +BC_DECLASM(xor,XOR) + +BC_DECLASM(add_op,ADD_OP) +BC_DECLASM(sub_op,SUB_OP) +BC_DECLASM(add_op_fast,ADD_OP_FAST) +BC_DECLASM(sub_op_fast,SUB_OP_FAST) +BC_DECLASM(mul_op,MUL_OP) +BC_DECLASM(div_op,DIV_OP) +BC_DECLASM(mul_op_fast,MUL_OP_FAST) +BC_DECLASM(div_op_fast,DIV_OP_FAST) +BC_DECLASM(and_op,AND_OP) +BC_DECLASM(or_op,OR_OP) +BC_DECLASM(xor_op,XOR_OP) + +BC_DECLASM(uminus,UMINUS) + +BC_DECLASM(assign,ASSIGN) +BC_DECLASM(assign_fast,ASSIGN_FAST) +BC_DECLASM(assign_fast_fromfp,ASSIGN_FAST_FROMFP) +BC_DECLASM(assign_fromfp,ASSIGN_FROMFP) +BC_DECLASM(mod,MOD) +BC_DECLASM(mod_op,MOD_OP) +BC_DECLASM(shr,SHR) +BC_DECLASM(shl,SHL) +BC_DECLASM(sqr,SQR) + +BC_DECLASM(min,MIN) +BC_DECLASM(max,MAX) +BC_DECLASM(min_fp,MIN_FP) +BC_DECLASM(max_fp,MAX_FP) +BC_DECLASM(abs,ABS) +BC_DECLASM(sign,SIGN) +BC_DECLASM(invsqrt,INVSQRT) +BC_DECLASM(dbg_getstackptr,DBG_GETSTACKPTR) + +BC_DECLASM(booltofp,BOOLTOFP) +BC_DECLASM(fptobool,FPTOBOOL) +BC_DECLASM(fptobool_rev,FPTOBOOL_REV) + +BC_DECLASM_N(stack_push,USERSTACK_PUSH,3) +BC_DECLASM_N(stack_pop,USERSTACK_POP,3) +BC_DECLASM_N(stack_pop_fast,USERSTACK_POPFAST,3) +BC_DECLASM_N(stack_peek,USERSTACK_PEEK,3) + +BC_DECLASM_N(stack_peek_int,USERSTACK_PEEK_INT,4) + +BC_DECLASM_N(stack_peek_top,USERSTACK_PEEK_TOP,1) +BC_DECLASM_N(stack_exch,USERSTACK_EXCH,1) + +BC_DECLASM_N(fcall,FCALL,1) + +BC_DECLASM_N(1pdd,CFUNC_1PDD,1) +BC_DECLASM_N(2pdd,CFUNC_2PDD,1) +BC_DECLASM_N(2pdds,CFUNC_2PDDS,1) + +BC_DECLASM_N2(megabuf,MEGABUF,1) +BC_DECLASM_N2(gmegabuf,GMEGABUF,2) +#define _asm_megabuf_end EEL_BC_ENDOF(_asm_megabuf) +#define _asm_gmegabuf_end EEL_BC_ENDOF(_asm_gmegabuf) + +BC_DECLASM_N_EXPORT(generic1parm,GENERIC1PARM,2) +BC_DECLASM_N_EXPORT(generic2parm,GENERIC2PARM,2) +BC_DECLASM_N_EXPORT(generic3parm,GENERIC3PARM,2) +BC_DECLASM_N_EXPORT(generic1parm_retd,GENERIC1PARM_RETD,2) +BC_DECLASM_N_EXPORT(generic2parm_retd,GENERIC2PARM_RETD,2) +BC_DECLASM_N_EXPORT(generic3parm_retd,GENERIC3PARM_RETD,2) + + +#define _asm_generic1parm_end EEL_BC_ENDOF(_asm_generic1parm) +#define _asm_generic2parm_end EEL_BC_ENDOF(_asm_generic2parm) +#define _asm_generic3parm_end EEL_BC_ENDOF(_asm_generic3parm) +#define _asm_generic1parm_retd_end EEL_BC_ENDOF(_asm_generic1parm_retd) +#define _asm_generic2parm_retd_end EEL_BC_ENDOF(_asm_generic2parm_retd) +#define _asm_generic3parm_retd_end EEL_BC_ENDOF(_asm_generic3parm_retd) + +#define nseel_asm_1pdd_end EEL_BC_ENDOF(nseel_asm_1pdd) +#define nseel_asm_2pdd_end EEL_BC_ENDOF(nseel_asm_2pdd) +#define nseel_asm_2pdds_end EEL_BC_ENDOF(nseel_asm_2pdds) + +#define nseel_asm_fcall_end EEL_BC_ENDOF(nseel_asm_fcall) + +#define nseel_asm_band_end EEL_BC_ENDOF(nseel_asm_band) +#define nseel_asm_bor_end EEL_BC_ENDOF(nseel_asm_bor) +#define nseel_asm_bnot_end EEL_BC_ENDOF(nseel_asm_bnot) +#define nseel_asm_bnotnot_end EEL_BC_ENDOF(nseel_asm_bnotnot) +#define nseel_asm_equal_end EEL_BC_ENDOF(nseel_asm_equal) +#define nseel_asm_equal_exact_end EEL_BC_ENDOF(nseel_asm_equal_exact) +#define nseel_asm_notequal_end EEL_BC_ENDOF(nseel_asm_notequal) +#define nseel_asm_notequal_exact_end EEL_BC_ENDOF(nseel_asm_notequal_exact) +#define nseel_asm_above_end EEL_BC_ENDOF(nseel_asm_above) +#define nseel_asm_beloweq_end EEL_BC_ENDOF(nseel_asm_beloweq) + +#define nseel_asm_min_end EEL_BC_ENDOF(nseel_asm_min) +#define nseel_asm_max_end EEL_BC_ENDOF(nseel_asm_max) +#define nseel_asm_abs_end EEL_BC_ENDOF(nseel_asm_abs) +#define nseel_asm_min_fp_end EEL_BC_ENDOF(nseel_asm_min_fp) +#define nseel_asm_max_fp_end EEL_BC_ENDOF(nseel_asm_max_fp) +#define nseel_asm_sign_end EEL_BC_ENDOF(nseel_asm_sign) +#define nseel_asm_invsqrt_end EEL_BC_ENDOF(nseel_asm_invsqrt) +#define nseel_asm_dbg_getstackptr_end EEL_BC_ENDOF(nseel_asm_dbg_getstackptr) + + +#define nseel_asm_add_end EEL_BC_ENDOF(nseel_asm_add) +#define nseel_asm_sub_end EEL_BC_ENDOF(nseel_asm_sub) +#define nseel_asm_mul_end EEL_BC_ENDOF(nseel_asm_mul) +#define nseel_asm_div_end EEL_BC_ENDOF(nseel_asm_div) +#define nseel_asm_and_end EEL_BC_ENDOF(nseel_asm_and) +#define nseel_asm_or_end EEL_BC_ENDOF(nseel_asm_or) +#define nseel_asm_or0_end EEL_BC_ENDOF(nseel_asm_or0) +#define nseel_asm_xor_end EEL_BC_ENDOF(nseel_asm_xor) + +#define nseel_asm_add_op_end EEL_BC_ENDOF(nseel_asm_add_op) +#define nseel_asm_sub_op_end EEL_BC_ENDOF(nseel_asm_sub_op) +#define nseel_asm_add_op_fast_end EEL_BC_ENDOF(nseel_asm_add_op_fast) +#define nseel_asm_sub_op_fast_end EEL_BC_ENDOF(nseel_asm_sub_op_fast) +#define nseel_asm_mul_op_end EEL_BC_ENDOF(nseel_asm_mul_op) +#define nseel_asm_mul_op_fast_end EEL_BC_ENDOF(nseel_asm_mul_op_fast) +#define nseel_asm_div_op_end EEL_BC_ENDOF(nseel_asm_div_op) +#define nseel_asm_div_op_fast_end EEL_BC_ENDOF(nseel_asm_div_op_fast) +#define nseel_asm_and_op_end EEL_BC_ENDOF(nseel_asm_and_op) +#define nseel_asm_or_op_end EEL_BC_ENDOF(nseel_asm_or_op) +#define nseel_asm_xor_op_end EEL_BC_ENDOF(nseel_asm_xor_op) + +#define nseel_asm_uminus_end EEL_BC_ENDOF(nseel_asm_uminus) +#define nseel_asm_assign_end EEL_BC_ENDOF(nseel_asm_assign) +#define nseel_asm_assign_fast_end EEL_BC_ENDOF(nseel_asm_assign_fast) +#define nseel_asm_assign_fast_fromfp_end EEL_BC_ENDOF(nseel_asm_assign_fast_fromfp) +#define nseel_asm_assign_fromfp_end EEL_BC_ENDOF(nseel_asm_assign_fromfp) +#define nseel_asm_mod_end EEL_BC_ENDOF(nseel_asm_mod) +#define nseel_asm_mod_op_end EEL_BC_ENDOF(nseel_asm_mod_op) +#define nseel_asm_shr_end EEL_BC_ENDOF(nseel_asm_shr) +#define nseel_asm_shl_end EEL_BC_ENDOF(nseel_asm_shl) + +#define nseel_asm_sqr_end EEL_BC_ENDOF(nseel_asm_sqr) + + +#define nseel_asm_booltofp_end EEL_BC_ENDOF(nseel_asm_booltofp) +#define nseel_asm_fptobool_end EEL_BC_ENDOF(nseel_asm_fptobool) +#define nseel_asm_fptobool_rev_end EEL_BC_ENDOF(nseel_asm_fptobool_rev) + +#define nseel_asm_stack_push_end EEL_BC_ENDOF(nseel_asm_stack_push) +#define nseel_asm_stack_pop_end EEL_BC_ENDOF(nseel_asm_stack_pop) +#define nseel_asm_stack_pop_fast_end EEL_BC_ENDOF(nseel_asm_stack_pop_fast) +#define nseel_asm_stack_peek_end EEL_BC_ENDOF(nseel_asm_stack_peek) +#define nseel_asm_stack_peek_int_end EEL_BC_ENDOF(nseel_asm_stack_peek_int) +#define nseel_asm_stack_peek_top_end EEL_BC_ENDOF(nseel_asm_stack_peek_top) +#define nseel_asm_stack_exch_end EEL_BC_ENDOF(nseel_asm_stack_exch) + + +BC_DECL_JMP(JMP_NC) +BC_DECL_JMP(JMP_IF_P1_Z) +BC_DECL_JMP(JMP_IF_P1_NZ) +BC_DECL(RET) +BC_DECL(FXCH) +BC_DECL(POP_FPSTACK) +BC_DECL(PUSH_P1) +BC_DECL(PUSH_P1PTR_AS_VALUE) +BC_DECL(POP_FPSTACK_TOSTACK) +BC_DECL(POP_FPSTACK_TO_WTP) +BC_DECL(SET_P1_Z) +BC_DECL(SET_P1_NZ) +BC_DECL_JMP(LOOP_LOADCNT) + +BC_DECL_JMP(LOOP_END) + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 + BC_DECL(WHILE_SETUP) + BC_DECL_JMP(WHILE_END) +#else + BC_DECL(WHILE_END) +#endif + +BC_DECL(WHILE_BEGIN); +BC_DECL_JMP(WHILE_CHECK_RV) + + +#define GLUE_MOV_PX_DIRECTVALUE_SIZE (sizeof(EEL_BC_TYPE) + sizeof(INT_PTR)) +#define GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE GLUE_MOV_PX_DIRECTVALUE_SIZE +static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) +{ + static const EEL_BC_TYPE tab[] = { + EEL_BC_MOV_FPTOP_DV, + EEL_BC_MOV_P1_DV, + EEL_BC_MOV_P2_DV, + EEL_BC_MOV_P3_DV, + }; + *(EEL_BC_TYPE *)b = tab[wv+1]; + *(INT_PTR *) ((char *)b + sizeof(EEL_BC_TYPE)) = v; +} + +#define GLUE_FUNC_ENTER_SIZE 0 +#define GLUE_FUNC_LEAVE_SIZE 0 +static const EEL_BC_TYPE GLUE_FUNC_ENTER[1]={NULL}; +static const EEL_BC_TYPE GLUE_FUNC_LEAVE[1]={NULL}; + +static int GLUE_RESET_WTP(unsigned char *out, void *ptr) +{ + BC_DECL(_RESET_WTP) + if (out) memcpy(out,&GLUE__RESET_WTP,sizeof(GLUE__RESET_WTP)); + if (out) *(void **) (out+sizeof(GLUE__RESET_WTP)) = ptr; + return sizeof(GLUE__RESET_WTP) + sizeof(void *); +} + +#define GLUE_POP_PX_SIZE sizeof(EEL_BC_TYPE) +static void GLUE_POP_PX(void *b, int wv) +{ + static const EEL_BC_TYPE tab[3] ={ + EEL_BC_POP_P1, + EEL_BC_POP_P2, + EEL_BC_POP_P3, + }; + *(EEL_BC_TYPE *)b = tab[wv]; +} + +#define GLUE_SET_PX_FROM_P1_SIZE sizeof(EEL_BC_TYPE) +static void GLUE_SET_PX_FROM_P1(void *b, int wv) +{ + static const EEL_BC_TYPE tab[3]={ + EEL_BC_NOP, + EEL_BC_SET_P2_FROM_P1, + EEL_BC_SET_P3_FROM_P1, + }; + *(EEL_BC_TYPE *)b = tab[wv]; +} + +#define GLUE_MOVE_STACK_SIZE (sizeof(EEL_BC_TYPE) + sizeof(int)) +static void GLUE_MOVE_STACK(void *b, int amt) +{ + *(EEL_BC_TYPE *)b = EEL_BC_MOVE_STACK; + *(int *)(((EEL_BC_TYPE *)b)+1) = amt; +} +#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) (sizeof(EEL_BC_TYPE) + sizeof(int)) +static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) +{ + *(EEL_BC_TYPE *)b = EEL_BC_STORE_P1_TO_STACK_AT_OFFS; + *(int *)(((EEL_BC_TYPE *)b)+1) = offs; +} + +#define GLUE_MOVE_PX_STACKPTR_SIZE sizeof(EEL_BC_TYPE) +static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) +{ + static const EEL_BC_TYPE tab[3] = { + EEL_BC_MOVE_STACKPTR_TO_P1, + EEL_BC_MOVE_STACKPTR_TO_P2, + EEL_BC_MOVE_STACKPTR_TO_P3 + }; + *(EEL_BC_TYPE *)b = tab[wv]; +} + + +static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) +{ + if (buf) + { + *(EEL_BC_TYPE *)buf = EEL_BC_POP_VALUE_TO_ADDR; + *(void **) (buf+sizeof(EEL_BC_TYPE)) = destptr; + } + return sizeof(EEL_BC_TYPE) + sizeof(void *); +} + +static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) +{ + if (buf) + { + *(EEL_BC_TYPE *)buf = EEL_BC_COPY_VALUE_AT_P1_TO_ADDR; + *(void **) (buf+sizeof(EEL_BC_TYPE)) = destptr; + } + return sizeof(EEL_BC_TYPE) + sizeof(void *); +} + + + + +static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) +{ + int mv=5; + char *p=(char*)_p; + p+=sizeof(EEL_BC_TYPE); + while (*(INT_PTR*)p && mv-- > 0) p++; + if (!mv) return (unsigned char *)p; + + *(INT_PTR *)p = newv; + return (unsigned char *) p + sizeof(INT_PTR) - sizeof(EEL_BC_TYPE); +} + +#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(EEL_BC_TYPE) +static void GLUE_SET_PX_FROM_WTP(void *b, int wv) +{ + static const EEL_BC_TYPE tab[3]={ + EEL_BC_SET_P1_FROM_WTP, + EEL_BC_SET_P2_FROM_WTP, + EEL_BC_SET_P3_FROM_WTP, + }; + *(EEL_BC_TYPE *)b = tab[wv]; +} + +static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) +{ + if (buf) + { + *(EEL_BC_TYPE *)buf = EEL_BC_POP_FPSTACK_TO_PTR; + *(void **) (buf+sizeof(EEL_BC_TYPE)) = destptr; + } + return sizeof(EEL_BC_TYPE) + sizeof(void *); +} + + #define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE sizeof(EEL_BC_TYPE) + static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) + { + static const EEL_BC_TYPE tab[3] = { + EEL_BC_PUSH_VAL_AT_P1_TO_FPSTACK, + EEL_BC_PUSH_VAL_AT_P2_TO_FPSTACK, + EEL_BC_PUSH_VAL_AT_P3_TO_FPSTACK, + }; + *(EEL_BC_TYPE *)b = tab[wv]; + } + +#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE) +static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) +{ + GLUE_SET_PX_FROM_WTP(buf,wv); + memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); +}; + +static unsigned char GLUE_POP_STACK_TO_FPSTACK[1] = { 0 }; // todo + +#define GLUE_INLINE_LOOPS + +// end of bytecode glue, now for stubbage + +static void *GLUE_realAddress(void *fn, void *fn_e, int *size) +{ + *size = (char *)fn_e - (char *)fn; + return fn; +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_ppc.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_ppc.h new file mode 100644 index 000000000..204959e68 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_ppc.h @@ -0,0 +1,285 @@ +#ifndef _NSEEL_GLUE_PPC_H_ +#define _NSEEL_GLUE_PPC_H_ + +#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support +#define GLUE_MAX_JMPSIZE 30000 // maximum relative jump size for this arch (if not defined, any jump is possible) + + +// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction. +// on PPC the offset needs to be from the start of the instruction (hence +4), and also the low two bits are flags so +// we make sure they are clear (they should always be clear, anyway, since we always generate 4 byte instructions) +#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((short *)(endOfInstruction))[-1] = ((offset) + 4) & 0xFFFC) + +static const unsigned char GLUE_JMP_NC[] = { 0x48,0, 0, 0, }; // b + +static const unsigned int GLUE_JMP_IF_P1_Z[]= +{ + 0x2f830000, //cmpwi cr7, r3, 0 + 0x419e0000, // beq cr7, offset-bytes-from-startofthisinstruction +}; +static const unsigned int GLUE_JMP_IF_P1_NZ[]= +{ + 0x2f830000, //cmpwi cr7, r3, 0 + 0x409e0000, // bne cr7, offset-bytes-from-startofthisinstruction +}; + + +#define GLUE_MOV_PX_DIRECTVALUE_SIZE 8 +static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) +{ + static const unsigned short tab[3][2] = { + {0x3C60, 0x6063}, // addis r3, r0, hw -- ori r3,r3, lw + {0x3DC0, 0x61CE}, // addis r14, r0, hw -- ori r14, r14, lw + {0x3DE0, 0x61EF}, // addis r15, r0, hw -- oris r15, r15, lw + }; + unsigned int uv=(unsigned int)v; + unsigned short *p=(unsigned short *)b; + + *p++ = tab[wv][0]; // addis rX, r0, hw + *p++ = (uv>>16)&0xffff; + *p++ = tab[wv][1]; // ori rX, rX, lw + *p++ = uv&0xffff; +} + + +// mflr r5 +// stwu r5, -16(r1) +const static unsigned int GLUE_FUNC_ENTER[2] = { 0x7CA802A6, 0x94A1FFF0 }; +#define GLUE_FUNC_ENTER_SIZE 8 + +// lwz r5, 0(r1) +// addi r1, r1, 16 +// mtlr r5 +const static unsigned int GLUE_FUNC_LEAVE[3] = { 0x80A10000, 0x38210010, 0x7CA803A6 }; +#define GLUE_FUNC_LEAVE_SIZE 12 + +const static unsigned int GLUE_RET[]={0x4E800020}; // blr + +static int GLUE_RESET_WTP(unsigned char *out, void *ptr) +{ + const static unsigned int GLUE_SET_WTP_FROM_R17=0x7E308B78; // mr r16 (dest), r17 (src) + if (out) memcpy(out,&GLUE_SET_WTP_FROM_R17,sizeof(GLUE_SET_WTP_FROM_R17)); + return sizeof(GLUE_SET_WTP_FROM_R17); + +} + + + +// stwu r3, -16(r1) +const static unsigned int GLUE_PUSH_P1[1]={ 0x9461FFF0}; + + +#define GLUE_POP_PX_SIZE 8 +static void GLUE_POP_PX(void *b, int wv) +{ + static const unsigned int tab[3] ={ + 0x80610000, // lwz r3, 0(r1) + 0x81c10000, // lwz r14, 0(r1) + 0x81e10000, // lwz r15, 0(r1) + }; + ((unsigned int *)b)[0] = tab[wv]; + ((unsigned int *)b)[1] = 0x38210010; // addi r1,r1, 16 +} + +#define GLUE_SET_PX_FROM_P1_SIZE 4 +static void GLUE_SET_PX_FROM_P1(void *b, int wv) +{ + static const unsigned int tab[3]={ + 0x7c631b78, // never used: mr r3, r3 + 0x7c6e1b78, // mr r14, r3 + 0x7c6f1b78, // mr r15, r3 + }; + *(unsigned int *)b = tab[wv]; +} + + + +// lfd f2, 0(r3) +// stfdu f2, -16(r1) +static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] = { 0xC8430000, 0xDC41FFF0 }; + +static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) +{ + // lfd f2, 0(r1) + // addi r1,r1,16 + // GLUE_MOV_PX_DIRECTVALUE_GEN / GLUE_MOV_PX_DIRECTVALUE_SIZE (r3) + // stfd f2, 0(r3) + if (buf) + { + unsigned int *bufptr = (unsigned int *)buf; + *bufptr++ = 0xC8410000; + *bufptr++ = 0x38210010; + GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); + bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; + *bufptr++ = 0xd8430000; + } + return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE + 4; +} + +static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) +{ + // lfd f2, 0(r3) + // GLUE_MOV_PX_DIRECTVALUE_GEN / GLUE_MOV_PX_DIRECTVALUE_SIZE (r3) + // stfd f2, 0(r3) + + if (buf) + { + unsigned int *bufptr = (unsigned int *)buf; + *bufptr++ = 0xc8430000; + GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); + bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; + *bufptr++ = 0xd8430000; + } + + return 4 + GLUE_MOV_PX_DIRECTVALUE_SIZE + 4; +} + + +static void GLUE_CALL_CODE(INT_PTR bp, INT_PTR cp, INT_PTR rt) +{ + static const double consttab[] = { + NSEEL_CLOSEFACTOR, + 4503601774854144.0 /* 0x43300000, 0x80000000, used for integer conversion*/, + }; + // we could have r18 refer to the current user-stack pointer, someday, perhaps + __asm__( + "subi r1, r1, 128\n" + "stfd f31, 8(r1)\n" + "stfd f30, 16(r1)\n" + "stmw r13, 32(r1)\n" + "mtctr %0\n" + "mr r17, %1\n" + "mr r13, %2\n" + "lfd f31, 0(%3)\n" + "lfd f30, 8(%3)\n" + "subi r17, r17, 8\n" + "mflr r0\n" + "stw r0, 24(r1)\n" + "bctrl\n" + "lwz r0, 24(r1)\n" + "mtlr r0\n" + "lmw r13, 32(r1)\n" + "lfd f31, 8(r1)\n" + "lfd f30, 16(r1)\n" + "addi r1, r1, 128\n" + ::"r" (cp), "r" (bp), "r" (rt), "r" (consttab)); +}; + +static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) +{ + // 64 bit ppc would take some work + unsigned int *p=(unsigned int *)_p; + while ((p[0]&0x0000FFFF) != 0x0000dead && + (p[1]&0x0000FFFF) != 0x0000beef) p++; + p[0] = (p[0]&0xFFFF0000) | (((newv)>>16)&0xFFFF); + p[1] = (p[1]&0xFFFF0000) | ((newv)&0xFFFF); + + return (unsigned char *)(p+1); +} + + #define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int) + static void GLUE_SET_PX_FROM_WTP(void *b, int wv) + { + static const unsigned int tab[3]={ + 0x7e038378, // mr r3, r16 + 0x7e0e8378, // mr r14, r16 + 0x7e0f8378, // mr r15, r16 + }; + *(unsigned int *)b = tab[wv]; + } + static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) + { + // set r3 to destptr + // stfd f1, 0(r3) + if (buf) + { + unsigned int *bufptr = (unsigned int *)buf; + GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); + bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; + + *bufptr++ = 0xD8230000; // stfd f1, 0(r3) + } + return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int); + } + #define GLUE_POP_FPSTACK_SIZE 0 + static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack + + static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = { + 0xdc21fff0, // stfdu f1, -16(r1) + }; + + static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = { + 0xdc300008, // stfdu f1, 8(r16) + }; + + #define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4 + static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) + { + static const unsigned int tab[3] = { + 0xC8230000, // lfd f1, 0(r3) + 0xC82E0000, // lfd f1, 0(r14) + 0xC82F0000, // lfd f1, 0(r15) + }; + *(unsigned int *)b = tab[wv]; + } + +#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE) +static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) +{ + memcpy(buf,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); + GLUE_SET_PX_FROM_WTP(buf + sizeof(GLUE_POP_FPSTACK_TO_WTP),wv); // ppc preincs the WTP, so we do this after +}; + +static unsigned int GLUE_POP_STACK_TO_FPSTACK[1] = { 0 }; // todo + + +static const unsigned int GLUE_SET_P1_Z[] = { 0x38600000 }; // li r3, 0 +static const unsigned int GLUE_SET_P1_NZ[] = { 0x38600001 }; // li r3, 1 + + +static void *GLUE_realAddress(void *fn, int *size) +{ + // magic numbers: mr r0,r0 ; mr r1,r1 ; mr r2, r2 + static const unsigned char sig[12] = { 0x7c, 0x00, 0x03, 0x78, 0x7c, 0x21, 0x0b, 0x78, 0x7c, 0x42, 0x13, 0x78 }; + unsigned char *p = (unsigned char *)fn; + + while (memcmp(p,sig,sizeof(sig))) p+=4; + p+=sizeof(sig); + fn = p; + + while (memcmp(p,sig,sizeof(sig))) p+=4; + *size = p - (unsigned char *)fn; + return fn; +} + + #define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) 4 + static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) + { + // limited to 32k offset + *(unsigned int *)b = 0x90610000 + (offs&0xffff); + } + + #define GLUE_MOVE_PX_STACKPTR_SIZE 4 + static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) + { + static const unsigned int tab[3] = + { + 0x7c230b78, // mr r3, r1 + 0x7c2e0b78, // mr r14, r1 + 0x7c2f0b78, // mr r15, r1 + }; + * (unsigned int *)b = tab[wv]; + } + + #define GLUE_MOVE_STACK_SIZE 4 + static void GLUE_MOVE_STACK(void *b, int amt) + { + // this should be updated to allow for more than 32k moves, but no real need + ((unsigned int *)b)[0] = 0x38210000 + (amt&0xffff); // addi r1,r1, amt + } + + + +// end of ppc + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_x86.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_x86.h new file mode 100644 index 000000000..d987e6afa --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_x86.h @@ -0,0 +1,678 @@ +#ifndef _NSEEL_GLUE_X86_H_ +#define _NSEEL_GLUE_X86_H_ + +#define GLUE_MAX_FPSTACK_SIZE 8 + +// endOfInstruction is end of jump with relative offset, offset is offset from end of instruction to jump to +#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (offset)) + +static const unsigned char GLUE_JMP_NC[] = { 0xE9, 0,0,0,0, }; // jmp +static const unsigned char GLUE_JMP_IF_P1_Z[] = {0x85, 0xC0, 0x0F, 0x84, 0,0,0,0 }; // test eax, eax, jz +static const unsigned char GLUE_JMP_IF_P1_NZ[] = {0x85, 0xC0, 0x0F, 0x85, 0,0,0,0 }; // test eax, eax, jnz + +#define GLUE_FUNC_ENTER_SIZE 0 +#define GLUE_FUNC_LEAVE_SIZE 0 +const static unsigned int GLUE_FUNC_ENTER[1]; +const static unsigned int GLUE_FUNC_LEAVE[1]; + + // x86 + // stack is 16 byte aligned + // when pushing values to stack, alignment pushed first, then value (value is at the lower address) + // when pushing pointers to stack, alignment pushed first, then pointer (pointer is at the lower address) + + static const unsigned char GLUE_PUSH_P1PTR_AS_VALUE[] = + { + 0x83, 0xEC, 8, /* sub esp, 8 */ + 0xff, 0x70, 0x4, /* push dword [eax+4] */ + 0xff, 0x30, /* push dword [eax] */ + }; + + static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) + { + if (buf) + { + *buf++ = 0xB8; *(void **) buf = destptr; buf+=4; // mov eax, directvalue + + *buf++ = 0x8f; *buf++ = 0x00; // pop dword [eax] + *buf++ = 0x8f; *buf++ = 0x40; *buf++ = 4; // pop dword [eax+4] + + *buf++ = 0x59; // pop ecx (alignment) + *buf++ = 0x59; // pop ecx (alignment) + } + + return 12; + } + + static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) + { + if (buf) + { + *buf++ = 0x8B; *buf++ = 0x38; // mov edi, [eax] + *buf++ = 0x8B; *buf++ = 0x48; *buf++ = 0x04; // mov ecx, [eax+4] + + + *buf++ = 0xB8; *(void **) buf = destptr; buf+=4; // mov eax, directvalue + *buf++ = 0x89; *buf++ = 0x38; // mov [eax], edi + *buf++ = 0x89; *buf++ = 0x48; *buf++ = 0x04; // mov [eax+4], ecx + } + + return 2 + 3 + 5 + 2 + 3; + } + + static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) + { + if (buf) + { + *buf++ = 0xB8; *(void **) buf = destptr; buf+=4; // mov eax, directvalue + *buf++ = 0xDD; *buf++ = 0x18; // fstp qword [eax] + } + return 1+4+2; + } + + + #define GLUE_MOV_PX_DIRECTVALUE_SIZE 5 + #define GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE 6 // length when wv == -1 + + static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) + { + if (wv==-1) + { + const static unsigned char t[2] = {0xDD, 0x05}; + memcpy(b,t,2); + b= ((unsigned char *)b)+2; + } + else + { + const static unsigned char tab[3] = { + 0xB8 /* mov eax, dv*/, + 0xBF /* mov edi, dv */ , + 0xB9 /* mov ecx, dv */ + }; + *((unsigned char *)b) = tab[wv]; // mov eax, dv + b= ((unsigned char *)b)+1; + } + *(INT_PTR *)b = v; + } + const static unsigned char GLUE_PUSH_P1[4]={0x83, 0xEC, 12, 0x50}; // sub esp, 12, push eax + + #define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) 7 + static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) + { + ((unsigned char *)b)[0] = 0x89; // mov [esp+offs], eax + ((unsigned char *)b)[1] = 0x84; + ((unsigned char *)b)[2] = 0x24; + *(int *)((unsigned char *)b+3) = offs; + } + + #define GLUE_MOVE_PX_STACKPTR_SIZE 2 + static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) + { + static const unsigned char tab[3][GLUE_MOVE_PX_STACKPTR_SIZE]= + { + { 0x89, 0xe0 }, // mov eax, esp + { 0x89, 0xe7 }, // mov edi, esp + { 0x89, 0xe1 }, // mov ecx, esp + }; + memcpy(b,tab[wv],GLUE_MOVE_PX_STACKPTR_SIZE); + } + + #define GLUE_MOVE_STACK_SIZE 6 + static void GLUE_MOVE_STACK(void *b, int amt) + { + ((unsigned char *)b)[0] = 0x81; + if (amt <0) + { + ((unsigned char *)b)[1] = 0xEC; + *(int *)((char*)b+2) = -amt; // sub esp, -amt + } + else + { + ((unsigned char *)b)[1] = 0xc4; + *(int *)((char*)b+2) = amt; // add esp, amt + } + } + + + #define GLUE_POP_PX_SIZE 4 + static void GLUE_POP_PX(void *b, int wv) + { + static const unsigned char tab[3][GLUE_POP_PX_SIZE]= + { + {0x58,/*pop eax*/ 0x83, 0xC4, 12 /* add esp, 12*/}, + {0x5F,/*pop edi*/ 0x83, 0xC4, 12}, + {0x59,/*pop ecx*/ 0x83, 0xC4, 12}, + }; + memcpy(b,tab[wv],GLUE_POP_PX_SIZE); + } + + #define GLUE_SET_PX_FROM_P1_SIZE 2 + static void GLUE_SET_PX_FROM_P1(void *b, int wv) + { + static const unsigned char tab[3][GLUE_SET_PX_FROM_P1_SIZE]={ + {0x90,0x90}, // should never be used! (nopnop) + {0x89,0xC7}, // mov edi, eax + {0x89,0xC1}, // mov ecx, eax + }; + memcpy(b,tab[wv],GLUE_SET_PX_FROM_P1_SIZE); + } + + #define GLUE_POP_FPSTACK_SIZE 2 + static const unsigned char GLUE_POP_FPSTACK[2] = { 0xDD, 0xD8 }; // fstp st0 + + static const unsigned char GLUE_POP_FPSTACK_TOSTACK[] = { + 0x83, 0xEC, 16, // sub esp, 16 + 0xDD, 0x1C, 0x24 // fstp qword (%esp) + }; + + static const unsigned char GLUE_POP_STACK_TO_FPSTACK[] = { + 0xDD, 0x04, 0x24, // fld qword (%esp) + 0x83, 0xC4, 16 // add esp, 16 + }; + + static const unsigned char GLUE_POP_FPSTACK_TO_WTP[] = { + 0xDD, 0x1E, /* fstp qword [esi] */ + 0x83, 0xC6, 8, /* add esi, 8 */ + }; + + #define GLUE_SET_PX_FROM_WTP_SIZE 2 + static void GLUE_SET_PX_FROM_WTP(void *b, int wv) + { + static const unsigned char tab[3][GLUE_SET_PX_FROM_WTP_SIZE]={ + {0x89,0xF0}, // mov eax, esi + {0x89,0xF7}, // mov edi, esi + {0x89,0xF1}, // mov ecx, esi + }; + memcpy(b,tab[wv],GLUE_SET_PX_FROM_WTP_SIZE); + } + + #define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 2 + static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) + { + static const unsigned char tab[3][GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE]={ + {0xDD,0x00}, // fld qword [eax] + {0xDD,0x07}, // fld qword [edi] + {0xDD,0x01}, // fld qword [ecx] + }; + memcpy(b,tab[wv],GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE); + } + +#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (GLUE_SET_PX_FROM_WTP_SIZE + sizeof(GLUE_POP_FPSTACK_TO_WTP)) +static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) +{ + GLUE_SET_PX_FROM_WTP(buf,wv); + memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); +}; + + +const static unsigned char GLUE_RET=0xC3; + +static int GLUE_RESET_WTP(unsigned char *out, void *ptr) +{ + if (out) + { + *out++ = 0xBE; // mov esi, constant + memcpy(out,&ptr,sizeof(void *)); + out+=sizeof(void *); + } + return 1+sizeof(void *); +} + + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4731) +#endif + +#define GLUE_TABPTR_IGNORED +#define GLUE_CALL_CODE(bp, cp, rt) do { \ + if (h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) eel_callcode32_fast(cp, rt); \ + else eel_callcode32(cp, rt);\ + } while(0) + +static void eel_callcode32(INT_PTR cp, INT_PTR ramptr) +{ + #ifndef NSEEL_EEL1_COMPAT_MODE + short oldsw, newsw; + #endif + #ifdef _MSC_VER + + __asm + { +#ifndef NSEEL_EEL1_COMPAT_MODE + fnstcw [oldsw] + mov ax, [oldsw] + or ax, 0x23F // 53 or 64 bit precision (depending on whether 0x100 is set), and masking all exceptions + mov [newsw], ax + fldcw [newsw] +#endif + + mov eax, cp + mov ebx, ramptr + + pushad + mov ebp, esp + and esp, -16 + + // on win32, which _MSC_VER implies, we keep things aligned to 16 bytes, and if we call a win32 function, + // the stack is 16 byte aligned before the call, meaning that if calling a function with no frame pointer, + // the stack would be aligned to a 16 byte boundary +4, which isn't good for performance. Having said that, + // normally we compile with frame pointers (which brings that to 16 byte + 8, which is fine), or ICC, which + // for nontrivial functions will align the stack itself (for very short functions, it appears to weigh the + // cost of aligning the stack vs that of the slower misaligned double accesses). + + // it may be worthwhile (at some point) to put some logic in the code that calls out to functions + // (generic1parm etc) to detect which alignment would be most optimal. + sub esp, 12 + call eax + mov esp, ebp + popad +#ifndef NSEEL_EEL1_COMPAT_MODE + fldcw [oldsw] +#endif + }; + + #else // gcc x86 + __asm__( +#ifndef NSEEL_EEL1_COMPAT_MODE + "fnstcw %2\n" + "movw %2, %%ax\n" + "orw $0x23F, %%ax\n" // 53 or 64 bit precision (depending on whether 0x100 is set), and masking all exceptions + "movw %%ax, %3\n" + "fldcw %3\n" +#endif + "pushl %%ebx\n" + "movl %%ecx, %%ebx\n" + "pushl %%ebp\n" + "movl %%esp, %%ebp\n" + "andl $-16, %%esp\n" // align stack to 16 bytes + "subl $12, %%esp\n" // call will push 4 bytes on stack, align for that + "call *%%edx\n" + "leave\n" + "popl %%ebx\n" +#ifndef NSEEL_EEL1_COMPAT_MODE + "fldcw %2\n" +#endif + :: + "d" (cp), "c" (ramptr) +#ifndef NSEEL_EEL1_COMPAT_MODE + , "m" (oldsw), "m" (newsw) +#endif + : "%eax","%esi","%edi"); + #endif //gcc x86 +} + +void eel_enterfp(int s[2]) +{ + #ifdef _MSC_VER + __asm + { + mov ecx, s + fnstcw [ecx] + mov ax, [ecx] + or ax, 0x23F // 53 or 64 bit precision (depending on whether 0x100 is set), and masking all exceptions + mov [ecx+4], ax + fldcw [ecx+4] + }; + #else + __asm__( + "fnstcw (%%ecx)\n" + "movw (%%ecx), %%ax\n" + "orw $0x23F, %%ax\n" // 53 or 64 bit precision (depending on whether 0x100 is set), and masking all exceptions + "movw %%ax, 4(%%ecx)\n" + "fldcw 4(%%ecx)\n" + :: "c" (s) : "%eax"); + #endif +} +void eel_leavefp(int s[2]) +{ + #ifdef _MSC_VER + __asm + { + mov ecx, s + fldcw [ecx] + }; + #else + __asm__( + "fldcw (%%ecx)\n" + :: "c" (s) : "%eax"); + #endif +} + +static void eel_callcode32_fast(INT_PTR cp, INT_PTR ramptr) +{ + #ifdef _MSC_VER + + __asm + { + mov eax, cp + mov ebx, ramptr + + pushad + mov ebp, esp + and esp, -16 + + // on win32, which _MSC_VER implies, we keep things aligned to 16 bytes, and if we call a win32 function, + // the stack is 16 byte aligned before the call, meaning that if calling a function with no frame pointer, + // the stack would be aligned to a 16 byte boundary +4, which isn't good for performance. Having said that, + // normally we compile with frame pointers (which brings that to 16 byte + 8, which is fine), or ICC, which + // for nontrivial functions will align the stack itself (for very short functions, it appears to weigh the + // cost of aligning the stack vs that of the slower misaligned double accesses). + + // it may be worthwhile (at some point) to put some logic in the code that calls out to functions + // (generic1parm etc) to detect which alignment would be most optimal. + sub esp, 12 + call eax + mov esp, ebp + popad + }; + + #else // gcc x86 + __asm__( + "pushl %%ebx\n" + "movl %%ecx, %%ebx\n" + "pushl %%ebp\n" + "movl %%esp, %%ebp\n" + "andl $-16, %%esp\n" // align stack to 16 bytes + "subl $12, %%esp\n" // call will push 4 bytes on stack, align for that + "call *%%edx\n" + "leave\n" + "popl %%ebx\n" + :: + "d" (cp), "c" (ramptr) + : "%eax","%esi","%edi"); + #endif //gcc x86 +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) +{ + char *p=(char*)_p; + INT_PTR scan = 0xFEFEFEFE; + while (*(INT_PTR *)p != scan) p++; + *(INT_PTR *)p = newv; + return (unsigned char *) (((INT_PTR*)p)+1); +} + +#define INT_TO_LECHARS(x) ((x)&0xff),(((x)>>8)&0xff), (((x)>>16)&0xff), (((x)>>24)&0xff) + + +#define GLUE_INLINE_LOOPS + +#define GLUE_LOOP_LOADCNT_SIZE (nseel_has_sse3() ? sizeof(GLUE_LOOP_LOADCNT_SSE3) : sizeof(GLUE_LOOP_LOADCNT_NOSSE3)) +#define GLUE_LOOP_LOADCNT (nseel_has_sse3() ? GLUE_LOOP_LOADCNT_SSE3 : GLUE_LOOP_LOADCNT_NOSSE3) +static const unsigned char GLUE_LOOP_LOADCNT_SSE3[]={ + 0xdb, 0x0e, // fisttp dword [esi] + 0x8B, 0x0E, // mov ecx, [esi] + 0x81, 0xf9, 1,0,0,0, // cmp ecx, 1 + 0x0F, 0x8C, 0,0,0,0, // JL +}; + +static const unsigned char GLUE_LOOP_LOADCNT_NOSSE3[]={ + 0xd9, 0x7e, 0x04, // fnstcw [esi+4] + 0x66, 0x8b, 0x46, 0x04, // mov ax, [esi+4] + 0x66, 0x0d, 0x00, 0x0c, // or ax, 0xC00 + 0x66, 0x89, 0x46, 0x08, // mov [esi+8], ax + 0xd9, 0x6e, 0x08, // fldcw [esi+8] + 0xDB, 0x1E, // fistp dword [esi] + 0xd9, 0x6e, 0x04, // fldcw [esi+4] + 0x8B, 0x0E, // mov ecx, [esi] + 0x81, 0xf9, 1,0,0,0, // cmp ecx, 1 + 0x0F, 0x8C, 0,0,0,0, // JL +}; + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 +#define GLUE_LOOP_CLAMPCNT_SIZE sizeof(GLUE_LOOP_CLAMPCNT) +static const unsigned char GLUE_LOOP_CLAMPCNT[]={ + 0x81, 0xf9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // cmp ecx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN + 0x0F, 0x8C, 5,0,0,0, // JL over-the-mov + 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // mov ecx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN +}; +#else + +#define GLUE_LOOP_CLAMPCNT_SIZE 0 +#define GLUE_LOOP_CLAMPCNT "" + +#endif + +#define GLUE_LOOP_BEGIN_SIZE sizeof(GLUE_LOOP_BEGIN) +static const unsigned char GLUE_LOOP_BEGIN[]={ + 0x56, //push esi + 0x51, // push ecx + 0x81, 0xEC, 0x08, 0,0,0, // sub esp, 8 +}; +static const unsigned char GLUE_LOOP_END[]={ + 0x81, 0xC4, 0x08, 0,0,0, // add esp, 8 + 0x59, //pop ecx + 0x5E, // pop esi + 0x49, // dec ecx + 0x0f, 0x85, 0,0,0,0, // jnz ... +}; + + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 +#define GLUE_WHILE_SETUP_SIZE sizeof(GLUE_WHILE_SETUP) +static const unsigned char GLUE_WHILE_SETUP[]={ + 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // mov ecx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN +}; +static const unsigned char GLUE_WHILE_BEGIN[]={ + 0x56, //push esi + 0x51, // push ecx + 0x81, 0xEC, 0x08, 0,0,0, // sub esp, 8 +}; +static const unsigned char GLUE_WHILE_END[]={ + 0x81, 0xC4, 0x08, 0,0,0, // add esp, 8 + 0x59, //pop ecx + 0x5E, // pop esi + + + 0x49, // dec ecx + 0x0f, 0x84, 0,0,0,0, // jz endpt +}; + + +#else + +#define GLUE_WHILE_SETUP_SIZE 0 +#define GLUE_WHILE_SETUP "" +#define GLUE_WHILE_END_NOJUMP +static const unsigned char GLUE_WHILE_BEGIN[]={ + 0x56, //push esi + 0x81, 0xEC, 12, 0,0,0, // sub esp, 12 +}; +static const unsigned char GLUE_WHILE_END[]={ + 0x81, 0xC4, 12, 0,0,0, // add esp, 12 + 0x5E, // pop esi +}; + +#endif + +static const unsigned char GLUE_WHILE_CHECK_RV[] = { + 0x85, 0xC0, // test eax, eax + 0x0F, 0x85, 0,0,0,0 // jnz looppt +}; + +static const unsigned char GLUE_SET_P1_Z[] = { 0x29, 0xC0 }; // sub eax, eax +static const unsigned char GLUE_SET_P1_NZ[] = { 0xb0, 0x01 }; // mov al, 1 + +static const unsigned char GLUE_FXCH[] = {0xd9, 0xc9}; + +#define GLUE_HAS_FLDZ +static const unsigned char GLUE_FLDZ[] = {0xd9, 0xee}; +#define GLUE_HAS_FLD1 +static const unsigned char GLUE_FLD1[] = {0xd9, 0xe8}; + +static EEL_F negativezeropointfive=-0.5f; +static EEL_F onepointfive=1.5f; +#define GLUE_INVSQRT_NEEDREPL &negativezeropointfive, &onepointfive, + + +#define GLUE_HAS_NATIVE_TRIGSQRTLOG + + +void nseel_asm_or(void); +void nseel_asm_or0(void); +void nseel_asm_or_op(void); +void nseel_asm_and(void); +void nseel_asm_and_op(void); +void nseel_asm_xor(void); +void nseel_asm_xor_op(void); +void nseel_asm_shl(void); +void nseel_asm_shr(void); +void nseel_asm_mod(void); +void nseel_asm_mod_op(void); +void nseel_asm_stack_peek(void); +void _asm_gmegabuf(void); +void _asm_megabuf(void); + +static struct roundinftab { + void *fn; + char istores; // number of fistp's + char flag; // 0=fistpll, 1=fistpl, 2=fistpl 4(%esp) + int newsz; + void *newfn; +} s_round_fixes[] = { + { nseel_asm_or, 2, }, + { nseel_asm_or_op, 2, }, + { nseel_asm_or0, 1, }, + { nseel_asm_and, 2, }, + { nseel_asm_and_op, 2, }, + { nseel_asm_xor, 2, }, + { nseel_asm_xor_op, 2, }, + { nseel_asm_shl, 2, 1, }, + { nseel_asm_shr, 2, 1, }, + { nseel_asm_mod, 2, 1, }, + { nseel_asm_mod_op, 2, 1, }, + { nseel_asm_stack_peek, 1, 1, }, + { _asm_megabuf, 1, 1, }, + { _asm_gmegabuf, 1, 2, }, +}; + +static void eel_fixup_sse3(unsigned char *p, unsigned char *endp, int np, int flag) +{ + const int isz = flag == 2 ? 3 : 2; + while (p+isz <= endp && np > 0) + { + if (flag == 0 && p[0] == 0xdf && (p[1]&0xbe) == 0x3e) + { + *p++ = 0xdd; + *p -= 0x30; + if (*p & 0x40) p++; + np--; + } + else if (flag == 1 && p[0] == 0xdb && (p[1]&0xbe) == 0x1e) + { + *++p -= 0x10; + if (*p & 0x40) p++; + np--; + } + else if (flag == 2 && p[0] == 0xdb && (p[1]&0xbf) == 0x1c && p[2] == 0x24) + { + *++p -= 0x10; + if (*p & 0x40) p++; + p++; + np--; + } + p++; + } + WDL_ASSERT(np == 0); +} + +static int nseel_has_sse3() +{ + static char c; + if (!c) + { + int features = 1; + #ifdef _MSC_VER + __asm { + mov eax, 1 + cpuid + mov [features], ecx + }; + #else + __asm__( + "movl $1, %%eax\n" + "pushl %%ebx\n" + "pushl %%edx\n" + "cpuid\n" + "popl %%edx\n" + "popl %%ebx\n" + : "=c" (features) : : "%eax"); + #endif + c=(features&1) ? 1 : -1; + } + return c>0; +} +static void *GLUE_realAddress(void *fn, int *size) +{ + static const unsigned char sig[12] = { 0x89, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; + unsigned char *p = (unsigned char *)fn; + + size_t rmatch; + const size_t nmatch = sizeof(s_round_fixes) / sizeof(s_round_fixes[0]); + for (rmatch = 0; rmatch < nmatch && s_round_fixes[rmatch].fn != fn; rmatch++); + if (rmatch < nmatch && s_round_fixes[rmatch].newfn && s_round_fixes[rmatch].newsz) + { + *size = s_round_fixes[rmatch].newsz; + return s_round_fixes[rmatch].newfn; + } + + #if defined(_DEBUG) && defined(_MSC_VER) + if (*p == 0xE9) // this means jump to the following address (debug stub) + { + p += 5 + *(int *)(p+1); + } + #endif + + while (memcmp(p,sig,sizeof(sig))) p++; + p+=sizeof(sig); + fn = p; + + while (memcmp(p,sig,sizeof(sig))) p++; + *size = p - (unsigned char *)fn; + + if (rmatch < nmatch) + { + static const unsigned char prefix[] = { + 0xd9, 0x7e, 0x10, // fnstcw [esi+16] + 0x66, 0x8b, 0x46, 0x10, // mov ax, [esi+16] + 0x66, 0x0d, 0x00, 0x0c, // or ax, 0xC00 + 0x66, 0x89, 0x46, 0x14, // mov [esi+20], ax + 0xd9, 0x6e, 0x14, // fldcw [esi+20] + }; + static const unsigned char postfix[] = { + 0xd9, 0x6e, 0x10, // fldcw [esi+16] + }; + + const int has_sse = nseel_has_sse3(); + + unsigned char *tmp = (unsigned char *) malloc(*size + (has_sse ? 0 : sizeof(prefix) + sizeof(postfix))); + if (tmp) + { + if (has_sse) + { + memcpy(tmp,fn,*size); + eel_fixup_sse3(tmp,tmp + *size,s_round_fixes[rmatch].istores, s_round_fixes[rmatch].flag); + } + else + { + memcpy(tmp,prefix,sizeof(prefix)); + memcpy(tmp+sizeof(prefix),fn,*size); + memcpy(tmp+sizeof(prefix)+*size,postfix,sizeof(postfix)); + + *size += sizeof(prefix) + sizeof(postfix); + } + fn = tmp; + s_round_fixes[rmatch].newsz = *size; + s_round_fixes[rmatch].newfn = fn; + } + } + + return fn; +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_x86_64.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_x86_64.h new file mode 100644 index 000000000..51c034c7f --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_x86_64.h @@ -0,0 +1,338 @@ +#ifndef _NSEEL_GLUE_X86_64_H_ +#define _NSEEL_GLUE_X86_64_H_ + + +// non-SSE version +// +#define GLUE_MAX_FPSTACK_SIZE 8 +#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (int) (offset)) + +#define GLUE_PREFER_NONFP_DV_ASSIGNS + +static const unsigned char GLUE_JMP_NC[] = { 0xE9, 0,0,0,0, }; // jmp +static const unsigned char GLUE_JMP_IF_P1_Z[] = {0x85, 0xC0, 0x0F, 0x84, 0,0,0,0 }; // test eax, eax, jz +static const unsigned char GLUE_JMP_IF_P1_NZ[] = {0x85, 0xC0, 0x0F, 0x85, 0,0,0,0 }; // test eax, eax, jnz + + +#define GLUE_FUNC_ENTER_SIZE 0 +#define GLUE_FUNC_LEAVE_SIZE 0 +const static unsigned int GLUE_FUNC_ENTER[1]; +const static unsigned int GLUE_FUNC_LEAVE[1]; + + // on x86-64: + // stack is always 16 byte aligned + // pushing values to the stack (for eel functions) has alignment pushed first, then value (value is at the lower address) + // pushing pointers to the stack has the pointer pushed first, then the alignment (pointer is at the higher address) + #define GLUE_MOV_PX_DIRECTVALUE_SIZE 10 + static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wr) { + const static unsigned short tab[3] = + { + 0xB848 /* mov rax, dv*/, + 0xBF48 /* mov rdi, dv */ , + 0xB948 /* mov rcx, dv */ + }; + unsigned short *bb = (unsigned short *)b; + *bb++ = tab[wr]; // mov rax, directvalue + *(INT_PTR *)bb = v; + } + + const static unsigned char GLUE_PUSH_P1[2]={ 0x50,0x50}; // push rax (pointer); push rax (alignment) + + #define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) 8 + static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) + { + ((unsigned char *)b)[0] = 0x48; // mov [rsp+offs], rax + ((unsigned char *)b)[1] = 0x89; + ((unsigned char *)b)[2] = 0x84; + ((unsigned char *)b)[3] = 0x24; + *(int *)((unsigned char *)b+4) = offs; + } + + #define GLUE_MOVE_PX_STACKPTR_SIZE 3 + static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) + { + static const unsigned char tab[3][GLUE_MOVE_PX_STACKPTR_SIZE]= + { + { 0x48, 0x89, 0xe0 }, // mov rax, rsp + { 0x48, 0x89, 0xe7 }, // mov rdi, rsp + { 0x48, 0x89, 0xe1 }, // mov rcx, rsp + }; + memcpy(b,tab[wv],GLUE_MOVE_PX_STACKPTR_SIZE); + } + + #define GLUE_MOVE_STACK_SIZE 7 + static void GLUE_MOVE_STACK(void *b, int amt) + { + ((unsigned char *)b)[0] = 0x48; + ((unsigned char *)b)[1] = 0x81; + if (amt < 0) + { + ((unsigned char *)b)[2] = 0xEC; + *(int *)((char*)b+3) = -amt; // sub rsp, -amt32 + } + else + { + ((unsigned char *)b)[2] = 0xc4; + *(int *)((char*)b+3) = amt; // add rsp, amt32 + } + } + + #define GLUE_POP_PX_SIZE 2 + static void GLUE_POP_PX(void *b, int wv) + { + static const unsigned char tab[3][GLUE_POP_PX_SIZE]= + { + {0x58,/*pop rax*/ 0x58}, // pop alignment, then pop pointer + {0x5F,/*pop rdi*/ 0x5F}, + {0x59,/*pop rcx*/ 0x59}, + }; + memcpy(b,tab[wv],GLUE_POP_PX_SIZE); + } + + static const unsigned char GLUE_PUSH_P1PTR_AS_VALUE[] = + { + 0x50, /*push rax - for alignment */ + 0xff, 0x30, /* push qword [rax] */ + }; + + static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) // trashes P2 (rdi) and P3 (rcx) + { + if (buf) + { + *buf++ = 0x48; *buf++ = 0xB9; *(void **) buf = destptr; buf+=8; // mov rcx, directvalue + *buf++ = 0x8f; *buf++ = 0x01; // pop qword [rcx] + *buf++ = 0x5F ; // pop rdi (alignment, safe to trash rdi though) + } + return 1+10+2; + } + + static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) // trashes P2/P3 + { + if (buf) + { + *buf++ = 0x48; *buf++ = 0xB9; *(void **) buf = destptr; buf+=8; // mov rcx, directvalue + *buf++ = 0x48; *buf++ = 0x8B; *buf++ = 0x38; // mov rdi, [rax] + *buf++ = 0x48; *buf++ = 0x89; *buf++ = 0x39; // mov [rcx], rdi + } + + return 3 + 10 + 3; + } + + static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) + { + if (buf) + { + *buf++ = 0x48; + *buf++ = 0xB8; + *(void **) buf = destptr; buf+=8; // mov rax, directvalue + *buf++ = 0xDD; *buf++ = 0x18; // fstp qword [rax] + } + return 2+8+2; + } + + + #define GLUE_SET_PX_FROM_P1_SIZE 3 + static void GLUE_SET_PX_FROM_P1(void *b, int wv) + { + static const unsigned char tab[3][GLUE_SET_PX_FROM_P1_SIZE]={ + {0x90,0x90,0x90}, // should never be used! (nopnop) + {0x48,0x89,0xC7}, // mov rdi, rax + {0x48,0x89,0xC1}, // mov rcx, rax + }; + memcpy(b,tab[wv],GLUE_SET_PX_FROM_P1_SIZE); + } + + + #define GLUE_POP_FPSTACK_SIZE 2 + static const unsigned char GLUE_POP_FPSTACK[2] = { 0xDD, 0xD8 }; // fstp st0 + + static const unsigned char GLUE_POP_FPSTACK_TOSTACK[] = { + 0x48, 0x81, 0xEC, 16, 0,0,0, // sub rsp, 16 + 0xDD, 0x1C, 0x24 // fstp qword (%rsp) + }; + + static const unsigned char GLUE_POP_FPSTACK_TO_WTP[] = { + 0xDD, 0x1E, /* fstp qword [rsi] */ + 0x48, 0x81, 0xC6, 8, 0,0,0,/* add rsi, 8 */ + }; + + #define GLUE_SET_PX_FROM_WTP_SIZE 3 + static void GLUE_SET_PX_FROM_WTP(void *b, int wv) + { + static const unsigned char tab[3][GLUE_SET_PX_FROM_WTP_SIZE]={ + {0x48, 0x89,0xF0}, // mov rax, rsi + {0x48, 0x89,0xF7}, // mov rdi, rsi + {0x48, 0x89,0xF1}, // mov rcx, rsi + }; + memcpy(b,tab[wv],GLUE_SET_PX_FROM_WTP_SIZE); + } + + #define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 2 + static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) + { + static const unsigned char tab[3][GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE]={ + {0xDD,0x00}, // fld qword [rax] + {0xDD,0x07}, // fld qword [rdi] + {0xDD,0x01}, // fld qword [rcx] + }; + memcpy(b,tab[wv],GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE); + } + static unsigned char GLUE_POP_STACK_TO_FPSTACK[] = { + 0xDD, 0x04, 0x24, // fld qword (%rsp) + 0x48, 0x81, 0xC4, 16, 0,0,0, // add rsp, 16 + }; + + +#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (GLUE_SET_PX_FROM_WTP_SIZE + sizeof(GLUE_POP_FPSTACK_TO_WTP)) +static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) +{ + GLUE_SET_PX_FROM_WTP(buf,wv); + memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); +}; + + +const static unsigned char GLUE_RET=0xC3; + +static int GLUE_RESET_WTP(unsigned char *out, void *ptr) +{ + if (out) + { + *out++ = 0x48; + *out++ = 0xBE; // mov rsi, constant64 + *(void **)out = ptr; + out+=sizeof(void *); + } + return 2+sizeof(void *); +} + +extern void eel_callcode64(INT_PTR code, INT_PTR ram_tab); +extern void eel_callcode64_fast(INT_PTR code, INT_PTR ram_tab); +#define GLUE_CALL_CODE(bp, cp, rt) do { \ + if (h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) eel_callcode64_fast(cp, rt); \ + else eel_callcode64(cp, rt);\ + } while(0) +#define GLUE_TABPTR_IGNORED + +static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) +{ + char *p=(char*)_p; + INT_PTR scan = 0xFEFEFEFEFEFEFEFE; + while (*(INT_PTR *)p != scan) p++; + *(INT_PTR *)p = newv; + return (unsigned char *) (((INT_PTR*)p)+1); +} + +#define INT_TO_LECHARS(x) ((x)&0xff),(((x)>>8)&0xff), (((x)>>16)&0xff), (((x)>>24)&0xff) + +#define GLUE_INLINE_LOOPS + +static const unsigned char GLUE_LOOP_LOADCNT[]={ + 0xDD, 0x0E, //fistTp qword [rsi] + 0x48, 0x8B, 0x0E, // mov rcx, [rsi] + 0x48, 0x81, 0xf9, 1,0,0,0, // cmp rcx, 1 + 0x0F, 0x8C, 0,0,0,0, // JL +}; + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 +#define GLUE_LOOP_CLAMPCNT_SIZE sizeof(GLUE_LOOP_CLAMPCNT) +static const unsigned char GLUE_LOOP_CLAMPCNT[]={ + 0x48, 0x81, 0xf9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // cmp rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN + 0x0F, 0x8C, 10,0,0,0, // JL over-the-mov + 0x48, 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), 0,0,0,0, // mov rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN +}; +#else +#define GLUE_LOOP_CLAMPCNT_SIZE 0 +#define GLUE_LOOP_CLAMPCNT "" +#endif + +#define GLUE_LOOP_BEGIN_SIZE sizeof(GLUE_LOOP_BEGIN) +static const unsigned char GLUE_LOOP_BEGIN[]={ + 0x56, //push rsi + 0x51, // push rcx +}; +static const unsigned char GLUE_LOOP_END[]={ + 0x59, //pop rcx + 0x5E, // pop rsi + 0xff, 0xc9, // dec rcx + 0x0f, 0x85, 0,0,0,0, // jnz ... +}; + + + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 +static const unsigned char GLUE_WHILE_SETUP[]={ + 0x48, 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), 0,0,0,0, // mov rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN +}; +#define GLUE_WHILE_SETUP_SIZE sizeof(GLUE_WHILE_SETUP) + +static const unsigned char GLUE_WHILE_BEGIN[]={ + 0x56, //push rsi + 0x51, // push rcx +}; +static const unsigned char GLUE_WHILE_END[]={ + 0x59, //pop rcx + 0x5E, // pop rsi + + 0xff, 0xc9, // dec rcx + 0x0f, 0x84, 0,0,0,0, // jz endpt +}; + + +#else +#define GLUE_WHILE_SETUP "" +#define GLUE_WHILE_SETUP_SIZE 0 +#define GLUE_WHILE_END_NOJUMP + +static const unsigned char GLUE_WHILE_BEGIN[]={ + 0x56, //push rsi + 0x51, // push rcx +}; +static const unsigned char GLUE_WHILE_END[]={ + 0x59, //pop rcx + 0x5E, // pop rsi +}; + +#endif + + +static const unsigned char GLUE_WHILE_CHECK_RV[] = { + 0x85, 0xC0, // test eax, eax + 0x0F, 0x85, 0,0,0,0 // jnz looppt +}; + +static const unsigned char GLUE_SET_P1_Z[] = { 0x48, 0x29, 0xC0 }; // sub rax, rax +static const unsigned char GLUE_SET_P1_NZ[] = { 0xb0, 0x01 }; // mov al, 1 + + +static const unsigned char GLUE_FXCH[] = {0xd9, 0xc9}; + +#define GLUE_HAS_FLDZ +static const unsigned char GLUE_FLDZ[] = {0xd9, 0xee}; +#define GLUE_HAS_FLD1 +static const unsigned char GLUE_FLD1[] = {0xd9, 0xe8}; + + +static EEL_F negativezeropointfive=-0.5f; +static EEL_F onepointfive=1.5f; +#define GLUE_INVSQRT_NEEDREPL &negativezeropointfive, &onepointfive, + +#define GLUE_HAS_NATIVE_TRIGSQRTLOG + + +static void *GLUE_realAddress(void *fn, void *fn_e, int *size) +{ + static const unsigned char sig[12] = { 0x89, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; + unsigned char *p = (unsigned char *)fn; + + while (memcmp(p,sig,sizeof(sig))) p++; + p+=sizeof(sig); + fn = p; + + while (memcmp(p,sig,sizeof(sig))) p++; + *size = (int) (p - (unsigned char *)fn); + return fn; +} + +// end of x86-64 + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_x86_64_sse.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_x86_64_sse.h new file mode 100644 index 000000000..c6780d1d8 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/glue_x86_64_sse.h @@ -0,0 +1,638 @@ +#ifndef _NSEEL_GLUE_X86_64_SSE_H_ +#define _NSEEL_GLUE_X86_64_SSE_H_ + +// SSE version (needs the appropriate .o linked!) + +#define GLUE_PREFER_NONFP_DV_ASSIGNS +#define GLUE_HAS_FPREG2 1 + +static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = { 0xc8100ff2 }; // movsd %xmm0,%xmm1 +static unsigned char GLUE_POP_STACK_TO_FPREG2[] = { + 0xf2, 0x0f, 0x10, 0x0c, 0x24, // movsd (%rsp), %xmm1 + 0x48, 0x81, 0xC4, 16, 0,0,0, // add rsp, 16 +}; + +// spill registers +#define GLUE_MAX_SPILL_REGS 4 +#ifdef _WIN32 + // win64: xmm6-xmm15 are nonvolatile, so we use xmm6-xmm9 as spill registers (xmm8/xmm9 are 5 byte encodings) + #define GLUE_SAVE_TO_SPILL_SIZE(x) (4 + ((x)>1)) + #define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4 + ((x)>1)) + + static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws) + { + if (ws < 2) + { + *(unsigned int *)b = 0xce100ff2 + (ws<<24); // movsd xmm1, xmm6+ws + } + else + { + // movsd xmm1, xmm8 + (ws-2) + *(unsigned int *)b = 0x100f41f2; + ((unsigned char *)b)[4] = 0xc8 + (ws-2); + } + } + static void GLUE_SAVE_TO_SPILL(void *b, int ws) + { + if (ws < 2) + { + *(unsigned int *)b = 0xf0100ff2 + (ws<<27); // movsd xmm6+ws, xmm0 + } + else + { + // movsd xmm8+(ws-2), xmm0 + *(unsigned int *)b = 0x100f44f2; + ((unsigned char *)b)[4] = 0xc0 + ((ws-2)<<3); + } + } +#else + // non-win32: our function stubs preserve xmm4-xmm7 + +#ifdef _DEBUG +#define GLUE_VALIDATE_SPILLS +#endif + +#ifdef GLUE_VALIDATE_SPILLS + +static unsigned char save_validate[]={ +0x48,0x83,0xec,0x10, // subq $16, %rsp +0xf2,0x0f,0x11,0x04,0x24, // movsd %xmm0, (%rsp) +0x66,0x48,0x0f,0x6e,0xe4, // movq %rsp, %xmm4 (+ws<<3) +}; + +static unsigned char restore_validate[] = { + 0xf2, 0x0f, 0x10, 0xcc, // movsd %xmm7, %xmm1 (+ws) + 0x66, 0x48, 0x0f, 0x6e, 0xdc, // movq %rsp, %xmm3 + 0x66, 0x0f, 0x2e, 0xd9, // ucomisd %xmm1, %xmm3 + 0x74, 0x02, // je 2 + 0xcd, 0x03, // int $3 + 0xf2, 0x0f, 0x10, 0x0c, 0x24, // movsd (%rsp), %xmm1 + 0x48, 0x83, 0xc4, 0x10, // addq $16, %rsp +}; + #define GLUE_SAVE_TO_SPILL_SIZE(x) (sizeof(save_validate)) + #define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (sizeof(restore_validate)) + +#else + + #define GLUE_SAVE_TO_SPILL_SIZE(x) (4) + #define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4) + +#endif + + static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws) + { +#ifdef GLUE_VALIDATE_SPILLS + char *p = (char*) b; + memcpy(p,restore_validate,sizeof(restore_validate)); + p[3] += ws; +#else + *(unsigned int *)b = 0xcc100ff2 + (ws<<24); // movsd xmm1, xmm4+ws +#endif + } + static void GLUE_SAVE_TO_SPILL(void *b, int ws) + { +#ifdef GLUE_VALIDATE_SPILLS + char *p = (char*) b; + memcpy(p,save_validate,sizeof(save_validate)); + p[sizeof(save_validate)-1] += ws<<3; +#else + *(unsigned int *)b = 0xe0100ff2 + (ws<<27); // movsd xmm4+ws, xmm0 +#endif + } +#endif + +#define GLUE_MAX_FPSTACK_SIZE 0 +#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (int) (offset)) + +static const unsigned char GLUE_JMP_NC[] = { 0xE9, 0,0,0,0, }; // jmp +static const unsigned char GLUE_JMP_IF_P1_Z[] = {0x85, 0xC0, 0x0F, 0x84, 0,0,0,0 }; // test eax, eax, jz +static const unsigned char GLUE_JMP_IF_P1_NZ[] = {0x85, 0xC0, 0x0F, 0x85, 0,0,0,0 }; // test eax, eax, jnz + + +#define GLUE_FUNC_ENTER_SIZE 0 +#define GLUE_FUNC_LEAVE_SIZE 0 +const static unsigned int GLUE_FUNC_ENTER[1]; +const static unsigned int GLUE_FUNC_LEAVE[1]; + + // on x86-64: + // stack is always 16 byte aligned + // pushing values to the stack (for eel functions) has alignment pushed first, then value (value is at the lower address) + // pushing pointers to the stack has the pointer pushed first, then the alignment (pointer is at the higher address) + #define GLUE_MOV_PX_DIRECTVALUE_SIZE 10 + #define GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE 14 // wr=-1, sets xmm0 + #define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 14 // wr=-2, sets xmm1 + static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wr) { + const static unsigned short tab[3] = + { + 0xB848 /* mov rax, dv*/, + 0xBF48 /* mov rdi, dv */ , + 0xB948 /* mov rcx, dv */ + }; + unsigned short *bb = (unsigned short *)b; + *bb++ = tab[wdl_max(wr,0)]; // mov rax, directvalue + *(INT_PTR *)bb = v; + if (wr == -2) *(unsigned int *)(bb + 4) = 0x08100ff2; // movsd (%rax), %xmm1 + else if (wr == -1) *(unsigned int *)(bb + 4) = 0x00100ff2; // movsd (%rax), %xmm0 + } + + const static unsigned char GLUE_PUSH_P1[2]={ 0x50,0x50}; // push rax (pointer); push rax (alignment) + + #define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) 8 + static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) + { + ((unsigned char *)b)[0] = 0x48; // mov [rsp+offs], rax + ((unsigned char *)b)[1] = 0x89; + ((unsigned char *)b)[2] = 0x84; + ((unsigned char *)b)[3] = 0x24; + *(int *)((unsigned char *)b+4) = offs; + } + + #define GLUE_MOVE_PX_STACKPTR_SIZE 3 + static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) + { + static const unsigned char tab[3][GLUE_MOVE_PX_STACKPTR_SIZE]= + { + { 0x48, 0x89, 0xe0 }, // mov rax, rsp + { 0x48, 0x89, 0xe7 }, // mov rdi, rsp + { 0x48, 0x89, 0xe1 }, // mov rcx, rsp + }; + memcpy(b,tab[wv],GLUE_MOVE_PX_STACKPTR_SIZE); + } + + #define GLUE_MOVE_STACK_SIZE 7 + static void GLUE_MOVE_STACK(void *b, int amt) + { + ((unsigned char *)b)[0] = 0x48; + ((unsigned char *)b)[1] = 0x81; + if (amt < 0) + { + ((unsigned char *)b)[2] = 0xEC; + *(int *)((char*)b+3) = -amt; // sub rsp, -amt32 + } + else + { + ((unsigned char *)b)[2] = 0xc4; + *(int *)((char*)b+3) = amt; // add rsp, amt32 + } + } + + #define GLUE_POP_PX_SIZE 2 + static void GLUE_POP_PX(void *b, int wv) + { + static const unsigned char tab[3][GLUE_POP_PX_SIZE]= + { + {0x58,/*pop rax*/ 0x58}, // pop alignment, then pop pointer + {0x5F,/*pop rdi*/ 0x5F}, + {0x59,/*pop rcx*/ 0x59}, + }; + memcpy(b,tab[wv],GLUE_POP_PX_SIZE); + } + + static const unsigned char GLUE_PUSH_P1PTR_AS_VALUE[] = + { + 0x50, /*push rax - for alignment */ + 0xff, 0x30, /* push qword [rax] */ + }; + + static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) // trashes P2 (rdi) and P3 (rcx) + { + if (buf) + { + *buf++ = 0x48; *buf++ = 0xB9; *(void **) buf = destptr; buf+=8; // mov rcx, directvalue + *buf++ = 0x8f; *buf++ = 0x01; // pop qword [rcx] + *buf++ = 0x5F ; // pop rdi (alignment, safe to trash rdi though) + } + return 1+10+2; + } + + static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) // trashes P2/P3 + { + if (buf) + { + *buf++ = 0x48; *buf++ = 0xB9; *(void **) buf = destptr; buf+=8; // mov rcx, directvalue + *buf++ = 0x48; *buf++ = 0x8B; *buf++ = 0x38; // mov rdi, [rax] + *buf++ = 0x48; *buf++ = 0x89; *buf++ = 0x39; // mov [rcx], rdi + } + + return 3 + 10 + 3; + } + + static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) + { + if (buf) + { + *buf++ = 0x48; + *buf++ = 0xB8; + *(void **) buf = destptr; buf+=8; // mov rax, directvalue + + *buf++ = 0xf2; // movsd %xmm0, (%rax) + *buf++ = 0x0f; + *buf++ = 0x11; + *buf++ = 0x00; + } + return 2+8+4; + } + + + #define GLUE_SET_PX_FROM_P1_SIZE 3 + static void GLUE_SET_PX_FROM_P1(void *b, int wv) + { + static const unsigned char tab[3][GLUE_SET_PX_FROM_P1_SIZE]={ + {0x90,0x90,0x90}, // should never be used! (nopnop) + {0x48,0x89,0xC7}, // mov rdi, rax + {0x48,0x89,0xC1}, // mov rcx, rax + }; + memcpy(b,tab[wv],GLUE_SET_PX_FROM_P1_SIZE); + } + + + #define GLUE_POP_FPSTACK_SIZE 0 + static const unsigned char GLUE_POP_FPSTACK[1] = { 0 }; + + static const unsigned char GLUE_POP_FPSTACK_TOSTACK[] = { + 0x48, 0x81, 0xEC, 16, 0,0,0, // sub rsp, 16 + 0xf2, 0x0f, 0x11, 0x04, 0x24, // movsd xmm0, (%rsp) + }; + + static const unsigned char GLUE_POP_FPSTACK_TO_WTP[] = { + 0xf2, 0x0f, 0x11, 0x06, // movsd xmm0, (%rsi) + 0x48, 0x81, 0xC6, 8, 0,0,0,/* add rsi, 8 */ + }; + + #define GLUE_SET_PX_FROM_WTP_SIZE 3 + static void GLUE_SET_PX_FROM_WTP(void *b, int wv) + { + static const unsigned char tab[3][GLUE_SET_PX_FROM_WTP_SIZE]={ + {0x48, 0x89,0xF0}, // mov rax, rsi + {0x48, 0x89,0xF7}, // mov rdi, rsi + {0x48, 0x89,0xF1}, // mov rcx, rsi + }; + memcpy(b,tab[wv],GLUE_SET_PX_FROM_WTP_SIZE); + } + + #define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4 + static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) + { + static const unsigned char tab[3][GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE]={ + {0xf2, 0x0f, 0x10, 0x00}, // movsd (%rax), %xmm0 + {0xf2, 0x0f, 0x10, 0x07}, // movsd (%rdi), %xmm0 + {0xf2, 0x0f, 0x10, 0x01}, // movsd (%rcx), %xmm0 + }; + memcpy(b,tab[wv],GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE); + } + +#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (GLUE_SET_PX_FROM_WTP_SIZE + sizeof(GLUE_POP_FPSTACK_TO_WTP)) +static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) +{ + GLUE_SET_PX_FROM_WTP(buf,wv); + memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); +}; + + +const static unsigned char GLUE_RET=0xC3; + +static int GLUE_RESET_WTP(unsigned char *out, void *ptr) +{ + if (out) + { + *out++ = 0x48; + *out++ = 0xBE; // mov rsi, constant64 + *(void **)out = ptr; + out+=sizeof(void *); + } + return 2+sizeof(void *); +} + +extern void eel_callcode64(INT_PTR code, INT_PTR ram_tab); +extern void eel_callcode64_fast(INT_PTR code, INT_PTR ram_tab); +#define GLUE_CALL_CODE(bp, cp, rt) do { \ + if (h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) eel_callcode64_fast(cp, rt); \ + else eel_callcode64(cp, rt);\ + } while(0) +#define GLUE_TABPTR_IGNORED + +static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) +{ + char *p=(char*)_p; + INT_PTR scan = 0xFEFEFEFEFEFEFEFE; + while (*(INT_PTR *)p != scan) p++; + *(INT_PTR *)p = newv; + return (unsigned char *) (((INT_PTR*)p)+1); +} + +#define INT_TO_LECHARS(x) ((x)&0xff),(((x)>>8)&0xff), (((x)>>16)&0xff), (((x)>>24)&0xff) + +#define GLUE_INLINE_LOOPS + +static const unsigned char GLUE_LOOP_LOADCNT[]={ + 0xf2, 0x48, 0x0f, 0x2c, 0xc8, // cvttsd2si %xmm0, %rcx + 0x48, 0x81, 0xf9, 1,0,0,0, // cmp rcx, 1 + 0x0F, 0x8C, 0,0,0,0, // JL +}; + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 +#define GLUE_LOOP_CLAMPCNT_SIZE sizeof(GLUE_LOOP_CLAMPCNT) +static const unsigned char GLUE_LOOP_CLAMPCNT[]={ + 0x48, 0x81, 0xf9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // cmp rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN + 0x0F, 0x8C, 10,0,0,0, // JL over-the-mov + 0x48, 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), 0,0,0,0, // mov rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN +}; +#else +#define GLUE_LOOP_CLAMPCNT_SIZE 0 +#define GLUE_LOOP_CLAMPCNT "" +#endif + +#define GLUE_LOOP_BEGIN_SIZE sizeof(GLUE_LOOP_BEGIN) +static const unsigned char GLUE_LOOP_BEGIN[]={ + 0x56, //push rsi + 0x51, // push rcx +}; +static const unsigned char GLUE_LOOP_END[]={ + 0x59, //pop rcx + 0x5E, // pop rsi + 0xff, 0xc9, // dec rcx + 0x0f, 0x85, 0,0,0,0, // jnz ... +}; + + + +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 +static const unsigned char GLUE_WHILE_SETUP[]={ + 0x48, 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), 0,0,0,0, // mov rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN +}; +#define GLUE_WHILE_SETUP_SIZE sizeof(GLUE_WHILE_SETUP) + +static const unsigned char GLUE_WHILE_BEGIN[]={ + 0x56, //push rsi + 0x51, // push rcx +}; +static const unsigned char GLUE_WHILE_END[]={ + 0x59, //pop rcx + 0x5E, // pop rsi + + 0xff, 0xc9, // dec rcx + 0x0f, 0x84, 0,0,0,0, // jz endpt +}; + + +#else +#define GLUE_WHILE_SETUP "" +#define GLUE_WHILE_SETUP_SIZE 0 +#define GLUE_WHILE_END_NOJUMP + +static const unsigned char GLUE_WHILE_BEGIN[]={ + 0x56, //push rsi + 0x51, // push rcx +}; +static const unsigned char GLUE_WHILE_END[]={ + 0x59, //pop rcx + 0x5E, // pop rsi +}; + +#endif + + +static const unsigned char GLUE_WHILE_CHECK_RV[] = { + 0x85, 0xC0, // test eax, eax + 0x0F, 0x85, 0,0,0,0 // jnz looppt +}; + +static const unsigned char GLUE_SET_P1_Z[] = { 0x48, 0x29, 0xC0 }; // sub rax, rax +static const unsigned char GLUE_SET_P1_NZ[] = { 0xb0, 0x01 }; // mov al, 1 + + +#define GLUE_HAS_FLDZ +static const unsigned char GLUE_FLDZ[] = { + 0x0f, 0x57, 0xc0 //xorps %xmm0, %xmm0 +}; + + +static EEL_F negativezeropointfive=-0.5f; +static EEL_F onepointfive=1.5f; +#define GLUE_INVSQRT_NEEDREPL &negativezeropointfive, &onepointfive, + + +static void *GLUE_realAddress(void *fn, int *size) +{ + static const unsigned char new_sig[8] = { 0x89, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00 }; + int sz = 0; + while (memcmp((char*)fn + sz,new_sig,sizeof(new_sig))) sz++; + *size = sz; + return fn; +} + +#define GLUE_HAS_FUSE 1 +static int GLUE_FUSE(compileContext *ctx, unsigned char *code, int left_size, int right_size, int fuse_flags, int spill_reg) +{ + const UINT_PTR base = (UINT_PTR) ctx->ram_state->blocks; + const int is_sse_op = right_size == 4 && // add/mul/sub/min/max + code[0] == 0xf2 && + code[1] == 0x0f && + code[3] == 0xc1 && // low nibble is xmm1 + (code[2] == 0x58 || code[2] == 0x59 || code[2] == 0x5c || code[2]==0x5d || code[2] == 0x5f); + + if (spill_reg >= 0) + { +#ifndef GLUE_VALIDATE_SPILLS + if (is_sse_op) + { + char tmp[32]; + const int sz = GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(spill_reg); + GLUE_RESTORE_SPILL_TO_FPREG2(tmp,spill_reg); + if (left_size>=sz && !memcmp(code-sz,tmp,sz)) + { + code[-2] = code[2]; // modify the movsd into an addsd + code[-1] -= 8; // movsd uses 0xc8+(xmmX&7), addsd etc use 0xc0 + return -4; + } + } +#endif + } + else + { + if (left_size==28) + { + // if const64_1 is within a 32-bit offset of ctx->ram_blocks->blocks, we can use [r12+offs] + if (code[-28] == 0x48 && code[-27] == 0xb8 && // mov rax, const64_1 + *(int *)(code - 18) == 0x08100ff2 && // movsd xmm1, [rax] + code[-14] == 0x48 && code[-13] == 0xb8 && // mov rax, const64_2 + *(int *)(code - 4) == 0x00100ff2 // movsd xmm0, [rax] + ) + { + UINT_PTR c1, c2; + INT_PTR c2offs,c1offs; + unsigned char opc[3]; + int wrpos = -28; + if (is_sse_op) memcpy(opc,code,3); + memcpy(&c1,code-26,8); + memcpy(&c2,code-12,8); + +#define PTR_32_OK(x) ((x) == (INT_PTR)(int)(x)) + c2offs = c2-base; + if (!PTR_32_OK(c2offs)) + { + code[wrpos++] = 0x48; + code[wrpos++] = 0xb8; + memcpy(code+wrpos,&c2,8); // mov rax, const64_2 + wrpos += 8; + } + + c1offs = c1-base; + if (!PTR_32_OK(c1offs)) + { + code[wrpos++] = 0x48; + code[wrpos++] = 0xbf; + memcpy(code+wrpos,&c1,8); // mov rdi, const64_1 + wrpos += 8; + } + + if (!PTR_32_OK(c2offs)) + { + *(int *)(code+wrpos) = 0x00100ff2; // movsd xmm0, [rax] + wrpos += 4; + } + else + { + // movsd xmm0, [r12+offs] + code[wrpos++] = 0xf2; + code[wrpos++] = 0x41; + code[wrpos++] = 0x0f; + code[wrpos++] = 0x10; + code[wrpos++] = 0x84; + code[wrpos++] = 0x24; + *(int *)(code+wrpos) = (int)c2offs; + wrpos += 4; + } + + if (!is_sse_op) + { + // load xmm1 from rdi/c1offs + if (!PTR_32_OK(c1offs)) + { + *(int *)(code+wrpos) = 0x0f100ff2; // movsd xmm1, [rdi] + wrpos += 4; + } + else + { + // movsd xmm1, [r12+offs] + code[wrpos++] = 0xf2; + code[wrpos++] = 0x41; + code[wrpos++] = 0x0f; + code[wrpos++] = 0x10; + code[wrpos++] = 0x8c; + code[wrpos++] = 0x24; + *(int *)(code+wrpos) = (int)c1offs; + wrpos += 4; + } + if (wrpos<0) memmove(code+wrpos,code,right_size); + return wrpos; + } + + // fuse to sse op + if (!PTR_32_OK(c1offs)) + { + memcpy(code+wrpos,opc,3); + code[wrpos+3] = 0x07; // [rdi] + wrpos += 4; + } + else + { + // mul/add/sub/min/max/sd xmm0, [r12+offs] + code[wrpos++] = opc[0]; // 0xf2 + code[wrpos++] = 0x41; + code[wrpos++] = opc[1]; // 0x0f + code[wrpos++] = opc[2]; // 0x58 etc + code[wrpos++] = 0x84; + code[wrpos++] = 0x24; + *(int *)(code+wrpos) = (int)c1offs; + wrpos += 4; + } + return wrpos - right_size; + } + } + if ((fuse_flags&1) && left_size >= 14) + { + if (code[-14] == 0x48 && code[-13] == 0xb8 && // mov rax, const64_2 + *(int *)(code - 4) == 0x00100ff2) // movsd xmm0, [rax] + { + INT_PTR c1; + memcpy(&c1,code-12,8); + c1 -= base; + if (PTR_32_OK(c1)) + { + // movsd xmm0, [r12+offs] + int wrpos = -14; + code[wrpos++] = 0xf2; + code[wrpos++] = 0x41; + code[wrpos++] = 0x0f; + code[wrpos++] = 0x10; + code[wrpos++] = 0x84; + code[wrpos++] = 0x24; + *(int *)(code+wrpos) = (int)c1; + wrpos += 4; + if (wrpos<0) memmove(code+wrpos,code,right_size); + return wrpos; + } + } + } + + if (left_size == 20 && right_size == 9 && + code[-20]==0x48 && code[-19] == 0xbf && // mov rdi, const64_1 + code[-10]==0x48 && code[-9] == 0xb8 // mov rax, const64_2 + ) + { + static unsigned char assign_copy[9] = { 0x48, 0x8b, 0x10, // mov rdx, [rax] + 0x48, 0x89, 0x17, // mov [rdi], rdx + 0x48, 0x89, 0xf8, // mov rax, rdi + }; + if (!memcmp(code,assign_copy,9)) + { + int wrpos = -20; + INT_PTR c1,c2; // c1 is dest, c2 is src + memcpy(&c1,code-18,8); + memcpy(&c2,code-8,8); + + if (!PTR_32_OK(c2-base)) + { + code[wrpos++] = 0x48; // mov rdi, src + code[wrpos++] = 0xbf; + memcpy(code+wrpos,&c2,8); + wrpos +=8; + } + + code[wrpos++] = 0x48; // mov rax, dest + code[wrpos++] = 0xb8; + memcpy(code+wrpos,&c1,8); + wrpos +=8; + + if (PTR_32_OK(c2-base)) + { + // mov rdx, [r12+offs] + code[wrpos++] = 0x49; + code[wrpos++] = 0x8b; + code[wrpos++] = 0x94; + code[wrpos++] = 0x24; + *(int *)(code+wrpos) = (int)(c2-base); + wrpos += 4; + } + else + { + code[wrpos++] = 0x48; // mov rdx, [rdi] + code[wrpos++] = 0x8b; + code[wrpos++] = 0x17; + } + + code[wrpos++] = 0x48; // mov [rax], rdx + code[wrpos++] = 0x89; + code[wrpos++] = 0x10; + + return wrpos - right_size; + } + } + + + } + return 0; +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel-addfuncs.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel-addfuncs.h new file mode 100644 index 000000000..ffb92a034 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel-addfuncs.h @@ -0,0 +1,63 @@ +/* + Nullsoft Expression Evaluator Library (NS-EEL) + Copyright (C) 1999-2003 Nullsoft, Inc. + + ns-eel-addfuncs.h: defines macros useful for adding functions to the compiler + + 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. +*/ + +#ifndef __NS_EEL_ADDFUNCS_H__ +#define __NS_EEL_ADDFUNCS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct _compileContext; + +void *NSEEL_PProc_RAM(void *data, int data_size, struct _compileContext *ctx); +void *NSEEL_PProc_THIS(void *data, int data_size, struct _compileContext *ctx); + + +#ifdef EEL_TARGET_PORTABLE + +extern EEL_BC_TYPE _asm_generic3parm[]; // 3 double * parms, returning double * +extern EEL_BC_TYPE _asm_generic3parm_retd[]; // 3 double * parms, returning double +extern EEL_BC_TYPE _asm_generic2parm[]; // 2 double * parms, returning double * +extern EEL_BC_TYPE _asm_generic2parm_retd[]; // 2 double * parms, returning double +extern EEL_BC_TYPE _asm_generic2xparm_retd[]; // 2 double * parms, returning double +extern EEL_BC_TYPE _asm_generic1parm[]; // 1 double * parms, returning double * +extern EEL_BC_TYPE _asm_generic1parm_retd[]; // 1 double * parms, returning double + +#else + +void _asm_generic3parm(void); // 3 double * parms, returning double * +void _asm_generic3parm_retd(void); // 3 double * parms, returning double +void _asm_generic2parm(void); // 2 double * parms, returning double * +void _asm_generic2parm_retd(void); // 2 double * parms, returning double +void _asm_generic2xparm_retd(void); // 2 double * parms, returning double +void _asm_generic1parm(void); // 1 double * parms, returning double * +void _asm_generic1parm_retd(void); // 1 double * parms, returning double + +#endif + +#ifdef __cplusplus +}; + +#endif +#endif//__NS_EEL_ADDFUNCS_H__ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel-func-ref.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel-func-ref.h new file mode 100644 index 000000000..9c2833e30 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel-func-ref.h @@ -0,0 +1,62 @@ +#ifndef _NSEEL_FUNC_REF_H_ +#define _NSEEL_FUNC_REF_H_ + +#include "ns-eel.h" +#define TMP_MKSTR2(x) #x +#define TMP_MKSTR(x) TMP_MKSTR2(x) + +const char *nseel_builtin_function_reference= + "while\texpression\tExecutes expression until expression evaluates to zero" +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN + ", or until " TMP_MKSTR(NSEEL_LOOPFUNC_SUPPORT_MAXLEN) "iterations occur" +#endif + ". An alternate and more useful syntax is while (expression) ( statements ), which evaluates statements after " + "every non-zero evaluation of expression.\0" + "loop\tcount,expression\tEvaluates count once, and then executes expression count" +#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN + ", but not more than " TMP_MKSTR(NSEEL_LOOPFUNC_SUPPORT_MAXLEN) "," +#endif + " times.\0" + "sin\tangle\tReturns the sine of the angle specified (specified in radians -- to convert from degrees to radians, multiply by $pi/180, or 0.017453).\0" + "cos\tangle\tReturns the cosine of the angle specified (specified in radians).\0" + "tan\tangle\tReturns the tangent of the angle specified (specified in radians).\0" + "sqrt\tvalue\tReturns the square root of the parameter. If the parameter is negative, the return value is undefined.\0" + "log\tvalue\tReturns the natural logarithm (base e) of the parameter. If the value is not greater than 0, the return value is undefined.\0" + "log10\tvalue\tReturns the base-10 logarithm of the parameter. If the value is not greater than 0, the return value is undefined.\0" + "asin\tvalue\tReturns the arc sine of the value specified (return value is in radians). If the parameter is not between -1.0 and 1.0 inclusive, the return value is undefined.\0" + "acos\tvalue\tReturns the arc cosine of the value specified (return value is in radians). If the parameter is not between -1.0 and 1.0 inclusive, the return value is undefined.\0" + "atan\tvalue\tReturns the arc tangent of the value specified (return value is in radians). If the parameter is not between -1.0 and 1.0 inclusive, the return value is undefined.\0" + "atan2\tnumerator,denominator\tReturns the arc tangent of the numerator divided by the denominator, allowing the denominator to be 0, and using their signs to produce a more meaningful result.\0" + "exp\texponent\tReturns the number e ($e, approximately 2.718) raised to the parameter-th power. This function is significantly faster than pow() or the ^ operator.\0" + "abs\tvalue\tReturns the absolute value of the parameter.\0" + "sqr\tvalue\tReturns the square of the parameter (similar to value*value, but only evaluating value once).\0" + "min\t&value,&value\tReturns (by reference) the minimum value of the two parameters. Since min() returns by reference, expressions such as min(x,y) = 5 are possible.\0" + "max\t&value,&value\tReturns (by reference) the maximum value of the two parameters. Since max() returns by reference, expressions such as max(x,y) = 5 are possible.\0" + "sign\tvalue\tReturns 1.0 if the parameter is greater than 0, -1.0 if the parameter is less than 0, or 0 if the parameter is 0.\0" + "floor\tvalue\tReturns the value rounded to the next lowest integer (floor(3.9)==3, floor(-3.1)==-4).\0" + "ceil\tvalue\tReturns the value rounded to the next highest integer (ceil(3.1)==4, ceil(-3.9)==-3).\0" + "invsqrt\tvalue\tReturns a fast inverse square root (1/sqrt(x)) approximation of the parameter.\0" + "freembuf\taddress\tHints the runtime that memory above the address specified may no longer be used. The runtime may, at its leisure, choose to lose the contents of memory above the address specified.\0" + "memcpy\tdest,src,length\tCopies length items of memory from src to dest. Regions are permitted to overlap.\0" + "memset\toffset,value,length\tSets length items of memory at offset to value.\0" + "mem_get_values\toffset, ...\tReads values from memory starting at offset into variables specified. Slower than regular memory reads for less than a few variables, faster for more than a few. Undefined behavior if used with more than 32767 variables.\0" + "mem_set_values\toffset, ...\tWrites values to memory starting at offset from variables specified. Slower than regular memory writes for less than a few variables, faster for more than a few. Undefined behavior if used with more than 32767 variables.\0" + "stack_push\t&value\tPushes value onto the user stack, returns a reference to the parameter.\0" + "stack_pop\t&value\tPops a value from the user stack into value, or into a temporary buffer if value is not specified, and returns a reference to where the stack was popped. Note that no checking is done to determine if the stack is empty, and as such stack_pop() will never fail.\0" + "stack_peek\tindex\tReturns a reference to the item on the top of the stack (if index is 0), or to the Nth item on the stack if index is greater than 0. \0" + "stack_exch\t&value\tExchanges a value with the top of the stack, and returns a reference to the parameter (with the new value).\0" +#ifdef NSEEL_EEL1_COMPAT_MODE + "rand\tmax\tReturns a psuedorandom non-negative integer number less than the parameter.\0" + "sigmoid\tvalue,constraint\tReturns 1.0/(1+exp(-x * (constraint))), or 0 if a divide by 0 would occur.\0" + "band\tx,y\tReturns 1 if both x and y evaluate to nonzero, 0 if otherwise. Both parameters are always evaluated.\0" + "bor\tx,y\tReturns 1 if either x or y evaluate to nonzero, 0 if otherwise. Both parameters are always evaluated.\0" + "exec2\tx,y\tEvaluates x, then evaluates and returns y.\0" + "exec3\tx,y,z\tEvaluates x, evaluates y, then evaluates and returns z.\0" +#else + "rand\t[max]\tReturns a psuedorandom real number between 0 and the parameter, inclusive. If the parameter is omitted or less than 1.0, 1.0 is used as a maximum instead.\0" + +#endif +; +#undef TMP_MKSTR + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel-int.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel-int.h new file mode 100644 index 000000000..db2538410 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel-int.h @@ -0,0 +1,329 @@ +/* + Nullsoft Expression Evaluator Library (NS-EEL) + Copyright (C) 1999-2003 Nullsoft, Inc. + + ns-eel-int.h: internal code definition header. + + 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. +*/ + +#ifndef __NS_EELINT_H__ +#define __NS_EELINT_H__ + +#ifdef _WIN32 +#include +#else +#include "../wdltypes.h" +#endif + +#include "ns-eel.h" +#include "ns-eel-addfuncs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +enum { + + // these ignore fn in opcodes, just use fntype to determine function + FN_MULTIPLY=0, + FN_DIVIDE, + FN_JOIN_STATEMENTS, + FN_DENORMAL_LIKELY, + FN_DENORMAL_UNLIKELY, + FN_ADD, + FN_SUB, + FN_AND, + FN_OR, + FN_UMINUS, + FN_NOT, + FN_NOTNOT, + FN_XOR, + FN_SHL, + FN_SHR, + FN_MOD, + FN_POW, + FN_LT, + FN_GT, + FN_LTE, + FN_GTE, + FN_EQ, + FN_EQ_EXACT, + FN_NE, + FN_NE_EXACT, + FN_LOGICAL_AND, + FN_LOGICAL_OR, + FN_IF_ELSE, + FN_MEMORY, + FN_GMEMORY, + FN_NONCONST_BEGIN, + FN_ASSIGN=FN_NONCONST_BEGIN, + + FN_ADD_OP, + FN_SUB_OP, + FN_MOD_OP, + FN_OR_OP, + FN_AND_OP, + FN_XOR_OP, + FN_DIV_OP, + FN_MUL_OP, + FN_POW_OP, + + FN_WHILE, + FN_LOOP, + + FUNCTYPE_SIMPLEMAX, + + + FUNCTYPE_FUNCTIONTYPEREC=1000, // fn is a functionType * + FUNCTYPE_EELFUNC, // fn is a _codeHandleFunctionRec * +}; + + + +#define YYSTYPE opcodeRec * + +#define NSEEL_CLOSEFACTOR 0.00001 + + +typedef struct opcodeRec opcodeRec; + +typedef struct _codeHandleFunctionRec +{ + struct _codeHandleFunctionRec *next; // main linked list (only used for high level functions) + struct _codeHandleFunctionRec *derivedCopies; // separate linked list, head being the main function, other copies being derived versions + + void *startptr; // compiled code (may be cleared + recompiled when shared) + opcodeRec *opcodes; + + int startptr_size; // 0=no code. -1 = needs calculation. >0 = size. + int startptr_base_size; // initially calculated size of root function + int tmpspace_req; + + int num_params; + + int rvMode; // RETURNVALUE_* + int fpStackUsage; // 0-8, usually + int canHaveDenormalOutput; + + // local storage's first items are the parameters, then locals. Note that the opcodes will reference localstorage[] via VARPTRPTR, but + // the values localstorage[x] points are reallocated from context-to-context, if it is a common function. + + // separately allocated list of pointers, the contents of the list should be zeroed on context changes if a common function + // note that when making variations on a function (context), it is shared, but since it is zeroed on context changes, it is context-local + int localstorage_size; + EEL_F **localstorage; + + int isCommonFunction; + int usesNamespaces; + unsigned int parameterAsNamespaceMask; + + char fname[NSEEL_MAX_FUNCSIG_NAME+1]; +} _codeHandleFunctionRec; + +typedef struct _llBlock { + struct _llBlock *next; + int sizeused; + int sizealloc; + // data follows +} llBlock; + +typedef struct { + llBlock *blocks_code, *blocks_data; + void *workTable; // references a chunk in blocks_data + + void *code; + int code_size; // in case the caller wants to write it out + int code_stats[4]; + + int want_stack; + void *stack; // references a chunk in blocks_data, somewhere within the complete NSEEL_STACK_SIZE aligned at NSEEL_STACK_SIZE + + void *ramPtr; + + int workTable_size; // size (minus padding/extra space) of workTable -- only used if EEL_VALIDATE_WORKTABLE_USE set, but might be handy to have around too + int compile_flags; +} codeHandleType; + +typedef struct +{ + EEL_F *value; + int refcnt; + char isreg; + char str[1]; +} varNameRec; + +typedef struct +{ + void *ptr; + int size, alloc; +} eel_growbuf; +#define EEL_GROWBUF(type) union { eel_growbuf _growbuf; type *_tval; } +#define EEL_GROWBUF_RESIZE(gb, newsz) __growbuf_resize(&(gb)->_growbuf, (newsz)*(int)sizeof((gb)->_tval[0])) // <0 to free, does not realloc down otherwise +#define EEL_GROWBUF_GET(gb) ((gb)->_tval) +#define EEL_GROWBUF_GET_SIZE(gb) ((gb)->_growbuf.size/(int)sizeof((gb)->_tval[0])) + +typedef struct _compileContext +{ + eel_function_table *registered_func_tab; + const char *(*func_check)(const char *fn_name, void *user); // return error message if not permitted + void *func_check_user; + + EEL_GROWBUF(varNameRec *) varNameList; + EEL_F *varValueStore; + int varValueStore_left; + + int errVar,gotEndOfInput; + opcodeRec *result; + char last_error_string[256]; + + void *scanner; + const char *rdbuf_start, *rdbuf, *rdbuf_end; + + llBlock *tmpblocks, // used while compiling, and freed after compiling + + *blocks_head_code, // used while compiling, transferred to code context (whole pages marked as executable) + *blocks_head_data, // used while compiling, transferred to code context + + *ctx_pblocks; // persistent blocks, stores data used by varTable_Names, varTable_Values, etc. + + int l_stats[4]; // source bytes, static code bytes, call code bytes, data bytes + int has_used_global_vars; + + _codeHandleFunctionRec *functions_local, *functions_common; + + // state used while generating functions + int optimizeDisableFlags; + int current_compile_flags; + struct opcodeRec *directValueCache; // linked list using fn as next + + int isSharedFunctions; + int isGeneratingCommonFunction; + int function_usesNamespaces; + int function_globalFlag; // set if restrict globals to function_localTable_Names[2] + // [0] is parameter+local symbols (combined space) + // [1] is symbols which get implied "this." if used + // [2] is globals permitted + int function_localTable_Size[3]; // for parameters only + char **function_localTable_Names[3]; // lists of pointers + EEL_F **function_localTable_ValuePtrs; + const char *function_curName; // name of current function + + EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list); + EEL_F (*onNamedString)(void *caller_this, const char *name); + + EEL_F *(*getVariable)(void *userctx, const char *name); + void *getVariable_userctx; + + codeHandleType *tmpCodeHandle; + + struct + { + WDL_UINT64 sign_mask[2]; + WDL_UINT64 abs_mask[2]; + int needfree; + int maxblocks; + double closefact; + EEL_F *blocks[NSEEL_RAM_BLOCKS]; + } *ram_state; // allocated from blocks with 16 byte alignment + + void *gram_blocks; + + void *caller_this; +} +compileContext; + +#define NSEEL_NPARAMS_FLAG_CONST 0x80000 +typedef struct functionType { + const char *name; + void *afunc; + int nParams; + void *replptrs[4]; + NSEEL_PPPROC pProc; +} functionType; + +functionType *nseel_getFunctionByName(compileContext *ctx, const char *name, int *mchk); // sets mchk (if non-NULL) to how far allowed to scan forward for duplicate names +functionType *nseel_enumFunctions(compileContext *ctx, int idx); + +opcodeRec *nseel_createCompiledValue(compileContext *ctx, EEL_F value); +opcodeRec *nseel_createCompiledValuePtr(compileContext *ctx, EEL_F *addrValue, const char *namestr); + +opcodeRec *nseel_createMoreParametersOpcode(compileContext *ctx, opcodeRec *code1, opcodeRec *code2); +opcodeRec *nseel_createSimpleCompiledFunction(compileContext *ctx, int fn, int np, opcodeRec *code1, opcodeRec *code2); +opcodeRec *nseel_createMemoryAccess(compileContext *ctx, opcodeRec *code1, opcodeRec *code2); +opcodeRec *nseel_createIfElse(compileContext *ctx, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3); +opcodeRec *nseel_createFunctionByName(compileContext *ctx, const char *name, int np, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3); + +// converts a generic identifier (VARPTR) opcode into either an actual variable reference (parmcnt = -1), +// or if parmcnt >= 0, to a function call (see nseel_setCompiledFunctionCallParameters()) +opcodeRec *nseel_resolve_named_symbol(compileContext *ctx, opcodeRec *rec, int parmcnt, int *errOut); + +// sets parameters and calculates parameter count for opcode, and calls nseel_resolve_named_symbol() with the right +// parameter count +opcodeRec *nseel_setCompiledFunctionCallParameters(compileContext *ctx, opcodeRec *fn, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3, opcodeRec *postCode, int *errOut); +// errOut will be set if return NULL: +// -1 if postCode set when not wanted (i.e. not while()) +// 0 if func not found, +// 1 if function requires 2+ parameters but was given more +// 2 if function needs more parameters +// 4 if function requires 1 parameter but was given more + + + +struct eelStringSegmentRec *nseel_createStringSegmentRec(compileContext *ctx, const char *str, int len); +opcodeRec *nseel_eelMakeOpcodeFromStringSegments(compileContext *ctx, struct eelStringSegmentRec *rec); + +EEL_F *nseel_int_register_var(compileContext *ctx, const char *name, int isReg, const char **namePtrOut); +_codeHandleFunctionRec *eel_createFunctionNamespacedInstance(compileContext *ctx, _codeHandleFunctionRec *fr, const char *nameptr); + +typedef struct nseel_globalVarItem +{ + EEL_F data; + struct nseel_globalVarItem *_next; + char name[1]; // varlen, does not include _global. prefix +} nseel_globalVarItem; + +extern nseel_globalVarItem *nseel_globalreg_list; // if NSEEL_EEL1_COMPAT_MODE, must use NSEEL_getglobalregs() for regxx values + +#include "y.tab.h" + +// nseel_simple_tokenizer will return comments as tokens if state is non-NULL +const char *nseel_simple_tokenizer(const char **ptr, const char *endptr, int *lenOut, int *state); +int nseel_filter_escaped_string(char *outbuf, int outbuf_sz, const char *rdptr, size_t rdptr_size, char delim_char); // returns length used, minus NUL char + +opcodeRec *nseel_translate(compileContext *ctx, const char *tmp, size_t tmplen); // tmplen=0 for nul-term +int nseel_lookup(compileContext *ctx, opcodeRec **opOut, const char *sname); + +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAMAlloc(EEL_F **blocks, unsigned int w); +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAMAllocGMEM(EEL_F ***blocks, unsigned int w); +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemSet(EEL_F **blocks,EEL_F *dest, EEL_F *v, EEL_F *lenptr); +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemFree(void *blocks, EEL_F *which); +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemTop(void *blocks, EEL_F *which); +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemCpy(EEL_F **blocks,EEL_F *dest, EEL_F *src, EEL_F *lenptr); +EEL_F NSEEL_CGEN_CALL __NSEEL_RAM_Mem_SetValues(EEL_F **blocks, INT_PTR np, EEL_F **parms); +EEL_F NSEEL_CGEN_CALL __NSEEL_RAM_Mem_GetValues(EEL_F **blocks, INT_PTR np, EEL_F **parms); + +extern EEL_F nseel_ramalloc_onfail; // address returned by __NSEEL_RAMAlloc et al on failure +extern EEL_F * volatile nseel_gmembuf_default; // can free/zero this on DLL unload if needed + +#ifdef __cplusplus +} +#endif + + +#endif//__NS_EELINT_H__ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel.h new file mode 100644 index 000000000..7e2ca47bb --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/ns-eel.h @@ -0,0 +1,263 @@ +/* + Nullsoft Expression Evaluator Library (NS-EEL) + Copyright (C) 1999-2003 Nullsoft, Inc. + + ns-eel.h: main application interface header + + 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. +*/ + + +#ifndef __NS_EEL_H__ +#define __NS_EEL_H__ + +// put standard includes here +#include +#include + +#ifndef EEL_F_SIZE +#define EEL_F_SIZE 8 +#endif + +#include "../wdltypes.h" + +typedef double EEL_F WDL_FIXALIGN; +typedef double *EEL_F_PTR; + +#ifdef _MSC_VER +#define NSEEL_CGEN_CALL __cdecl +#else +#define NSEEL_CGEN_CALL +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +// host should implement these (can be empty stub functions if no VM will execute code in multiple threads at once) + + // implement if you will be running the code in same VM from multiple threads, + // or VMs that have the same GRAM pointer from different threads, or multiple + // VMs that have a NULL GRAM pointer from multiple threads. + // if you give each VM it's own unique GRAM and only run each VM in one thread, then you can leave it blank. + + // or if you're daring.... + +void NSEEL_HOSTSTUB_EnterMutex(); +void NSEEL_HOSTSTUB_LeaveMutex(); + + +int NSEEL_init(); // returns nonzero on failure (only if EEL_VALIDATE_FSTUBS defined), otherwise the same as NSEEL_quit(), and completely optional +void NSEEL_quit(); // clears any added functions + + +// adds a function that returns a value (EEL_F) +#define NSEEL_addfunc_retval(name,np,pproc,fptr) \ + NSEEL_addfunc_ret_type(name,np,1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) + +// adds a function that returns a pointer (EEL_F*) +#define NSEEL_addfunc_retptr(name,np,pproc,fptr) \ + NSEEL_addfunc_ret_type(name,np,0,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) + +// adds a void or bool function +#define NSEEL_addfunc_retbool(name,np,pproc,fptr) \ + NSEEL_addfunc_ret_type(name,np,-1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) + +// adds a function that takes min_np or more parameters (func sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms) +#define NSEEL_addfunc_varparm(name, min_np, pproc, fptr) \ + NSEEL_addfunc_varparm_ex(name,min_np,0,pproc,fptr,NSEEL_ADDFUNC_DESTINATION) + +// adds a function that takes np parameters via func: sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms) +#define NSEEL_addfunc_exparms(name, np, pproc, fptr) \ + NSEEL_addfunc_varparm_ex(name,np,1,pproc,fptr,NSEEL_ADDFUNC_DESTINATION) + + +// deprecated +#define NSEEL_addfunction(name,nparms,code,len) NSEEL_addfunctionex((name),(nparms),(code),(len),0,0) +#define NSEEL_addfunctionex(name,nparms,code,len,pproc,fptr) NSEEL_addfunctionex2((name),(nparms),(code),(len),(pproc),(fptr),0, NSEEL_ADDFUNC_DESTINATION) + +#ifndef NSEEL_ADDFUNC_DESTINATION +#define NSEEL_ADDFUNC_DESTINATION (NULL) +#endif + +struct functionType; + +typedef struct +{ + struct functionType *list; + int list_size; +} eel_function_table; + +struct _compileContext; +typedef void *(*NSEEL_PPPROC)(void *data, int data_size, struct _compileContext *userfunc_data); +void NSEEL_addfunctionex2(const char *name, int nparms, char *code_startaddr, int code_len, NSEEL_PPPROC pproc, void *fptr, void *fptr2, eel_function_table *destination); + +void NSEEL_addfunc_ret_type(const char *name, int np, int ret_type, NSEEL_PPPROC pproc, void *fptr, eel_function_table *destination); // ret_type=-1 for bool, 1 for value, 0 for ptr +void NSEEL_addfunc_varparm_ex(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination); +void NSEEL_addfunc_varparm_ctxptr(const char *name, int min_np, int want_exact, void *ctxptr, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination); +void NSEEL_addfunc_varparm_ctxptr2(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, void *ctx, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, void *,INT_PTR, EEL_F **), eel_function_table *destination); + +int *NSEEL_getstats(); // returns a pointer to 5 ints... source bytes, static code bytes, call code bytes, data bytes, number of code handles + +typedef void *NSEEL_VMCTX; +typedef void *NSEEL_CODEHANDLE; + +NSEEL_VMCTX NSEEL_VM_alloc(); // return a handle +void NSEEL_VM_free(NSEEL_VMCTX ctx); // free when done with a VM and ALL of its code have been freed, as well + +void NSEEL_VM_SetFunctionTable(NSEEL_VMCTX, eel_function_table *tab); // use NULL to use default (global) table + +// validateFunc can return error message if not permitted +void NSEEL_VM_SetFunctionValidator(NSEEL_VMCTX, const char * (*validateFunc)(const char *fn_name, void *user), void *user); + +void NSEEL_VM_remove_unused_vars(NSEEL_VMCTX _ctx); +void NSEEL_VM_clear_var_refcnts(NSEEL_VMCTX _ctx); +void NSEEL_VM_remove_all_nonreg_vars(NSEEL_VMCTX _ctx); +void NSEEL_VM_enumallvars(NSEEL_VMCTX ctx, int (*func)(const char *name, EEL_F *val, void *ctx), void *userctx); // return false from func to stop + +EEL_F *NSEEL_VM_regvar(NSEEL_VMCTX ctx, const char *name); // register a variable (before compilation) +EEL_F *NSEEL_VM_getvar(NSEEL_VMCTX ctx, const char *name); // get a variable (if registered or created by code) +int NSEEL_VM_get_var_refcnt(NSEEL_VMCTX _ctx, const char *name); // returns -1 if not registered, or >=0 +void NSEEL_VM_set_var_resolver(NSEEL_VMCTX ctx, EEL_F *(*res)(void *userctx, const char *name), void *userctx); + +void NSEEL_VM_freeRAM(NSEEL_VMCTX ctx); // clears and frees all (VM) RAM used +void NSEEL_VM_freeRAMIfCodeRequested(NSEEL_VMCTX); // call after code to free the script-requested memory +int NSEEL_VM_wantfreeRAM(NSEEL_VMCTX ctx); // want NSEEL_VM_freeRAMIfCodeRequested? + +// if you set this, it uses a local GMEM context. +// Must be set before compilation. +// void *p=NULL; +// NSEEL_VM_SetGRAM(ctx,&p); +// .. do stuff +// NSEEL_VM_FreeGRAM(&p); +void NSEEL_VM_SetGRAM(NSEEL_VMCTX ctx, void **gram); +void NSEEL_VM_FreeGRAM(void **ufd); // frees a gmem context. +void NSEEL_VM_SetCustomFuncThis(NSEEL_VMCTX ctx, void *thisptr); + +EEL_F *NSEEL_VM_getramptr(NSEEL_VMCTX ctx, unsigned int offs, int *validCount); +EEL_F *NSEEL_VM_getramptr_noalloc(NSEEL_VMCTX ctx, unsigned int offs, int *validCount); + + +// set 0 to query. returns actual value used (limits, granularity apply -- see NSEEL_RAM_BLOCKS) +int NSEEL_VM_setramsize(NSEEL_VMCTX ctx, int maxent); + + +struct eelStringSegmentRec { + struct eelStringSegmentRec *_next; + const char *str_start; // escaped characters, including opening/trailing characters + int str_len; +}; +void NSEEL_VM_SetStringFunc(NSEEL_VMCTX ctx, + EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list), + EEL_F (*onNamedString)(void *caller_this, const char *name)); + +// call with NULL to calculate size, or non-null to generate to buffer (returning size used -- will not null terminate, caller responsibility) +int nseel_stringsegments_tobuf(char *bufOut, int bufout_sz, struct eelStringSegmentRec *list); + + +NSEEL_CODEHANDLE NSEEL_code_compile(NSEEL_VMCTX ctx, const char *code, int lineoffs); +#define NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS 1 // allows that code's functions to be used in other code (note you shouldn't destroy that codehandle without destroying others first if used) +#define NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS_RESET 2 // resets common code functions +#define NSEEL_CODE_COMPILE_FLAG_NOFPSTATE 4 // hint that the FPU/SSE state should be good-to-go +#define NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS 8 // very restrictive mode (only math functions really) + +NSEEL_CODEHANDLE NSEEL_code_compile_ex(NSEEL_VMCTX ctx, const char *code, int lineoffs, int flags); + +char *NSEEL_code_getcodeerror(NSEEL_VMCTX ctx); +int NSEEL_code_geterror_flag(NSEEL_VMCTX ctx); +void NSEEL_code_execute(NSEEL_CODEHANDLE code); +void NSEEL_code_free(NSEEL_CODEHANDLE code); +int *NSEEL_code_getstats(NSEEL_CODEHANDLE code); // 4 ints...source bytes, static code bytes, call code bytes, data bytes + + +// global memory control/view +extern unsigned int NSEEL_RAM_limitmem; // if nonzero, memory limit for user data, in bytes +extern unsigned int NSEEL_RAM_memused; +extern int NSEEL_RAM_memused_errors; + + + +// configuration: + +// use the handwritten lexer -- the flex (eel2.l generated) lexer mostly works, but doesn't support string parsing at the moment +// this mode is faster and uses less ram than eel2.l anyway, so leave it on +#define NSEEL_SUPER_MINIMAL_LEXER + + // #define NSEEL_EEL1_COMPAT_MODE // supports old behaviors (continue after failed compile), old functions _bnot etc. disables string support (strings were used as comments in eel1 etc) + +#define NSEEL_MAX_VARIABLE_NAMELEN 128 // define this to override the max variable length +#define NSEEL_MAX_EELFUNC_PARAMETERS 40 +#define NSEEL_MAX_FUNCSIG_NAME 2048 // longer than variable maxlen, due to multiple namespaces + +// maximum loop length (0 for unlimited) +#ifndef NSEEL_LOOPFUNC_SUPPORT_MAXLEN +#define NSEEL_LOOPFUNC_SUPPORT_MAXLEN 1048576 +#endif + +#define NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE 2048 + +// when a VM ctx doesn't have a GRAM context set, make the global one this big +#define NSEEL_SHARED_GRAM_SIZE (1<<20) + +//#define EEL_DUMP_OPS // used for testing frontend parser/logic changes + +// note: if you wish to change NSEEL_RAM_*, and your target is x86-64, you will +// need to edit asm-nseel-x64-sse.asm to match + +// 512 * 65536 = 32 million entries maximum (256MB RAM) +// default is limited to 128 * 65536 = 8 million entries (64MB RAM) + +// default to 8 million entries, use NSEEL_VM_setramsize() to change at runtime +#define NSEEL_RAM_BLOCKS_DEFAULTMAX 128 + +// 512 entry block table maximum (2k/4k per VM) +#define NSEEL_RAM_BLOCKS_LOG2 9 + + // 65536 items per block (512KB) +#define NSEEL_RAM_ITEMSPERBLOCK_LOG2 16 + +#define NSEEL_RAM_BLOCKS (1 << NSEEL_RAM_BLOCKS_LOG2) +#define NSEEL_RAM_ITEMSPERBLOCK (1< +#include + + + +// these are used by our assembly code + + +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0dfUL /* constant vector a */ +#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ + +static unsigned int genrand_int32(void) +{ + + unsigned int y; + static unsigned int mag01[2]={0x0UL, MATRIX_A}; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + static unsigned int mt[N]; /* the array for the state vector */ + static unsigned int __idx; + + unsigned int mti = __idx; + + if (!mti) + { + unsigned int s=0x4141f00d; + mt[0]= s & 0xffffffffUL; + for (mti=1; mti> 30)) + mti); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + mt[mti] &= 0xffffffffUL; + /* for >32 bit machines */ + } + __idx = N; // mti = N (from loop) + } + + if (mti >= N) { /* generate N words at one time */ + int kk; + __idx = 1; + + for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; + } + for (;kk> 1) ^ mag01[y & 0x1UL]; + } + y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); + mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + mti = 0; + } + else + __idx++; + + y = mt[mti]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + + + +//--------------------------------------------------------------------------------------------------------------- +EEL_F NSEEL_CGEN_CALL nseel_int_rand(EEL_F f) +{ + EEL_F x=floor(f); + if (x < 1.0) x=1.0; + +#ifdef NSEEL_EEL1_COMPAT_MODE + return (EEL_F)(genrand_int32()%(int)x); +#else + return (EEL_F) (genrand_int32()*(1.0/(double)0xFFFFFFFF)*x); +#endif +} + +//--------------------------------------------------------------------------------------------------------------- + + +#ifndef EEL_TARGET_PORTABLE + +#ifdef __ppc__ +#include "asm-nseel-ppc-gcc.c" +#elif defined(__aarch64__) +#include "asm-nseel-aarch64-gcc.c" +#elif defined(__arm__) +#include "asm-nseel-arm-gcc.c" +#elif defined (_M_ARM) && _M_ARM == 7 + // vc on ARM, tbd +#else + #ifdef _MSC_VER + #ifdef _WIN64 + //nasm + #else + #include "asm-nseel-x86-msvc.c" + #endif + #elif !(defined(_WIN64) || defined(__LP64__)) + #define EEL_F_SUFFIX "l" + #define FUNCTION_MARKER "\n.byte 0x89,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90\n" + #include "asm-nseel-x86-gcc.c" + #endif +#endif + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-compiler.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-compiler.c new file mode 100644 index 000000000..5b74645ca --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-compiler.c @@ -0,0 +1,5807 @@ +/* + Expression Evaluator Library (NS-EEL) v2 + Copyright (C) 2004-2013 Cockos Incorporated + Copyright (C) 1999-2003 Nullsoft, Inc. + + nseel-compiler.c + + 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 "ns-eel-int.h" + +#include "../denormal.h" + +#include +#include +#include +#include + +#include "../wdlcstring.h" + +#if !defined(EEL_TARGET_PORTABLE) && !defined(_WIN32) +#include +#include +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +#define NSEEL_VARS_MALLOC_CHUNKSIZE 8 + +//#define LOG_OPT +//#define EEL_PRINT_FAILS +//#define EEL_VALIDATE_WORKTABLE_USE + + +#ifdef EEL_PRINT_FAILS + #ifdef _WIN32 + #define RET_MINUS1_FAIL(x) { OutputDebugString(x); return -1; } + #else + #define RET_MINUS1_FAIL(x) { printf("%s\n",x); return -1; } + #endif +#else +#define RET_MINUS1_FAIL(x) return -1; +#endif + +#ifdef EEL_DUMP_OPS +FILE *g_eel_dump_fp, *g_eel_dump_fp2; +#endif + +#ifdef EEL_VALIDATE_WORKTABLE_USE + #define MIN_COMPUTABLE_SIZE 0 + #define COMPUTABLE_EXTRA_SPACE 64 // safety buffer, if EEL_VALIDATE_WORKTABLE_USE set, used for magic-value-checking +#else + #define MIN_COMPUTABLE_SIZE 32 // always use at least this big of a temp storage table (and reset the temp ptr when it goes past this boundary) + #define COMPUTABLE_EXTRA_SPACE 16 // safety buffer, if EEL_VALIDATE_WORKTABLE_USE set, used for magic-value-checking +#endif + +#ifdef NSEEL_ATOF + double NSEEL_ATOF(const char *); +#else + #define NSEEL_ATOF atof +#endif + + +/* + P1 is rightmost parameter + P2 is second rightmost, if any + P3 is third rightmost, if any + registers on x86 are (RAX etc on x86-64) + P1(ret) EAX + P2 EDI + P3 ECX + WTP RSI + x86_64: r12 is a pointer to ram_state->blocks + x86_64: r13 is a pointer to closenessfactor + + registers on PPC are: + P1(ret) r3 + P2 r14 + P3 r15 + WTP r16 (r17 has the original value) + r13 is a pointer to ram_state->blocks + + ppc uses f31 and f30 and others for certain constants + + */ + + +#ifdef EEL_TARGET_PORTABLE + +#define EEL_DOESNT_NEED_EXEC_PERMS + +#ifdef EEL_PORTABLE_TAILCALL +#include "glue_port_new.h" +#else +#include "glue_port.h" +#endif + +#elif defined(__ppc__) + +#include "glue_ppc.h" + +#elif defined(__aarch64__) + +#include "glue_aarch64.h" + +#elif defined(__arm__) || (defined (_M_ARM) && _M_ARM == 7) + +#include "glue_arm.h" + +#elif defined(_WIN64) || defined(__LP64__) + +#include "glue_x86_64_sse.h" + +#else + +#include "glue_x86.h" + +#endif + +#ifndef GLUE_INVSQRT_NEEDREPL +#define GLUE_INVSQRT_NEEDREPL 0 +#endif + + +// used by //#eel-no-optimize:xxx, in ctx->optimizeDisableFlags +#define OPTFLAG_NO_OPTIMIZE 1 +#define OPTFLAG_NO_FPSTACK 2 +#define OPTFLAG_NO_INLINEFUNC 4 +#define OPTFLAG_FULL_DENORMAL_CHECKS 8 // if set, denormals/NaN are always filtered on assign +#define OPTFLAG_NO_DENORMAL_CHECKS 16 // if set and FULL not set, denormals/NaN are never filtered on assign + + +#define DENORMAL_CLEARING_THRESHOLD 1.0e-50 // when adding/subtracting a constant, assume if it's greater than this, it will clear denormal (the actual value is probably 10^-290...) + + +#define MAX_SUB_NAMESPACES 32 +typedef struct +{ + const char *namespacePathToThis; + const char *subParmInfo[MAX_SUB_NAMESPACES]; +} namespaceInformation; + + + + +static int nseel_evallib_stats[5]; // source bytes, static code bytes, call code bytes, data bytes, segments +int *NSEEL_getstats() +{ + return nseel_evallib_stats; +} + +static int findLineNumber(const char *exp, int byteoffs) +{ + int lc=0; + while (byteoffs-->0 && *exp) if (*exp++ =='\n') lc++; + return lc; +} + + +static int nseel_vms_referencing_globallist_cnt; +nseel_globalVarItem *nseel_globalreg_list; +static EEL_F *get_global_var(compileContext *ctx, const char *gv, int addIfNotPresent); + +static void *__newBlock_align(llBlock **start,int size, int align, int code_page_size); + +#define OPCODE_IS_TRIVIAL(x) ((x)->opcodeType <= OPCODETYPE_VARPTRPTR) +enum { + OPCODETYPE_DIRECTVALUE=0, + OPCODETYPE_DIRECTVALUE_TEMPSTRING, // like directvalue, but will generate a new tempstring value on generate + OPCODETYPE_VALUE_FROM_NAMESPACENAME, // this.* or namespace.* are encoded this way + OPCODETYPE_VARPTR, + OPCODETYPE_VARPTRPTR, + OPCODETYPE_FUNC1, + OPCODETYPE_FUNC2, + OPCODETYPE_FUNC3, + OPCODETYPE_FUNCX, + + OPCODETYPE_MOREPARAMS, + + OPCODETYPE_INVALID, +}; + +struct opcodeRec +{ + int opcodeType; + int fntype; + void *fn; + + union { + struct opcodeRec *parms[3]; + struct { + double directValue; + EEL_F *valuePtr; // if direct value, valuePtr can be cached + } dv; + } parms; + + int namespaceidx; + + // OPCODETYPE_VALUE_FROM_NAMESPACENAME (relname is either empty or blah) + // OPCODETYPE_VARPTR if it represents a global variable, will be nonempty + // OPCODETYPE_FUNC* with fntype=FUNCTYPE_EELFUNC + const char *relname; +}; + + + + + +static opcodeRec *newOpCode(compileContext *ctx, const char *str, int opType) +{ + const size_t strszfull = str ? strlen(str) : 0; + const size_t str_sz = wdl_min(NSEEL_MAX_VARIABLE_NAMELEN, strszfull); + + opcodeRec *rec = (opcodeRec*)__newBlock_align(ctx->isSharedFunctions ? &ctx->blocks_head_data : &ctx->tmpblocks, + (int) (sizeof(opcodeRec) + (str_sz>0 ? str_sz+1 : 0)), + 8, 0); + if (rec) + { + memset(rec,0,sizeof(*rec)); + rec->opcodeType = opType; + + if (str_sz > 0) + { + char *p = (char *)(rec+1); + memcpy(p,str,str_sz); + p[str_sz]=0; + + rec->relname = p; + } + else + { + rec->relname = ""; + } + } + + return rec; +} + +#ifndef EEL_DOESNT_NEED_EXEC_PERMS +static int eel_get_page_size(void) +{ + static int pagesize; + if (!pagesize) + { +#ifndef _WIN32 + const int ps = (int)sysconf(_SC_PAGESIZE); + pagesize = wdl_max(ps, 4096); +#else + SYSTEM_INFO inf = { 0 }; + GetSystemInfo(&inf); + pagesize = wdl_max(inf.dwPageSize, 4096); +#endif + } + return pagesize; +} +#endif + +#define newCodeBlock(x,a) __newBlock_align(&ctx->blocks_head_code,x,a, 1) +#define newDataBlock(x,a) __newBlock_align(&ctx->blocks_head_data,x,a,0) +#define newCtxDataBlock(x,a) __newBlock_align(&ctx->ctx_pblocks,x,a,0) +#define newTmpBlock(ctx, size) __newBlock_align(&(ctx)->tmpblocks, size, 8,0) + +static char *eel_get_llblock_buffer(llBlock *llb) { return (char *) (llb+1); } + +#ifndef EEL_DOESNT_NEED_EXEC_PERMS +static void eel_set_blocks_allow_execute(llBlock *llb, int exec) +{ + while (llb) + { + const size_t sz = sizeof(*llb) + llb->sizealloc; + #ifdef _WIN32 + DWORD ov; + VirtualProtect(llb,sz,exec ? (PAGE_EXECUTE_READ) : (PAGE_READWRITE),&ov); + FlushInstructionCache(GetCurrentProcess(),llb,sz); + #else + mprotect(llb,sz,exec ? (PROT_READ|PROT_EXEC) : (PROT_READ|PROT_WRITE)); + #ifdef __APPLE__ + if (exec) sys_icache_invalidate(llb,sz); + #endif + #endif + WDL_ASSERT((((INT_PTR)llb) & (eel_get_page_size()-1)) == 0); + WDL_ASSERT((sz & (eel_get_page_size()-1)) == 0); + llb = llb->next; + } +} +#endif + +static void freeBlocks(llBlock **start, int is_code); + +static int __growbuf_resize(eel_growbuf *buf, int newsize) +{ + if (newsize<0) + { + free(buf->ptr); + buf->ptr=NULL; + buf->alloc=buf->size=0; + return 0; + } + + if (newsize > buf->alloc) + { + const int newalloc = newsize + 4096 + newsize/2; + void *newptr = realloc(buf->ptr,newalloc); + if (!newptr) + { + newptr = malloc(newalloc); + if (!newptr) return 1; + if (buf->ptr && buf->size) memcpy(newptr,buf->ptr,buf->size); + free(buf->ptr); + buf->ptr=newptr; + } + else + buf->ptr = newptr; + + buf->alloc=newalloc; + } + buf->size = newsize; + return 0; +} + + +#ifndef DECL_ASMFUNC +#define DECL_ASMFUNC(x) void nseel_asm_##x(void); + + +void _asm_megabuf(void); +void _asm_gmegabuf(void); + +#endif + + + DECL_ASMFUNC(booltofp) + DECL_ASMFUNC(fptobool) + DECL_ASMFUNC(fptobool_rev) + DECL_ASMFUNC(sin) + DECL_ASMFUNC(cos) + DECL_ASMFUNC(tan) + DECL_ASMFUNC(1pdd) + DECL_ASMFUNC(2pdd) + DECL_ASMFUNC(2pdds) + DECL_ASMFUNC(1pp) + DECL_ASMFUNC(2pp) + DECL_ASMFUNC(sqr) + DECL_ASMFUNC(sqrt) + DECL_ASMFUNC(log) + DECL_ASMFUNC(log10) + DECL_ASMFUNC(abs) + DECL_ASMFUNC(min) + DECL_ASMFUNC(max) + DECL_ASMFUNC(min_fp) + DECL_ASMFUNC(max_fp) + DECL_ASMFUNC(sig) + DECL_ASMFUNC(sign) + DECL_ASMFUNC(band) + DECL_ASMFUNC(bor) + DECL_ASMFUNC(bnot) + DECL_ASMFUNC(if) + DECL_ASMFUNC(fcall) + DECL_ASMFUNC(repeat) + DECL_ASMFUNC(repeatwhile) + DECL_ASMFUNC(equal) + DECL_ASMFUNC(equal_exact) + DECL_ASMFUNC(notequal_exact) + DECL_ASMFUNC(notequal) + DECL_ASMFUNC(below) + DECL_ASMFUNC(above) + DECL_ASMFUNC(beloweq) + DECL_ASMFUNC(aboveeq) + DECL_ASMFUNC(assign) + DECL_ASMFUNC(assign_fromfp) + DECL_ASMFUNC(assign_fast) + DECL_ASMFUNC(assign_fast_fromfp) + DECL_ASMFUNC(add) + DECL_ASMFUNC(sub) + DECL_ASMFUNC(add_op) + DECL_ASMFUNC(sub_op) + DECL_ASMFUNC(add_op_fast) + DECL_ASMFUNC(sub_op_fast) + DECL_ASMFUNC(mul) + DECL_ASMFUNC(div) + DECL_ASMFUNC(mul_op) + DECL_ASMFUNC(div_op) + DECL_ASMFUNC(mul_op_fast) + DECL_ASMFUNC(div_op_fast) + DECL_ASMFUNC(mod) + DECL_ASMFUNC(shl) + DECL_ASMFUNC(shr) + DECL_ASMFUNC(mod_op) + DECL_ASMFUNC(or) + DECL_ASMFUNC(or0) + DECL_ASMFUNC(xor) + DECL_ASMFUNC(xor_op) + DECL_ASMFUNC(and) + DECL_ASMFUNC(or_op) + DECL_ASMFUNC(and_op) + DECL_ASMFUNC(uminus) + DECL_ASMFUNC(invsqrt) + DECL_ASMFUNC(dbg_getstackptr) + + DECL_ASMFUNC(stack_push) + DECL_ASMFUNC(stack_pop) + DECL_ASMFUNC(stack_pop_fast) // just returns value, doesn't mod param + DECL_ASMFUNC(stack_peek) + DECL_ASMFUNC(stack_peek_int) + DECL_ASMFUNC(stack_peek_top) + DECL_ASMFUNC(stack_exch) + +static void *NSEEL_PProc_GRAM(void *data, int data_size, compileContext *ctx) +{ + if (data_size>0) data=EEL_GLUE_set_immediate(data, (INT_PTR)ctx->gram_blocks); + return data; +} + +static void *NSEEL_PProc_Stack(void *data, int data_size, compileContext *ctx) +{ + codeHandleType *ch=ctx->tmpCodeHandle; + + if (data_size>0) + { + UINT_PTR m1=(UINT_PTR)(NSEEL_STACK_SIZE * sizeof(EEL_F) - 1); + UINT_PTR stackptr = ((UINT_PTR) (&ch->stack)); + + ch->want_stack=1; + if (!ch->stack) ch->stack = newDataBlock(NSEEL_STACK_SIZE*sizeof(EEL_F),NSEEL_STACK_SIZE*sizeof(EEL_F)); // stack functions need this alignment + + data=EEL_GLUE_set_immediate(data, stackptr); + data=EEL_GLUE_set_immediate(data, m1); // and + data=EEL_GLUE_set_immediate(data, ((UINT_PTR)ch->stack&~m1)); //or + } + return data; +} + +static void *NSEEL_PProc_Stack_PeekInt(void *data, int data_size, compileContext *ctx, INT_PTR offs) +{ + codeHandleType *ch=ctx->tmpCodeHandle; + + if (data_size>0) + { + UINT_PTR m1=(UINT_PTR)(NSEEL_STACK_SIZE * sizeof(EEL_F) - 1); + UINT_PTR stackptr = ((UINT_PTR) (&ch->stack)); + + ch->want_stack=1; + if (!ch->stack) ch->stack = newDataBlock(NSEEL_STACK_SIZE*sizeof(EEL_F),NSEEL_STACK_SIZE*sizeof(EEL_F)); // stack functions need this alignment + + data=EEL_GLUE_set_immediate(data, stackptr); + data=EEL_GLUE_set_immediate(data, offs); + data=EEL_GLUE_set_immediate(data, m1); // and + data=EEL_GLUE_set_immediate(data, ((UINT_PTR)ch->stack&~m1)); //or + } + return data; +} +static void *NSEEL_PProc_Stack_PeekTop(void *data, int data_size, compileContext *ctx) +{ + codeHandleType *ch=ctx->tmpCodeHandle; + + if (data_size>0) + { + UINT_PTR stackptr = ((UINT_PTR) (&ch->stack)); + + ch->want_stack=1; + if (!ch->stack) ch->stack = newDataBlock(NSEEL_STACK_SIZE*sizeof(EEL_F),NSEEL_STACK_SIZE*sizeof(EEL_F)); // stack functions need this alignment + + data=EEL_GLUE_set_immediate(data, stackptr); + } + return data; +} + +#if defined(_MSC_VER) && _MSC_VER >= 1400 +static double eel__floor(double a) { return floor(a); } +static double eel__ceil(double a) { return ceil(a); } +#define floor eel__floor +#define ceil eel__ceil +#endif + + +#ifdef NSEEL_EEL1_COMPAT_MODE +static double eel1band(double a, double b) +{ + return (fabs(a)>NSEEL_CLOSEFACTOR && fabs(b) > NSEEL_CLOSEFACTOR) ? 1.0 : 0.0; +} +static double eel1bor(double a, double b) +{ + return (fabs(a)>NSEEL_CLOSEFACTOR || fabs(b) > NSEEL_CLOSEFACTOR) ? 1.0 : 0.0; +} + +static double eel1sigmoid(double x, double constraint) +{ + double t = (1+exp(-x * (constraint))); + return fabs(t)>NSEEL_CLOSEFACTOR ? 1.0/t : 0; +} + +#endif + + + +#define FUNCTIONTYPE_PARAMETERCOUNTMASK 0xff + +#define BIF_NPARAMS_MASK 0x7ffff00 +#define BIF_RETURNSONSTACK 0x0000100 +#define BIF_LASTPARMONSTACK 0x0000200 +#define BIF_RETURNSBOOL 0x0000400 +#define BIF_LASTPARM_ASBOOL 0x0000800 +// 0x00?0000 -- taken by FP stack flags +#define BIF_TAKES_VARPARM 0x0400000 +#define BIF_TAKES_VARPARM_EX 0x0C00000 // this is like varparm but check count exactly +#define BIF_WONTMAKEDENORMAL 0x0100000 +#define BIF_CLEARDENORMAL 0x0200000 + +#if GLUE_HAS_FPREG2 > 0 && GLUE_MAX_FPSTACK_SIZE > 0 +#error GLUE_HAS_FPREG2 and GLUE_MAX_FPSTACK_SIZE are exclusive +#endif + +#if GLUE_MAX_SPILL_REGS > 0 && GLUE_HAS_FPREG2 <= 0 +#error GLUE_MAX_SPILL_REGS requires GLUE_HAS_FPREG2 +#endif + +#if GLUE_MAX_FPSTACK_SIZE > 0 || GLUE_HAS_FPREG2 > 0 + #define BIF_SECONDLASTPARMST 0x0001000 // use with BIF_LASTPARMONSTACK only (last two parameters get passed on fp stack) + #define BIF_LAZYPARMORDERING 0x0002000 // allow optimizer to avoid fxch when using BIF_TWOPARMSONFPSTACK_LAZY etc +#else + #define BIF_SECONDLASTPARMST 0 + #define BIF_LAZYPARMORDERING 0 +#endif + +#if GLUE_MAX_FPSTACK_SIZE > 0 + #define BIF_REVERSEFPORDER 0x0004000 // force a fxch (reverse order of last two parameters on fp stack, used by comparison functions) + #ifndef BIF_FPSTACKUSE + #define BIF_FPSTACKUSE(x) (((x)>=0&&(x)<8) ? ((7-(x))<<16):0) + #endif + #ifndef BIF_GETFPSTACKUSE + #define BIF_GETFPSTACKUSE(x) (7 - (((x)>>16)&7)) + #endif +#else + #define BIF_REVERSEFPORDER 0 + #define BIF_FPSTACKUSE(x) 0 + #define BIF_GETFPSTACKUSE(x) 0 +#endif + +#define BIF_TWOPARMSONFPSTACK (BIF_SECONDLASTPARMST|BIF_LASTPARMONSTACK) +#define BIF_TWOPARMSONFPSTACK_LAZY (BIF_LAZYPARMORDERING|BIF_SECONDLASTPARMST|BIF_LASTPARMONSTACK) + + +#ifndef GLUE_HAS_NATIVE_TRIGSQRTLOG +static double sqrt_fabs(double a) { return sqrt(fabs(a)); } +#endif + + +EEL_F NSEEL_CGEN_CALL nseel_int_rand(EEL_F f); + +#define FNPTR_HAS_CONDITIONAL_EXEC(op) \ + (op->fntype == FN_LOGICAL_AND || \ + op->fntype == FN_LOGICAL_OR || \ + op->fntype == FN_IF_ELSE || \ + op->fntype == FN_WHILE || \ + op->fntype == FN_LOOP) + +static functionType fnTable1[] = { +#ifndef GLUE_HAS_NATIVE_TRIGSQRTLOG + { "sin", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL, {&sin} }, + { "cos", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&cos} }, + { "tan", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&tan} }, + { "sqrt", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL, {&sqrt_fabs}, }, + { "log", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&log} }, + { "log10", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&log10} }, +#else + { "sin", nseel_asm_sin, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL|BIF_FPSTACKUSE(1) }, + { "cos", nseel_asm_cos, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL|BIF_FPSTACKUSE(1) }, + { "tan", nseel_asm_tan, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1) }, + { "sqrt", nseel_asm_sqrt, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1)|BIF_WONTMAKEDENORMAL }, + { "log", nseel_asm_log, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(3), }, + { "log10", nseel_asm_log10, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(3), }, +#endif + + + { "asin", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&asin}, }, + { "acos", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&acos}, }, + { "atan", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&atan}, }, + { "atan2", nseel_asm_2pdd, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK, {&atan2}, }, + { "exp", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK, {&exp}, }, + { "abs", nseel_asm_abs, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(0)|BIF_WONTMAKEDENORMAL }, + { "sqr", nseel_asm_sqr, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1) }, + { "min", nseel_asm_min, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_FPSTACKUSE(3)|BIF_WONTMAKEDENORMAL }, + { "max", nseel_asm_max, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_FPSTACKUSE(3)|BIF_WONTMAKEDENORMAL }, + { "sign", nseel_asm_sign, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL, }, + { "rand", nseel_asm_1pdd, 1|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&nseel_int_rand}, }, + + { "floor", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&floor} }, + { "ceil", nseel_asm_1pdd, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL, {&ceil} }, + + { "invsqrt", nseel_asm_invsqrt, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(3), {GLUE_INVSQRT_NEEDREPL} }, + + { "__dbg_getstackptr", nseel_asm_dbg_getstackptr, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1), }, + +#ifdef NSEEL_EEL1_COMPAT_MODE + { "sigmoid", nseel_asm_2pdd, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK, {&eel1sigmoid}, }, + + // these differ from _and/_or, they always evaluate both... + { "band", nseel_asm_2pdd, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_CLEARDENORMAL , {&eel1band}, }, + { "bor", nseel_asm_2pdd, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_CLEARDENORMAL , {&eel1bor}, }, + + {"exec2", NULL, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_WONTMAKEDENORMAL}, + {"exec3", NULL, 3|NSEEL_NPARAMS_FLAG_CONST|BIF_WONTMAKEDENORMAL}, +#endif // end EEL1 compat + + + {"freembuf",_asm_generic1parm,1,{&__NSEEL_RAM_MemFree},NSEEL_PProc_RAM}, + {"memcpy",_asm_generic3parm, 3,{&__NSEEL_RAM_MemCpy},NSEEL_PProc_RAM}, + {"memset",_asm_generic3parm, 3,{&__NSEEL_RAM_MemSet},NSEEL_PProc_RAM}, + {"__memtop",_asm_generic1parm,1,{&__NSEEL_RAM_MemTop},NSEEL_PProc_RAM}, + {"mem_set_values",_asm_generic2parm_retd,2|BIF_TAKES_VARPARM|BIF_RETURNSONSTACK,{&__NSEEL_RAM_Mem_SetValues},NSEEL_PProc_RAM}, + {"mem_get_values",_asm_generic2parm_retd,2|BIF_TAKES_VARPARM|BIF_RETURNSONSTACK,{&__NSEEL_RAM_Mem_GetValues},NSEEL_PProc_RAM}, + + {"stack_push",nseel_asm_stack_push,1|BIF_FPSTACKUSE(0),{0,},NSEEL_PProc_Stack}, + {"stack_pop",nseel_asm_stack_pop, 1|BIF_FPSTACKUSE(1),{0,},NSEEL_PProc_Stack}, + {"stack_peek",nseel_asm_stack_peek,1|NSEEL_NPARAMS_FLAG_CONST|BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(0),{0,},NSEEL_PProc_Stack}, + {"stack_exch",nseel_asm_stack_exch,1|BIF_FPSTACKUSE(1), {0,},NSEEL_PProc_Stack_PeekTop}, +}; +static functionType fn_min2 = { "min2", nseel_asm_min_fp, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_WONTMAKEDENORMAL }; +static functionType fn_max2 = { "max2", nseel_asm_max_fp, 2|NSEEL_NPARAMS_FLAG_CONST|BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_WONTMAKEDENORMAL }; +static functionType fn_or0 = { "or0", nseel_asm_or0, 1|NSEEL_NPARAMS_FLAG_CONST|BIF_LASTPARMONSTACK|BIF_RETURNSONSTACK|BIF_CLEARDENORMAL }; + +static eel_function_table default_user_funcs; + +static int functable_lowerbound(functionType *list, int list_sz, const char *name, int *ismatch) +{ + int a = 0, c = list_sz; + while (a != c) + { + const int b = (a+c)/2; + const int cmp = stricmp(name,list[b].name); + if (cmp > 0) a = b+1; + else if (cmp < 0) c = b; + else + { + *ismatch = 1; + return b; + } + } + *ismatch = 0; + return a; +} + +static int funcTypeCmp(const void *a, const void *b) { return stricmp(((functionType*)a)->name,((functionType*)b)->name); } + +functionType *nseel_getFunctionByName(compileContext *ctx, const char *name, int *mchk) +{ + eel_function_table *tab = ctx && ctx->registered_func_tab ? ctx->registered_func_tab : &default_user_funcs; + static char sorted; + const int fn1size = (int) (sizeof(fnTable1)/sizeof(fnTable1[0])); + int idx,match; + if (!sorted) + { + NSEEL_HOSTSTUB_EnterMutex(); + if (!sorted) qsort(fnTable1,fn1size,sizeof(fnTable1[0]),funcTypeCmp); + sorted=1; + NSEEL_HOSTSTUB_LeaveMutex(); + } + idx=functable_lowerbound(fnTable1,fn1size,name,&match); + if (match) return fnTable1+idx; + + if ((!ctx || !(ctx->current_compile_flags&NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS)) && tab->list) + { + idx=functable_lowerbound(tab->list,tab->list_size,name,&match); + if (match) + { + if (mchk) + { + while (idx>0 && !stricmp(tab->list[idx-1].name,name)) idx--; + *mchk = tab->list_size - 1 - idx; + } + return tab->list + idx; + } + } + + return NULL; +} + +functionType *nseel_enumFunctions(compileContext *ctx, int idx) +{ + eel_function_table *tab = ctx && ctx->registered_func_tab ? ctx->registered_func_tab : &default_user_funcs; + const int fn1size = (int) (sizeof(fnTable1)/sizeof(fnTable1[0])); + if (idx >= 0 && idx < fn1size) return fnTable1 + idx; + if ((!ctx || !(ctx->current_compile_flags&NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS)) && tab->list) + { + idx -= fn1size; + if (idx>=0 && idx < tab->list_size) + return tab->list + idx; + } + + return NULL; +} + +int NSEEL_init() // returns 0 on success +{ + NSEEL_quit(); + return 0; +} + +void NSEEL_quit() +{ + free(default_user_funcs.list); + default_user_funcs.list = NULL; + default_user_funcs.list_size = 0; +} + +void NSEEL_addfunc_varparm_ex(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination) +{ + NSEEL_addfunctionex2(name,min_np|(want_exact?BIF_TAKES_VARPARM_EX:BIF_TAKES_VARPARM),(char *)_asm_generic2parm_retd,0,pproc,fptr,NULL,destination); +} + +void NSEEL_addfunc_varparm_ctxptr(const char *name, int min_np, int want_exact, void *ctxptr, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination) +{ + NSEEL_addfunctionex2(name,min_np|(want_exact?BIF_TAKES_VARPARM_EX:BIF_TAKES_VARPARM),(char *)_asm_generic2parm_retd,0,NULL,ctxptr,fptr,destination); +} + +void NSEEL_addfunc_varparm_ctxptr2(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, void *ctx, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, void *, INT_PTR, EEL_F **), eel_function_table *destination) +{ + NSEEL_addfunctionex2(name,min_np|(want_exact?BIF_TAKES_VARPARM_EX:BIF_TAKES_VARPARM),(char *)_asm_generic2xparm_retd,0,pproc,ctx,fptr,destination); +} + +void NSEEL_addfunc_ret_type(const char *name, int np, int ret_type, NSEEL_PPPROC pproc, void *fptr, eel_function_table *destination) // ret_type=-1 for bool, 1 for value, 0 for ptr +{ + char *stub=NULL; + int stubsz=0; +#define DOSTUB(np) { \ + stub = (ret_type == 1 ? (char*)_asm_generic##np##parm_retd : (char*)_asm_generic##np##parm); \ + } + + if (np == 1) DOSTUB(1) + else if (np == 2) DOSTUB(2) + else if (np == 3) DOSTUB(3) +#undef DOSTUB + + if (stub) NSEEL_addfunctionex2(name,np|(ret_type == -1 ? BIF_RETURNSBOOL:0), stub, stubsz, pproc,fptr,NULL,destination); +} + +void NSEEL_addfunctionex2(const char *name, int nparms, char *code_startaddr, int code_len /* ignored*/, + NSEEL_PPPROC pproc, void *fptr, void *fptr2, eel_function_table *destination) +{ + const int list_size_chunk = 128; + functionType *r; + if (!destination) destination = &default_user_funcs; + + if (!destination->list || !(destination->list_size & (list_size_chunk-1))) + { + void *nv = realloc(destination->list, (destination->list_size + list_size_chunk)*sizeof(functionType)); + if (!nv) return; + destination->list = (functionType *)nv; + } + if (destination->list) + { + int match,idx; + + idx=functable_lowerbound(destination->list,destination->list_size,name,&match); + + r = destination->list + idx; + if (idx < destination->list_size) + memmove(r + 1, r, (destination->list_size - idx) * sizeof(functionType)); + destination->list_size++; + + memset(r, 0, sizeof(functionType)); + + if (!(nparms & BIF_RETURNSBOOL)) + { + if (code_startaddr == (void *)&_asm_generic1parm_retd || + code_startaddr == (void *)&_asm_generic2parm_retd || + code_startaddr == (void *)&_asm_generic2xparm_retd || + code_startaddr == (void *)&_asm_generic3parm_retd) + { + nparms |= BIF_RETURNSONSTACK; + } + } + r->nParams = nparms; + r->name = name; + r->afunc = code_startaddr; + r->pProc = pproc; + r->replptrs[0] = fptr; + r->replptrs[1] = fptr2; + } +} + + +//--------------------------------------------------------------------------------------------------------------- +static void freeBlocks(llBlock **start, int is_code) +{ + llBlock *s=*start; + *start=0; + while (s) + { + llBlock *llB = s->next; +#ifndef EEL_DOESNT_NEED_EXEC_PERMS + if (is_code) + { + #ifdef _WIN32 + VirtualFree(s, 0, MEM_RELEASE); + #else + munmap(s,sizeof(*s) + s->sizealloc); + #endif + } + else +#endif + { + free(s); + } + s=llB; + } +} + + +//--------------------------------------------------------------------------------------------------------------- +static void *__newBlock_align(llBlock **start, int size, int align, int is_for_code) +{ + llBlock *llb = *start; + int alloc_amt, align_pos, scan_cnt=8; + if (WDL_NOT_NORMALLY(align < 1)) align = 1; + + while (llb && --scan_cnt > 0) + { + const int sizeused = llb->sizeused; + if (sizeused + size <= llb->sizealloc) + { + align_pos = (int) (((INT_PTR)eel_get_llblock_buffer(llb) + sizeused)&(align-1)); + if (align_pos) align_pos = align - align_pos; + + if (sizeused + size + align_pos <= llb->sizealloc) + { + llb->sizeused += size + align_pos; + return eel_get_llblock_buffer(llb) + sizeused + align_pos; + } + } + llb = llb->next; + } + +#ifndef EEL_DOESNT_NEED_EXEC_PERMS + if (is_for_code) + { + const int code_page_size = eel_get_page_size(); + alloc_amt = (sizeof(*llb) + size + code_page_size - 1) & ~(code_page_size-1); + #ifdef _WIN32 + llb = (llBlock *)VirtualAlloc(NULL,alloc_amt,MEM_COMMIT,PAGE_READWRITE); + if (llb == NULL) return NULL; + #else + #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) + llb = (llBlock *)mmap(NULL,alloc_amt, PROT_READ|PROT_WRITE,MAP_ANON|MAP_PRIVATE,-1,0); + #else + llb = (llBlock *)mmap(NULL,alloc_amt, PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_PRIVATE,-1,0); + #endif + if (llb == MAP_FAILED) return NULL; + #endif + alloc_amt -= sizeof(*llb); + align_pos = 0; + + WDL_ASSERT(((INT_PTR)llb & (code_page_size - 1))==0); + WDL_ASSERT(((INT_PTR)(eel_get_llblock_buffer(llb) + alloc_amt) & (code_page_size - 1))==0); + } + else +#endif + { + // data block, allocate in larger chunks + alloc_amt = (size + align - 1 + 31)&~31; + if (alloc_amt < 65536-64) alloc_amt = 65536-64; + + llb = (llBlock *)malloc(sizeof(*llb) + alloc_amt); + if (!llb) return NULL; + align_pos = (int) (((INT_PTR)eel_get_llblock_buffer(llb))&(align-1)); + if (align_pos) align_pos = align - align_pos; + } + + WDL_ASSERT(size+align_pos <= alloc_amt); + llb->sizeused = size + align_pos; + llb->sizealloc = alloc_amt; + llb->next = *start; + *start = llb; + return eel_get_llblock_buffer(llb) + align_pos; +} + + +//--------------------------------------------------------------------------------------------------------------- +opcodeRec *nseel_createCompiledValue(compileContext *ctx, EEL_F value) +{ + opcodeRec *r=newOpCode(ctx,NULL,OPCODETYPE_DIRECTVALUE); + if (r) + { + r->parms.dv.directValue = value; + } + return r; +} + +opcodeRec *nseel_createCompiledValuePtr(compileContext *ctx, EEL_F *addrValue, const char *namestr) +{ + opcodeRec *r=newOpCode(ctx,namestr,OPCODETYPE_VARPTR); + if (!r) return 0; + + r->parms.dv.valuePtr=addrValue; + + return r; +} + +static int validate_varname_for_function(compileContext *ctx, const char *name) +{ + if (!ctx->function_curName || !ctx->function_globalFlag) return 1; + + if (ctx->function_localTable_Size[2] > 0 && ctx->function_localTable_Names[2]) + { + char * const * const namelist = ctx->function_localTable_Names[2]; + const int namelist_sz = ctx->function_localTable_Size[2]; + int i; + const size_t name_len = strlen(name); + + for (i=0;i 1 && nmchk[l-1] == '*') + { + if (name_len >= l && !strnicmp(nmchk,name,l-1) && name[l-1]=='.') return 1; + } + else + { + if (name_len == l && !stricmp(nmchk,name)) return 1; + } + } + } + + return 0; +} + +opcodeRec *nseel_resolve_named_symbol(compileContext *ctx, opcodeRec *rec, int parmcnt, int *errOut) +{ + const int isFunctionMode = parmcnt >= 0; + int rel_prefix_len=0; + int rel_prefix_idx=-2; + int i; + char match_parmcnt[4]={-1,-1,-1,-1}; // [3] is guess + unsigned char match_parmcnt_pos=0; + char *sname = (char *)rec->relname; + int is_string_prefix = parmcnt < 0 && sname[0] == '#'; + const char *prevent_function_calls = NULL; + + if (errOut) *errOut = 0; + + if (sname) sname += is_string_prefix; + + if (rec->opcodeType != OPCODETYPE_VARPTR || !sname || !sname[0]) return NULL; + + if (!isFunctionMode && !is_string_prefix && !strnicmp(sname,"reg",3) && isdigit(sname[3]) && isdigit(sname[4]) && !sname[5]) + { + EEL_F *a=get_global_var(ctx,sname,1); + if (a) + { + rec->parms.dv.valuePtr = a; + sname[0]=0; // for dump_ops compat really, but this shouldn't be needed anyway + } + return rec; + } + + if (ctx->function_curName) + { + if (!strnicmp(sname,"this.",5)) + { + rel_prefix_len=5; + rel_prefix_idx=-1; + } + else if (!stricmp(sname,"this")) + { + rel_prefix_len=4; + rel_prefix_idx=-1; + } + + // scan for parameters/local variables before user functions + if (rel_prefix_idx < -1 && + ctx->function_localTable_Size[0] > 0 && + ctx->function_localTable_Names[0] && + ctx->function_localTable_ValuePtrs) + { + char * const * const namelist = ctx->function_localTable_Names[0]; + const int namelist_sz = ctx->function_localTable_Size[0]; + for (i=0; i < namelist_sz; i++) + { + const char *p = namelist[i]; + if (p) + { + if (!isFunctionMode && !is_string_prefix && !strnicmp(p,sname,NSEEL_MAX_VARIABLE_NAMELEN)) + { + rec->opcodeType = OPCODETYPE_VARPTRPTR; + rec->parms.dv.valuePtr=(EEL_F *)(ctx->function_localTable_ValuePtrs+i); + rec->parms.dv.directValue=0.0; + return rec; + } + else + { + const size_t plen = strlen(p); + if (plen > 1 && p[plen-1] == '*' && !strnicmp(p,sname,plen-1) && ((sname[plen-1] == '.'&&sname[plen]) || !sname[plen-1])) + { + rel_prefix_len=(int) (sname[plen-1] ? plen : plen-1); + rel_prefix_idx=i; + break; + } + } + } + } + } + // if instance name set, translate sname or sname.* into "this.sname.*" + if (rel_prefix_idx < -1 && + ctx->function_localTable_Size[1] > 0 && + ctx->function_localTable_Names[1]) + { + char * const * const namelist = ctx->function_localTable_Names[1]; + const int namelist_sz = ctx->function_localTable_Size[1]; + const char *full_sname = rec->relname; // include # in checks + for (i=0; i < namelist_sz; i++) + { + const char *p = namelist[i]; + if (p && *p) + { + const size_t tl = strlen(p); + if (!strnicmp(p,full_sname,tl) && (full_sname[tl] == 0 || full_sname[tl] == '.')) + { + rel_prefix_len=0; // treat as though this. prefixes is present + rel_prefix_idx=-1; + break; + } + } + } + } + if (rel_prefix_idx >= -1) + { + ctx->function_usesNamespaces=1; + } + } // ctx->function_curName + + if (!isFunctionMode) + { + // instance variables + if (rel_prefix_idx >= -1) + { + rec->opcodeType = OPCODETYPE_VALUE_FROM_NAMESPACENAME; + rec->namespaceidx = rel_prefix_idx; + if (rel_prefix_len > 0) + { + if (is_string_prefix) sname[-1] = '#'; + memmove(sname, sname+rel_prefix_len, strlen(sname + rel_prefix_len) + 1); + } + } + else + { + // no namespace index, so it must be a global + if (!validate_varname_for_function(ctx,rec->relname)) + { + if (errOut) *errOut = 1; + if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); + snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"global '%s' inaccessible",rec->relname); + return NULL; + } + } + + return rec; + } + + if (ctx->func_check) + prevent_function_calls = ctx->func_check(sname,ctx->func_check_user); + + ////////// function mode + // first off, while() and loop() are special and can't be overridden + // + if (parmcnt == 1 && !stricmp("while",sname) && !prevent_function_calls) + { + rec->opcodeType = OPCODETYPE_FUNC1; + rec->fntype = FN_WHILE; + return rec; + } + if (parmcnt == 2 && !stricmp("loop",sname) && !prevent_function_calls) + { + rec->opcodeType = OPCODETYPE_FUNC2; + rec->fntype = FN_LOOP; + return rec; + } + + // + // resolve user function names before builtin functions -- this allows the user to override default functions + if (!(ctx->current_compile_flags & NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS)) + { + _codeHandleFunctionRec *best=NULL; + size_t bestlen=0; + const char * const ourcall = sname+rel_prefix_len; + const size_t ourcall_len = strlen(ourcall); + int pass; + for (pass=0;pass<2;pass++) + { + _codeHandleFunctionRec *fr = pass ? ctx->functions_common : ctx->functions_local; + // sname is [namespace.[ns.]]function, find best match of function that matches the right end + while (fr) + { + int this_np = fr->num_params; + const char *thisfunc = fr->fname; + const size_t thisfunc_len = strlen(thisfunc); + if (this_np < 1) this_np=1; + if (thisfunc_len == ourcall_len && !stricmp(thisfunc,ourcall)) + { + if (this_np == parmcnt) + { + bestlen = thisfunc_len; + best = fr; + break; // found exact match, finished + } + else + { + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = fr->num_params; + } + } + + if (thisfunc_len > bestlen && thisfunc_len < ourcall_len && ourcall[ourcall_len - thisfunc_len - 1] == '.' && !stricmp(thisfunc,ourcall + ourcall_len - thisfunc_len)) + { + if (this_np == parmcnt) + { + bestlen = thisfunc_len; + best = fr; + } + else + if (match_parmcnt[3]<0) match_parmcnt[3]=fr->num_params; + } + fr=fr->next; + } + if (fr) break; // found exact match, finished + } + + if (best) + { + switch (parmcnt) + { + case 0: + case 1: rec->opcodeType = OPCODETYPE_FUNC1; break; + case 2: rec->opcodeType = OPCODETYPE_FUNC2; break; + case 3: rec->opcodeType = OPCODETYPE_FUNC3; break; + default: rec->opcodeType = OPCODETYPE_FUNCX; break; + } + if (ourcall != rec->relname) memmove((char *)rec->relname, ourcall, strlen(ourcall)+1); + + if (ctx->function_curName && rel_prefix_idx<0) + { + // if no namespace specified, and this.commonprefix.func() called, remove common prefixes and set prefixidx to be this + const char *p=ctx->function_curName; + if (*p) p++; + while (*p && *p != '.') p++; + if (*p && p[1]) // we have a dot! + { + while (p[1]) p++; // go to last char of string, which doesn't allow possible trailing dot to be checked + + while (--p > ctx->function_curName) // do not check possible leading dot + { + if (*p == '.') + { + const size_t cmplen = p+1-ctx->function_curName; + if (!strnicmp(rec->relname,ctx->function_curName,cmplen) && rec->relname[cmplen]) + { + const char *src=rec->relname + cmplen; + memmove((char *)rec->relname, src, strlen(src)+1); + rel_prefix_idx=-1; + ctx->function_usesNamespaces=1; + break; + } + } + } + } + } + + if (ctx->function_curName && rel_prefix_idx < -1 && + strchr(rec->relname,'.') && !validate_varname_for_function(ctx,rec->relname)) + { + if (errOut) *errOut = 1; + if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); + snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"namespaced function '%s' inaccessible",rec->relname); + return NULL; + } + + rec->namespaceidx = rel_prefix_idx; + rec->fntype = FUNCTYPE_EELFUNC; + rec->fn = best; + return rec; + } + } + + if (prevent_function_calls) + { + if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); + snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"'%.30s': %s",sname, prevent_function_calls); + if (errOut) *errOut = 0; + return NULL; + } + +#ifdef NSEEL_EEL1_COMPAT_MODE + if (!stricmp(sname,"assign")) + { + if (parmcnt == 2) + { + rec->opcodeType = OPCODETYPE_FUNC2; + rec->fntype = FN_ASSIGN; + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; + } + else if (!stricmp(sname,"if")) + { + if (parmcnt == 3) + { + rec->opcodeType = OPCODETYPE_FUNC3; + rec->fntype = FN_IF_ELSE; + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 3; + } + else if (!stricmp(sname,"equal")) + { + if (parmcnt == 2) + { + rec->opcodeType = OPCODETYPE_FUNC2; + rec->fntype = FN_EQ; + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; + } + else if (!stricmp(sname,"below")) + { + if (parmcnt == 2) + { + rec->opcodeType = OPCODETYPE_FUNC2; + rec->fntype = FN_LT; + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; + } + else if (!stricmp(sname,"above")) + { + if (parmcnt == 2) + { + rec->opcodeType = OPCODETYPE_FUNC2; + rec->fntype = FN_GT; + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; + } + else if (!stricmp(sname,"bnot")) + { + if (parmcnt == 1) + { + rec->opcodeType = OPCODETYPE_FUNC1; + rec->fntype = FN_NOT; + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 1; + } + else if (!stricmp(sname,"megabuf")) + { + if (parmcnt == 1) + { + rec->opcodeType = OPCODETYPE_FUNC1; + rec->fntype = FN_MEMORY; + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 1; + } + else if (!stricmp(sname,"gmegabuf")) + { + if (parmcnt == 1) + { + rec->opcodeType = OPCODETYPE_FUNC1; + rec->fntype = FN_GMEMORY; + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 1; + } + else +#endif + // convert legacy pow() to FN_POW + if (!stricmp("pow",sname)) + { + if (parmcnt == 2) + { + rec->opcodeType = OPCODETYPE_FUNC2; + rec->fntype = FN_POW; + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = 2; + } + else if (!stricmp("__denormal_likely",sname) || !stricmp("__denormal_unlikely",sname)) + { + if (parmcnt == 1) + { + rec->opcodeType = OPCODETYPE_FUNC1; + rec->fntype = !stricmp("__denormal_likely",sname) ? FN_DENORMAL_LIKELY : FN_DENORMAL_UNLIKELY; + return rec; + } + } + + { + int chkamt=0; + functionType *f=nseel_getFunctionByName(ctx,sname,&chkamt); + if (f) while (chkamt-->=0) + { + const int pc_needed=(f->nParams&FUNCTIONTYPE_PARAMETERCOUNTMASK); + if ((f->nParams&BIF_TAKES_VARPARM_EX)==BIF_TAKES_VARPARM ? (parmcnt >= pc_needed) : (parmcnt == pc_needed)) + { + rec->fntype = FUNCTYPE_FUNCTIONTYPEREC; + rec->fn = (void *)f; + switch (parmcnt) + { + case 0: + case 1: rec->opcodeType = OPCODETYPE_FUNC1; break; + case 2: rec->opcodeType = OPCODETYPE_FUNC2; break; + case 3: rec->opcodeType = OPCODETYPE_FUNC3; break; + default: rec->opcodeType = OPCODETYPE_FUNCX; break; + } + return rec; + } + if (match_parmcnt_pos < 3) match_parmcnt[match_parmcnt_pos++] = (f->nParams&FUNCTIONTYPE_PARAMETERCOUNTMASK); + f++; + if (stricmp(f->name,sname)) break; + } + } + if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); + if (match_parmcnt[3] >= 0) + { + if (match_parmcnt_pos<3) match_parmcnt[match_parmcnt_pos] = match_parmcnt[3]; + match_parmcnt_pos++; + } + + if (!match_parmcnt_pos) + snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"'%.30s' undefined",sname); + else + { + int x; + snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"'%.30s' needs ",sname); + for (x = 0; x < match_parmcnt_pos; x++) + snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"%s%d",x==0?"" : x == match_parmcnt_pos-1?" or ":",",match_parmcnt[x]); + lstrcatn(ctx->last_error_string," parms",sizeof(ctx->last_error_string)); + } + if (errOut) *errOut = match_parmcnt_pos > 0 ? parmcntopcodeType != OPCODETYPE_VARPTR || !fn->relname || !fn->relname[0]) + { + return NULL; + } + fn->parms.parms[0] = code1; + fn->parms.parms[1] = code2; + fn->parms.parms[2] = code3; + + for (x=0;x<3;x++) + { + opcodeRec *prni=fn->parms.parms[x]; + while (prni && np < NSEEL_MAX_EELFUNC_PARAMETERS) + { + const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS; + np++; + if (!isMP) break; + prni = prni->parms.parms[1]; + } + } + r = nseel_resolve_named_symbol(ctx, fn, np<1 ? 1 : np ,errOut); + if (postCode && r) + { + if (code1 && r->opcodeType == OPCODETYPE_FUNC1 && r->fntype == FN_WHILE) + { + // change while(x) (postcode) to be + // while ((x) ? (postcode;1) : 0); + + r->parms.parms[0] = + nseel_createIfElse(ctx,r->parms.parms[0], + nseel_createSimpleCompiledFunction(ctx,FN_JOIN_STATEMENTS,2,postCode,nseel_createCompiledValue(ctx,1.0f)), + NULL); // NULL defaults to 0.0 + + } + else + { + snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"syntax error following function"); + *errOut = -1; + return NULL; + } + } + return r; +} + + +struct eelStringSegmentRec *nseel_createStringSegmentRec(compileContext *ctx, const char *str, int len) +{ + struct eelStringSegmentRec *r = newTmpBlock(ctx,sizeof(struct eelStringSegmentRec)); + if (r) + { + r->_next=0; + r->str_start=str; + r->str_len = len; + } + return r; +} + +opcodeRec *nseel_eelMakeOpcodeFromStringSegments(compileContext *ctx, struct eelStringSegmentRec *rec) +{ + if (ctx && ctx->onString) + { + return nseel_createCompiledValue(ctx, ctx->onString(ctx->caller_this,rec)); + } + + return NULL; +} + +opcodeRec *nseel_createMoreParametersOpcode(compileContext *ctx, opcodeRec *code1, opcodeRec *code2) +{ + opcodeRec *r=code1 && code2 ? newOpCode(ctx,NULL,OPCODETYPE_MOREPARAMS) : NULL; + if (r) + { + r->parms.parms[0] = code1; + r->parms.parms[1] = code2; + } + return r; +} + + +opcodeRec *nseel_createIfElse(compileContext *ctx, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3) +{ + opcodeRec *r=code1 ? newOpCode(ctx,NULL,OPCODETYPE_FUNC3) : NULL; + if (r) + { + if (!code2) code2 = nseel_createCompiledValue(ctx,0.0); + if (!code3) code3 = nseel_createCompiledValue(ctx,0.0); + if (!code2||!code3) return NULL; + + r->fntype = FN_IF_ELSE; + r->parms.parms[0] = code1; + r->parms.parms[1] = code2; + r->parms.parms[2] = code3; + } + return r; +} + + +opcodeRec *nseel_createMemoryAccess(compileContext *ctx, opcodeRec *code1, opcodeRec *code2) +{ + if (code1 && code1->opcodeType == OPCODETYPE_VARPTR && !stricmp(code1->relname,"gmem")) + { + return nseel_createSimpleCompiledFunction(ctx, FN_GMEMORY,1,code2?code2:nseel_createCompiledValue(ctx,0.0),0); + } + if (code2 && (code2->opcodeType != OPCODETYPE_DIRECTVALUE || code2->parms.dv.directValue != 0.0)) + { + code1 = nseel_createSimpleCompiledFunction(ctx,FN_ADD,2,code1,code2); + } + return nseel_createSimpleCompiledFunction(ctx, FN_MEMORY,1,code1,0); +} + +opcodeRec *nseel_createSimpleCompiledFunction(compileContext *ctx, int fn, int np, opcodeRec *code1, opcodeRec *code2) +{ + opcodeRec *r=code1 && (np<2 || code2) ? newOpCode(ctx,NULL,np>=2 ? OPCODETYPE_FUNC2:OPCODETYPE_FUNC1) : NULL; + if (r) + { + r->fntype = fn; + r->parms.parms[0] = code1; + r->parms.parms[1] = code2; + if (fn == FN_JOIN_STATEMENTS) + { + r->fn = r; // for joins, fn is temporarily used for tail pointers + if (code1 && code1->opcodeType == OPCODETYPE_FUNC2 && code1->fntype == fn) + { + opcodeRec *t = (opcodeRec *)code1->fn; + // keep joins in the form of dosomething->morestuff. + // in this instance, code1 is previous stuff to do, code2 is new stuff to do + r->parms.parms[0] = t->parms.parms[1]; + + code1->fn = (t->parms.parms[1] = r); + return code1; + } + } + } + return r; +} + + +// these are bitmasks; on request you can tell what is supported, and compileOpcodes will return one of them +#define RETURNVALUE_IGNORE 0 // ignore return value +#define RETURNVALUE_NORMAL 1 // pointer +#define RETURNVALUE_FPSTACK 2 +#define RETURNVALUE_BOOL 4 // P1 is nonzero if true +#define RETURNVALUE_BOOL_REVERSED 8 // P1 is zero if true +#define RETURNVALUE_CACHEABLE 16 // only to be used when (at least) RETURNVALUE_NORMAL is set +#if GLUE_HAS_FPREG2 > 0 +#define RETURNVALUE_FPREG2 32 // only usable for compileOpcodes() on a trivial opcode +#endif + + + +static int compileOpcodes(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTable, const namespaceInformation *namespacePathToThis, + int supportedReturnValues, int *rvType, int *fpStackUsage, int *canHaveDenormalOutput); + + +static unsigned char *compileCodeBlockWithRet(compileContext *ctx, opcodeRec *rec, int *computTableSize, const namespaceInformation *namespacePathToThis, + int supportedReturnValues, int *rvType, int *fpStackUse, int *canHaveDenormalOutput); + +_codeHandleFunctionRec *eel_createFunctionNamespacedInstance(compileContext *ctx, _codeHandleFunctionRec *fr, const char *nameptr) +{ + size_t n; + _codeHandleFunctionRec *subfr = + fr->isCommonFunction ? + ctx->isSharedFunctions ? newDataBlock(sizeof(_codeHandleFunctionRec),8) : + newCtxDataBlock(sizeof(_codeHandleFunctionRec),8) : // if common function, but derived version is in non-common context, set ownership to VM rather than us + newTmpBlock(ctx,sizeof(_codeHandleFunctionRec)); + + if (!subfr) return 0; + // fr points to functionname()'s rec, nameptr to blah.functionname() + + *subfr = *fr; + n = strlen(nameptr); + if (n > sizeof(subfr->fname)-1) n=sizeof(subfr->fname)-1; + memcpy(subfr->fname,nameptr,n); + subfr->fname[n]=0; + + subfr->next = NULL; + subfr->startptr=0; // make sure this code gets recompiled (with correct member ptrs) for this instance! + subfr->startptr_size=-1; + + // subfr->derivedCopies already points to the right place + fr->derivedCopies = subfr; + + return subfr; + +} +static void combineNamespaceFields(char *nm, const namespaceInformation *namespaceInfo, const char *relname, int thisctx) // nm must be NSEEL_MAX_VARIABLE_NAMELEN+1 bytes +{ + const char *prefix = namespaceInfo ? + thisctx<0 ? (thisctx == -1 ? namespaceInfo->namespacePathToThis : NULL) : (thisctx < MAX_SUB_NAMESPACES ? namespaceInfo->subParmInfo[thisctx] : NULL) + : NULL; + int lfp = 0, lrn=relname ? (int)strlen(relname) : 0; + if (prefix) while (prefix[lfp] && prefix[lfp] != ':' && lfp < NSEEL_MAX_VARIABLE_NAMELEN) lfp++; + if (!relname) relname = ""; + + while (*relname == '.') // if relname begins with ., then remove a chunk of context from prefix + { + relname++; + while (lfp>0 && prefix[lfp-1] != '.') lfp--; + if (lfp>0) lfp--; + } + + if (lfp > NSEEL_MAX_VARIABLE_NAMELEN-3) lfp=NSEEL_MAX_VARIABLE_NAMELEN-3; + if (lfp>0) memcpy(nm,prefix,lfp); + + if (lrn > NSEEL_MAX_VARIABLE_NAMELEN - lfp - (lfp>0)) lrn=NSEEL_MAX_VARIABLE_NAMELEN - lfp - (lfp>0); + if (lrn > 0) + { + if (lfp>0) nm[lfp++] = '.'; + memcpy(nm+lfp,relname,lrn); + lfp+=lrn; + } + nm[lfp++]=0; +} + + +//--------------------------------------------------------------------------------------------------------------- +static void *nseel_getBuiltinFunctionAddress(compileContext *ctx, + int fntype, void *fn, + NSEEL_PPPROC *pProc, void ***replList, + int *abiInfo, int preferredReturnValues, const EEL_F *hasConstParm1, const EEL_F *hasConstParm2) +{ + const EEL_F *firstConstParm = hasConstParm1 ? hasConstParm1 : hasConstParm2; + static void *pow_replptrs[4]={&pow,}; + + switch (fntype) + { +#define RF(x) return (void*)nseel_asm_##x + + case FN_MUL_OP: + *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(mul_op); + case FN_DIV_OP: + *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(div_op); + case FN_OR_OP: + *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(or_op); + case FN_XOR_OP: + *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(xor_op); + case FN_AND_OP: + *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(and_op); + case FN_MOD_OP: + *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(mod_op); + case FN_ADD_OP: + *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(add_op); + case FN_SUB_OP: + *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(sub_op); + case FN_POW_OP: + *abiInfo=BIF_LASTPARMONSTACK|BIF_CLEARDENORMAL; + *replList = pow_replptrs; + RF(2pdds); + case FN_POW: + *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK;//BIF_FPSTACKUSE(2) might be safe, need to look at pow()'s implementation, but safer bet is to disallow fp stack caching for this expression + *replList = pow_replptrs; + RF(2pdd); + case FN_ADD: + *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2); + // for x +- non-denormal-constant, we can set BIF_CLEARDENORMAL + if (firstConstParm && fabs(*firstConstParm) > DENORMAL_CLEARING_THRESHOLD) *abiInfo |= BIF_CLEARDENORMAL; + RF(add); + case FN_SUB: + *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2); + // for x +- non-denormal-constant, we can set BIF_CLEARDENORMAL + if (firstConstParm && fabs(*firstConstParm) > DENORMAL_CLEARING_THRESHOLD) *abiInfo |= BIF_CLEARDENORMAL; + RF(sub); + case FN_MULTIPLY: + *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2); + // for x*constant-greater-than-eq-1, we can set BIF_WONTMAKEDENORMAL + if (firstConstParm && fabs(*firstConstParm) >= 1.0) *abiInfo |= BIF_WONTMAKEDENORMAL; + RF(mul); + case FN_DIVIDE: + *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2); + // for x/constant-less-than-eq-1, we can set BIF_WONTMAKEDENORMAL + if (firstConstParm && fabs(*firstConstParm) <= 1.0) *abiInfo |= BIF_WONTMAKEDENORMAL; + RF(div); + case FN_MOD: + *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL; + RF(mod); + case FN_ASSIGN: + *abiInfo = BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL; + RF(assign); + case FN_AND: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(and); + case FN_OR: *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; RF(or); + case FN_XOR: + *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK_LAZY|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(xor); + case FN_SHR: + *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(shr); + case FN_SHL: + *abiInfo = BIF_RETURNSONSTACK|BIF_TWOPARMSONFPSTACK|BIF_FPSTACKUSE(2)|BIF_CLEARDENORMAL; + RF(shl); + case FN_NOTNOT: *abiInfo = BIF_LASTPARM_ASBOOL|BIF_RETURNSBOOL|BIF_FPSTACKUSE(1); break; + case FN_UMINUS: *abiInfo = BIF_RETURNSONSTACK|BIF_LASTPARMONSTACK|BIF_WONTMAKEDENORMAL; RF(uminus); + case FN_NOT: *abiInfo = BIF_LASTPARM_ASBOOL|BIF_RETURNSBOOL|BIF_FPSTACKUSE(1); RF(bnot); + + case FN_EQ: + *abiInfo = BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); + RF(equal); + case FN_EQ_EXACT: + *abiInfo=BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); + RF(equal_exact); + case FN_NE: + *abiInfo=BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); + RF(notequal); + case FN_NE_EXACT: + *abiInfo=BIF_TWOPARMSONFPSTACK_LAZY|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); + RF(notequal_exact); + case FN_LOGICAL_AND: + *abiInfo = BIF_RETURNSBOOL; + RF(band); + case FN_LOGICAL_OR: + *abiInfo = BIF_RETURNSBOOL; + RF(bor); + + case FN_GT: + *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); + RF(above); + case FN_GTE: + *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_REVERSEFPORDER|BIF_FPSTACKUSE(2); +#if BIF_REVERSEFPORDER > 0 + RF(beloweq); +#else + RF(aboveeq); +#endif + case FN_LT: + *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_REVERSEFPORDER|BIF_FPSTACKUSE(2); +#if BIF_REVERSEFPORDER > 0 + RF(above); +#else + RF(below); +#endif + case FN_LTE: + *abiInfo = BIF_TWOPARMSONFPSTACK|BIF_RETURNSBOOL|BIF_FPSTACKUSE(2); + RF(beloweq); + +#undef RF +#define RF(x) return (void*)_asm_##x + + case FN_MEMORY: + { + static void *replptrs[4]={&__NSEEL_RAMAlloc,}; + *replList = replptrs; + *abiInfo = BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL; + #ifdef GLUE_MEM_NEEDS_PPROC + *pProc = NSEEL_PProc_RAM; + #endif + RF(megabuf); + } + break; + case FN_GMEMORY: + { + static void *replptrs[4]={&__NSEEL_RAMAllocGMEM,}; + *replList = replptrs; + *abiInfo=BIF_LASTPARMONSTACK|BIF_FPSTACKUSE(1)|BIF_CLEARDENORMAL; + *pProc=NSEEL_PProc_GRAM; + RF(gmegabuf); + } + break; +#undef RF + + case FUNCTYPE_FUNCTIONTYPEREC: + if (fn) + { + functionType *p=(functionType *)fn; + + // if prefers fpstack or bool, or ignoring value, then use fp-stack versions + if ((preferredReturnValues&(RETURNVALUE_BOOL|RETURNVALUE_FPSTACK)) || !preferredReturnValues) + { + if (p->afunc == (void*)nseel_asm_min) p = &fn_min2; + else if (p->afunc == (void*)nseel_asm_max) p = &fn_max2; + } + + *replList=p->replptrs; + *pProc=p->pProc; + *abiInfo = p->nParams & BIF_NPARAMS_MASK; + if (firstConstParm) + { + const char *name=p->name; + if (!strcmp(name,"min") && *firstConstParm < -1.0e-10) *abiInfo |= BIF_CLEARDENORMAL; + else if (!strcmp(name,"max") && *firstConstParm > 1.0e-10) *abiInfo |= BIF_CLEARDENORMAL; + } + return p->afunc; + } + break; + } + + return NULL; +} + + + +static void *nseel_getEELFunctionAddress(compileContext *ctx, + opcodeRec *op, + int *customFuncParmSize, int *customFuncLocalStorageSize, + EEL_F ***customFuncLocalStorage, int *computTableTop, + void **endP, int *isRaw, int wantCodeGenerated, + const namespaceInformation *namespacePathToThis, int *rvMode, int *fpStackUse, int *canHaveDenormalOutput, + opcodeRec **ordered_parmptrs, int num_ordered_parmptrs + ) // if wantCodeGenerated is false, can return bogus pointers in raw mode +{ + _codeHandleFunctionRec *fn = (_codeHandleFunctionRec*)op->fn; + + namespaceInformation local_namespace={NULL}; + char prefix_buf[NSEEL_MAX_VARIABLE_NAMELEN+1], nm[NSEEL_MAX_FUNCSIG_NAME+1]; + if (!fn) return NULL; + + // op->relname ptr is [whatever.]funcname + if (fn->parameterAsNamespaceMask || fn->usesNamespaces) + { + if (wantCodeGenerated) + { + char *p = prefix_buf; + combineNamespaceFields(nm,namespacePathToThis,op->relname,op->namespaceidx); + lstrcpyn_safe(prefix_buf,nm,sizeof(prefix_buf)); + local_namespace.namespacePathToThis = prefix_buf; + // nm is full path of function, prefix_buf will be the path not including function name (unless function name only) + while (*p) p++; + while (p >= prefix_buf && *p != '.') p--; + if (p > prefix_buf) *p=0; + } + if (fn->parameterAsNamespaceMask) + { + int x; + for(x=0;xnum_params;x++) + { + if (fn->parameterAsNamespaceMask & (((unsigned int)1)<opcodeType == OPCODETYPE_VARPTR) + { + rn=ordered_parmptrs[x]->relname; + } + else if (ordered_parmptrs[x]->opcodeType == OPCODETYPE_VALUE_FROM_NAMESPACENAME) + { + const char *p=ordered_parmptrs[x]->relname; + if (*p == '#') p++; + combineNamespaceFields(tmp,namespacePathToThis,p,ordered_parmptrs[x]->namespaceidx); + rn = tmp; + } + } + + if (!rn) + { + // todo: figure out how to give correct line number/offset (ugh) + snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"parameter %d to %.120s() must be namespace",x+1,fn->fname); + return NULL; + } + + lstrcatn(nm,":",sizeof(nm)); + + local_namespace.subParmInfo[x] = nm+strlen(nm); + lstrcatn(nm,rn,sizeof(nm)); + } + ordered_parmptrs[x] = NULL; // prevent caller from bothering generating parameters + } + } + } + if (wantCodeGenerated) + { + _codeHandleFunctionRec *fr = fn; + // find namespace-adjusted function (if generating code, otherwise assume size is the same) + fn = 0; // if this gets re-set, it will be the new function + while (fr && !fn) + { + if (!stricmp(fr->fname,nm)) fn = fr; + fr=fr->derivedCopies; + } + if (!fn) // generate copy of function + { + fn = eel_createFunctionNamespacedInstance(ctx,(_codeHandleFunctionRec*)op->fn,nm); + } + } + } + if (!fn) return NULL; + + if (!fn->startptr && fn->opcodes && fn->startptr_size != 0) + { + int sz = fn->startptr_size; + + if (sz < 0) + { + fn->tmpspace_req=0; + fn->rvMode = RETURNVALUE_IGNORE; + fn->canHaveDenormalOutput=0; + + sz = compileOpcodes(ctx,fn->opcodes,NULL,128*1024*1024,&fn->tmpspace_req, + wantCodeGenerated ? &local_namespace : NULL,RETURNVALUE_NORMAL|RETURNVALUE_FPSTACK, + &fn->rvMode,&fn->fpStackUsage,&fn->canHaveDenormalOutput); + if (sz<0) return NULL; + + fn->startptr_base_size = fn->startptr_size = sz; + } + + if (!wantCodeGenerated) + { + // don't compile anything for now, just give stats + if (computTableTop) *computTableTop += fn->tmpspace_req; + *customFuncParmSize = fn->num_params; + *customFuncLocalStorage = fn->localstorage; + *customFuncLocalStorageSize = fn->localstorage_size; + *rvMode = fn->rvMode; + *fpStackUse = fn->fpStackUsage; + if (canHaveDenormalOutput) *canHaveDenormalOutput=fn->canHaveDenormalOutput; + + if (fn->startptr_base_size <= NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE && + !(ctx->optimizeDisableFlags&OPTFLAG_NO_INLINEFUNC)) + { + *isRaw = 1; + *endP = ((char *)1) + fn->startptr_base_size; + return (char *)1; + } + return (void*)nseel_asm_fcall; + } + + if (fn->startptr_base_size <= NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE && + !(ctx->optimizeDisableFlags&OPTFLAG_NO_INLINEFUNC)) + { + void *p=newTmpBlock(ctx,sz); + fn->tmpspace_req=0; + if (p) + { + fn->canHaveDenormalOutput=0; + if (fn->isCommonFunction) ctx->isGeneratingCommonFunction++; + sz=compileOpcodes(ctx,fn->opcodes,(unsigned char*)p,sz,&fn->tmpspace_req,&local_namespace,RETURNVALUE_NORMAL|RETURNVALUE_FPSTACK,&fn->rvMode,&fn->fpStackUsage,&fn->canHaveDenormalOutput); + if (fn->isCommonFunction) ctx->isGeneratingCommonFunction--; + // recompile function with native context pointers + if (sz>0) + { + fn->startptr_size=sz; + fn->startptr=p; + } + } + } + else + { + unsigned char *codeCall; + fn->tmpspace_req=0; + fn->fpStackUsage=0; + fn->canHaveDenormalOutput=0; + if (fn->isCommonFunction) ctx->isGeneratingCommonFunction++; + codeCall=compileCodeBlockWithRet(ctx,fn->opcodes,&fn->tmpspace_req,&local_namespace,RETURNVALUE_NORMAL|RETURNVALUE_FPSTACK,&fn->rvMode,&fn->fpStackUsage,&fn->canHaveDenormalOutput); + if (fn->isCommonFunction) ctx->isGeneratingCommonFunction--; + if (codeCall) + { + void *f=GLUE_realAddress(nseel_asm_fcall,&sz); + fn->startptr = newTmpBlock(ctx,sz); + if (fn->startptr) + { + memcpy(fn->startptr,f,sz); + EEL_GLUE_set_immediate(fn->startptr,(INT_PTR)codeCall); + fn->startptr_size = sz; + } + } + } + } + + if (fn->startptr) + { + if (computTableTop) *computTableTop += fn->tmpspace_req; + *customFuncParmSize = fn->num_params; + *customFuncLocalStorage = fn->localstorage; + *customFuncLocalStorageSize = fn->localstorage_size; + *rvMode = fn->rvMode; + *fpStackUse = fn->fpStackUsage; + if (canHaveDenormalOutput) *canHaveDenormalOutput= fn->canHaveDenormalOutput; + *endP = (char*)fn->startptr + fn->startptr_size; + if (!wantCodeGenerated && + fn->startptr_base_size <= NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE && + !(ctx->optimizeDisableFlags&OPTFLAG_NO_INLINEFUNC)) + { + // report the correct maximum base length for the calculation pass + *endP = (char*)fn->startptr + fn->startptr_base_size; + } + *isRaw=1; + return fn->startptr; + } + + return 0; +} + + + +// returns true if does something (other than calculating and throwing away a value) +static char optimizeOpcodes(compileContext *ctx, opcodeRec *op, int needsResult) +{ + opcodeRec *lastJoinOp=NULL; + char retv, retv_parm[3], joined_retv=0; + while (op && op->opcodeType == OPCODETYPE_FUNC2 && op->fntype == FN_JOIN_STATEMENTS) + { + if (!optimizeOpcodes(ctx,op->parms.parms[0], 0) || OPCODE_IS_TRIVIAL(op->parms.parms[0])) + { + // direct value, can skip ourselves + memcpy(op,op->parms.parms[1],sizeof(*op)); + } + else + { + joined_retv |= 1; + lastJoinOp = op; + op = op->parms.parms[1]; + } + } +goto start_over; + +#define RESTART_DIRECTVALUE(X) { op->parms.dv.directValue = (X); goto start_over_directvalue; } +start_over_directvalue: + op->opcodeType = OPCODETYPE_DIRECTVALUE; + op->parms.dv.valuePtr=NULL; + +start_over: // when an opcode changed substantially in optimization, goto here to reprocess it + + retv = retv_parm[0]=retv_parm[1]=retv_parm[2]=0; + + if (!op || // should never really happen + OPCODE_IS_TRIVIAL(op) || // should happen often (vars) + op->opcodeType < 0 || op->opcodeType >= OPCODETYPE_INVALID // should never happen (assert would be appropriate heh) + ) return joined_retv; + + if (!needsResult) + { + if (op->fntype == FUNCTYPE_EELFUNC) + { + needsResult=1; // assume eel functions are non-const for now + } + else if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC) + { + functionType *pfn = (functionType *)op->fn; + if (!pfn || !(pfn->nParams&NSEEL_NPARAMS_FLAG_CONST)) needsResult=1; + } + else if (op->fntype >= FN_NONCONST_BEGIN && op->fntype < FUNCTYPE_SIMPLEMAX) + { + needsResult=1; + } + } + + if (op->opcodeType>=OPCODETYPE_FUNC2) retv_parm[1] = optimizeOpcodes(ctx,op->parms.parms[1], needsResult); + if (op->opcodeType>=OPCODETYPE_FUNC3) retv_parm[2] = optimizeOpcodes(ctx,op->parms.parms[2], needsResult); + + retv_parm[0] = optimizeOpcodes(ctx,op->parms.parms[0], needsResult || + (FNPTR_HAS_CONDITIONAL_EXEC(op) && (retv_parm[1] || retv_parm[2] || op->opcodeType <= OPCODETYPE_FUNC1)) ); + + if (op->opcodeType != OPCODETYPE_MOREPARAMS) + { + if (op->fntype >= 0 && op->fntype < FUNCTYPE_SIMPLEMAX) + { + if (op->opcodeType == OPCODETYPE_FUNC1) // within FUNCTYPE_SIMPLE + { + if (op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE) + { + switch (op->fntype) + { + case FN_NOTNOT: RESTART_DIRECTVALUE(fabs(op->parms.parms[0]->parms.dv.directValue)>=NSEEL_CLOSEFACTOR ? 1.0 : 0.0); + case FN_NOT: RESTART_DIRECTVALUE(fabs(op->parms.parms[0]->parms.dv.directValue)>=NSEEL_CLOSEFACTOR ? 0.0 : 1.0); + case FN_UMINUS: RESTART_DIRECTVALUE(- op->parms.parms[0]->parms.dv.directValue); + } + } + else if (op->fntype == FN_NOT || op->fntype == FN_NOTNOT) + { + if (op->parms.parms[0]->opcodeType == OPCODETYPE_FUNC1) + { + switch (op->parms.parms[0]->fntype) + { + case FN_UMINUS: + case FN_NOTNOT: // ignore any NOTNOTs UMINUS or UPLUS, they would have no effect anyway + op->parms.parms[0] = op->parms.parms[0]->parms.parms[0]; + goto start_over; + + case FN_NOT: + op->fntype = op->fntype==FN_NOT ? FN_NOTNOT : FN_NOT; // switch between FN_NOT and FN_NOTNOT + op->parms.parms[0] = op->parms.parms[0]->parms.parms[0]; + goto start_over; + } + } + else if (op->parms.parms[0]->opcodeType == OPCODETYPE_FUNC2) + { + int repl_type = -1; + switch (op->parms.parms[0]->fntype) + { + case FN_EQ: repl_type = FN_NE; break; + case FN_NE: repl_type = FN_EQ; break; + case FN_EQ_EXACT: repl_type = FN_NE_EXACT; break; + case FN_NE_EXACT: repl_type = FN_EQ_EXACT; break; + case FN_LT: repl_type = FN_GTE; break; + case FN_LTE: repl_type = FN_GT; break; + case FN_GT: repl_type = FN_LTE; break; + case FN_GTE: repl_type = FN_LT; break; + } + if (repl_type != -1) + { + const int oldtype = op->fntype; + memcpy(op,op->parms.parms[0],sizeof(*op)); + if (oldtype == FN_NOT) op->fntype = repl_type; + goto start_over; + } + } + } + } + else if (op->opcodeType == OPCODETYPE_FUNC2) // within FUNCTYPE_SIMPLE + { + const int dv0 = op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE; + const int dv1 = op->parms.parms[1]->opcodeType == OPCODETYPE_DIRECTVALUE; + if (dv0 && dv1) + { + int reval = -1; + switch (op->fntype) + { + case FN_MOD: + { + int a = (int) op->parms.parms[1]->parms.dv.directValue; + if (a) + { + a = (int) op->parms.parms[0]->parms.dv.directValue % a; + if (a<0) a=-a; + } + RESTART_DIRECTVALUE((EEL_F)a); + } + break; + case FN_SHL: RESTART_DIRECTVALUE(((int)op->parms.parms[0]->parms.dv.directValue) << ((int)op->parms.parms[1]->parms.dv.directValue)); + case FN_SHR: RESTART_DIRECTVALUE(((int)op->parms.parms[0]->parms.dv.directValue) >> ((int)op->parms.parms[1]->parms.dv.directValue)); + case FN_POW: RESTART_DIRECTVALUE(pow(op->parms.parms[0]->parms.dv.directValue, op->parms.parms[1]->parms.dv.directValue)); + case FN_DIVIDE: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue / op->parms.parms[1]->parms.dv.directValue); + case FN_MULTIPLY: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue * op->parms.parms[1]->parms.dv.directValue); + + case FN_ADD: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue + op->parms.parms[1]->parms.dv.directValue); + case FN_SUB: RESTART_DIRECTVALUE(op->parms.parms[0]->parms.dv.directValue - op->parms.parms[1]->parms.dv.directValue); + case FN_AND: RESTART_DIRECTVALUE((double) (((WDL_INT64)op->parms.parms[0]->parms.dv.directValue) & ((WDL_INT64)op->parms.parms[1]->parms.dv.directValue))); + case FN_OR: RESTART_DIRECTVALUE((double) (((WDL_INT64)op->parms.parms[0]->parms.dv.directValue) | ((WDL_INT64)op->parms.parms[1]->parms.dv.directValue))); + case FN_XOR: RESTART_DIRECTVALUE((double) (((WDL_INT64)op->parms.parms[0]->parms.dv.directValue) ^ ((WDL_INT64)op->parms.parms[1]->parms.dv.directValue))); + + case FN_EQ: reval = fabs(op->parms.parms[0]->parms.dv.directValue - op->parms.parms[1]->parms.dv.directValue) < NSEEL_CLOSEFACTOR; break; + case FN_NE: reval = fabs(op->parms.parms[0]->parms.dv.directValue - op->parms.parms[1]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; break; + case FN_EQ_EXACT: reval = op->parms.parms[0]->parms.dv.directValue == op->parms.parms[1]->parms.dv.directValue; break; + case FN_NE_EXACT: reval = op->parms.parms[0]->parms.dv.directValue != op->parms.parms[1]->parms.dv.directValue; break; + case FN_LT: reval = op->parms.parms[0]->parms.dv.directValue < op->parms.parms[1]->parms.dv.directValue; break; + case FN_LTE: reval = op->parms.parms[0]->parms.dv.directValue <= op->parms.parms[1]->parms.dv.directValue; break; + case FN_GT: reval = op->parms.parms[0]->parms.dv.directValue > op->parms.parms[1]->parms.dv.directValue; break; + case FN_GTE: reval = op->parms.parms[0]->parms.dv.directValue >= op->parms.parms[1]->parms.dv.directValue; break; + case FN_LOGICAL_AND: reval = fabs(op->parms.parms[0]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR && fabs(op->parms.parms[1]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; break; + case FN_LOGICAL_OR: reval = fabs(op->parms.parms[0]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR || fabs(op->parms.parms[1]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; break; + } + + if (reval >= 0) RESTART_DIRECTVALUE((EEL_F) reval); + } + else if (dv0 || dv1) + { + double dvalue = op->parms.parms[!dv0]->parms.dv.directValue; + switch (op->fntype) + { + case FN_OR: + case FN_XOR: + if (!(WDL_INT64)dvalue) + { + // replace with or0 + op->opcodeType = OPCODETYPE_FUNC1; + op->fntype = FUNCTYPE_FUNCTIONTYPEREC; + op->fn = &fn_or0; + if (dv0) op->parms.parms[0] = op->parms.parms[1]; + goto start_over; + } + break; + case FN_SUB: + if (dv0) + { + if (dvalue == 0.0) + { + op->opcodeType = OPCODETYPE_FUNC1; + op->fntype = FN_UMINUS; + op->parms.parms[0] = op->parms.parms[1]; + goto start_over; + } + break; + } + // fall through, if dv1 we can remove +0.0 + + case FN_ADD: + if (dvalue == 0.0) + { + memcpy(op,op->parms.parms[!!dv0],sizeof(*op)); + goto start_over; + } + break; + case FN_AND: + if ((WDL_INT64)dvalue) break; + dvalue = 0.0; // treat x&0 as x*0, which optimizes to 0 + + // fall through + case FN_MULTIPLY: + if (dvalue == 0.0) // remove multiply by 0.0 (using 0.0 direct value as replacement), unless the nonzero side did something + { + if (!retv_parm[!!dv0]) + { + memcpy(op,op->parms.parms[!dv0],sizeof(*op)); // set to 0 if other action wouldn't do anything + goto start_over; + } + else + { + // this is 0.0 * oldexpressionthatmustbeprocessed or oldexpressionthatmustbeprocessed*0.0 + op->fntype = FN_JOIN_STATEMENTS; + + if (dv0) // 0.0*oldexpression, reverse the order so that 0 is returned + { + // set to (oldexpression;0) + opcodeRec *tmp = op->parms.parms[1]; + op->parms.parms[1] = op->parms.parms[0]; + op->parms.parms[0] = tmp; + } + goto start_over; + } + } + else if (dvalue == 1.0) // remove multiply by 1.0 (using non-1.0 value as replacement) + { + memcpy(op,op->parms.parms[!!dv0],sizeof(*op)); + goto start_over; + } + break; + case FN_POW: + if (dv1) + { + // x^0 = 1 + if (fabs(dvalue) < 1e-30) + { + RESTART_DIRECTVALUE(1.0); + } + // x^1 = x + if (fabs(dvalue-1.0) < 1e-30) + { + memcpy(op,op->parms.parms[0],sizeof(*op)); + goto start_over; + } + } + else if (dv0) + { + // pow(constant, x) = exp((x) * ln(constant)), if constant>0 + // opcodeRec *parm0 = op->parms.parms[0]; + if (dvalue > 0.0) + { + static functionType *expcpy; + if (!expcpy) + { + expcpy = nseel_getFunctionByName(NULL,"exp",NULL); + if (WDL_NOT_NORMALLY(!expcpy)) break; + } + + // 1^x = 1 + if (fabs(dvalue-1.0) < 1e-30) + { + RESTART_DIRECTVALUE(1.0); + } + + dvalue=log(dvalue); + + if (fabs(dvalue-1.0) < 1e-9) + { + // caller wanted e^x + op->parms.parms[0]=op->parms.parms[1]; + } + else + { + // it would be nice to replace 10^x with exp(log(10)*x) or 2^x with exp(log(2),x), but + // doing so breaks rounding. we could maybe only allow 10^x, which is used for dB conversion, + // but for now we should just force the programmer do it exp(log(10)*x) themselves. + break; + + /* + parm0->opcodeType = OPCODETYPE_FUNC2; + parm0->fntype = FN_MULTIPLY; + parm0->parms.parms[0] = nseel_createCompiledValue(ctx,dvalue); + parm0->parms.parms[1] = op->parms.parms[1]; + */ + } + + op->opcodeType = OPCODETYPE_FUNC1; + op->fntype = FUNCTYPE_FUNCTIONTYPEREC; + op->fn = expcpy; + goto start_over; + } + } + break; + case FN_MOD: + if (dv1) + { + const int a = (int) dvalue; + if (!a) + { + RESTART_DIRECTVALUE(0.0); + } + } + break; + case FN_DIVIDE: + if (dv1) + { + if (dvalue == 1.0) // remove divide by 1.0 (using non-1.0 value as replacement) + { + memcpy(op,op->parms.parms[!!dv0],sizeof(*op)); + goto start_over; + } + else + { + // change to a multiply + if (dvalue == 0.0) + { + op->fntype = FN_MULTIPLY; + goto start_over; + } + else + { + double d = 1.0/dvalue; + WDL_UINT64 w; + memcpy(&w,&d,sizeof(d)); + // allow conversion to multiply if reciprocal is exact + // we could also just look to see if the last few digits of the mantissa were 0, which would probably be good + // enough, but if the user really wants it they should do * (1/x) instead to force precalculation of reciprocal. + if (!(w & WDL_UINT64_CONST(0xfffffffffffff))) + { + op->fntype = FN_MULTIPLY; + op->parms.parms[1]->parms.dv.directValue = d; + op->parms.parms[1]->parms.dv.valuePtr=NULL; + goto start_over; + } + } + } + } + else if (dvalue == 0.0) + { + if (!retv_parm[!!dv0]) + { + // if 0/x set to always 0. + // this is 0.0 / (oldexpression that can be eliminated) + memcpy(op,op->parms.parms[!dv0],sizeof(*op)); // set to 0 if other action wouldn't do anything + } + else + { + opcodeRec *tmp; + // this is 0.0 / oldexpressionthatmustbeprocessed + op->fntype = FN_JOIN_STATEMENTS; + tmp = op->parms.parms[1]; + op->parms.parms[1] = op->parms.parms[0]; + op->parms.parms[0] = tmp; + // set to (oldexpression;0) + } + goto start_over; + } + break; + case FN_EQ: + if (dvalue == 0.0) + { + // convert x == 0.0 to !x + op->opcodeType=OPCODETYPE_FUNC1; + op->fntype = FN_NOT; + if (dv0) op->parms.parms[0]=op->parms.parms[1]; + goto start_over; + } + break; + case FN_NE: + if (dvalue == 0.0) + { + // convert x != 0.0 to !! + op->opcodeType=OPCODETYPE_FUNC1; + op->fntype = FN_NOTNOT; + if (dv0) op->parms.parms[0]=op->parms.parms[1]; + goto start_over; + } + break; + case FN_LOGICAL_AND: + if (dv0) + { + // dvalue && expr + if (fabs(dvalue) < NSEEL_CLOSEFACTOR) + { + // 0 && expr, replace with 0 + RESTART_DIRECTVALUE(0.0); + } + else + { + // 1 && expr, replace with 0 != expr + op->fntype = FN_NE; + op->parms.parms[0]->parms.dv.valuePtr=NULL; + op->parms.parms[0]->parms.dv.directValue = 0.0; + } + } + else + { + // expr && dvalue + if (fabs(dvalue) < NSEEL_CLOSEFACTOR) + { + // expr && 0 + if (!retv_parm[0]) + { + // expr has no consequence, drop it + RESTART_DIRECTVALUE(0.0); + } + else + { + // replace with (expr; 0) + op->fntype = FN_JOIN_STATEMENTS; + op->parms.parms[1]->parms.dv.valuePtr=NULL; + op->parms.parms[1]->parms.dv.directValue = 0.0; + } + } + else + { + // expr && 1, replace with expr != 0 + op->fntype = FN_NE; + op->parms.parms[1]->parms.dv.valuePtr=NULL; + op->parms.parms[1]->parms.dv.directValue = 0.0; + } + } + goto start_over; + case FN_LOGICAL_OR: + if (dv0) + { + // dvalue || expr + if (fabs(dvalue) >= NSEEL_CLOSEFACTOR) + { + // 1 || expr, replace with 1 + RESTART_DIRECTVALUE(1.0); + } + else + { + // 0 || expr, replace with 0 != expr + op->fntype = FN_NE; + op->parms.parms[0]->parms.dv.valuePtr=NULL; + op->parms.parms[0]->parms.dv.directValue = 0.0; + } + } + else + { + // expr || dvalue + if (fabs(dvalue) >= NSEEL_CLOSEFACTOR) + { + // expr || 1 + if (!retv_parm[0]) + { + // expr has no consequence, drop it and return 1 + RESTART_DIRECTVALUE(1.0); + } + else + { + // replace with (expr; 1) + op->fntype = FN_JOIN_STATEMENTS; + op->parms.parms[1]->parms.dv.valuePtr=NULL; + op->parms.parms[1]->parms.dv.directValue = 1.0; + } + } + else + { + // expr || 0, replace with expr != 0 + op->fntype = FN_NE; + op->parms.parms[1]->parms.dv.valuePtr=NULL; + op->parms.parms[1]->parms.dv.directValue = 0.0; + } + } + goto start_over; + } + } // dv0 || dv1 + + // general optimization of two parameters + switch (op->fntype) + { + case FN_MULTIPLY: + { + opcodeRec *first_parm = op->parms.parms[0],*second_parm = op->parms.parms[1]; + + if (second_parm->opcodeType == first_parm->opcodeType) + { + switch(first_parm->opcodeType) + { + case OPCODETYPE_VALUE_FROM_NAMESPACENAME: + if (first_parm->namespaceidx != second_parm->namespaceidx) break; + // fall through + case OPCODETYPE_VARPTR: + if (first_parm->relname && second_parm->relname && !stricmp(second_parm->relname,first_parm->relname)) second_parm=NULL; + break; + case OPCODETYPE_VARPTRPTR: + if (first_parm->parms.dv.valuePtr && first_parm->parms.dv.valuePtr==second_parm->parms.dv.valuePtr) second_parm=NULL; + break; + + } + if (!second_parm) // switch from x*x to sqr(x) + { + static functionType *sqrcpy; + if (!sqrcpy) sqrcpy = nseel_getFunctionByName(NULL,"sqr",NULL); + if (WDL_NORMALLY(sqrcpy)) + { + op->opcodeType = OPCODETYPE_FUNC1; + op->fntype = FUNCTYPE_FUNCTIONTYPEREC; + op->fn = sqrcpy; + goto start_over; + } + } + } + } + break; + case FN_POW: + { + opcodeRec *first_parm = op->parms.parms[0]; + if (first_parm->opcodeType == op->opcodeType && first_parm->fntype == FN_POW) + { + // since first_parm is a pow too, we can multiply the exponents. + + // set our base to be the base of the inner pow + op->parms.parms[0] = first_parm->parms.parms[0]; + + // make the old extra pow be a multiply of the exponents + first_parm->fntype = FN_MULTIPLY; + first_parm->parms.parms[0] = op->parms.parms[1]; + + // put that as the exponent + op->parms.parms[1] = first_parm; + + goto start_over; + } + } + break; + case FN_LOGICAL_AND: + case FN_LOGICAL_OR: + if (op->parms.parms[0]->fntype == FN_NOTNOT) + { + // remove notnot, unnecessary for input to &&/|| operators + op->parms.parms[0] = op->parms.parms[0]->parms.parms[0]; + goto start_over; + } + if (op->parms.parms[1]->fntype == FN_NOTNOT) + { + // remove notnot, unnecessary for input to &&/|| operators + op->parms.parms[1] = op->parms.parms[1]->parms.parms[0]; + goto start_over; + } + break; + } + } + else if (op->opcodeType==OPCODETYPE_FUNC3) // within FUNCTYPE_SIMPLE + { + if (op->fntype == FN_IF_ELSE) + { + if (op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE) + { + int s = fabs(op->parms.parms[0]->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; + memcpy(op,op->parms.parms[s ? 1 : 2],sizeof(opcodeRec)); + goto start_over; + } + if (op->parms.parms[0]->opcodeType == OPCODETYPE_FUNC1) + { + if (op->parms.parms[0]->fntype == FN_NOTNOT) + { + // remove notnot, unnecessary for input to ? operator + op->parms.parms[0] = op->parms.parms[0]->parms.parms[0]; + goto start_over; + } + } + } + } + if (op->fntype >= FN_NONCONST_BEGIN && op->fntype < FUNCTYPE_SIMPLEMAX) retv|=1; + + // FUNCTYPE_SIMPLE + } + else if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC && op->fn) + { + + /* + probably worth doing reduction on: + _divop (constant change to multiply) + _and + _or + abs + + maybe: + min + max + + + also, optimize should (recursively or maybe iteratively?) search transitive functions (mul/div) for more constant reduction possibilities + + + */ + + + functionType *pfn = (functionType *)op->fn; + + if (!(pfn->nParams&NSEEL_NPARAMS_FLAG_CONST)) retv|=1; + + if (op->opcodeType==OPCODETYPE_FUNC1) // within FUNCTYPE_FUNCTIONTYPEREC + { + if (op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE) + { + int suc=1; + EEL_F v = op->parms.parms[0]->parms.dv.directValue; + #define DOF(x) if (!strcmp(pfn->name,#x)) v = x(v); else + #define DOF2(x,y) if (!strcmp(pfn->name,#x)) v = x(y); else + DOF(sin) + DOF(cos) + DOF(tan) + DOF(asin) + DOF(acos) + DOF(atan) + DOF2(sqrt, fabs(v)) + DOF(exp) + DOF(log) + DOF(log10) + /* else */ suc=0; + #undef DOF + #undef DOF2 + if (suc) + { + RESTART_DIRECTVALUE(v); + } + + + } + } + else if (op->opcodeType==OPCODETYPE_FUNC2) // within FUNCTYPE_FUNCTIONTYPEREC + { + const int dv0=op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE; + const int dv1=op->parms.parms[1]->opcodeType == OPCODETYPE_DIRECTVALUE; + if (dv0 && dv1) + { + if (!strcmp(pfn->name,"atan2")) + { + RESTART_DIRECTVALUE(atan2(op->parms.parms[0]->parms.dv.directValue, op->parms.parms[1]->parms.dv.directValue)); + } + } + } + // FUNCTYPE_FUNCTIONTYPEREC + } + else + { + // unknown or eel func, assume non-const + retv |= 1; + } + } + + // if we need results, or our function has effects itself, then finish + if (retv || needsResult) + { + return retv || joined_retv || retv_parm[0] || retv_parm[1] || retv_parm[2]; + } + + // we don't need results here, and our function is const, which means we can remove it + { + int cnt=0, idx1=0, idx2=0, x; + for (x=0;x<3;x++) if (retv_parm[x]) { if (!cnt++) idx1=x; else idx2=x; } + + if (!cnt) // none of the parameters do anything, remove this opcode + { + if (lastJoinOp) + { + // replace previous join with its first linked opcode, removing this opcode completely + memcpy(lastJoinOp,lastJoinOp->parms.parms[0],sizeof(*lastJoinOp)); + } + else if (op->opcodeType != OPCODETYPE_DIRECTVALUE) + { + // allow caller to easily detect this as trivial and remove it + op->opcodeType = OPCODETYPE_DIRECTVALUE; + op->parms.dv.valuePtr=NULL; + op->parms.dv.directValue=0.0; + } + // return joined_retv below + } + else + { + // if parameters are non-const, and we're a conditional, preserve our function + if (FNPTR_HAS_CONDITIONAL_EXEC(op)) return 1; + + // otherwise, condense into either the non-const statement, or a join + if (cnt==1) + { + memcpy(op,op->parms.parms[idx1],sizeof(*op)); + } + else if (cnt == 2) + { + op->opcodeType = OPCODETYPE_FUNC2; + op->fntype = FN_JOIN_STATEMENTS; + op->fn = op; + op->parms.parms[0] = op->parms.parms[idx1]; + op->parms.parms[1] = op->parms.parms[idx2]; + op->parms.parms[2] = NULL; + } + else + { + // todo need to create a new opcodeRec here, for now just leave as is + // (non-conditional const 3 parameter functions are rare anyway) + } + return 1; + } + } + return joined_retv; +} + + +static int generateValueToReg(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int whichReg, const namespaceInformation *functionPrefix, int allowCache) +{ + EEL_F *b=NULL; + if (op->opcodeType==OPCODETYPE_VALUE_FROM_NAMESPACENAME) + { + char nm[NSEEL_MAX_VARIABLE_NAMELEN+1]; + const char *p = op->relname; + combineNamespaceFields(nm,functionPrefix,p+(*p == '#'),op->namespaceidx); + if (!nm[0]) return -1; + if (*p == '#') + { + if (ctx->isGeneratingCommonFunction) + b = newCtxDataBlock(sizeof(EEL_F),sizeof(EEL_F)); + else + b = newDataBlock(sizeof(EEL_F),sizeof(EEL_F)); + + if (!b) RET_MINUS1_FAIL("error creating storage for str") + + if (!ctx->onNamedString) return -1; // should never happen, will not generate OPCODETYPE_VALUE_FROM_NAMESPACENAME with # prefix if !onNamedString + + *b = ctx->onNamedString(ctx->caller_this,nm); + } + else + { + b = nseel_int_register_var(ctx,nm,0,NULL); + if (!b) RET_MINUS1_FAIL("error registering var") + } + } + else + { + if (op->opcodeType != OPCODETYPE_DIRECTVALUE) allowCache=0; + + if (op->opcodeType==OPCODETYPE_DIRECTVALUE_TEMPSTRING && ctx->onNamedString) + { + op->parms.dv.directValue = ctx->onNamedString(ctx->caller_this,""); + op->parms.dv.valuePtr = NULL; + } + + b=op->parms.dv.valuePtr; + if (!b && op->opcodeType == OPCODETYPE_VARPTR && op->relname && op->relname[0]) + { + op->parms.dv.valuePtr = b = nseel_int_register_var(ctx,op->relname,0,NULL); + } + + if (b && op->opcodeType == OPCODETYPE_VARPTRPTR) b = *(EEL_F **)b; + if (!b && allowCache) + { + int n=50; // only scan last X items + opcodeRec *r = ctx->directValueCache; + while (r && n--) + { + if (r->parms.dv.directValue == op->parms.dv.directValue && (b=r->parms.dv.valuePtr)) break; + r=(opcodeRec*)r->fn; + } + } + if (!b) + { + ctx->l_stats[3]++; + if (ctx->isGeneratingCommonFunction) + b = newCtxDataBlock(sizeof(EEL_F),sizeof(EEL_F)); + else + b = newDataBlock(sizeof(EEL_F),sizeof(EEL_F)); + + if (!b) RET_MINUS1_FAIL("error allocating data block") + + if (op->opcodeType != OPCODETYPE_VARPTRPTR) op->parms.dv.valuePtr = b; + *b = denormal_filter_double2(op->parms.dv.directValue); + + if (allowCache) + { + op->fn = ctx->directValueCache; + ctx->directValueCache = op; + } + } + } + + GLUE_MOV_PX_DIRECTVALUE_GEN(bufOut,(INT_PTR)b,whichReg); + return GLUE_MOV_PX_DIRECTVALUE_SIZE; +} + + +unsigned char *compileCodeBlockWithRet(compileContext *ctx, opcodeRec *rec, int *computTableSize, const namespaceInformation *namespacePathToThis, + int supportedReturnValues, int *rvType, int *fpStackUsage, int *canHaveDenormalOutput) +{ + unsigned char *p, *newblock2; + // generate code call + int funcsz=compileOpcodes(ctx,rec,NULL,1024*1024*128,NULL,namespacePathToThis,supportedReturnValues, rvType,fpStackUsage, NULL); + if (funcsz<0) return NULL; + + p = newblock2 = newCodeBlock(funcsz+ sizeof(GLUE_RET)+GLUE_FUNC_ENTER_SIZE+GLUE_FUNC_LEAVE_SIZE,32); + if (!newblock2) return NULL; + #if GLUE_FUNC_ENTER_SIZE > 0 + memcpy(p,&GLUE_FUNC_ENTER,GLUE_FUNC_ENTER_SIZE); + p += GLUE_FUNC_ENTER_SIZE; + #endif + *fpStackUsage=0; + funcsz=compileOpcodes(ctx,rec,p, funcsz, computTableSize,namespacePathToThis,supportedReturnValues, rvType,fpStackUsage, canHaveDenormalOutput); + if (funcsz<0) return NULL; + p+=funcsz; + + #if GLUE_FUNC_LEAVE_SIZE > 0 + memcpy(p,&GLUE_FUNC_LEAVE,GLUE_FUNC_LEAVE_SIZE); + p+=GLUE_FUNC_LEAVE_SIZE; + #endif + memcpy(p,&GLUE_RET,sizeof(GLUE_RET)); p+=sizeof(GLUE_RET); +#if defined(__arm__) || defined(__aarch64__) + __clear_cache(newblock2,p); +#endif + + ctx->l_stats[2]+=funcsz+2; + return newblock2; +} + +static int compileNativeFunctionCall(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis, + int *rvMode, int *fpStackUsage, int preferredReturnValues, int *canHaveDenormalOutput) +{ + // builtin function generation + int func_size=0; + int cfunc_abiinfo=0; + int local_fpstack_use=0; // how many items we have pushed onto the fp stack + int parm_size=0; + int restore_stack_amt=0; +#ifdef GLUE_HAS_FUSE + int fuse_flags = 0; // 1 = last parm was trivial (likely mov rax, movsd xmm0 ,[rax]) +#endif +#if GLUE_MAX_SPILL_REGS > 0 + int spill_reg = -1; +#endif + + NSEEL_PPPROC preProc=0; + void **repl=NULL; + + int n_params= 1 + op->opcodeType - OPCODETYPE_FUNC1; + + const int parm0_dv = op->parms.parms[0]->opcodeType == OPCODETYPE_DIRECTVALUE; + const int parm1_dv = n_params > 1 && op->parms.parms[1]->opcodeType == OPCODETYPE_DIRECTVALUE; + + void *func = nseel_getBuiltinFunctionAddress(ctx, op->fntype, op->fn, &preProc,&repl, + &cfunc_abiinfo,preferredReturnValues, + parm0_dv ? &op->parms.parms[0]->parms.dv.directValue : NULL, + parm1_dv ? &op->parms.parms[1]->parms.dv.directValue : NULL + ); + + *fpStackUsage=BIF_GETFPSTACKUSE(cfunc_abiinfo); + *rvMode = RETURNVALUE_NORMAL; + + if (cfunc_abiinfo & BIF_TAKES_VARPARM) + { +#if defined(__arm__) || (defined (_M_ARM) && _M_ARM == 7) + const int max_params=16384; // arm uses up to two instructions, should be good for at leaast 64k (16384*4) +#elif defined(__ppc__) + const int max_params=4096; // 32kb max offset addressing for stack, so 4096*4 = 16384, should be safe +#elif defined(__aarch64__) + const int max_params=32768; +#else + const int max_params=32768; // sanity check, the stack is free to grow on x86/x86-64 +#endif + int x; + // this mode is less efficient in that it creates a list of pointers on the stack to pass to the function + // but it is more flexible and works for >3 parameters. + if (op->opcodeType == OPCODETYPE_FUNCX) + { + n_params=0; + for (x=0;x<3;x++) + { + opcodeRec *prni=op->parms.parms[x]; + while (prni) + { + const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS; + n_params++; + if (!isMP||n_params>=max_params) break; + prni = prni->parms.parms[1]; + } + } + } + + restore_stack_amt = (sizeof(void *) * n_params + 15)&~15; + + if (restore_stack_amt) + { + int offs = restore_stack_amt; + while (offs > 0) + { + int amt = offs; + if (amt > 4096) amt=4096; + + if (bufOut_len < parm_size+GLUE_MOVE_STACK_SIZE) RET_MINUS1_FAIL("insufficient size for varparm") + if (bufOut) GLUE_MOVE_STACK(bufOut+parm_size, - amt); + parm_size += GLUE_MOVE_STACK_SIZE; + offs -= amt; + + if (offs>0) // make sure this page is in memory + { + if (bufOut_len < parm_size+GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(0)) + RET_MINUS1_FAIL("insufficient size for varparm stackchk") + if (bufOut) GLUE_STORE_P1_TO_STACK_AT_OFFS(bufOut+parm_size,0); + parm_size += GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(0); + } + } + } + + if (op->opcodeType == OPCODETYPE_FUNCX) + { + n_params=0; + for (x=0;x<3;x++) + { + opcodeRec *prni=op->parms.parms[x]; + while (prni) + { + const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS; + opcodeRec *r = isMP ? prni->parms.parms[0] : prni; + if (r) + { + int canHaveDenorm=0; + int rvt=RETURNVALUE_NORMAL; + int subfpstackuse=0, use_offs; + + int lsz = compileOpcodes(ctx,r,bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, rvt,&rvt, &subfpstackuse, &canHaveDenorm); + if (canHaveDenorm && canHaveDenormalOutput) *canHaveDenormalOutput = 1; + + if (lsz<0) RET_MINUS1_FAIL("call coc for varparmX failed") + if (rvt != RETURNVALUE_NORMAL) RET_MINUS1_FAIL("call coc for varparmX gave bad type back"); + + parm_size += lsz; + use_offs = n_params*(int) sizeof(void *); + + if (bufOut_len < parm_size+GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs)) + RET_MINUS1_FAIL("call coc for varparmX size"); + if (bufOut) GLUE_STORE_P1_TO_STACK_AT_OFFS(bufOut + parm_size, use_offs); + parm_size+=GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs); + + if (subfpstackuse+local_fpstack_use > *fpStackUsage) *fpStackUsage = subfpstackuse+local_fpstack_use; + } + else RET_MINUS1_FAIL("zero parameter varparmX") + + n_params++; + + if (!isMP||n_params>=max_params) break; + prni = prni->parms.parms[1]; + } + } + } + else for (x=0;xparms.parms[x]; + if (r) + { + int canHaveDenorm=0; + int subfpstackuse=0; + int rvt=RETURNVALUE_NORMAL; + int use_offs; + + int lsz = compileOpcodes(ctx,r,bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, rvt,&rvt, &subfpstackuse, &canHaveDenorm); + if (canHaveDenorm && canHaveDenormalOutput) *canHaveDenormalOutput = 1; + + if (lsz<0) RET_MINUS1_FAIL("call coc for varparm123 failed") + if (rvt != RETURNVALUE_NORMAL) RET_MINUS1_FAIL("call coc for varparm123 gave bad type back"); + + parm_size += lsz; + + use_offs = x*(int)sizeof(void *); + if (bufOut_len < parm_size+GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs)) + RET_MINUS1_FAIL("call coc for varparm123 size"); + if (bufOut) GLUE_STORE_P1_TO_STACK_AT_OFFS(bufOut + parm_size, use_offs); + parm_size+=GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(use_offs); + + if (subfpstackuse+local_fpstack_use > *fpStackUsage) *fpStackUsage = subfpstackuse+local_fpstack_use; + } + else RET_MINUS1_FAIL("zero parameter for varparm123"); + } + + if (bufOut_len < parm_size+GLUE_MOV_PX_DIRECTVALUE_SIZE+GLUE_MOVE_PX_STACKPTR_SIZE) RET_MINUS1_FAIL("insufficient size for varparm p1") + if (bufOut) GLUE_MOV_PX_DIRECTVALUE_GEN(bufOut+parm_size, (INT_PTR)n_params,1); + parm_size+=GLUE_MOV_PX_DIRECTVALUE_SIZE; + if (bufOut) GLUE_MOVE_PX_STACKPTR_GEN(bufOut+parm_size, 0); + parm_size+=GLUE_MOVE_PX_STACKPTR_SIZE; + + } + else // not varparm + { + int pn; + #if GLUE_MAX_FPSTACK_SIZE > 0 + int need_fxch=0; + #endif + int last_nt_parm=-1, last_nt_parm_type=-1; + + if (op->opcodeType == OPCODETYPE_FUNCX) + { + // this is not yet supported (calling conventions will need to be sorted, among other things) + RET_MINUS1_FAIL("funcx for native functions requires BIF_TAKES_VARPARM or BIF_TAKES_VARPARM_EX") + } + + if (parm0_dv) + { + if (func == nseel_asm_stack_pop) + { + func = GLUE_realAddress(nseel_asm_stack_pop_fast,&func_size); + if (!func || bufOut_len < func_size) RET_MINUS1_FAIL(func?"failed on popfast size":"failed on popfast addr") + + if (bufOut) + { + memcpy(bufOut,func,func_size); + NSEEL_PProc_Stack(bufOut,func_size,ctx); + } + return func_size; + } + else if (func == nseel_asm_stack_peek) + { + int f = (int) op->parms.parms[0]->parms.dv.directValue; + if (!f) + { + func = GLUE_realAddress(nseel_asm_stack_peek_top,&func_size); + if (!func || bufOut_len < func_size) RET_MINUS1_FAIL(func?"failed on peek size":"failed on peek addr") + + if (bufOut) + { + memcpy(bufOut,func,func_size); + NSEEL_PProc_Stack_PeekTop(bufOut,func_size,ctx); + } + return func_size; + } + else + { + func = GLUE_realAddress(nseel_asm_stack_peek_int,&func_size); + if (!func || bufOut_len < func_size) RET_MINUS1_FAIL(func?"failed on peekint size":"failed on peekint addr") + + if (bufOut) + { + memcpy(bufOut,func,func_size); + NSEEL_PProc_Stack_PeekInt(bufOut,func_size,ctx,f*sizeof(EEL_F)); + } + return func_size; + } + } + } + // end of built-in function specific special casing + + + // first pass, calculate any non-trivial parameters + for (pn=0; pn < n_params; pn++) + { + if (!OPCODE_IS_TRIVIAL(op->parms.parms[pn])) + { + int canHaveDenorm=0; + int subfpstackuse=0; + int lsz=0; + int rvt=RETURNVALUE_NORMAL; + int may_need_fppush=-1; + if (last_nt_parm>=0) + { + if (last_nt_parm_type==RETURNVALUE_FPSTACK) + { + may_need_fppush= parm_size; + } + else + { + // push last result + if (bufOut_len < parm_size + (int)sizeof(GLUE_PUSH_P1)) RET_MINUS1_FAIL("failed on size, pushp1") + if (bufOut) memcpy(bufOut + parm_size, &GLUE_PUSH_P1, sizeof(GLUE_PUSH_P1)); + parm_size += sizeof(GLUE_PUSH_P1); + } + } + + if (func == nseel_asm_bnot) rvt=RETURNVALUE_BOOL_REVERSED|RETURNVALUE_BOOL; + else if (pn == n_params - 1) + { + if (cfunc_abiinfo&BIF_LASTPARMONSTACK) rvt=RETURNVALUE_FPSTACK; + else if (cfunc_abiinfo&BIF_LASTPARM_ASBOOL) rvt=RETURNVALUE_BOOL; + else if (func == nseel_asm_assign) rvt=RETURNVALUE_FPSTACK|RETURNVALUE_NORMAL; + } + else if (pn == n_params -2 && (cfunc_abiinfo&BIF_SECONDLASTPARMST)) + { + rvt=RETURNVALUE_FPSTACK; + } + + lsz = compileOpcodes(ctx,op->parms.parms[pn],bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, rvt,&rvt, &subfpstackuse, &canHaveDenorm); + + if (lsz<0) RET_MINUS1_FAIL("call coc failed") + + if (func == nseel_asm_bnot && rvt==RETURNVALUE_BOOL_REVERSED) + { + // remove bnot, compileOpcodes() used fptobool_rev + func = NULL; + rvt = RETURNVALUE_BOOL; + } + + if (canHaveDenorm && canHaveDenormalOutput) *canHaveDenormalOutput = 1; + + parm_size += lsz; + + if (may_need_fppush>=0) + { +#if GLUE_MAX_SPILL_REGS > 0 + if (local_fpstack_use+subfpstackuse < GLUE_MAX_SPILL_REGS && pn == n_params - 1 && !(ctx->optimizeDisableFlags&OPTFLAG_NO_FPSTACK)) + { + int spill_sz; + spill_reg = local_fpstack_use+subfpstackuse; + spill_sz = GLUE_SAVE_TO_SPILL_SIZE(spill_reg); + if (bufOut_len < parm_size + spill_sz) RET_MINUS1_FAIL("failed on size, savetospilll") + + if (bufOut) + { + memmove(bufOut + may_need_fppush + spill_sz, bufOut + may_need_fppush, parm_size - may_need_fppush); + GLUE_SAVE_TO_SPILL(bufOut + may_need_fppush, spill_reg); + } + parm_size += spill_sz; + local_fpstack_use++; + } + else +#endif + if (local_fpstack_use+subfpstackuse >= (GLUE_MAX_FPSTACK_SIZE-1) || (ctx->optimizeDisableFlags&OPTFLAG_NO_FPSTACK)) + { + if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_FPSTACK_TOSTACK)) + RET_MINUS1_FAIL("failed on size, popfpstacktostack") + + if (bufOut) + { + memmove(bufOut + may_need_fppush + sizeof(GLUE_POP_FPSTACK_TOSTACK), bufOut + may_need_fppush, parm_size - may_need_fppush); + memcpy(bufOut + may_need_fppush, &GLUE_POP_FPSTACK_TOSTACK, sizeof(GLUE_POP_FPSTACK_TOSTACK)); + + } + parm_size += sizeof(GLUE_POP_FPSTACK_TOSTACK); + } + else + { + local_fpstack_use++; + } + } + + if (subfpstackuse+local_fpstack_use > *fpStackUsage) *fpStackUsage = subfpstackuse+local_fpstack_use; + + last_nt_parm = pn; + last_nt_parm_type = rvt; + + if (pn == n_params - 1 && func == nseel_asm_assign) + { + if (!(ctx->optimizeDisableFlags & OPTFLAG_FULL_DENORMAL_CHECKS) && + (!canHaveDenorm || (ctx->optimizeDisableFlags & OPTFLAG_NO_DENORMAL_CHECKS))) + { + if (rvt == RETURNVALUE_FPSTACK) + { + cfunc_abiinfo |= BIF_LASTPARMONSTACK; + func = nseel_asm_assign_fast_fromfp; + } + else + { + func = nseel_asm_assign_fast; + } + } + else + { + if (rvt == RETURNVALUE_FPSTACK) + { + cfunc_abiinfo |= BIF_LASTPARMONSTACK; + func = nseel_asm_assign_fromfp; + } + } + + } + } + } + + pn = last_nt_parm; + + if (pn >= 0) // if the last thing executed doesn't go to the last parameter, move it there + { + if ((cfunc_abiinfo&BIF_SECONDLASTPARMST) && pn == n_params-2) + { + // do nothing, things are in the right place + } + else if (pn != n_params-1) + { + // generate mov p1->pX + if (bufOut_len < parm_size + GLUE_SET_PX_FROM_P1_SIZE) RET_MINUS1_FAIL("size, pxfromp1") + if (bufOut) GLUE_SET_PX_FROM_P1(bufOut + parm_size,n_params - 1 - pn); + parm_size += GLUE_SET_PX_FROM_P1_SIZE; + } + } + + // pop any pushed parameters + while (--pn >= 0) + { + if (!OPCODE_IS_TRIVIAL(op->parms.parms[pn])) + { + if ((cfunc_abiinfo&BIF_SECONDLASTPARMST) && pn == n_params-2) + { + if (!local_fpstack_use) + { + #if GLUE_HAS_FPREG2 > 0 + if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_STACK_TO_FPREG2)) RET_MINUS1_FAIL("size, popstacktofpstack2 2") + if (bufOut) memcpy(bufOut+parm_size,GLUE_POP_STACK_TO_FPREG2,sizeof(GLUE_POP_STACK_TO_FPREG2)); + parm_size += sizeof(GLUE_POP_STACK_TO_FPREG2); + #else + if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_STACK_TO_FPSTACK)) RET_MINUS1_FAIL("size, popstacktofpstack 2") + if (bufOut) memcpy(bufOut+parm_size,GLUE_POP_STACK_TO_FPSTACK,sizeof(GLUE_POP_STACK_TO_FPSTACK)); + parm_size += sizeof(GLUE_POP_STACK_TO_FPSTACK); + #endif + #if GLUE_MAX_FPSTACK_SIZE > 0 + need_fxch = 1; + #endif + } + else + { +#if GLUE_MAX_SPILL_REGS > 0 + if (spill_reg<0) RET_MINUS1_FAIL("spill reg not allocated"); + + if (bufOut_len < parm_size + GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(spill_reg)) RET_MINUS1_FAIL("size, copy fps to fpreg2") + if (bufOut) GLUE_RESTORE_SPILL_TO_FPREG2(bufOut+parm_size,spill_reg); + parm_size += GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(spill_reg); +#endif + local_fpstack_use--; + } + } + else + { + if (bufOut_len < parm_size + GLUE_POP_PX_SIZE) RET_MINUS1_FAIL("size, poppx") + if (bufOut) GLUE_POP_PX(bufOut + parm_size,n_params - 1 - pn); + parm_size += GLUE_POP_PX_SIZE; + } + } + } + + // finally, set trivial pointers + for (pn=0; pn < n_params; pn++) + { + if (OPCODE_IS_TRIVIAL(op->parms.parms[pn])) + { + if (pn == n_params-2 && (cfunc_abiinfo&(BIF_SECONDLASTPARMST))) // second to last parameter + { + #if GLUE_HAS_FPREG2 > 0 + const int req = RETURNVALUE_FPREG2; + #else + const int req = RETURNVALUE_FPSTACK; + #endif + int a = compileOpcodes(ctx,op->parms.parms[pn],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size,computTableSize,namespacePathToThis, + req,NULL,NULL,canHaveDenormalOutput); + if (a<0) RET_MINUS1_FAIL("coc call here 2") + parm_size+=a; + + #if GLUE_MAX_FPSTACK_SIZE > 0 + need_fxch = 1; + #endif + } + else if (pn == n_params-1) // last parameter, but we should call compileOpcodes to get it in the right format (compileOpcodes can optimize that process if it needs to) + { + int rvt=0, a; + int wantFpStack = func == nseel_asm_assign; + #ifdef GLUE_PREFER_NONFP_DV_ASSIGNS // x86-64, and maybe others, prefer to avoid the fp stack for a simple copy + if (wantFpStack && + (op->parms.parms[pn]->opcodeType != OPCODETYPE_DIRECTVALUE || + op->parms.parms[pn]->parms.dv.directValue != 0.0)) + { + wantFpStack=-1; // cacheable but non-FP stack + } + #endif + #if GLUE_HAS_FPREG2 > 0 + if (pn > 0 && (cfunc_abiinfo&(BIF_SECONDLASTPARMST) && !OPCODE_IS_TRIVIAL(op->parms.parms[pn-1]))) + { + if (bufOut_len < parm_size+(int)sizeof(GLUE_COPY_FPSTACK_TO_FPREG2)) RET_MINUS1_FAIL("fptofpstack2tfp"); + if (bufOut) memcpy(bufOut+parm_size,GLUE_COPY_FPSTACK_TO_FPREG2,sizeof(GLUE_COPY_FPSTACK_TO_FPREG2)); + parm_size += sizeof(GLUE_COPY_FPSTACK_TO_FPREG2); + } + #endif + + a = compileOpcodes(ctx,op->parms.parms[pn],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size,computTableSize,namespacePathToThis, + func == nseel_asm_bnot ? (RETURNVALUE_BOOL_REVERSED|RETURNVALUE_BOOL) : + (cfunc_abiinfo & BIF_LASTPARMONSTACK) ? RETURNVALUE_FPSTACK : + (cfunc_abiinfo & BIF_LASTPARM_ASBOOL) ? RETURNVALUE_BOOL : + wantFpStack < 0 ? (RETURNVALUE_CACHEABLE|RETURNVALUE_NORMAL) : + wantFpStack ? (RETURNVALUE_FPSTACK|RETURNVALUE_NORMAL) : + RETURNVALUE_NORMAL, + &rvt, NULL,canHaveDenormalOutput); + + if (a<0) RET_MINUS1_FAIL("coc call here 3") +#ifdef GLUE_HAS_FUSE + if (rvt == RETURNVALUE_FPSTACK) fuse_flags |= 1; +#endif + + if (func == nseel_asm_bnot && rvt == RETURNVALUE_BOOL_REVERSED) + { + // remove bnot, compileOpcodes() used fptobool_rev + // case: !b ? 2 : 3, for example + func = NULL; + rvt = RETURNVALUE_BOOL; + } + + parm_size+=a; + #if GLUE_MAX_FPSTACK_SIZE > 0 + need_fxch = 0; + #endif + + if (func == nseel_asm_assign) + { + if (rvt == RETURNVALUE_FPSTACK) + { + if (!(ctx->optimizeDisableFlags & OPTFLAG_FULL_DENORMAL_CHECKS)) + { + func = nseel_asm_assign_fast_fromfp; + } + else + { + func = nseel_asm_assign_fromfp; + } + } + else if (!(ctx->optimizeDisableFlags & OPTFLAG_FULL_DENORMAL_CHECKS)) + { + // assigning a value (from a variable or other non-computer), can use a fast assign (no denormal/result checking) + func = nseel_asm_assign_fast; + } + } + } + else + { + if (bufOut_len < parm_size + GLUE_MOV_PX_DIRECTVALUE_SIZE) RET_MINUS1_FAIL("size, pxdvsz") + if (bufOut) + { + if (generateValueToReg(ctx,op->parms.parms[pn],bufOut + parm_size,n_params - 1 - pn,namespacePathToThis, 0/*nocaching, function gets pointer*/)<0) RET_MINUS1_FAIL("gvtr") + } + parm_size += GLUE_MOV_PX_DIRECTVALUE_SIZE; + } + } + } + + #if GLUE_MAX_FPSTACK_SIZE > 0 + if ((cfunc_abiinfo&(BIF_SECONDLASTPARMST)) && !(cfunc_abiinfo&(BIF_LAZYPARMORDERING))&& + ((!!need_fxch)^!!(cfunc_abiinfo&BIF_REVERSEFPORDER)) + ) + { + // emit fxch + if (bufOut_len < sizeof(GLUE_FXCH)) RET_MINUS1_FAIL("len,fxch") + if (bufOut) + { + memcpy(bufOut+parm_size,GLUE_FXCH,sizeof(GLUE_FXCH)); + } + parm_size+=sizeof(GLUE_FXCH); + } + #endif + + if (!*canHaveDenormalOutput) + { + // if add_op or sub_op, and constant non-denormal input, safe to omit denormal checks + if (func == (void*)nseel_asm_add_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) >= DENORMAL_CLEARING_THRESHOLD) + { + func = nseel_asm_add_op_fast; + } + else if (func == (void*)nseel_asm_sub_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) >= DENORMAL_CLEARING_THRESHOLD) + { + func = nseel_asm_sub_op_fast; + } + // or if mul/div by a fixed value of >= or <= 1.0 + else if (func == (void *)nseel_asm_mul_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) >= 1.0) + { + func = nseel_asm_mul_op_fast; + } + else if (func == (void *)nseel_asm_div_op && parm1_dv && fabs(op->parms.parms[1]->parms.dv.directValue) <= 1.0) + { + func = nseel_asm_div_op_fast; + } + } + } // not varparm + + if (cfunc_abiinfo & (BIF_CLEARDENORMAL | BIF_RETURNSBOOL) ) *canHaveDenormalOutput=0; + else if (!(cfunc_abiinfo & BIF_WONTMAKEDENORMAL)) *canHaveDenormalOutput=1; + + if (func) + { + func = GLUE_realAddress(func,&func_size); + if (!func) RET_MINUS1_FAIL("failrealladdrfunc") + } + else + { + func_size = 0; + } + + if (bufOut_len < parm_size + func_size) RET_MINUS1_FAIL("funcsz") + + if (bufOut) + { + unsigned char *p=bufOut + parm_size; + memcpy(p, func, func_size); +#if GLUE_HAS_FUSE + parm_size += GLUE_FUSE(ctx,p,parm_size,func_size,fuse_flags, +#if GLUE_MAX_SPILL_REGS > 0 + spill_reg +#else + -1 +#endif + ); + p = bufOut + parm_size; +#endif + if (preProc) p=preProc(p,func_size,ctx); + if (repl) + { + if (repl[0]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[0]); + if (repl[1]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[1]); + if (repl[2]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[2]); + if (repl[3]) p=EEL_GLUE_set_immediate(p,(INT_PTR)repl[3]); + } + } + + if (restore_stack_amt) + { + int rem = restore_stack_amt; + while (rem > 0) + { + int amt = rem; + if (amt > 4096) amt=4096; + rem -= amt; + + if (bufOut_len < parm_size + func_size + GLUE_MOVE_STACK_SIZE) RET_MINUS1_FAIL("insufficient size for varparm") + if (bufOut) GLUE_MOVE_STACK(bufOut + parm_size + func_size, amt); + parm_size += GLUE_MOVE_STACK_SIZE; + } + } + + if (cfunc_abiinfo&BIF_RETURNSONSTACK) *rvMode = RETURNVALUE_FPSTACK; + else if (cfunc_abiinfo&BIF_RETURNSBOOL) *rvMode=RETURNVALUE_BOOL; + + return parm_size + func_size; +} + +static int compileEelFunctionCall(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis, + int *rvMode, int *fpStackUse, int *canHaveDenormalOutput) +{ + int func_size=0, parm_size=0; + int pn; + int last_nt_parm=-1,last_nt_parm_mode=0; + void *func_e=NULL; + int n_params; + opcodeRec *parmptrs[NSEEL_MAX_EELFUNC_PARAMETERS]; + int cfp_numparams=-1; + int cfp_statesize=0; + EEL_F **cfp_ptrs=NULL; + int func_raw=0; + int do_parms; + int x; + + void *func; + + for (x=0; x < 3; x ++) parmptrs[x] = op->parms.parms[x]; + + if (op->opcodeType == OPCODETYPE_FUNCX) + { + n_params=0; + for (x=0;x<3;x++) + { + opcodeRec *prni=op->parms.parms[x]; + while (prni && n_params < NSEEL_MAX_EELFUNC_PARAMETERS) + { + const int isMP = prni->opcodeType == OPCODETYPE_MOREPARAMS; + parmptrs[n_params++] = isMP ? prni->parms.parms[0] : prni; + if (!isMP) break; + prni = prni->parms.parms[1]; + } + } + } + else + { + n_params = 1 + op->opcodeType - OPCODETYPE_FUNC1; + } + + *fpStackUse = 0; + func = nseel_getEELFunctionAddress(ctx, op, + &cfp_numparams,&cfp_statesize,&cfp_ptrs, + computTableSize, + &func_e, &func_raw, + !!bufOut,namespacePathToThis,rvMode,fpStackUse,canHaveDenormalOutput, parmptrs, n_params); + + if (func_raw) func_size = (int) ((char*)func_e - (char*)func); + else if (func) func = GLUE_realAddress(func,&func_size); + + if (!func) RET_MINUS1_FAIL("eelfuncaddr") + +#if GLUE_MAX_FPSTACK_SIZE > 0 + *fpStackUse += 1; +#endif + + if (cfp_numparams>0 && n_params != cfp_numparams) + { + RET_MINUS1_FAIL("eelfuncnp") + } + + // user defined function + do_parms = cfp_numparams>0 && cfp_ptrs && cfp_statesize>0; + + // if function local/parameter state is zero, we need to allocate storage for it + if (cfp_statesize>0 && cfp_ptrs && !cfp_ptrs[0]) + { + EEL_F *pstate = newDataBlock(sizeof(EEL_F)*cfp_statesize,8); + if (!pstate) RET_MINUS1_FAIL("eelfuncdb") + + for (pn=0;pn= 0 && do_parms) + { + if (last_nt_parm_mode == RETURNVALUE_FPSTACK) + { + if (bufOut_len < parm_size + (int)sizeof(GLUE_POP_FPSTACK_TOSTACK)) RET_MINUS1_FAIL("eelfunc_size popfpstacktostack") + if (bufOut) memcpy(bufOut + parm_size,GLUE_POP_FPSTACK_TOSTACK,sizeof(GLUE_POP_FPSTACK_TOSTACK)); + parm_size+=sizeof(GLUE_POP_FPSTACK_TOSTACK); + } + else + { + if (bufOut_len < parm_size + (int)sizeof(GLUE_PUSH_P1PTR_AS_VALUE)) RET_MINUS1_FAIL("eelfunc_size pushp1ptrasval") + + // push + if (bufOut) memcpy(bufOut + parm_size,&GLUE_PUSH_P1PTR_AS_VALUE,sizeof(GLUE_PUSH_P1PTR_AS_VALUE)); + parm_size+=sizeof(GLUE_PUSH_P1PTR_AS_VALUE); + } + } + + last_nt_parm_mode=0; + lsz = compileOpcodes(ctx,parmptrs[pn],bufOut ? bufOut + parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, + do_parms ? (RETURNVALUE_FPSTACK|RETURNVALUE_NORMAL) : RETURNVALUE_IGNORE,&last_nt_parm_mode,&sUse, &needDenorm); + + // todo: if needDenorm, denorm convert when copying parameter + + if (lsz<0) RET_MINUS1_FAIL("eelfunc, coc fail") + + if (last_nt_parm_mode == RETURNVALUE_FPSTACK) sUse++; + if (sUse > *fpStackUse) *fpStackUse=sUse; + parm_size += lsz; + + last_nt_parm = pn; + } + // pop non-trivial results into place + if (last_nt_parm >=0 && do_parms) + { + while (--pn >= 0) + { + if (!parmptrs[pn] || OPCODE_IS_TRIVIAL(parmptrs[pn])) continue; // skip and process after + if (pn == last_nt_parm) + { + if (last_nt_parm_mode == RETURNVALUE_FPSTACK) + { + // pop to memory directly + const int cpsize = GLUE_POP_FPSTACK_TO_PTR(NULL,NULL); + if (bufOut_len < parm_size + cpsize) RET_MINUS1_FAIL("eelfunc size popfpstacktoptr") + + if (bufOut) GLUE_POP_FPSTACK_TO_PTR((unsigned char *)bufOut + parm_size,cfp_ptrs[pn]); + parm_size += cpsize; + } + else + { + // copy direct p1ptr to mem + const int cpsize = GLUE_COPY_VALUE_AT_P1_TO_PTR(NULL,NULL); + if (bufOut_len < parm_size + cpsize) RET_MINUS1_FAIL("eelfunc size copyvalueatp1toptr") + + if (bufOut) GLUE_COPY_VALUE_AT_P1_TO_PTR((unsigned char *)bufOut + parm_size,cfp_ptrs[pn]); + parm_size += cpsize; + } + } + else + { + const int popsize = GLUE_POP_VALUE_TO_ADDR(NULL,NULL); + if (bufOut_len < parm_size + popsize) RET_MINUS1_FAIL("eelfunc size pop value to addr") + + if (bufOut) GLUE_POP_VALUE_TO_ADDR((unsigned char *)bufOut + parm_size,cfp_ptrs[pn]); + parm_size+=popsize; + + } + } + } + + // finally, set any trivial parameters + if (do_parms) + { + const int cpsize = GLUE_MOV_PX_DIRECTVALUE_SIZE + GLUE_COPY_VALUE_AT_P1_TO_PTR(NULL,NULL); + for (pn=0; pn < n_params; pn++) + { + if (!parmptrs[pn] || !OPCODE_IS_TRIVIAL(parmptrs[pn])) continue; // set trivial values, we already set nontrivials + + if (bufOut_len < parm_size + cpsize) RET_MINUS1_FAIL("eelfunc size trivial set") + + if (bufOut) + { + if (generateValueToReg(ctx,parmptrs[pn],bufOut + parm_size,0,namespacePathToThis, 1)<0) RET_MINUS1_FAIL("eelfunc gvr fail") + GLUE_COPY_VALUE_AT_P1_TO_PTR(bufOut + parm_size + GLUE_MOV_PX_DIRECTVALUE_SIZE,cfp_ptrs[pn]); + } + parm_size += cpsize; + + } + } + + if (bufOut_len < parm_size + func_size) RET_MINUS1_FAIL("eelfunc size combined") + + if (bufOut) memcpy(bufOut + parm_size, func, func_size); + + return parm_size + func_size; + // end of EEL function generation +} + +#ifdef DUMP_OPS_DURING_COMPILE +void dumpOp(compileContext *ctx, opcodeRec *op, int start); +#endif + +#ifdef EEL_DUMP_OPS +void dumpOpcodeTree(compileContext *ctx, FILE *fp, opcodeRec *op, int indent_amt) +{ + const char *fname=""; + fprintf(fp,"%*sOP TYPE %d", indent_amt, "", + op->opcodeType==OPCODETYPE_DIRECTVALUE_TEMPSTRING ? 10000 : // remap around OPCODETYPE_DIRECTVALUE_TEMPSTRING + op->opcodeType > OPCODETYPE_DIRECTVALUE_TEMPSTRING ? op->opcodeType - 1 : + op->opcodeType); + + if ((op->opcodeType == OPCODETYPE_FUNC1 || + op->opcodeType == OPCODETYPE_FUNC2 || + op->opcodeType == OPCODETYPE_FUNC3 || + op->opcodeType == OPCODETYPE_FUNCX)) + { + if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC) + { + functionType *fn_ptr = (functionType *)op->fn; + fname = fn_ptr->name; + } + else if (op->fntype == FUNCTYPE_EELFUNC) + { + fname = op->relname; + } + if (!fname) fname =""; + } + + switch (op->opcodeType) + { + case OPCODETYPE_DIRECTVALUE: + fprintf(fp," DV=%f\r\n",op->parms.dv.directValue); + break; + case OPCODETYPE_VALUE_FROM_NAMESPACENAME: // this.* or namespace.* are encoded this way + fprintf(fp," NSN=%s(%d)\r\n",op->relname?op->relname : "(null)",op->namespaceidx); + break; + case OPCODETYPE_VARPTR: + { + const char *nm = op->relname; + if (!nm || !*nm) + { + int wb; + for (wb = 0; wb < ctx->varTable_numBlocks; wb ++) + { + char **plist=ctx->varTable_Names[wb]; + if (!plist) break; + + if (op->parms.dv.valuePtr >= ctx->varTable_Values[wb] && op->parms.dv.valuePtr < ctx->varTable_Values[wb] + NSEEL_VARS_PER_BLOCK) + { + nm = plist[op->parms.dv.valuePtr - ctx->varTable_Values[wb]]; + break; + } + } + } + fprintf(fp," VP=%s\r\n", nm?nm : "(null)"); + } + break; + case OPCODETYPE_VARPTRPTR: + fprintf(fp, " VPP?\r\n"); + break; + case OPCODETYPE_FUNC1: + if (op->fntype == FN_NOT) + fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_not"); + else if (op->fntype == FN_NOTNOT) + fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_notnot"); + else if (op->fntype == FN_MEMORY) + fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_mem"); + else if (op->fntype == FN_GMEMORY) + fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_gmem"); + else if (op->fntype == FN_WHILE) + fprintf(fp," FUNC1 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "while"); + else + fprintf(fp," FUNC1 %d %s {\r\n",op->fntype, fname); + + if (op->parms.parms[0]) + dumpOpcodeTree(ctx,fp,op->parms.parms[0],indent_amt+2); + else + fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); + fprintf(fp,"%*s}\r\n", indent_amt, ""); + break; + case OPCODETYPE_MOREPARAMS: + case OPCODETYPE_FUNC2: + if (op->opcodeType == OPCODETYPE_MOREPARAMS) + fprintf(fp," MOREPARAMS {\r\n"); + else + { + if (op->fntype == FN_POW) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "pow"); + else if (op->fntype == FN_MOD) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_mod"); + else if (op->fntype == FN_XOR) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_xor"); + else if (op->fntype == FN_SHL) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_shl"); + else if (op->fntype == FN_SHR) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_shr"); + else if (op->fntype == FN_LT) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_below"); + else if (op->fntype == FN_GT) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_above"); + else if (op->fntype == FN_LTE) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_beleq"); + else if (op->fntype == FN_GTE) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_aboeq"); + else if (op->fntype == FN_EQ) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_equal"); + else if (op->fntype == FN_NE) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_noteq"); + else if (op->fntype == FN_EQ_EXACT) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_equal_exact"); + else if (op->fntype == FN_NE_EXACT) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_noteq_exact"); + else if (op->fntype == FN_LOGICAL_AND) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_and"); + else if (op->fntype == FN_LOGICAL_OR) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_or"); + else if (op->fntype == FN_ASSIGN) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_set"); + else if (op->fntype == FN_ADD_OP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_addop"); + else if (op->fntype == FN_SUB_OP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_subop"); + else if (op->fntype == FN_MUL_OP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_mulop"); + else if (op->fntype == FN_DIV_OP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_divop"); + else if (op->fntype == FN_OR_OP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_orop"); + else if (op->fntype == FN_AND_OP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_andop"); + else if (op->fntype == FN_XOR_OP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_xorop"); + else if (op->fntype == FN_MOD_OP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_modop"); + else if (op->fntype == FN_POW_OP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_powop"); + else if (op->fntype == FN_LOOP) + fprintf(fp," FUNC2 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "loop"); + else + fprintf(fp," FUNC2 %d %s {\r\n",op->fntype, fname); + } + if (op->parms.parms[0]) + dumpOpcodeTree(ctx,fp,op->parms.parms[0],indent_amt+2); + else + fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); + + if (op->parms.parms[1]) + dumpOpcodeTree(ctx,fp,op->parms.parms[1],indent_amt+2); + else + fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); + fprintf(fp,"%*s}\r\n", indent_amt, ""); + break; + case OPCODETYPE_FUNCX: + case OPCODETYPE_FUNC3: + if (op->opcodeType == OPCODETYPE_FUNCX) + fprintf(fp," FUNCX %d %s {\r\n",op->fntype, fname); + else if (op->fntype == FN_IF_ELSE) + fprintf(fp," FUNC3 %d %s {\r\n",FUNCTYPE_FUNCTIONTYPEREC, "_if"); + else + fprintf(fp," FUNC3 %d %s {\r\n",op->fntype, fname); + if (op->parms.parms[0]) + dumpOpcodeTree(ctx,fp,op->parms.parms[0],indent_amt+2); + else + fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); + + if (op->parms.parms[1]) + dumpOpcodeTree(ctx,fp,op->parms.parms[1],indent_amt+2); + else + fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); + + if (op->parms.parms[2]) + dumpOpcodeTree(ctx,fp,op->parms.parms[2],indent_amt+2); + else + fprintf(fp,"%*sINVALID PARM\r\n",indent_amt+2,""); + fprintf(fp,"%*s}\r\n", indent_amt, ""); + + break; + } +} + +#endif + +#ifdef GLUE_MAX_JMPSIZE +#define CHECK_SIZE_FORJMP(x,y) if ((x)<0 || (x)>=GLUE_MAX_JMPSIZE) goto y; +#define RET_MINUS1_FAIL_FALLBACK(err,j) goto j; +#else +#define CHECK_SIZE_FORJMP(x,y) +#define RET_MINUS1_FAIL_FALLBACK(err,j) RET_MINUS1_FAIL(err) +#endif +static int compileOpcodesInternal(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis, int *calledRvType, int preferredReturnValues, int *fpStackUse, int *canHaveDenormalOutput) +{ + int rv_offset=0, denormal_force=-1; + if (!op) RET_MINUS1_FAIL("coi !op") + + *fpStackUse=0; + for (;;) + { + // special case: statement delimiting means we can process the left side into place, and iteratively do the second parameter without recursing + // also we don't need to save/restore anything to the stack (which the normal 2 parameter function processing does) + if (op->opcodeType == OPCODETYPE_FUNC2 && op->fntype == FN_JOIN_STATEMENTS) + { + int fUse1; + int parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_IGNORE, NULL,&fUse1,NULL); + if (parm_size < 0) RET_MINUS1_FAIL("coc join fail") + op = op->parms.parms[1]; + if (!op) RET_MINUS1_FAIL("join got to null") + + if (fUse1>*fpStackUse) *fpStackUse=fUse1; + if (bufOut) bufOut += parm_size; + bufOut_len -= parm_size; + rv_offset += parm_size; +#ifdef DUMP_OPS_DURING_COMPILE + if (op->opcodeType != OPCODETYPE_FUNC2 || op->fntype != FN_JOIN_STATEMENTS) dumpOp(ctx,op,0); +#endif + denormal_force=-1; + } + // special case: __denormal_likely(), __denormal_unlikely() + else if (op->opcodeType == OPCODETYPE_FUNC1 && (op->fntype == FN_DENORMAL_LIKELY || op->fntype == FN_DENORMAL_UNLIKELY)) + { + denormal_force = op->fntype == FN_DENORMAL_LIKELY; + op = op->parms.parms[0]; + } + else + { + break; + } + } + if (denormal_force >= 0 && canHaveDenormalOutput) + { + *canHaveDenormalOutput = denormal_force; + canHaveDenormalOutput = &denormal_force; // prevent it from being changed by functions below + } + + // special case: BAND/BOR + if (op->opcodeType == OPCODETYPE_FUNC2 && (op->fntype == FN_LOGICAL_AND || op->fntype == FN_LOGICAL_OR)) + { + int fUse1=0; + int parm_size; +#ifdef GLUE_MAX_JMPSIZE + int parm_size_pre; +#endif + int retType=RETURNVALUE_IGNORE; + if (preferredReturnValues != RETURNVALUE_IGNORE) retType = RETURNVALUE_BOOL; + + *calledRvType = retType; + + parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_BOOL, NULL, &fUse1, NULL); + if (parm_size < 0) RET_MINUS1_FAIL("loop band/bor coc fail") + + if (fUse1 > *fpStackUse) *fpStackUse=fUse1; + + +#ifdef GLUE_MAX_JMPSIZE + parm_size_pre=parm_size; +#endif + + { + int sz2, fUse2=0; + unsigned char *destbuf; + const int testsz=op->fntype == FN_LOGICAL_OR ? sizeof(GLUE_JMP_IF_P1_NZ) : sizeof(GLUE_JMP_IF_P1_Z); + if (bufOut_len < parm_size+testsz) RET_MINUS1_FAIL_FALLBACK("band/bor size fail",doNonInlinedAndOr_) + + if (bufOut) memcpy(bufOut+parm_size,op->fntype == FN_LOGICAL_OR ? GLUE_JMP_IF_P1_NZ : GLUE_JMP_IF_P1_Z,testsz); + parm_size += testsz; + destbuf = bufOut + parm_size; + + sz2= compileOpcodes(ctx,op->parms.parms[1],bufOut?bufOut+parm_size:NULL,bufOut_len-parm_size, computTableSize, namespacePathToThis, retType, NULL,&fUse2, NULL); + + CHECK_SIZE_FORJMP(sz2,doNonInlinedAndOr_) + if (sz2<0) RET_MINUS1_FAIL("band/bor coc fail") + + parm_size+=sz2; + if (bufOut) GLUE_JMP_SET_OFFSET(destbuf, (bufOut + parm_size) - destbuf); + + if (fUse2 > *fpStackUse) *fpStackUse=fUse2; + return rv_offset + parm_size; + } +#ifdef GLUE_MAX_JMPSIZE + if (0) + { + void *stub; + int stubsize; + unsigned char *newblock2, *p; + + // encode as function call +doNonInlinedAndOr_: + parm_size = parm_size_pre; + + if (op->fntype == FN_LOGICAL_AND) + { + stub = GLUE_realAddress(nseel_asm_band,&stubsize); + } + else + { + stub = GLUE_realAddress(nseel_asm_bor,&stubsize); + } + + if (bufOut_len < parm_size + stubsize) RET_MINUS1_FAIL("band/bor len fail") + + if (bufOut) + { + int fUse2=0; + newblock2 = compileCodeBlockWithRet(ctx,op->parms.parms[1],computTableSize,namespacePathToThis, retType, NULL, &fUse2, NULL); + if (!newblock2) RET_MINUS1_FAIL("band/bor ccbwr fail") + + if (fUse2 > *fpStackUse) *fpStackUse=fUse2; + + p = bufOut + parm_size; + memcpy(p, stub, stubsize); + + p=EEL_GLUE_set_immediate(p,(INT_PTR)newblock2); + } + return rv_offset + parm_size + stubsize; + } +#endif + } + + if (op->opcodeType == OPCODETYPE_FUNC3 && op->fntype == FN_IF_ELSE) // special case: IF + { + int fUse1=0; +#ifdef GLUE_MAX_JMPSIZE + int parm_size_pre; +#endif + int use_rv = RETURNVALUE_IGNORE; + int rvMode=0; + int parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_BOOL|RETURNVALUE_BOOL_REVERSED, &rvMode,&fUse1, NULL); + if (parm_size < 0) RET_MINUS1_FAIL("if coc fail") + if (fUse1 > *fpStackUse) *fpStackUse=fUse1; + + if (preferredReturnValues & RETURNVALUE_NORMAL) use_rv=RETURNVALUE_NORMAL; + else if (preferredReturnValues & RETURNVALUE_FPSTACK) use_rv=RETURNVALUE_FPSTACK; + else if (preferredReturnValues & RETURNVALUE_BOOL) use_rv=RETURNVALUE_BOOL; + + *calledRvType = use_rv; +#ifdef GLUE_MAX_JMPSIZE + parm_size_pre = parm_size; +#endif + + { + int csz,hasSecondHalf; + if (rvMode & RETURNVALUE_BOOL_REVERSED) + { + if (bufOut_len < parm_size + (int)sizeof(GLUE_JMP_IF_P1_NZ)) RET_MINUS1_FAIL_FALLBACK("if size fail",doNonInlineIf_) + if (bufOut) memcpy(bufOut+parm_size,GLUE_JMP_IF_P1_NZ,sizeof(GLUE_JMP_IF_P1_NZ)); + parm_size += sizeof(GLUE_JMP_IF_P1_NZ); + } + else + { + if (bufOut_len < parm_size + (int)sizeof(GLUE_JMP_IF_P1_Z)) RET_MINUS1_FAIL_FALLBACK("if size fail",doNonInlineIf_) + if (bufOut) memcpy(bufOut+parm_size,GLUE_JMP_IF_P1_Z,sizeof(GLUE_JMP_IF_P1_Z)); + parm_size += sizeof(GLUE_JMP_IF_P1_Z); + } + csz=compileOpcodes(ctx,op->parms.parms[1],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, use_rv, NULL,&fUse1, canHaveDenormalOutput); + if (fUse1 > *fpStackUse) *fpStackUse=fUse1; + hasSecondHalf = preferredReturnValues || !OPCODE_IS_TRIVIAL(op->parms.parms[2]); + + CHECK_SIZE_FORJMP(csz,doNonInlineIf_) + if (csz<0) RET_MINUS1_FAIL("if coc fial") + + if (bufOut) GLUE_JMP_SET_OFFSET(bufOut + parm_size, csz + (hasSecondHalf?sizeof(GLUE_JMP_NC):0)); + parm_size+=csz; + + if (hasSecondHalf) + { + if (bufOut_len < parm_size + (int)sizeof(GLUE_JMP_NC)) RET_MINUS1_FAIL_FALLBACK("if len fail",doNonInlineIf_) + if (bufOut) memcpy(bufOut+parm_size,GLUE_JMP_NC,sizeof(GLUE_JMP_NC)); + parm_size+=sizeof(GLUE_JMP_NC); + + csz=compileOpcodes(ctx,op->parms.parms[2],bufOut ? bufOut+parm_size : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, use_rv, NULL, &fUse1, canHaveDenormalOutput); + + CHECK_SIZE_FORJMP(csz,doNonInlineIf_) + if (csz<0) RET_MINUS1_FAIL("if coc 2 fail") + + // update jump address + if (bufOut) GLUE_JMP_SET_OFFSET(bufOut + parm_size,csz); + parm_size+=csz; + if (fUse1 > *fpStackUse) *fpStackUse=fUse1; + } + return rv_offset + parm_size; + } +#ifdef GLUE_MAX_JMPSIZE + if (0) + { + unsigned char *newblock2,*newblock3,*ptr; + void *stub; + int stubsize; +doNonInlineIf_: + parm_size = parm_size_pre; + stub = GLUE_realAddress(nseel_asm_if,&stubsize); + + if (!stub || bufOut_len < parm_size + stubsize) RET_MINUS1_FAIL(stub ? "if sz fail" : "if addr fail") + + if (bufOut) + { + int fUse2=0; + newblock2 = compileCodeBlockWithRet(ctx,op->parms.parms[1],computTableSize,namespacePathToThis, use_rv, NULL,&fUse2, canHaveDenormalOutput); + if (fUse2 > *fpStackUse) *fpStackUse=fUse2; + newblock3 = compileCodeBlockWithRet(ctx,op->parms.parms[2],computTableSize,namespacePathToThis, use_rv, NULL,&fUse2, canHaveDenormalOutput); + if (fUse2 > *fpStackUse) *fpStackUse=fUse2; + if (!newblock2 || !newblock3) RET_MINUS1_FAIL("if subblock gen fail") + + ptr = bufOut + parm_size; + memcpy(ptr, stub, stubsize); + + ptr=EEL_GLUE_set_immediate(ptr,(INT_PTR)newblock2); + EEL_GLUE_set_immediate(ptr,(INT_PTR)newblock3); + } + return rv_offset + parm_size + stubsize; + } +#endif + } + + { + // special case: while + if (op->opcodeType == OPCODETYPE_FUNC1 && op->fntype == FN_WHILE) + { + *calledRvType = RETURNVALUE_BOOL; + +#ifndef GLUE_INLINE_LOOPS + // todo: PPC looping support when loop length is small enough + { + unsigned char *pwr=bufOut; + unsigned char *newblock2; + int stubsz; + void *stubfunc = GLUE_realAddress(nseel_asm_repeatwhile,&stubsz); + if (!stubfunc || bufOut_len < stubsz) RET_MINUS1_FAIL(stubfunc ? "repeatwhile size fail" :"repeatwhile addr fail") + + if (bufOut) + { + int fUse1 = 0; + newblock2=compileCodeBlockWithRet(ctx,op->parms.parms[0],computTableSize,namespacePathToThis, RETURNVALUE_BOOL, NULL, &fUse1, NULL); + if (!newblock2) RET_MINUS1_FAIL("repeatwhile ccbwr fail") + + if (fUse1 > *fpStackUse) *fpStackUse = fUse1; + memcpy(pwr,stubfunc,stubsz); + pwr=EEL_GLUE_set_immediate(pwr,(INT_PTR)newblock2); + } + + return rv_offset+stubsz; + } +#else + { +#ifndef GLUE_WHILE_END_NOJUMP + unsigned char *jzoutpt; +#endif + unsigned char *looppt; + int parm_size=0,subsz, fUse1=0; + if (bufOut_len < parm_size + (int)(GLUE_WHILE_SETUP_SIZE + sizeof(GLUE_WHILE_BEGIN))) RET_MINUS1_FAIL("while size fail 1") + + if (bufOut) memcpy(bufOut + parm_size,GLUE_WHILE_SETUP,GLUE_WHILE_SETUP_SIZE); + parm_size+=GLUE_WHILE_SETUP_SIZE; + + looppt = bufOut + parm_size; + if (bufOut) memcpy(bufOut + parm_size,GLUE_WHILE_BEGIN,sizeof(GLUE_WHILE_BEGIN)); + parm_size+=sizeof(GLUE_WHILE_BEGIN); + + subsz = compileOpcodes(ctx,op->parms.parms[0],bufOut ? (bufOut + parm_size) : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, RETURNVALUE_BOOL, NULL,&fUse1, NULL); + if (fUse1 > *fpStackUse) *fpStackUse = fUse1; + if (subsz<0) RET_MINUS1_FAIL("while coc fail") + + if (bufOut_len < parm_size + (int)(sizeof(GLUE_WHILE_END) + sizeof(GLUE_WHILE_CHECK_RV))) RET_MINUS1_FAIL("which size fial 2") + + parm_size+=subsz; + if (bufOut) memcpy(bufOut + parm_size, GLUE_WHILE_END, sizeof(GLUE_WHILE_END)); + parm_size+=sizeof(GLUE_WHILE_END); +#ifndef GLUE_WHILE_END_NOJUMP + jzoutpt = bufOut + parm_size; +#endif + + if (bufOut) memcpy(bufOut + parm_size, GLUE_WHILE_CHECK_RV, sizeof(GLUE_WHILE_CHECK_RV)); + parm_size+=sizeof(GLUE_WHILE_CHECK_RV); + if (bufOut) + { + GLUE_JMP_SET_OFFSET(bufOut + parm_size,(looppt - (bufOut+parm_size)) ); +#ifndef GLUE_WHILE_END_NOJUMP + GLUE_JMP_SET_OFFSET(jzoutpt, (bufOut + parm_size) - jzoutpt); +#endif + } + return rv_offset+parm_size; + } + +#endif + } + + // special case: loop + if (op->opcodeType == OPCODETYPE_FUNC2 && op->fntype == FN_LOOP) + { + int fUse1; + int parm_size = compileOpcodes(ctx,op->parms.parms[0],bufOut,bufOut_len, computTableSize, namespacePathToThis, RETURNVALUE_FPSTACK, NULL,&fUse1, NULL); + if (parm_size < 0) RET_MINUS1_FAIL("loop coc fail") + + *calledRvType = RETURNVALUE_BOOL; + if (fUse1 > *fpStackUse) *fpStackUse=fUse1; + +#ifndef GLUE_INLINE_LOOPS + // todo: PPC looping support when loop length is small enough + { + void *stub; + int stubsize; + unsigned char *newblock2, *p; + stub = GLUE_realAddress(nseel_asm_repeat,&stubsize); + if (bufOut_len < parm_size + stubsize) RET_MINUS1_FAIL("loop size fail") + if (bufOut) + { + newblock2 = compileCodeBlockWithRet(ctx,op->parms.parms[1],computTableSize,namespacePathToThis, RETURNVALUE_IGNORE, NULL,fpStackUse, NULL); + + p = bufOut + parm_size; + memcpy(p, stub, stubsize); + + p=EEL_GLUE_set_immediate(p,(INT_PTR)newblock2); + } + return rv_offset + parm_size + stubsize; + } +#else + { + int subsz; + int fUse2=0; + unsigned char *skipptr1,*loopdest; + +#ifndef GLUE_LOOP_LOADCNT_SIZE +#define GLUE_LOOP_LOADCNT_SIZE sizeof(GLUE_LOOP_LOADCNT) +#endif + if (bufOut_len < parm_size + (int)(GLUE_LOOP_LOADCNT_SIZE + GLUE_LOOP_CLAMPCNT_SIZE + GLUE_LOOP_BEGIN_SIZE)) RET_MINUS1_FAIL("loop size fail") + + // store, convert to int, compare against 1, if less than, skip to end + if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_LOADCNT,GLUE_LOOP_LOADCNT_SIZE); + parm_size += GLUE_LOOP_LOADCNT_SIZE; + skipptr1 = bufOut+parm_size; + + // compare aginst max loop length, jump to loop start if not above it + if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_CLAMPCNT,GLUE_LOOP_CLAMPCNT_SIZE); + parm_size += GLUE_LOOP_CLAMPCNT_SIZE; + + // loop code: + loopdest = bufOut + parm_size; + + if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_BEGIN,GLUE_LOOP_BEGIN_SIZE); + parm_size += GLUE_LOOP_BEGIN_SIZE; + + subsz = compileOpcodes(ctx,op->parms.parms[1],bufOut ? (bufOut + parm_size) : NULL,bufOut_len - parm_size, computTableSize, namespacePathToThis, RETURNVALUE_IGNORE, NULL, &fUse2, NULL); + if (subsz<0) RET_MINUS1_FAIL("loop coc fail") + if (fUse2 > *fpStackUse) *fpStackUse=fUse2; + + parm_size += subsz; + + if (bufOut_len < parm_size + (int)sizeof(GLUE_LOOP_END)) RET_MINUS1_FAIL("loop size fail 2") + + if (bufOut) memcpy(bufOut+parm_size,GLUE_LOOP_END,sizeof(GLUE_LOOP_END)); + parm_size += sizeof(GLUE_LOOP_END); + + if (bufOut) + { + GLUE_JMP_SET_OFFSET(bufOut + parm_size,loopdest - (bufOut+parm_size)); + GLUE_JMP_SET_OFFSET(skipptr1, (bufOut+parm_size) - skipptr1); + } + + return rv_offset + parm_size; + + } +#endif + } + } + + switch (op->opcodeType) + { + case OPCODETYPE_DIRECTVALUE: + if (preferredReturnValues == RETURNVALUE_BOOL) + { + int w = fabs(op->parms.dv.directValue) >= NSEEL_CLOSEFACTOR; + int wsz=(w?sizeof(GLUE_SET_P1_NZ):sizeof(GLUE_SET_P1_Z)); + + *calledRvType = RETURNVALUE_BOOL; + if (bufOut_len < wsz) RET_MINUS1_FAIL("direct bool size fail3") + if (bufOut) memcpy(bufOut,w?GLUE_SET_P1_NZ:GLUE_SET_P1_Z,wsz); + return rv_offset+wsz; + } + else if (preferredReturnValues & RETURNVALUE_FPSTACK) + { +#ifdef GLUE_HAS_FLDZ + if (op->parms.dv.directValue == 0.0) + { +#if GLUE_MAX_FPSTACK_SIZE > 0 + if (*fpStackUse < 1) *fpStackUse = 1; +#endif + *calledRvType = RETURNVALUE_FPSTACK; + if (bufOut_len < sizeof(GLUE_FLDZ)) RET_MINUS1_FAIL("direct fp fail 1") + if (bufOut) memcpy(bufOut,GLUE_FLDZ,sizeof(GLUE_FLDZ)); + return rv_offset+sizeof(GLUE_FLDZ); + } +#endif +#ifdef GLUE_HAS_FLD1 + if (op->parms.dv.directValue == 1.0) + { +#if GLUE_MAX_FPSTACK_SIZE > 0 + if (*fpStackUse < 1) *fpStackUse = 1; +#endif + *calledRvType = RETURNVALUE_FPSTACK; + if (bufOut_len < sizeof(GLUE_FLD1)) RET_MINUS1_FAIL("direct fp fail 1") + if (bufOut) memcpy(bufOut,GLUE_FLD1,sizeof(GLUE_FLD1)); + return rv_offset+sizeof(GLUE_FLD1); + } +#endif + } + // fall through + case OPCODETYPE_DIRECTVALUE_TEMPSTRING: + case OPCODETYPE_VALUE_FROM_NAMESPACENAME: + case OPCODETYPE_VARPTR: + case OPCODETYPE_VARPTRPTR: + + #if GLUE_HAS_FPREG2 > 0 + if (preferredReturnValues & RETURNVALUE_FPREG2) + { + if (preferredReturnValues != RETURNVALUE_FPREG2) RET_MINUS1_FAIL("RETURNVALUE_FPREG2 but not exact hm") + if (bufOut_len < GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE) RET_MINUS1_FAIL("direct fp2 fail 2") + if (bufOut) + { + if (generateValueToReg(ctx,op,bufOut,-2,namespacePathToThis, 1 /*allow caching*/)<0) RET_MINUS1_FAIL("direct fp2 fail gvr") + } + *calledRvType = RETURNVALUE_FPREG2; + return rv_offset+GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE; + } + #endif + + #ifdef GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE + if (OPCODE_IS_TRIVIAL(op)) + { + if (preferredReturnValues & RETURNVALUE_FPSTACK) + { +#if GLUE_MAX_FPSTACK_SIZE > 0 + if (*fpStackUse < 1) *fpStackUse = 1; +#endif + if (bufOut_len < GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE) RET_MINUS1_FAIL("direct fp fail 2") + if (bufOut) + { + if (generateValueToReg(ctx,op,bufOut,-1,namespacePathToThis, 1 /*allow caching*/)<0) RET_MINUS1_FAIL("direct fp fail gvr") + } + *calledRvType = RETURNVALUE_FPSTACK; + return rv_offset+GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE; + } + } + #endif + + if (bufOut_len < GLUE_MOV_PX_DIRECTVALUE_SIZE) + { + RET_MINUS1_FAIL("direct value fail 1") + } + if (bufOut) + { + if (generateValueToReg(ctx,op,bufOut,0,namespacePathToThis, + (preferredReturnValues&(RETURNVALUE_FPSTACK|RETURNVALUE_CACHEABLE))!=0)<0) + { + RET_MINUS1_FAIL("direct value gvr fail3") + } + } + return rv_offset + GLUE_MOV_PX_DIRECTVALUE_SIZE; + + case OPCODETYPE_FUNCX: + case OPCODETYPE_FUNC1: + case OPCODETYPE_FUNC2: + case OPCODETYPE_FUNC3: + + if (op->fntype == FUNCTYPE_EELFUNC) + { + int a, fpUse1=0; + + a = compileEelFunctionCall(ctx,op,bufOut,bufOut_len,computTableSize,namespacePathToThis, calledRvType,&fpUse1,canHaveDenormalOutput); + if (a<0) return a; + if (fpUse1 > *fpStackUse) *fpStackUse = fpUse1; + rv_offset += a; + } + else + { + int a, fpUse1=0; + a = compileNativeFunctionCall(ctx,op,bufOut,bufOut_len,computTableSize,namespacePathToThis, calledRvType,&fpUse1,preferredReturnValues,canHaveDenormalOutput); + if (a<0)return a; + if (fpUse1 > *fpStackUse) *fpStackUse = fpUse1; + rv_offset += a; + } + return rv_offset; + } + + RET_MINUS1_FAIL("default opcode fail") +} + +#ifdef DUMP_OPS_DURING_COMPILE +FILE *g_debugfp; +int g_debugfp_indent; +int g_debugfp_histsz=0; + +void dumpOp(compileContext *ctx, opcodeRec *op, int start) +{ + if (start>=0) + { + if (g_debugfp) + { + static opcodeRec **hist; + + int x; + int hit=0; + if (!hist) hist = (opcodeRec**) calloc(1024,1024*sizeof(opcodeRec*)); + for(x=0;x=100) *(char *)1=0; + fprintf(g_debugfp,"%*s{ %p : %d%s: ",g_debugfp_indent," ",op,op->opcodeType, hit ? " -- DUPLICATE" : ""); + switch (op->opcodeType) + { + case OPCODETYPE_DIRECTVALUE: + fprintf(g_debugfp,"dv %f",op->parms.dv.directValue); + break; + case OPCODETYPE_VARPTR: + if (op->relname && op->relname[0]) + { + fprintf(g_debugfp,"var %s",op->relname); + } + else + { + int wb; + for (wb = 0; wb < ctx->varTable_numBlocks; wb ++) + { + char **plist=ctx->varTable_Names[wb]; + if (!plist) break; + + if (op->parms.dv.valuePtr >= ctx->varTable_Values[wb] && op->parms.dv.valuePtr < ctx->varTable_Values[wb] + NSEEL_VARS_PER_BLOCK) + { + fprintf(g_debugfp,"var %s",plist[op->parms.dv.valuePtr - ctx->varTable_Values[wb]]); + break; + } + } + } + break; + case OPCODETYPE_FUNC1: + case OPCODETYPE_FUNC2: + case OPCODETYPE_FUNC3: + case OPCODETYPE_FUNCX: + if (op->fntype == FUNCTYPE_FUNCTIONTYPEREC) + { + functionType *p=(functionType*)op->fn; + fprintf(g_debugfp,"func %d: %s",p->nParams&0xff,p->name); + } + else + fprintf(g_debugfp,"sf %d",op->fntype); + break; + + } + fprintf(g_debugfp,"\n"); + g_debugfp_indent+=2; + } + } + else + { + if (g_debugfp) + { + g_debugfp_indent-=2; + fprintf(g_debugfp,"%*s}%p\n",g_debugfp_indent," ",op); + } + } +} +#endif + +int compileOpcodes(compileContext *ctx, opcodeRec *op, unsigned char *bufOut, int bufOut_len, int *computTableSize, const namespaceInformation *namespacePathToThis, + int supportedReturnValues, int *rvType, int *fpStackUse, int *canHaveDenormalOutput) +{ + int code_returns=RETURNVALUE_NORMAL; + int fpsu=0; + int codesz; + int denorm=0; + +#ifdef DUMP_OPS_DURING_COMPILE + dumpOp(ctx,op,1); +#endif + + codesz = compileOpcodesInternal(ctx,op,bufOut,bufOut_len,computTableSize,namespacePathToThis,&code_returns, supportedReturnValues,&fpsu,&denorm); + if (denorm && canHaveDenormalOutput) *canHaveDenormalOutput=1; + +#ifdef DUMP_OPS_DURING_COMPILE + dumpOp(ctx,op,-1); +#endif +#ifdef EEL_DUMP_OPS + // dump opcode trees for verification, after optimizing + if (g_eel_dump_fp2) + { + fprintf(g_eel_dump_fp2,"-- compileOpcodes generated %d bytes of code!\r\n",codesz); + } +#endif + if (codesz < 0) return codesz; + +#if GLUE_HAS_FPREG2 > 0 + if (supportedReturnValues & RETURNVALUE_FPREG2) + { + if (code_returns != RETURNVALUE_FPREG2) + RET_MINUS1_FAIL("fpstack2 not return correct fail"); + } +#endif + + /* + { + char buf[512]; + sprintf(buf,"opcode %d %d (%s): fpu use: %d\n",op->opcodeType,op->fntype, + op->opcodeType >= OPCODETYPE_FUNC1 && op->fntype == FUNCTYPE_FUNCTIONTYPEREC ? ( + ((functionType *)op->fn)->name + ) : "", + fpsu); + OutputDebugString(buf); + } + */ + + if (fpStackUse) *fpStackUse=fpsu; + + if (bufOut) bufOut += codesz; + bufOut_len -= codesz; + + + if (code_returns == RETURNVALUE_BOOL && !(supportedReturnValues & RETURNVALUE_BOOL) && supportedReturnValues) + { + int stubsize; + void *stub = GLUE_realAddress(nseel_asm_booltofp,&stubsize); + if (!stub || bufOut_len < stubsize) RET_MINUS1_FAIL(stub?"booltofp size":"booltfp addr") + if (bufOut) + { + memcpy(bufOut,stub,stubsize); + bufOut += stubsize; + } + codesz+=stubsize; + bufOut_len -= stubsize; + + code_returns = RETURNVALUE_FPSTACK; + } + + + // default processing of code_returns to meet return value requirements + if (supportedReturnValues & code_returns) + { + if (rvType) *rvType = code_returns; + return codesz; + } + + + if (rvType) *rvType = RETURNVALUE_IGNORE; + + + if (code_returns == RETURNVALUE_NORMAL) + { + if (supportedReturnValues & (RETURNVALUE_FPSTACK|RETURNVALUE_BOOL)) + { + if (bufOut_len < GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE) RET_MINUS1_FAIL("pushvalatpxtofpstack,size") + if (bufOut) + { + GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(bufOut,0); // always fld qword [eax] but we might change that later + bufOut += GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE; + } + codesz += GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE; + bufOut_len -= GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE; + + if (supportedReturnValues & RETURNVALUE_BOOL) + { + code_returns = RETURNVALUE_FPSTACK; + } + else + { + if (rvType) *rvType = RETURNVALUE_FPSTACK; + } + } + } + + if (code_returns == RETURNVALUE_FPSTACK) + { + if (supportedReturnValues & (RETURNVALUE_BOOL|RETURNVALUE_BOOL_REVERSED)) + { + int stubsize; + void *stub; + + if (supportedReturnValues & RETURNVALUE_BOOL_REVERSED) + { + if (rvType) *rvType = RETURNVALUE_BOOL_REVERSED; + stub = GLUE_realAddress(nseel_asm_fptobool_rev,&stubsize); + } + else + { + if (rvType) *rvType = RETURNVALUE_BOOL; + stub = GLUE_realAddress(nseel_asm_fptobool,&stubsize); + } + + + if (!stub || bufOut_len < stubsize) RET_MINUS1_FAIL(stub?"fptobool size":"fptobool addr") + if (bufOut) + { + memcpy(bufOut,stub,stubsize); + bufOut += stubsize; + } + codesz+=stubsize; + bufOut_len -= stubsize; + } + else if (supportedReturnValues & RETURNVALUE_NORMAL) + { + if (computTableSize) (*computTableSize) ++; + + if (bufOut_len < GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE) RET_MINUS1_FAIL("popfpstacktowtptopxsize") + + // generate fp-pop to temp space + if (bufOut) GLUE_POP_FPSTACK_TO_WTP_TO_PX(bufOut,0); + codesz+=GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE; + if (rvType) *rvType = RETURNVALUE_NORMAL; + } + else + { + // toss return value that will be ignored + if (bufOut_len < GLUE_POP_FPSTACK_SIZE) RET_MINUS1_FAIL("popfpstack size") + if (bufOut) memcpy(bufOut,GLUE_POP_FPSTACK,GLUE_POP_FPSTACK_SIZE); + codesz+=GLUE_POP_FPSTACK_SIZE; + } + } + + return codesz; +} + + +#if 0 +static void movestringover(char *str, int amount) +{ + char tmp[1024+8]; + + int l=(int)strlen(str); + l=wdl_min(1024-amount-1,l); + + memcpy(tmp,str,l+1); + + while (l >= 0 && tmp[l]!='\n') l--; + l++; + + tmp[l]=0;//ensure we null terminate + + memcpy(str+amount,tmp,l+1); +} +#endif + +//------------------------------------------------------------------------------ +NSEEL_CODEHANDLE NSEEL_code_compile(NSEEL_VMCTX _ctx, const char *_expression, int lineoffs) +{ + return NSEEL_code_compile_ex(_ctx,_expression,lineoffs,0); +} + +typedef struct topLevelCodeSegmentRec { + struct topLevelCodeSegmentRec *_next; + void *code; + int codesz; + int tmptable_use; +} topLevelCodeSegmentRec; + + +NSEEL_CODEHANDLE NSEEL_code_compile_ex(NSEEL_VMCTX _ctx, const char *_expression, int lineoffs, int compile_flags) +{ + compileContext *ctx = (compileContext *)_ctx; + const char *endptr; + const char *_expression_end; + codeHandleType *handle; + topLevelCodeSegmentRec *startpts_tail=NULL; + topLevelCodeSegmentRec *startpts=NULL; + _codeHandleFunctionRec *oldCommonFunctionList; + int curtabptr_sz=0; + void *curtabptr=NULL; + int had_err=0; + + if (!ctx) return 0; + + ctx->directValueCache=0; + ctx->optimizeDisableFlags=0; + ctx->gotEndOfInput=0; + ctx->current_compile_flags = compile_flags; + + if (compile_flags & NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS_RESET) + { + ctx->functions_common=NULL; // reset common function list + } + else + { + // reset common compiled function code, forcing a recompile if shared + _codeHandleFunctionRec *a = ctx->functions_common; + while (a) + { + _codeHandleFunctionRec *b = a->derivedCopies; + + if (a->localstorage) + { + // force local storage actual values to be reallocated if used again + memset(a->localstorage,0,sizeof(EEL_F *) * a->localstorage_size); + } + + a->startptr = NULL; // force this copy to be recompiled + a->startptr_size = -1; + + while (b) + { + b->startptr = NULL; // force derived copies to get recompiled + b->startptr_size = -1; + // no need to reset b->localstorage, since it points to a->localstorage + b=b->derivedCopies; + } + + a=a->next; + } + } + + ctx->last_error_string[0]=0; + + if (!_expression || !*_expression) return 0; + + _expression_end = _expression + strlen(_expression); + + oldCommonFunctionList = ctx->functions_common; + + ctx->isGeneratingCommonFunction=0; + ctx->isSharedFunctions = !!(compile_flags & NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS); + ctx->functions_local = NULL; + + freeBlocks(&ctx->tmpblocks,0); + freeBlocks(&ctx->blocks_head_code,1); + freeBlocks(&ctx->blocks_head_data,0); + memset(ctx->l_stats,0,sizeof(ctx->l_stats)); + + handle = (codeHandleType*)newDataBlock(sizeof(codeHandleType),8); + + if (!handle) + { + return 0; + } + + + memset(handle,0,sizeof(codeHandleType)); + + ctx->l_stats[0] += (int)(_expression_end - _expression); + ctx->tmpCodeHandle = handle; + endptr=_expression; + + while (*endptr) + { + int computTableTop = 0; + int startptr_size=0; + void *startptr=NULL; + opcodeRec *start_opcode=NULL; + const char *expr=endptr; + + int function_numparms=0; + char is_fname[NSEEL_MAX_VARIABLE_NAMELEN+1]; + is_fname[0]=0; + + memset(ctx->function_localTable_Size,0,sizeof(ctx->function_localTable_Size)); + memset(ctx->function_localTable_Names,0,sizeof(ctx->function_localTable_Names)); + ctx->function_localTable_ValuePtrs=0; + ctx->function_usesNamespaces=0; + ctx->function_curName=NULL; + ctx->function_globalFlag=0; + + ctx->errVar=0; + + // single out top level segment + { + int had_something = 0, pcnt=0, pcnt2=0; + int state=0; + for (;;) + { + int l; + const char *p=nseel_simple_tokenizer(&endptr,_expression_end,&l,&state); + if (!p) + { + if (pcnt || pcnt2) ctx->gotEndOfInput|=4; + break; + } + + if (*p == ';') + { + if (had_something && !pcnt && !pcnt2) break; + } + else if (*p == '/' && l > 1 && (p[1] == '/' || p[1] == '*')) + { + if (l > 19 && !strnicmp(p,"//#eel-no-optimize:",19)) + ctx->optimizeDisableFlags = atoi(p+19); + } + else + { + if (!had_something) + { + expr = p; + had_something = 1; + } + + if (*p == '(') pcnt++; + else if (*p == ')') { if (--pcnt<0) pcnt=0; } + else if (*p == '[') pcnt2++; + else if (*p == ']') { if (--pcnt2<0) pcnt2=0; } + } + } + if (!*expr || !had_something) break; + } + + // parse + + { + int tmplen,funcname_len; + const char *p = expr; + const char *tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL); + const char *funcname = nseel_simple_tokenizer(&p,endptr,&funcname_len,NULL); + if (tok1 && funcname && tmplen == 8 && !strnicmp(tok1,"function",8) && (isalpha((unsigned char)funcname[0]) || funcname[0] == '_')) + { + int had_parms_locals=0; + if (funcname_len > sizeof(is_fname)-1) funcname_len=sizeof(is_fname)-1; + memcpy(is_fname, funcname, funcname_len); + is_fname[funcname_len]=0; + ctx->function_curName = is_fname; // only assigned for the duration of the loop, cleared later //-V507 + + while (NULL != (tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL))) + { + int is_parms = 0, localTableContext = 0; + int maxcnt=0; + const char *sp_save; + + if (tok1[0] == '(') + { + if (had_parms_locals) + { + expr = p-1; // begin compilation at this code! + break; + } + is_parms = 1; + } + else + { + if (tmplen == 5 && !strnicmp(tok1,"local",tmplen)) localTableContext=0; + else if (tmplen == 6 && !strnicmp(tok1,"static",tmplen)) localTableContext=0; + else if (tmplen == 8 && !strnicmp(tok1,"instance",tmplen)) localTableContext=1; + else if ((tmplen == 7 && !strnicmp(tok1,"globals",tmplen)) || + (tmplen == 6 && !strnicmp(tok1,"global",tmplen))) + { + ctx->function_globalFlag = 1; + localTableContext=2; + } + else break; // unknown token! + + tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL); + if (!tok1 || tok1[0] != '(') break; + } + had_parms_locals = 1; + + + sp_save=p; + + while (NULL != (tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL))) + { + if (tok1[0] == ')') break; + if (*tok1 == '#' && localTableContext!=1 && localTableContext!=2) + { + ctx->errVar = (int) (tok1 - _expression); + lstrcpyn_safe(ctx->last_error_string,"#string can only be in instance() or globals()",sizeof(ctx->last_error_string)); + goto had_error; + } + + if (isalpha((unsigned char)*tok1) || *tok1 == '_' || *tok1 == '#') + { + maxcnt++; + if (p < endptr && *p == '*') + { + if (!is_parms && localTableContext!=2) + { + ctx->errVar = (int) (p - _expression); + lstrcpyn_safe(ctx->last_error_string,"namespace* can only be used in parameters or globals()",sizeof(ctx->last_error_string)); + goto had_error; + } + p++; + } + } + else if (*tok1 != ',') + { + ctx->errVar = (int)(tok1 - _expression); + lstrcpyn_safe(ctx->last_error_string,"unknown character in function parameters",sizeof(ctx->last_error_string)); + goto had_error; + } + } + + if (tok1 && maxcnt > 0) + { + char **ot = ctx->function_localTable_Names[localTableContext]; + const int osz = ctx->function_localTable_Size[localTableContext]; + + maxcnt += osz; + + ctx->function_localTable_Names[localTableContext] = (char **)newTmpBlock(ctx,sizeof(char *) * maxcnt); + + if (ctx->function_localTable_Names[localTableContext]) + { + int i=osz; + if (osz && ot) memcpy(ctx->function_localTable_Names[localTableContext],ot,sizeof(char *) * osz); + p=sp_save; + + while (NULL != (tok1 = nseel_simple_tokenizer(&p,endptr,&tmplen,NULL))) + { + if (tok1[0] == ')') break; + if (isalpha((unsigned char)*tok1) || *tok1 == '_' || *tok1 == '#') + { + char *newstr; + int l = tmplen; + if (*p == '*') // xyz* for namespace + { + p++; + l++; + } + if (l > NSEEL_MAX_VARIABLE_NAMELEN) l = NSEEL_MAX_VARIABLE_NAMELEN; + newstr = newTmpBlock(ctx,l+1); + if (newstr) + { + memcpy(newstr,tok1,l); + newstr[l]=0; + ctx->function_localTable_Names[localTableContext][i++] = newstr; + } + } + } + ctx->function_localTable_Size[localTableContext]=i; + if (is_parms) function_numparms = i; + } + } + } + } + } + if (ctx->function_localTable_Size[0]>0) + { + ctx->function_localTable_ValuePtrs = + ctx->isSharedFunctions ? newDataBlock(ctx->function_localTable_Size[0] * sizeof(EEL_F *),8) : + newTmpBlock(ctx,ctx->function_localTable_Size[0] * sizeof(EEL_F *)); + if (!ctx->function_localTable_ValuePtrs) + { + ctx->function_localTable_Size[0]=0; + function_numparms=0; + } + else + { + memset(ctx->function_localTable_ValuePtrs,0,sizeof(EEL_F *) * ctx->function_localTable_Size[0]); // force values to be allocated + } + } + + { + int nseelparse(compileContext* context); + void nseelrestart (void *input_file ,void *yyscanner ); + + ctx->rdbuf_start = _expression; + +#ifdef NSEEL_SUPER_MINIMAL_LEXER + + ctx->rdbuf = expr; + ctx->rdbuf_end = endptr; + if (!nseelparse(ctx) && !ctx->errVar) + { + start_opcode = ctx->result; + } +#else + + nseelrestart(NULL,ctx->scanner); + + ctx->rdbuf = expr; + ctx->rdbuf_end = endptr; + + if (!nseelparse(ctx) && !ctx->errVar) + { + start_opcode = ctx->result; + } + if (ctx->errVar) + { + const char *p=expr; + ctx->errVar += expr-_expression; + } +#endif + ctx->rdbuf = NULL; + } + + if (start_opcode) + { + int rvMode=0, fUse=0; + +#ifdef LOG_OPT + char buf[512]; + int sd=0; + sprintf(buf,"pre opt sz=%d (tsackDepth=%d)\n",compileOpcodes(ctx,start_opcode,NULL,1024*1024*256,NULL, NULL,RETURNVALUE_IGNORE,NULL,&sd,NULL),sd); +#ifdef _WIN32 + OutputDebugString(buf); +#else + printf("%s\n",buf); +#endif +#endif + +#ifdef EEL_DUMP_OPS + // dump opcode trees for verification, before optimizing + if (g_eel_dump_fp) + { + fprintf(g_eel_dump_fp,"-- opcode chunk --\r\n"); + dumpOpcodeTree(ctx,g_eel_dump_fp,start_opcode,2); + } +#endif + + if (!(ctx->optimizeDisableFlags&OPTFLAG_NO_OPTIMIZE)) optimizeOpcodes(ctx,start_opcode,is_fname[0] ? 1 : 0); +#ifdef LOG_OPT + sprintf(buf,"post opt sz=%d, stack depth=%d\n",compileOpcodes(ctx,start_opcode,NULL,1024*1024*256,NULL,NULL, RETURNVALUE_IGNORE,NULL,&sd,NULL),sd); +#ifdef _WIN32 + OutputDebugString(buf); +#else + printf("%s\n",buf); +#endif +#endif + +#ifdef EEL_DUMP_OPS + // dump opcode trees for verification, after optimizing + if (g_eel_dump_fp2) + { + fprintf(g_eel_dump_fp2,"-- POST-OPTIMIZED opcode chunk --\r\n"); + dumpOpcodeTree(ctx,g_eel_dump_fp2,start_opcode,2); + } +#endif + + if (is_fname[0]) + { + _codeHandleFunctionRec *fr = ctx->isSharedFunctions ? newDataBlock(sizeof(_codeHandleFunctionRec),8) : + newTmpBlock(ctx,sizeof(_codeHandleFunctionRec)); + if (fr) + { + memset(fr,0,sizeof(_codeHandleFunctionRec)); + fr->startptr_size = -1; + fr->opcodes = start_opcode; + + if (ctx->function_localTable_Size[0] > 0 && ctx->function_localTable_ValuePtrs) + { + if (ctx->function_localTable_Names[0]) + { + int i; + for(i=0;ifunction_localTable_Names[0][i]; + if (nptr && *nptr && nptr[strlen(nptr)-1] == '*') + { + fr->parameterAsNamespaceMask |= ((unsigned int)1)<num_params=function_numparms; + fr->localstorage = ctx->function_localTable_ValuePtrs; + fr->localstorage_size = ctx->function_localTable_Size[0]; + } + + fr->usesNamespaces = ctx->function_usesNamespaces; + fr->isCommonFunction = ctx->isSharedFunctions; + + lstrcpyn_safe(fr->fname,is_fname,sizeof(fr->fname)); + + if (ctx->isSharedFunctions) + { + fr->next = ctx->functions_common; + ctx->functions_common = fr; + } + else + { + fr->next = ctx->functions_local; + ctx->functions_local = fr; + } + } + continue; + } + +#ifdef DUMP_OPS_DURING_COMPILE + g_debugfp_indent=0; + g_debugfp_histsz=0; + g_debugfp = fopen("C:/temp/foo.txt","w"); +#endif + startptr_size = compileOpcodes(ctx,start_opcode,NULL,1024*1024*256,NULL, NULL, + is_fname[0] ? (RETURNVALUE_NORMAL|RETURNVALUE_FPSTACK) : RETURNVALUE_IGNORE, &rvMode, &fUse, NULL); // if not a function, force return value as address (avoid having to pop it ourselves + // if a function, allow the code to decide how return values are generated + +#ifdef DUMP_OPS_DURING_COMPILE + if (g_debugfp) fclose(g_debugfp); + g_debugfp=0; +#endif + + + if (!startptr_size) continue; // optimized away + if (startptr_size>0) + { + startptr = newTmpBlock(ctx,startptr_size); + if (startptr) + { + startptr_size=compileOpcodes(ctx,start_opcode,(unsigned char*)startptr,startptr_size,&computTableTop, NULL, RETURNVALUE_IGNORE, NULL,NULL, NULL); + if (startptr_size<=0) startptr = NULL; + + } + } + } + + if (!startptr) + { +had_error: +#ifdef NSEEL_EEL1_COMPAT_MODE + continue; + +#else + //if (!ctx->last_error_string[0]) + { + int byteoffs = ctx->errVar; + int linenumber; + char cur_err[sizeof(ctx->last_error_string)]; + lstrcpyn_safe(cur_err,ctx->last_error_string,sizeof(cur_err)); + if (cur_err[0]) lstrcatn(cur_err,": ",sizeof(cur_err)); + else lstrcpyn_safe(cur_err,"syntax error: ",sizeof(cur_err)); + + if (_expression + byteoffs >= _expression_end) + { + if (ctx->gotEndOfInput&4) byteoffs = (int)(expr-_expression); + else byteoffs=(int)(_expression_end-_expression); + } + + if (byteoffs < 0) byteoffs=0; + + linenumber=findLineNumber(_expression,byteoffs)+1; + + if (ctx->gotEndOfInput&4) + { + snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"%d: %.200smissing ) or ]",linenumber+lineoffs,cur_err); + } + else + { + const char *p = _expression + byteoffs; + int x=1, right_amt_nospace=0, left_amt_nospace=0; + while (x < 32 && p-x >= _expression && p[-x] != '\r' && p[-x] != '\n') + { + if (!isspace((unsigned char)p[-x])) left_amt_nospace=x; + x++; + } + x=0; + while (x < 60 && p[x] && p[x] != '\r' && p[x] != '\n') + { + if (!isspace((unsigned char)p[x])) right_amt_nospace=x+1; + x++; + } + + // display left_amt >>>> right_amt_nospace + snprintf(ctx->last_error_string,sizeof(ctx->last_error_string),"%d: %.200s'%.*s %.*s'",linenumber+lineoffs,cur_err, + left_amt_nospace, p-left_amt_nospace, + right_amt_nospace ? right_amt_nospace : 5,right_amt_nospace ? p : ""); + } + } + + startpts=NULL; + startpts_tail=NULL; + had_err=1; + break; +#endif + } + + if (!is_fname[0]) // redundant check (if is_fname[0] is set and we succeeded, it should continue) + // but we'll be on the safe side + { + topLevelCodeSegmentRec *p = newTmpBlock(ctx,sizeof(topLevelCodeSegmentRec)); + p->_next=0; + p->code = startptr; + p->codesz = startptr_size; + p->tmptable_use = computTableTop; + + if (!startpts_tail) startpts_tail=startpts=p; + else + { + startpts_tail->_next=p; + startpts_tail=p; + } + + if (curtabptr_sz < computTableTop) + { + curtabptr_sz=computTableTop; + } + } + } + + memset(ctx->function_localTable_Size,0,sizeof(ctx->function_localTable_Size)); + memset(ctx->function_localTable_Names,0,sizeof(ctx->function_localTable_Names)); + ctx->function_localTable_ValuePtrs=0; + ctx->function_usesNamespaces=0; + ctx->function_curName=NULL; + ctx->function_globalFlag=0; + + ctx->tmpCodeHandle = NULL; + + if (handle->want_stack) + { + if (!handle->stack) startpts=NULL; + } + + if (startpts) + { + curtabptr_sz += 2; // many functions use the worktable for temporary storage of up to 2 EEL_F's + + handle->workTable_size = curtabptr_sz; + handle->workTable = curtabptr = newDataBlock((curtabptr_sz+MIN_COMPUTABLE_SIZE + COMPUTABLE_EXTRA_SPACE) * sizeof(EEL_F),32); + +#ifdef EEL_VALIDATE_WORKTABLE_USE + if (curtabptr) memset(curtabptr,0x3a,(curtabptr_sz+MIN_COMPUTABLE_SIZE + COMPUTABLE_EXTRA_SPACE) * sizeof(EEL_F)); +#endif + if (!curtabptr) startpts=NULL; + } + + + if (startpts || (!had_err && (compile_flags & NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS))) + { + unsigned char *writeptr; + topLevelCodeSegmentRec *p=startpts; + int size=sizeof(GLUE_RET)+GLUE_FUNC_ENTER_SIZE+GLUE_FUNC_LEAVE_SIZE; // for ret at end :) + int wtpos=0; + + // now we build one big code segment out of our list of them, inserting a mov esi, computable before each item as necessary + while (p) + { + if (wtpos <= 0) + { + wtpos=MIN_COMPUTABLE_SIZE; + size += GLUE_RESET_WTP(NULL,0); + } + size+=p->codesz; + wtpos -= p->tmptable_use; + p=p->_next; + } + handle->code = newCodeBlock(size,32); + if (handle->code) + { + writeptr=(unsigned char *)handle->code; + #if GLUE_FUNC_ENTER_SIZE > 0 + memcpy(writeptr,&GLUE_FUNC_ENTER,GLUE_FUNC_ENTER_SIZE); + writeptr += GLUE_FUNC_ENTER_SIZE; + #endif + p=startpts; + wtpos=0; + while (p) + { + if (wtpos <= 0) + { + wtpos=MIN_COMPUTABLE_SIZE; + writeptr+=GLUE_RESET_WTP(writeptr,curtabptr); + } + memcpy(writeptr,(char*)p->code,p->codesz); + writeptr += p->codesz; + wtpos -= p->tmptable_use; + + p=p->_next; + } + #if GLUE_FUNC_LEAVE_SIZE > 0 + memcpy(writeptr,&GLUE_FUNC_LEAVE,GLUE_FUNC_LEAVE_SIZE); + writeptr += GLUE_FUNC_LEAVE_SIZE; + #endif + memcpy(writeptr,&GLUE_RET,sizeof(GLUE_RET)); writeptr += sizeof(GLUE_RET); + ctx->l_stats[1]=size; + handle->code_size = (int) (writeptr - (unsigned char *)handle->code); +#if defined(__arm__) || defined(__aarch64__) + __clear_cache(handle->code,writeptr); +#endif + } + + handle->blocks_code = ctx->blocks_head_code; +#ifndef EEL_DOESNT_NEED_EXEC_PERMS + eel_set_blocks_allow_execute(handle->blocks_code,1); +#endif + handle->blocks_data = ctx->blocks_head_data; + ctx->blocks_head_code=0; + ctx->blocks_head_data=0; + } + else + { + // failed compiling, or failed calloc() + handle=NULL; + } + + + ctx->directValueCache=0; + ctx->functions_local = NULL; + + ctx->isGeneratingCommonFunction=0; + ctx->isSharedFunctions=0; + + freeBlocks(&ctx->tmpblocks,0); + freeBlocks(&ctx->blocks_head_code,1); + freeBlocks(&ctx->blocks_head_data,0); + + if (handle) + { + handle->compile_flags = compile_flags; + handle->ramPtr = ctx->ram_state->blocks; + memcpy(handle->code_stats,ctx->l_stats,sizeof(ctx->l_stats)); + nseel_evallib_stats[0]+=ctx->l_stats[0]; + nseel_evallib_stats[1]+=ctx->l_stats[1]; + nseel_evallib_stats[2]+=ctx->l_stats[2]; + nseel_evallib_stats[3]+=ctx->l_stats[3]; + nseel_evallib_stats[4]++; + } + else + { + ctx->functions_common = oldCommonFunctionList; // failed compiling, remove any added common functions from the list + + // remove any derived copies of functions due to error, since we may have added some that have been freed + while (oldCommonFunctionList) + { + oldCommonFunctionList->derivedCopies=NULL; + oldCommonFunctionList=oldCommonFunctionList->next; + } + } + memset(ctx->l_stats,0,sizeof(ctx->l_stats)); + + return (NSEEL_CODEHANDLE)handle; +} + +//------------------------------------------------------------------------------ +void NSEEL_code_execute(NSEEL_CODEHANDLE code) +{ +#ifndef GLUE_TABPTR_IGNORED + INT_PTR tabptr; +#endif + INT_PTR codeptr; + codeHandleType *h = (codeHandleType *)code; + if (!h || !h->code) return; + + codeptr = (INT_PTR) h->code; +#if 0 + { + unsigned int *p=(unsigned int *)codeptr; + while (*p != GLUE_RET[0]) + { + printf("instr:%04X:%04X\n",*p>>16,*p&0xffff); + p++; + } + } +#endif + +#ifndef GLUE_TABPTR_IGNORED + tabptr=(INT_PTR)h->workTable; +#endif + //printf("calling code!\n"); + GLUE_CALL_CODE(tabptr,codeptr,(INT_PTR)h->ramPtr); + +} + +int NSEEL_code_geterror_flag(NSEEL_VMCTX ctx) +{ + compileContext *c=(compileContext *)ctx; + if (c) return (c->gotEndOfInput ? 1 : 0); + return 0; +} + +char *NSEEL_code_getcodeerror(NSEEL_VMCTX ctx) +{ + compileContext *c=(compileContext *)ctx; + if (ctx && c->last_error_string[0]) return c->last_error_string; + return 0; +} + +//------------------------------------------------------------------------------ +void NSEEL_code_free(NSEEL_CODEHANDLE code) +{ + codeHandleType *h = (codeHandleType *)code; + if (h != NULL) + { +#ifdef EEL_VALIDATE_WORKTABLE_USE + if (h->workTable) + { + char *p = ((char*)h->workTable) + h->workTable_size*sizeof(EEL_F); + int x; + for(x=COMPUTABLE_EXTRA_SPACE*sizeof(EEL_F) - 1;x >= 0; x --) + if (p[x] != 0x3a) + { + char buf[512]; + snprintf(buf,sizeof(buf),"worktable overrun at byte %d (wts=%d), value = %f\n",x,h->workTable_size, *(EEL_F*)(p+(x&~(sizeof(EEL_F)-1)))); +#ifdef _WIN32 + OutputDebugString(buf); +#else + printf("%s",buf); +#endif + break; + } + } +#endif + + nseel_evallib_stats[0]-=h->code_stats[0]; + nseel_evallib_stats[1]-=h->code_stats[1]; + nseel_evallib_stats[2]-=h->code_stats[2]; + nseel_evallib_stats[3]-=h->code_stats[3]; + nseel_evallib_stats[4]--; + + freeBlocks(&h->blocks_code,1); + freeBlocks(&h->blocks_data,0); + } + +} + +//------------------------------------------------------------------------------ + +NSEEL_VMCTX NSEEL_VM_alloc() // return a handle +{ + compileContext *ctx=calloc(1,sizeof(compileContext)); + + #ifdef NSEEL_SUPER_MINIMAL_LEXER + if (ctx) ctx->scanner = ctx; + #else + if (ctx) + { + int nseellex_init(void ** ptr_yy_globals); + void nseelset_extra(void *user_defined , void *yyscanner); + if (nseellex_init(&ctx->scanner)) + { + free(ctx); + return NULL; + } + nseelset_extra(ctx,ctx->scanner); + } + #endif + + if (ctx) + { + ctx->ram_state = __newBlock_align(&ctx->ctx_pblocks,sizeof(*ctx->ram_state),16,0); + memset(ctx->ram_state,0,sizeof(*ctx->ram_state)); + ctx->ram_state->sign_mask[0] = ctx->ram_state->sign_mask[1] = WDL_UINT64_CONST(0x8000000000000000); + ctx->ram_state->abs_mask[0] = ctx->ram_state->abs_mask[1] = WDL_UINT64_CONST(0x7FFFFFFFFFFFFFFF); + ctx->ram_state->maxblocks = NSEEL_RAM_BLOCKS_DEFAULTMAX; + ctx->ram_state->closefact = NSEEL_CLOSEFACTOR; + } + return ctx; +} + +int NSEEL_VM_setramsize(NSEEL_VMCTX _ctx, int maxent) +{ + compileContext *ctx = (compileContext *)_ctx; + if (!ctx) return 0; + if (maxent > 0) + { + maxent = (maxent + NSEEL_RAM_ITEMSPERBLOCK - 1)/NSEEL_RAM_ITEMSPERBLOCK; + if (maxent > NSEEL_RAM_BLOCKS) maxent = NSEEL_RAM_BLOCKS; + ctx->ram_state->maxblocks = maxent; + } + + return ctx->ram_state->maxblocks * NSEEL_RAM_ITEMSPERBLOCK; +} + +void NSEEL_VM_SetFunctionValidator(NSEEL_VMCTX _ctx, const char * (*validateFunc)(const char *fn_name, void *user), void *user) +{ + if (_ctx) + { + compileContext *ctx = (compileContext *)_ctx; + ctx->func_check = validateFunc; + ctx->func_check_user = user; + } +} + +void NSEEL_VM_SetFunctionTable(NSEEL_VMCTX _ctx, eel_function_table *tab) +{ + if (_ctx) + { + compileContext *ctx = (compileContext *)_ctx; + ctx->registered_func_tab = tab; + } +} +void NSEEL_VM_free(NSEEL_VMCTX _ctx) // free when done with a VM and ALL of its code have been freed, as well +{ + + if (_ctx) + { + compileContext *ctx=(compileContext *)_ctx; + EEL_GROWBUF_RESIZE(&ctx->varNameList,-1); + NSEEL_VM_freeRAM(_ctx); + + freeBlocks(&ctx->ctx_pblocks,0); + + // these should be 0 normally but just in case + freeBlocks(&ctx->tmpblocks,0); + freeBlocks(&ctx->blocks_head_code,1); + freeBlocks(&ctx->blocks_head_data,0); + + + #ifndef NSEEL_SUPER_MINIMAL_LEXER + if (ctx->scanner) + { + int nseellex_destroy(void *yyscanner); + nseellex_destroy(ctx->scanner); + } + #endif + ctx->scanner=0; + if (ctx->has_used_global_vars) + { + nseel_globalVarItem *p = NULL; + NSEEL_HOSTSTUB_EnterMutex(); + if (--nseel_vms_referencing_globallist_cnt == 0) + { + // clear and free globals + p = nseel_globalreg_list; + nseel_globalreg_list=0; + } + NSEEL_HOSTSTUB_LeaveMutex(); + + while (p) + { + nseel_globalVarItem *op = p; + p=p->_next; + free(op); + } + } + free(ctx); + } + +} + +int *NSEEL_code_getstats(NSEEL_CODEHANDLE code) +{ + codeHandleType *h = (codeHandleType *)code; + if (h) + { + return h->code_stats; + } + return 0; +} + +void NSEEL_VM_SetStringFunc(NSEEL_VMCTX ctx, + EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list), + EEL_F (*onNamedString)(void *caller_this, const char *name)) +{ + if (ctx) + { + compileContext *c=(compileContext*)ctx; + c->onString = onString; + c->onNamedString = onNamedString; + } +} + +void NSEEL_VM_SetCustomFuncThis(NSEEL_VMCTX ctx, void *thisptr) +{ + if (ctx) + { + compileContext *c=(compileContext*)ctx; + c->caller_this=thisptr; + } +} + + + + + +void *NSEEL_PProc_RAM(void *data, int data_size, compileContext *ctx) +{ + if (data_size>0) data=EEL_GLUE_set_immediate(data, (INT_PTR)ctx->ram_state->blocks); + return data; +} + +void *NSEEL_PProc_THIS(void *data, int data_size, compileContext *ctx) +{ + if (data_size>0) data=EEL_GLUE_set_immediate(data, (INT_PTR)ctx->caller_this); + return data; +} + +static int vartable_lowerbound(compileContext *ctx, const char *name, int *ismatch) +{ + int a = 0, c = EEL_GROWBUF_GET_SIZE(&ctx->varNameList); + varNameRec **list = EEL_GROWBUF_GET(&ctx->varNameList); + while (a != c) + { + const int b = (a+c)/2; + const int cmp = strnicmp(name,list[b]->str,NSEEL_MAX_VARIABLE_NAMELEN); + if (cmp > 0) a = b+1; + else if (cmp < 0) c = b; + else + { + *ismatch = 1; + return b; + } + } + *ismatch = 0; + return a; +} + +static void vartable_cull_list(compileContext *ctx, int refcnt_chk) +{ + const int ni = EEL_GROWBUF_GET_SIZE(&ctx->varNameList); + int i = ni, ndel = 0; + varNameRec **rd = EEL_GROWBUF_GET(&ctx->varNameList), **wr=rd; + while (i--) + { + varNameRec *v = rd[0]; + if ((!refcnt_chk || !v->refcnt) && !v->isreg) + { + ndel++; + } + else + { + if (wr != rd) *wr = *rd; + wr++; + } + rd++; + } + if (ndel) EEL_GROWBUF_RESIZE(&ctx->varNameList,ni - ndel); +} + +void NSEEL_VM_remove_unused_vars(NSEEL_VMCTX _ctx) +{ + compileContext *ctx = (compileContext *)_ctx; + if (ctx) vartable_cull_list(ctx,1); +} + +void NSEEL_VM_remove_all_nonreg_vars(NSEEL_VMCTX _ctx) +{ + compileContext *ctx = (compileContext *)_ctx; + if (ctx) vartable_cull_list(ctx,0); +} + +void NSEEL_VM_clear_var_refcnts(NSEEL_VMCTX _ctx) +{ + compileContext *ctx = (compileContext *)_ctx; + if (ctx) + { + int i = EEL_GROWBUF_GET_SIZE(&ctx->varNameList); + varNameRec **rd = EEL_GROWBUF_GET(&ctx->varNameList); + while (i--) + { + rd[0]->refcnt=0; + rd++; + } + } +} + + +#ifdef NSEEL_EEL1_COMPAT_MODE +static EEL_F __nseel_global_regs[100]; +double *NSEEL_getglobalregs() { return __nseel_global_regs; } +#endif + +EEL_F *get_global_var(compileContext *ctx, const char *gv, int addIfNotPresent) +{ + nseel_globalVarItem *p; +#ifdef NSEEL_EEL1_COMPAT_MODE + if (!strnicmp(gv,"reg",3) && gv[3]>='0' && gv[3] <= '9' && gv[4] >= '0' && gv[4] <= '9' && !gv[5]) + { + return __nseel_global_regs + atoi(gv+3); + } +#endif + + NSEEL_HOSTSTUB_EnterMutex(); + if (!ctx->has_used_global_vars) + { + ctx->has_used_global_vars++; + nseel_vms_referencing_globallist_cnt++; + } + + p = nseel_globalreg_list; + while (p) + { + if (!stricmp(p->name,gv)) break; + p=p->_next; + } + + if (!p && addIfNotPresent) + { + size_t gvl = strlen(gv); + p = (nseel_globalVarItem*)malloc(sizeof(nseel_globalVarItem) + gvl); + if (p) + { + p->data=0.0; + strcpy(p->name,gv); + p->_next = nseel_globalreg_list; + nseel_globalreg_list=p; + } + } + NSEEL_HOSTSTUB_LeaveMutex(); + return p ? &p->data : NULL; +} + + + +EEL_F *nseel_int_register_var(compileContext *ctx, const char *name, int isReg, const char **namePtrOut) +{ + int slot, match; + + if (isReg == 0 && ctx->getVariable) + { + EEL_F *ret = ctx->getVariable(ctx->getVariable_userctx, name); + if (ret) return ret; + } + + if (!strnicmp(name,"_global.",8) && name[8]) + { + EEL_F *a=get_global_var(ctx,name+8,isReg >= 0); + if (a) return a; + } + + slot = vartable_lowerbound(ctx,name, &match); + if (match) + { + varNameRec *v = EEL_GROWBUF_GET(&ctx->varNameList)[slot]; + if (isReg >= 0) + { + v->refcnt++; + if (isReg) v->isreg=isReg; + if (namePtrOut) *namePtrOut = v->str; + } + return v->value; + } + if (isReg < 0) return NULL; + + if (ctx->varValueStore_left<1) + { + const int sz=500; + ctx->varValueStore_left = sz; + ctx->varValueStore = (EEL_F *)newCtxDataBlock((int)sizeof(EEL_F)*sz,8); + } + if (ctx->varValueStore) + { + int listsz = EEL_GROWBUF_GET_SIZE(&ctx->varNameList); + size_t l = strlen(name); + varNameRec *vh; + if (l > NSEEL_MAX_VARIABLE_NAMELEN) l = NSEEL_MAX_VARIABLE_NAMELEN; + vh = (varNameRec*) newCtxDataBlock( (int) (sizeof(varNameRec) + l),8); + if (!vh || EEL_GROWBUF_RESIZE(&ctx->varNameList, (listsz+1))) return NULL; // alloc fail + + (vh->value = ctx->varValueStore++)[0]=0.0; + ctx->varValueStore_left--; + + vh->refcnt=1; + vh->isreg=isReg; + memcpy(vh->str,name,l); + vh->str[l] = 0; + if (namePtrOut) *namePtrOut = vh->str; + + if (slot < listsz) + { + memmove(EEL_GROWBUF_GET(&ctx->varNameList) + slot+1, + EEL_GROWBUF_GET(&ctx->varNameList) + slot, (listsz - slot) * sizeof(EEL_GROWBUF_GET(&ctx->varNameList)[0])); + } + EEL_GROWBUF_GET(&ctx->varNameList)[slot] = vh; + + return vh->value; + } + return NULL; +} + + +//------------------------------------------------------------------------------ + +void NSEEL_VM_enumallvars(NSEEL_VMCTX ctx, int (*func)(const char *name, EEL_F *val, void *ctx), void *userctx) +{ + compileContext *tctx = (compileContext *) ctx; + int ni; + varNameRec **rd; + if (!tctx) return; + + ni = EEL_GROWBUF_GET_SIZE(&tctx->varNameList); + rd = EEL_GROWBUF_GET(&tctx->varNameList); + while (ni--) + { + if (!func(rd[0]->str,rd[0]->value,userctx)) break; + rd++; + } +} + + +//------------------------------------------------------------------------------ +EEL_F *NSEEL_VM_regvar(NSEEL_VMCTX _ctx, const char *var) +{ + compileContext *ctx = (compileContext *)_ctx; + if (!ctx) return 0; + + if (!strnicmp(var,"reg",3) && strlen(var) == 5 && isdigit(var[3]) && isdigit(var[4])) + { + EEL_F *a=get_global_var(ctx,var,1); + if (a) return a; + } + + return nseel_int_register_var(ctx,var,1,NULL); +} + +EEL_F *NSEEL_VM_getvar(NSEEL_VMCTX _ctx, const char *var) +{ + compileContext *ctx = (compileContext *)_ctx; + if (!ctx) return 0; + + if (!strnicmp(var,"reg",3) && strlen(var) == 5 && isdigit(var[3]) && isdigit(var[4])) + { + EEL_F *a=get_global_var(ctx,var,0); + if (a) return a; + } + + return nseel_int_register_var(ctx,var,-1,NULL); +} + +int NSEEL_VM_get_var_refcnt(NSEEL_VMCTX _ctx, const char *name) +{ + compileContext *ctx = (compileContext *)_ctx; + int slot,match; + if (!ctx) return -1; + slot = vartable_lowerbound(ctx,name, &match); + return match ? EEL_GROWBUF_GET(&ctx->varNameList)[slot]->refcnt : -1; +} + + + + +opcodeRec *nseel_createFunctionByName(compileContext *ctx, const char *name, int np, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3) +{ + int chkamt=0; + functionType *f=nseel_getFunctionByName(ctx,name,&chkamt); + if (f) while (chkamt-->=0) + { + if ((f->nParams&FUNCTIONTYPE_PARAMETERCOUNTMASK) == np) + { + opcodeRec *o=newOpCode(ctx,NULL, np==3?OPCODETYPE_FUNC3:np==2?OPCODETYPE_FUNC2:OPCODETYPE_FUNC1); + if (o) + { + o->fntype = FUNCTYPE_FUNCTIONTYPEREC; + o->fn = f; + o->parms.parms[0]=code1; + o->parms.parms[1]=code2; + o->parms.parms[2]=code3; + } + return o; + } + f++; + if (stricmp(f->name,name)) break; + } + return NULL; +} + + + + +//------------------------------------------------------------------------------ +opcodeRec *nseel_translate(compileContext *ctx, const char *tmp, size_t tmplen) // tmplen 0 = null term +{ + // this depends on the string being nul terminated eventually, tmplen is used more as a hint than anything else + if ((tmp[0] == '0' || tmp[0] == '$') && toupper(tmp[1])=='X') + { + char *p; + return nseel_createCompiledValue(ctx,(EEL_F)strtoul(tmp+2,&p,16)); + } + else if (tmp[0] == '$') + { + if (tmp[1] == '~') + { + char *p=(char*)tmp+2; + unsigned int v=(unsigned int) strtoul(tmp+2,&p,10); + if (v>53) v=53; + return nseel_createCompiledValue(ctx,(EEL_F)((((WDL_INT64)1) << v) - 1)); + } + else if (!tmplen ? !stricmp(tmp,"$E") : (tmplen == 2 && !strnicmp(tmp,"$E",2))) + return nseel_createCompiledValue(ctx,(EEL_F)2.718281828459045); + else if (!tmplen ? !stricmp(tmp, "$PI") : (tmplen == 3 && !strnicmp(tmp, "$PI", 3))) + return nseel_createCompiledValue(ctx,(EEL_F)3.141592653589793); + else if (!tmplen ? !stricmp(tmp, "$PHI") : (tmplen == 4 && !strnicmp(tmp, "$PHI", 4))) + return nseel_createCompiledValue(ctx,(EEL_F)1.6180339887498948); + else if ((!tmplen || tmplen == 4) && tmp[1] == '\'' && tmp[2] && tmp[3] == '\'') + return nseel_createCompiledValue(ctx,(EEL_F)tmp[2]); + else return NULL; + } + else if (tmp[0] == '\'') + { + char b[64]; + int x,sz; + unsigned int rv=0; + + if (!tmplen) // nul terminated tmplen, calculate a workable length + { + // faster than strlen(tmp) if tmp is large, we'll never need more than ~18 chars anyway + while (tmplen < 32 && tmp[tmplen]) tmplen++; + } + + sz = tmplen > 0 ? nseel_filter_escaped_string(b,sizeof(b),tmp+1, tmplen - 1, '\'') : 0; + + if (sz > 4) + { + if (ctx->last_error_string[0]) lstrcatn(ctx->last_error_string, ", ", sizeof(ctx->last_error_string)); + snprintf_append(ctx->last_error_string,sizeof(ctx->last_error_string),"multi-byte character '%.5s...' too long",b); + return NULL; // do not allow 'xyzxy', limit to 4 bytes + } + + for (x=0;x sizeof(buf)-1) tmplen = sizeof(buf)-1; + memcpy(buf,tmp,tmplen); + buf[tmplen]=0; + if (ctx->onNamedString) + { + if (tmplen>0 && buf[1]&&ctx->function_curName) + { + int err=0; + opcodeRec *r = nseel_resolve_named_symbol(ctx,nseel_createCompiledValuePtr(ctx,NULL,buf),-1, &err); + if (r) + { + if (r->opcodeType!=OPCODETYPE_VALUE_FROM_NAMESPACENAME) + { + r->opcodeType = OPCODETYPE_DIRECTVALUE; + r->parms.dv.directValue = ctx->onNamedString(ctx->caller_this,buf+1); + r->parms.dv.valuePtr=NULL; + } + return r; + } + if (err) return NULL; + } + + // if not namespaced symbol, return directly + if (!buf[1]) + { + opcodeRec *r=newOpCode(ctx,NULL,OPCODETYPE_DIRECTVALUE_TEMPSTRING); + if (r) r->parms.dv.directValue = -10000.0; + return r; + } + return nseel_createCompiledValue(ctx,ctx->onNamedString(ctx->caller_this,buf+1)); + } + } + return nseel_createCompiledValue(ctx,(EEL_F)NSEEL_ATOF(tmp)); +} + +void NSEEL_VM_set_var_resolver(NSEEL_VMCTX _ctx, EEL_F *(*res)(void *userctx, const char *name), void *userctx) +{ + compileContext *ctx = (compileContext *)_ctx; + if (ctx) + { + ctx->getVariable = res; + ctx->getVariable_userctx = userctx; + } +} + + +#if defined(__ppc__) || defined(EEL_TARGET_PORTABLE) + // blank stubs + void eel_enterfp(int s[2]) {} + void eel_leavefp(int s[2]) {} +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-eval.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-eval.c new file mode 100644 index 000000000..a9689dc19 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-eval.c @@ -0,0 +1,471 @@ +/* + Expression Evaluator Library (NS-EEL) v2 + Copyright (C) 2004-2013 Cockos Incorporated + Copyright (C) 1999-2003 Nullsoft, Inc. + + nseel-eval.c + + 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 "ns-eel-int.h" +#include "../wdlcstring.h" + + +static const char *nseel_skip_space_and_comments(const char *p, const char *endptr) +{ + for (;;) + { + while (p < endptr && isspace((unsigned char)p[0])) p++; + if (p >= endptr-1 || *p != '/') return p; + + if (p[1]=='/') + { + while (p < endptr && *p != '\r' && *p != '\n') p++; + } + else if (p[1] == '*') + { + p+=2; + while (p < endptr-1 && (p[0] != '*' || p[1] != '/')) p++; + p+=2; + if (p>=endptr) return endptr; + } + else return p; + } +} + +// removes any escaped characters, also will convert pairs delim_char into single delim_chars +int nseel_filter_escaped_string(char *outbuf, int outbuf_sz, const char *rdptr, size_t rdptr_size, char delim_char) +{ + int outpos = 0; + const char *rdptr_end = rdptr + rdptr_size; + while (rdptr < rdptr_end && outpos < outbuf_sz-1) + { + char thisc=*rdptr; + if (thisc == '\\' && rdptr < rdptr_end-1) + { + const char nc = rdptr[1]; + if (nc == 'r' || nc == 'R') { thisc = '\r'; } + else if (nc == 'n' || nc == 'N') { thisc = '\n'; } + else if (nc == 't' || nc == 'T') { thisc = '\t'; } + else if (nc == 'b' || nc == 'B') { thisc = '\b'; } + else if ((nc >= '0' && nc <= '9') || nc == 'x' || nc == 'X') + { + unsigned char c=0; + char base_shift = 3; + char num_top = '7'; + + rdptr++; // skip backslash + if (nc > '9') // implies xX + { + base_shift = 4; + num_top = '9'; + rdptr ++; // skip x + } + + while (rdptr < rdptr_end) + { + char tc=*rdptr; + if (tc >= '0' && tc <= num_top) + { + c = (c<= 'a' && tc <= 'f') + { + c = (c<= 'A' && tc <= 'F') + { + c = (c<str_len; + } + else if (list->str_len > 1) + { + if (pos >= bufout_sz) break; + pos += nseel_filter_escaped_string(bufOut + pos, bufout_sz-pos, list->str_start+1, list->str_len-1, list->str_start[0]); + } + list = list->_next; + } + return pos; +} + + + +// state can be NULL, it will be set if finished with unterminated thing: 1 for multiline comment, ' or " for string +const char *nseel_simple_tokenizer(const char **ptr, const char *endptr, int *lenOut, int *state) +{ + const char *p = *ptr; + const char *rv = p; + char delim; + + if (state) // if state set, returns comments as tokens + { + if (*state == 1) goto in_comment; + + #ifndef NSEEL_EEL1_COMPAT_MODE + if (*state == '\'' || *state == '\"') + { + delim = (char)*state; + goto in_string; + } + #endif + + // skip any whitespace + while (p < endptr && isspace((unsigned char)p[0])) p++; + } + else + { + // state not passed, skip comments (do not return them as tokens) + p = nseel_skip_space_and_comments(p,endptr); + } + + if (p >= endptr) + { + *ptr = endptr; + *lenOut = 0; + return NULL; + } + + rv=p; + + if (*p == '$' && p+3 < endptr && p[1] == '\'' && p[3] == '\'') + { + p+=4; + } + else if (state && *p == '/' && p < endptr-1 && (p[1] == '/' || p[1] == '*')) + { + if (p[1] == '/') + { + while (p < endptr && *p != '\r' && *p != '\n') p++; // advance to end of line + } + else + { + if (state) *state=1; + p+=2; +in_comment: + while (p < endptr) + { + const char c = *p++; + if (c == '*' && p < endptr && *p == '/') + { + p++; + if (state) *state=0; + break; + } + } + + } + } + else if (isalnum((unsigned char)*p) || *p == '_' || *p == '#' || *p == '$' || (*p == '.' && p < endptr-1 && p[1] >= '0' && p[1] <= '9')) + { + if (*p == '$' && p < endptr-1 && p[1] == '~') p++; + p++; + while (p < endptr && (isalnum((unsigned char)*p) || *p == '_' || *p == '.')) p++; + } +#ifndef NSEEL_EEL1_COMPAT_MODE + else if (*p == '\'' || *p == '\"') + { + delim = *p++; + if (state) *state=delim; +in_string: + + while (p < endptr) + { + const char c = *p++; + if (p < endptr && c == '\\') p++; // skip escaped characters + else if (c == delim) + { + if (state) *state=0; + break; + } + } + } +#endif + else + { + p++; + } + *ptr = p; + *lenOut = (int) (p - rv); + return p>rv ? rv : NULL; +} + + + +#ifdef NSEEL_SUPER_MINIMAL_LEXER + + int nseellex(opcodeRec **output, YYLTYPE * yylloc_param, compileContext *scctx) + { + int rv=0,toklen=0; + const char *rdptr = scctx->rdbuf; + const char *endptr = scctx->rdbuf_end; + const char *tok = nseel_simple_tokenizer(&rdptr,endptr,&toklen,NULL); + *output = 0; + if (tok) + { + rv = tok[0]; + if ((rv == '0' || rv == '$') && toklen > 1 && (tok[1] == 'x' || tok[1] == 'X')) // 0xf00 or $xf00 + { + int x; + for (x = 2; x < toklen; x ++) + if (!((tok[x] >= '0' && tok[x] <= '9') || + (tok[x] >= 'a' && tok[x] <= 'f') || + (tok[x] >= 'A' && tok[x] <= 'F'))) + { + tok += x; + break; + } + + *output = x == toklen && toklen > 2 ? nseel_translate(scctx,tok,toklen) : NULL; + if (*output) rv=VALUE; + } +#ifndef NSEEL_EEL1_COMPAT_MODE + else if (rv == '#' && scctx->onNamedString) + { + *output = nseel_translate(scctx,tok,toklen); + if (*output) rv=STRING_IDENTIFIER; + } + else if (rv == '\'') + { + if (toklen > 1 && tok[toklen-1] == '\'') + { + *output = nseel_translate(scctx, tok, toklen); + if (*output) rv = VALUE; + } + else scctx->gotEndOfInput|=8; + } + else if (rv == '\"' && scctx->onString) + { + if (toklen > 1 && tok[toklen-1] == '\"') + { + *output = (opcodeRec *)nseel_createStringSegmentRec(scctx,tok,toklen); + if (*output) rv = STRING_LITERAL; + } + else scctx->gotEndOfInput|=16; + } +#endif + else if (isalpha((unsigned char)rv) || rv == '_') + { + char buf[NSEEL_MAX_VARIABLE_NAMELEN*2]; + if (toklen > sizeof(buf) - 1) toklen=sizeof(buf) - 1; + memcpy(buf,tok,toklen); + buf[toklen]=0; + *output = nseel_createCompiledValuePtr(scctx, NULL, buf); + if (*output) rv = IDENTIFIER; + } + else if ((rv >= '0' && rv <= '9') || (rv == '.' && toklen > 1 && tok[1] >= '0' && tok[1] <= '9')) // 123.45 or .45 + { + int x, pcnt = 0; + for (x = 0; x < toklen; x ++) + { + if (tok[x] == '.' ? (++pcnt > 1) : (tok[x] < '0' || tok[x] > '9')) + { + tok += x; + break; + } + } + *output = x == toklen ? nseel_translate(scctx,tok,toklen) : NULL; + if (*output) rv=VALUE; + } + else if (rv == '$' && toklen > 1) + { + if (tok[1] == '~') + { + int x; + for (x = 2; x < toklen; x ++) + if (tok[x] < '0' || tok[x] > '9') + { + tok += x; + break; + } + + if (x 1 ? nseel_translate(scctx,tok,toklen) : NULL; + if (*output) rv=VALUE; + } + else if (rv == '<') + { + const char nc=*rdptr; + if (nc == '<') + { + rdptr++; + rv=TOKEN_SHL; + } + else if (nc == '=') + { + rdptr++; + rv=TOKEN_LTE; + } + } + else if (rv == '>') + { + const char nc=*rdptr; + if (nc == '>') + { + rdptr++; + rv=TOKEN_SHR; + } + else if (nc == '=') + { + rdptr++; + rv=TOKEN_GTE; + } + } + else if (rv == '&' && *rdptr == '&') + { + rdptr++; + rv = TOKEN_LOGICAL_AND; + } + else if (rv == '|' && *rdptr == '|') + { + rdptr++; + rv = TOKEN_LOGICAL_OR; + } + else if (*rdptr == '=') + { + switch (rv) + { + case '+': rv=TOKEN_ADD_OP; rdptr++; break; + case '-': rv=TOKEN_SUB_OP; rdptr++; break; + case '%': rv=TOKEN_MOD_OP; rdptr++; break; + case '|': rv=TOKEN_OR_OP; rdptr++; break; + case '&': rv=TOKEN_AND_OP; rdptr++; break; + case '~': rv=TOKEN_XOR_OP; rdptr++; break; + case '/': rv=TOKEN_DIV_OP; rdptr++; break; + case '*': rv=TOKEN_MUL_OP; rdptr++; break; + case '^': rv=TOKEN_POW_OP; rdptr++; break; + case '!': + rdptr++; + if (rdptr < endptr && *rdptr == '=') + { + rdptr++; + rv=TOKEN_NE_EXACT; + } + else + rv=TOKEN_NE; + break; + case '=': + rdptr++; + if (rdptr < endptr && *rdptr == '=') + { + rdptr++; + rv=TOKEN_EQ_EXACT; + } + else + rv=TOKEN_EQ; + break; + } + } + } + + scctx->rdbuf = rdptr; + yylloc_param->first_column = (int)(tok - scctx->rdbuf_start); + return rv; + } + + + void nseelerror(YYLTYPE *pos,compileContext *ctx, const char *str) + { + ctx->errVar=pos->first_column>0?pos->first_column:(int)(ctx->rdbuf_end - ctx->rdbuf_start); + } + + +#else + + int nseel_gets(compileContext *ctx, char *buf, size_t sz) + { + int n=0; + const char *endptr = ctx->rdbuf_end; + const char *rdptr = ctx->rdbuf; + if (!rdptr) return 0; + + while (n < sz && rdptr < endptr) buf[n++] = *rdptr++; + ctx->rdbuf=rdptr; + return n; + + } + + + //#define EEL_TRACE_LEX + + #ifdef EEL_TRACE_LEX + #define nseellex nseellex2 + + #endif + #include "lex.nseel.c" + + #ifdef EEL_TRACE_LEX + + #undef nseellex + + int nseellex(YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) + { + int a=nseellex2(yylval_param,yylloc_param,yyscanner); + + char buf[512]; + sprintf(buf,"tok: %c (%d)\n",a,a); + OutputDebugString(buf); + return a; + } + #endif//EEL_TRACE_LEX + + + void nseelerror(YYLTYPE *pos,compileContext *ctx, const char *str) + { + ctx->errVar=pos->first_column>0?pos->first_column:(int)(ctx->rdbuf_end - ctx->rdbuf_start); + } +#endif // !NSEEL_SUPER_MINIMAL_LEXER diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-lextab.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-lextab.c new file mode 100644 index 000000000..6138b65df --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-lextab.c @@ -0,0 +1 @@ +// no longer used \ No newline at end of file diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-ram.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-ram.c new file mode 100644 index 000000000..a8bcb395c --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-ram.c @@ -0,0 +1,463 @@ +/* + Expression Evaluator Library (NS-EEL) v2 + Copyright (C) 2004-2013 Cockos Incorporated + Copyright (C) 1999-2003 Nullsoft, Inc. + + 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 "ns-eel.h" +#include "ns-eel-int.h" +#include +#include +#include +#include + + +#ifdef _WIN32 +#include +#ifdef _MSC_VER +#define inline __inline +#endif + +#endif + +unsigned int NSEEL_RAM_limitmem=0; +unsigned int NSEEL_RAM_memused=0; +int NSEEL_RAM_memused_errors=0; + + + +int NSEEL_VM_wantfreeRAM(NSEEL_VMCTX ctx) +{ + if (ctx) + { + compileContext *c=(compileContext*)ctx; + if (c->ram_state->needfree) + return 1; + } + return 0; +} + +void NSEEL_VM_freeRAMIfCodeRequested(NSEEL_VMCTX ctx) // check to see if our free flag was set +{ + if (ctx) + { + compileContext *c=(compileContext*)ctx; + if (c->ram_state->needfree) + { + NSEEL_HOSTSTUB_EnterMutex(); + { + INT_PTR startpos=((INT_PTR)c->ram_state->needfree)-1; + EEL_F **blocks = c->ram_state->blocks; + INT_PTR pos=0; + int x; + for (x = 0; x < NSEEL_RAM_BLOCKS; x ++) + { + if (pos >= startpos) + { + if (blocks[x]) + { + if (NSEEL_RAM_memused >= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK) + NSEEL_RAM_memused -= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK; + else NSEEL_RAM_memused_errors++; + free(blocks[x]); + blocks[x]=0; + } + } + pos+=NSEEL_RAM_ITEMSPERBLOCK; + } + c->ram_state->needfree=0; + } + NSEEL_HOSTSTUB_LeaveMutex(); + } + + } +} + +EEL_F nseel_ramalloc_onfail; +EEL_F * volatile nseel_gmembuf_default; + + +void *(*nseel_gmem_calloc)(size_t a, size_t b); + +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAMAllocGMEM(EEL_F ***blocks, unsigned int w) +{ + if (blocks) + { + EEL_F **pblocks=*blocks; + + if (w < NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) + { + const unsigned int whichblock = w/NSEEL_RAM_ITEMSPERBLOCK; + EEL_F *p=NULL; + if (!pblocks || !(p=pblocks[whichblock])) + { + NSEEL_HOSTSTUB_EnterMutex(); + if (!nseel_gmem_calloc) nseel_gmem_calloc=calloc; + + if (!(pblocks=*blocks)) pblocks = *blocks = (EEL_F **)nseel_gmem_calloc(sizeof(EEL_F *),NSEEL_RAM_BLOCKS); + else p = pblocks[whichblock]; + + if (!p && pblocks) + { + p=pblocks[whichblock]=(EEL_F *)nseel_gmem_calloc(sizeof(EEL_F),NSEEL_RAM_ITEMSPERBLOCK); + } + NSEEL_HOSTSTUB_LeaveMutex(); + } + if (p) return p + (w&(NSEEL_RAM_ITEMSPERBLOCK-1)); + } + return &nseel_ramalloc_onfail; + } + + if (!nseel_gmembuf_default) + { + NSEEL_HOSTSTUB_EnterMutex(); + if (!nseel_gmembuf_default) nseel_gmembuf_default=(EEL_F*)calloc(sizeof(EEL_F),NSEEL_SHARED_GRAM_SIZE); + NSEEL_HOSTSTUB_LeaveMutex(); + if (!nseel_gmembuf_default) return &nseel_ramalloc_onfail; + } + + return nseel_gmembuf_default+(((unsigned int)w)&((NSEEL_SHARED_GRAM_SIZE)-1)); +} + + +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAMAlloc(EEL_F **pblocks, unsigned int w) +{ +// fprintf(stderr,"got request at %d, %d\n",w/NSEEL_RAM_ITEMSPERBLOCK, w&(NSEEL_RAM_ITEMSPERBLOCK-1)); + if (w < NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) + { + unsigned int whichblock = w/NSEEL_RAM_ITEMSPERBLOCK; + EEL_F *p=pblocks[whichblock]; + if (!p && whichblock < ((unsigned int *)pblocks)[-3]) // pblocks -1/-2 are closefact, -3 is maxblocks + { + NSEEL_HOSTSTUB_EnterMutex(); + + if (!(p=pblocks[whichblock])) + { + + const int msize=sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK; + if (!NSEEL_RAM_limitmem || NSEEL_RAM_memused+msize < NSEEL_RAM_limitmem) + { + p=pblocks[whichblock]=(EEL_F *)calloc(sizeof(EEL_F),NSEEL_RAM_ITEMSPERBLOCK); + if (p) NSEEL_RAM_memused+=msize; + } + } + NSEEL_HOSTSTUB_LeaveMutex(); + } + if (p) return p + (w&(NSEEL_RAM_ITEMSPERBLOCK-1)); + } +// fprintf(stderr,"ret 0\n"); + return &nseel_ramalloc_onfail; +} + + +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemFree(void *blocks, EEL_F *which) +{ + // blocks points to ram_state->blocks, so back it up past closefact and maxblocks to needfree + int *flag = (int *)((char *)blocks - sizeof(double) - 2*sizeof(int)); + int d=(int)(*which); + if (d < 0) d=0; + if (d < flag[1]*NSEEL_RAM_ITEMSPERBLOCK) flag[0]=1+d; + return which; +} + +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemTop(void *blocks, EEL_F *which) +{ + // blocks points to ram_state->blocks, so back it up past closefact to maxblocks + const int *flag = (int *)((char *)blocks - sizeof(double) - sizeof(int)); + *which = flag[0]*NSEEL_RAM_ITEMSPERBLOCK; + return which; +} + + + + + + +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemCpy(EEL_F **blocks,EEL_F *dest, EEL_F *src, EEL_F *lenptr) +{ + const int mem_size=NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK; + int dest_offs = (int)(*dest + 0.0001); + int src_offs = (int)(*src + 0.0001); + int len = (int)(*lenptr + 0.0001); + int want_mmove=0; + + // trim to front + if (src_offs<0) + { + len += src_offs; + dest_offs -= src_offs; + src_offs=0; + } + if (dest_offs<0) + { + len += dest_offs; + src_offs -= dest_offs; + dest_offs=0; + } + if (src_offs + len > mem_size) len = mem_size-src_offs; + if (dest_offs + len > mem_size) len = mem_size-dest_offs; + + if (src_offs == dest_offs || len < 1) return dest; + + if (src_offs < dest_offs && src_offs+len > dest_offs) + { + // if src_offs < dest_offs and overlapping, must copy right to left + if ((dest_offs - src_offs) < NSEEL_RAM_ITEMSPERBLOCK) want_mmove = 1; + src_offs += len; + dest_offs += len; + while (len > 0) + { + const int maxdlen=((dest_offs-1)&(NSEEL_RAM_ITEMSPERBLOCK-1)) + 1; + const int maxslen=((src_offs-1)&(NSEEL_RAM_ITEMSPERBLOCK-1)) + 1; + int copy_len = len; + EEL_F *srcptr,*destptr; + + if (copy_len > maxdlen) copy_len=maxdlen; + if (copy_len > maxslen) copy_len=maxslen; + + srcptr = __NSEEL_RAMAlloc(blocks,src_offs - copy_len); + destptr = __NSEEL_RAMAlloc(blocks,dest_offs - copy_len); + if (srcptr==&nseel_ramalloc_onfail || destptr==&nseel_ramalloc_onfail) break; + + if (want_mmove) memmove(destptr,srcptr,sizeof(EEL_F)*copy_len); + else memcpy(destptr,srcptr,sizeof(EEL_F)*copy_len); + src_offs-=copy_len; + dest_offs-=copy_len; + len-=copy_len; + } + return dest; + } + + if (dest_offs < src_offs && dest_offs+len > src_offs) + { + // if dest_offs < src_offs and overlapping, and less than NSEEL_RAM_ITEMSPERBLOCK apart, use memmove() + if ((src_offs-dest_offs) < NSEEL_RAM_ITEMSPERBLOCK) want_mmove = 1; + } + + while (len > 0) + { + const int maxdlen=NSEEL_RAM_ITEMSPERBLOCK - (dest_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)); + const int maxslen=NSEEL_RAM_ITEMSPERBLOCK - (src_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)); + int copy_len = len; + EEL_F *srcptr,*destptr; + + if (copy_len > maxdlen) copy_len=maxdlen; + if (copy_len > maxslen) copy_len=maxslen; + + srcptr = __NSEEL_RAMAlloc(blocks,src_offs); + destptr = __NSEEL_RAMAlloc(blocks,dest_offs); + if (srcptr==&nseel_ramalloc_onfail || destptr==&nseel_ramalloc_onfail) break; + + if (want_mmove) memmove(destptr,srcptr,sizeof(EEL_F)*copy_len); + else memcpy(destptr,srcptr,sizeof(EEL_F)*copy_len); + src_offs+=copy_len; + dest_offs+=copy_len; + len-=copy_len; + } + return dest; +} + +EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemSet(EEL_F **blocks,EEL_F *dest, EEL_F *v, EEL_F *lenptr) +{ + int offs = (int)(*dest + 0.0001); + int len = (int)(*lenptr + 0.0001); + EEL_F t; + if (offs<0) + { + len += offs; + offs=0; + } + if (offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) return dest; + + if (offs+len > NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) len = NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK - offs; + + if (len < 1) return dest; + + + t=*v; // set value + +// int lastBlock=-1; + while (len > 0) + { + int lcnt; + EEL_F *ptr=__NSEEL_RAMAlloc(blocks,offs); + if (ptr==&nseel_ramalloc_onfail) break; + + lcnt=NSEEL_RAM_ITEMSPERBLOCK-(offs&(NSEEL_RAM_ITEMSPERBLOCK-1)); + if (lcnt > len) lcnt=len; + + len -= lcnt; + offs += lcnt; + + while (lcnt--) + { + *ptr++=t; + } + } + return dest; +} + + +static inline int __getset_values(EEL_F **blocks, int isset, int len, EEL_F **parms) +{ + int offs, lout=0; + unsigned int pageidx, sub_offs; + if (--len < 1) return 0; + offs = (int)(parms++[0][0] + 0.0001); + + if (offs<=0) + { + len += offs; + parms -= offs; + offs=0; + pageidx=sub_offs=0; + if (len<1) return 0; + } + else + { + sub_offs = ((unsigned int)offs) & (NSEEL_RAM_ITEMSPERBLOCK-1); + pageidx = ((unsigned int)offs)>>NSEEL_RAM_ITEMSPERBLOCK_LOG2; + if (pageidx>=NSEEL_RAM_BLOCKS) return 0; + } + + for (;;) + { + int lcnt=NSEEL_RAM_ITEMSPERBLOCK-sub_offs; + EEL_F *ptr=blocks[pageidx]; + if (!ptr) + { + ptr = __NSEEL_RAMAlloc(blocks,offs + lout); + if (ptr==&nseel_ramalloc_onfail) return lout; + } + else + { + ptr += sub_offs; + } + + if (lcnt >= len) + { + // this page satisfies the request (normal behavior) + lout += len; + if (isset) while (len--) *ptr++=parms++[0][0]; + else while (len--) parms++[0][0] = *ptr++; + return lout; + } + + // crossing a page boundary + len -= lcnt; + lout += lcnt; + if (isset) while (lcnt--) *ptr++=parms++[0][0]; + else while (lcnt--) parms++[0][0] = *ptr++; + + if (len <= 0 || ++pageidx >= NSEEL_RAM_BLOCKS) return lout; + sub_offs=0; + } +} + +EEL_F NSEEL_CGEN_CALL __NSEEL_RAM_Mem_SetValues(EEL_F **blocks, INT_PTR np, EEL_F **parms) +{ + return __getset_values(blocks,1,(int)np,parms); +} + +EEL_F NSEEL_CGEN_CALL __NSEEL_RAM_Mem_GetValues(EEL_F **blocks, INT_PTR np, EEL_F **parms) +{ + return __getset_values(blocks,0,(int)np,parms); +} + +void NSEEL_VM_SetGRAM(NSEEL_VMCTX ctx, void **gram) +{ + if (ctx) + { + compileContext *c=(compileContext*)ctx; + c->gram_blocks = gram; + } +} + + +void NSEEL_VM_freeRAM(NSEEL_VMCTX ctx) +{ + if (ctx) + { + int x; + compileContext *c=(compileContext*)ctx; + EEL_F **blocks = c->ram_state->blocks; + for (x = 0; x < NSEEL_RAM_BLOCKS; x ++) + { + if (blocks[x]) + { + if (NSEEL_RAM_memused >= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK) + NSEEL_RAM_memused -= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK; + else NSEEL_RAM_memused_errors++; + free(blocks[x]); + blocks[x]=0; + } + } + c->ram_state->needfree=0; // no need to free anymore + } +} + +void NSEEL_VM_FreeGRAM(void **ufd) +{ + if (ufd[0]) + { + EEL_F **blocks = (EEL_F **)ufd[0]; + int x; + for (x = 0; x < NSEEL_RAM_BLOCKS; x ++) + { + if (blocks[x]) + { + if (NSEEL_RAM_memused >= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK) + NSEEL_RAM_memused -= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK; + else NSEEL_RAM_memused_errors++; + } + free(blocks[x]); + blocks[x]=0; + } + free(blocks); + ufd[0]=0; + } +} + +EEL_F *NSEEL_VM_getramptr(NSEEL_VMCTX ctx, unsigned int offs, int *validCount) +{ + EEL_F *d=__NSEEL_RAMAlloc(ctx ? ((compileContext*)ctx)->ram_state->blocks : 0,offs); + if (!d || d == &nseel_ramalloc_onfail) return NULL; + if (validCount) *validCount = NSEEL_RAM_ITEMSPERBLOCK - (offs%NSEEL_RAM_ITEMSPERBLOCK); + + return d; +} + +EEL_F *NSEEL_VM_getramptr_noalloc(NSEEL_VMCTX ctx, unsigned int offs, int *validCount) +{ + EEL_F *d; + compileContext *cc = (compileContext *)ctx; + + if (!cc || + offs >= NSEEL_RAM_ITEMSPERBLOCK*NSEEL_RAM_BLOCKS || + NULL == (d = cc->ram_state->blocks[offs/NSEEL_RAM_ITEMSPERBLOCK]) + ) + { + if (validCount) *validCount = 0; + return NULL; + } + + offs %= NSEEL_RAM_ITEMSPERBLOCK; + if (validCount) *validCount = NSEEL_RAM_ITEMSPERBLOCK - offs; + return d + offs; +} diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-yylex.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-yylex.c new file mode 100644 index 000000000..84a29de1b --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/nseel-yylex.c @@ -0,0 +1,43 @@ +/* + Expression Evaluator Library (NS-EEL) + Copyright (C) 2004-2013 Cockos Incorporated + Copyright (C) 1999-2003 Nullsoft, Inc. + + nseel-yylex.c + + 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 "ns-eel-int.h" + + + +# define YYMALLOC malloc +# define YYFREE free + +int nseellex(void * yylval_param,void * yylloc_param ,void *yyscanner); +void nseelerror(void *pos,compileContext *ctx, const char *str); + +// inhibit a warning: +static void WDL_STATICFUNC_UNUSED yydestruct(const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, compileContext* context); + +#include +#include + +#include "y.tab.c" + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/y.tab.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/y.tab.c new file mode 100644 index 000000000..6b264b77a --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/y.tab.c @@ -0,0 +1,2186 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 1 + +/* Substitute the variable and function names. */ +#define yyparse nseelparse +#define yylex nseellex +#define yyerror nseelerror +#define yylval nseellval +#define yychar nseelchar +#define yydebug nseeldebug +#define yynerrs nseelnerrs +#define yylloc nseellloc + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + VALUE = 258, + IDENTIFIER = 259, + TOKEN_SHL = 260, + TOKEN_SHR = 261, + TOKEN_LTE = 262, + TOKEN_GTE = 263, + TOKEN_EQ = 264, + TOKEN_EQ_EXACT = 265, + TOKEN_NE = 266, + TOKEN_NE_EXACT = 267, + TOKEN_LOGICAL_AND = 268, + TOKEN_LOGICAL_OR = 269, + TOKEN_ADD_OP = 270, + TOKEN_SUB_OP = 271, + TOKEN_MOD_OP = 272, + TOKEN_OR_OP = 273, + TOKEN_AND_OP = 274, + TOKEN_XOR_OP = 275, + TOKEN_DIV_OP = 276, + TOKEN_MUL_OP = 277, + TOKEN_POW_OP = 278, + STRING_LITERAL = 279, + STRING_IDENTIFIER = 280 + }; +#endif +/* Tokens. */ +#define VALUE 258 +#define IDENTIFIER 259 +#define TOKEN_SHL 260 +#define TOKEN_SHR 261 +#define TOKEN_LTE 262 +#define TOKEN_GTE 263 +#define TOKEN_EQ 264 +#define TOKEN_EQ_EXACT 265 +#define TOKEN_NE 266 +#define TOKEN_NE_EXACT 267 +#define TOKEN_LOGICAL_AND 268 +#define TOKEN_LOGICAL_OR 269 +#define TOKEN_ADD_OP 270 +#define TOKEN_SUB_OP 271 +#define TOKEN_MOD_OP 272 +#define TOKEN_OR_OP 273 +#define TOKEN_AND_OP 274 +#define TOKEN_XOR_OP 275 +#define TOKEN_DIV_OP 276 +#define TOKEN_MUL_OP 277 +#define TOKEN_POW_OP 278 +#define STRING_LITERAL 279 +#define STRING_IDENTIFIER 280 + + + + +/* Copy the first part of user declarations. */ +#line 13 "eel2.y" + +#ifdef _WIN32 +#include +#endif +#include +#include +#include +#include + +#include "y.tab.h" +#include "ns-eel-int.h" + +#define scanner context->scanner +#define YY_(x) ("") + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 193 "y.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + YYLTYPE yyls; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 68 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 141 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 47 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 19 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 73 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 127 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 280 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 34, 2, 2, 2, 36, 39, 2, + 27, 28, 38, 32, 26, 33, 2, 37, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 45, 46, + 42, 31, 43, 44, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 29, 2, 30, 35, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 40, 2, 41, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 5, 9, 11, 14, 16, 20, 28, + 33, 37, 44, 53, 57, 62, 64, 66, 68, 70, + 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, + 112, 116, 120, 122, 125, 128, 131, 133, 137, 139, + 143, 147, 151, 153, 157, 159, 163, 165, 169, 171, + 175, 177, 181, 185, 189, 191, 195, 199, 203, 207, + 211, 215, 219, 223, 225, 229, 233, 235, 241, 246, + 250, 252, 256, 259 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 65, 0, -1, 64, -1, 64, 26, 48, -1, 24, + -1, 24, 49, -1, 4, -1, 27, 64, 28, -1, + 4, 27, 64, 28, 27, 64, 28, -1, 4, 27, + 64, 28, -1, 4, 27, 28, -1, 4, 27, 64, + 26, 64, 28, -1, 4, 27, 64, 26, 64, 26, + 48, 28, -1, 51, 29, 30, -1, 51, 29, 64, + 30, -1, 3, -1, 25, -1, 49, -1, 50, -1, + 51, -1, 50, 31, 63, -1, 50, 15, 63, -1, + 50, 16, 63, -1, 50, 17, 63, -1, 50, 18, + 63, -1, 50, 19, 63, -1, 50, 20, 63, -1, + 50, 21, 63, -1, 50, 22, 63, -1, 50, 23, + 63, -1, 25, 31, 63, -1, 25, 15, 63, -1, + 52, -1, 32, 53, -1, 33, 53, -1, 34, 53, + -1, 53, -1, 54, 35, 53, -1, 54, -1, 55, + 36, 54, -1, 55, 5, 54, -1, 55, 6, 54, + -1, 55, -1, 56, 37, 55, -1, 56, -1, 57, + 38, 56, -1, 57, -1, 58, 33, 57, -1, 58, + -1, 59, 32, 58, -1, 59, -1, 60, 39, 59, + -1, 60, 40, 59, -1, 60, 41, 59, -1, 60, + -1, 61, 42, 60, -1, 61, 43, 60, -1, 61, + 7, 60, -1, 61, 8, 60, -1, 61, 9, 60, + -1, 61, 10, 60, -1, 61, 11, 60, -1, 61, + 12, 60, -1, 61, -1, 62, 13, 61, -1, 62, + 14, 61, -1, 62, -1, 62, 44, 63, 45, 63, + -1, 62, 44, 45, 63, -1, 62, 44, 63, -1, + 63, -1, 64, 46, 63, -1, 64, 46, -1, 64, + -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 40, 40, 41, 48, 49, 57, 67, 71, 83, + 93, 103, 114, 126, 130, 137, 138, 139, 143, 148, + 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, + 189, 193, 200, 201, 205, 209, 216, 217, 224, 225, + 229, 233, 240, 241, 249, 250, 258, 259, 266, 267, + 274, 275, 279, 283, 290, 291, 295, 299, 303, 307, + 311, 315, 319, 326, 327, 331, 338, 339, 343, 347, + 355, 356, 360, 368 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "VALUE", "IDENTIFIER", "TOKEN_SHL", + "TOKEN_SHR", "TOKEN_LTE", "TOKEN_GTE", "TOKEN_EQ", "TOKEN_EQ_EXACT", + "TOKEN_NE", "TOKEN_NE_EXACT", "TOKEN_LOGICAL_AND", "TOKEN_LOGICAL_OR", + "TOKEN_ADD_OP", "TOKEN_SUB_OP", "TOKEN_MOD_OP", "TOKEN_OR_OP", + "TOKEN_AND_OP", "TOKEN_XOR_OP", "TOKEN_DIV_OP", "TOKEN_MUL_OP", + "TOKEN_POW_OP", "STRING_LITERAL", "STRING_IDENTIFIER", "','", "'('", + "')'", "'['", "']'", "'='", "'+'", "'-'", "'!'", "'^'", "'%'", "'/'", + "'*'", "'&'", "'|'", "'~'", "'<'", "'>'", "'?'", "':'", "';'", "$accept", + "more_params", "string", "assignable_value", "rvalue", "assignment", + "unary_expr", "pow_expr", "mod_expr", "div_expr", "mul_expr", "sub_expr", + "add_expr", "andor_expr", "cmp_expr", "logical_and_or_expr", + "if_else_expr", "expression", "program", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 44, 40, 41, 91, + 93, 61, 43, 45, 33, 94, 37, 47, 42, 38, + 124, 126, 60, 62, 63, 58, 59 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 47, 48, 48, 49, 49, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 51, 51, 51, 51, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 53, 53, 53, 53, 54, 54, 55, 55, + 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, + 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 62, 62, 62, 63, 63, 63, 63, + 64, 64, 64, 65 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 3, 1, 2, 1, 3, 7, 4, + 3, 6, 8, 3, 4, 1, 1, 1, 1, 1, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 1, 2, 2, 2, 1, 3, 1, 3, + 3, 3, 1, 3, 1, 3, 1, 3, 1, 3, + 1, 3, 3, 3, 1, 3, 3, 3, 3, 3, + 3, 3, 3, 1, 3, 3, 1, 5, 4, 3, + 1, 3, 2, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 15, 6, 4, 16, 0, 0, 0, 0, 17, + 18, 19, 32, 36, 38, 42, 44, 46, 48, 50, + 54, 63, 66, 70, 73, 0, 0, 5, 0, 0, + 0, 33, 34, 35, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 72, 1, 10, + 0, 31, 30, 7, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 20, 13, 0, 37, 40, 41, 39, + 43, 45, 47, 49, 51, 52, 53, 57, 58, 59, + 60, 61, 62, 55, 56, 64, 65, 0, 69, 71, + 0, 9, 14, 68, 0, 0, 0, 67, 0, 11, + 0, 0, 2, 8, 12, 0, 3 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 121, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 122, 25 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -38 +static const yytype_int8 yypact[] = +{ + 70, -38, -21, 12, -11, 70, 70, 70, 70, -38, + 102, 6, -38, -38, 50, 19, 4, 8, 14, 25, + 51, 22, 40, -38, 47, 96, 34, -38, 70, 70, + 43, -38, -38, -38, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 45, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 18, 70, -38, -38, + 55, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, 30, -38, 50, 50, 50, + 19, 4, 8, 14, 25, 25, 25, 51, 51, 51, + 51, 51, 51, 51, 51, 22, 22, 70, 53, -38, + 70, 72, -38, -38, 70, 60, 70, -38, 70, -38, + 54, 77, -23, -38, -38, 70, -38 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -38, -10, 111, -38, -38, -38, 11, 61, 79, 76, + 80, 75, 58, 78, -37, -38, -27, 0, -38 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 24, 71, 72, 125, 28, 30, 26, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 31, 32, 33, + 29, 1, 2, 67, 46, 47, 70, 105, 106, 56, + 57, 58, 59, 60, 61, 44, 3, 1, 2, 108, + 109, 49, 3, 4, 85, 5, 50, 51, 1, 2, + 6, 7, 8, 64, 65, 48, 86, 52, 3, 4, + 112, 5, 69, 107, 62, 63, 6, 7, 8, 3, + 4, 73, 5, 1, 2, 84, 67, 6, 7, 8, + 113, 110, 123, 111, 66, 45, 118, 117, 119, 67, + 53, 54, 55, 67, 3, 4, 68, 5, 114, 116, + 67, 67, 6, 7, 8, 124, 67, 87, 88, 89, + 115, 94, 95, 96, 27, 126, 120, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 91, 93, 90, 0, + 0, 92, 0, 43, 97, 98, 99, 100, 101, 102, + 103, 104 +}; + +static const yytype_int8 yycheck[] = +{ + 0, 28, 29, 26, 15, 5, 27, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 6, 7, 8, + 31, 3, 4, 46, 5, 6, 26, 64, 65, 7, + 8, 9, 10, 11, 12, 29, 24, 3, 4, 66, + 67, 37, 24, 25, 44, 27, 38, 33, 3, 4, + 32, 33, 34, 13, 14, 36, 45, 32, 24, 25, + 30, 27, 28, 45, 42, 43, 32, 33, 34, 24, + 25, 28, 27, 3, 4, 30, 46, 32, 33, 34, + 107, 26, 28, 28, 44, 35, 26, 114, 28, 46, + 39, 40, 41, 46, 24, 25, 0, 27, 45, 27, + 46, 46, 32, 33, 34, 28, 46, 46, 47, 48, + 110, 53, 54, 55, 3, 125, 116, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 50, 52, 49, -1, + -1, 51, -1, 31, 56, 57, 58, 59, 60, 61, + 62, 63 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 4, 24, 25, 27, 32, 33, 34, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 27, 49, 15, 31, + 64, 53, 53, 53, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 31, 29, 35, 5, 6, 36, 37, + 38, 33, 32, 39, 40, 41, 7, 8, 9, 10, + 11, 12, 42, 43, 13, 14, 44, 46, 0, 28, + 64, 63, 63, 28, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 30, 64, 53, 54, 54, 54, + 55, 56, 57, 58, 59, 59, 59, 60, 60, 60, + 60, 60, 60, 60, 60, 61, 61, 45, 63, 63, + 26, 28, 30, 63, 45, 64, 27, 63, 26, 28, + 64, 48, 64, 28, 28, 26, 48 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, &yylloc, scanner) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, compileContext* context) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + compileContext* context; +#endif +{ + if (!yyvaluep) + return; + YYUSE (yylocationp); + YYUSE (context); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, compileContext* context) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + compileContext* context; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, compileContext* context) +#else +static void +yy_reduce_print (yyvsp, yylsp, yyrule, context) + YYSTYPE *yyvsp; + YYLTYPE *yylsp; + int yyrule; + compileContext* context; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) , context); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, yylsp, Rule, context); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, compileContext* context) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, yylocationp, context) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + YYLTYPE *yylocationp; + compileContext* context; +#endif +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (context); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + case 3: /* "VALUE" */ +#line 8 "eel2.y" + { + #define yydestruct(a,b,c,d,e) +}; +#line 1222 "y.tab.c" + break; + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (compileContext* context); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (compileContext* context) +#else +int +yyparse (context) + compileContext* context; +#endif +#endif +{ + /* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; +/* Location data for the look-ahead symbol. */ +YYLTYPE yylloc; + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[2]; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + yylsp = yyls; +#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + /* Initialize the default location before parsing starts. */ + yylloc.first_line = yylloc.last_line = 1; + yylloc.first_column = yylloc.last_column = 0; +#endif + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + YYSTACK_RELOCATE (yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: +#line 42 "eel2.y" + { + (yyval) = nseel_createMoreParametersOpcode(context,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 5: +#line 50 "eel2.y" + { + ((struct eelStringSegmentRec *)(yyvsp[(1) - (2)]))->_next = (struct eelStringSegmentRec *)(yyvsp[(2) - (2)]); + (yyval) = (yyvsp[(1) - (2)]); + } + break; + + case 6: +#line 58 "eel2.y" + { + if (!((yyval) = nseel_resolve_named_symbol(context, (yyvsp[(1) - (1)]), -1, NULL))) /* convert from purely named to namespace-relative, etc */ + { + yyerror(&yyloc, context, ""); + YYERROR; + } + } + break; + + case 7: +#line 68 "eel2.y" + { + (yyval) = (yyvsp[(2) - (3)]); + } + break; + + case 8: +#line 72 "eel2.y" + { + int err; + if (!((yyval) = nseel_setCompiledFunctionCallParameters(context,(yyvsp[(1) - (7)]), (yyvsp[(3) - (7)]), 0, 0, (yyvsp[(6) - (7)]), &err))) + { + if (err == -1) yyerror(&yylsp[-2], context, ""); + else if (err == 0) yyerror(&yylsp[-6], context, ""); + else yyerror(&yylsp[-3], context, ""); // parameter count wrong + + YYERROR; + } + } + break; + + case 9: +#line 84 "eel2.y" + { + int err; + if (!((yyval) = nseel_setCompiledFunctionCallParameters(context,(yyvsp[(1) - (4)]), (yyvsp[(3) - (4)]), 0, 0, 0, &err))) + { + if (err == 0) yyerror(&yylsp[-3], context, ""); + else yyerror(&yylsp[0], context, ""); // parameter count wrong + YYERROR; + } + } + break; + + case 10: +#line 94 "eel2.y" + { + int err; + if (!((yyval) = nseel_setCompiledFunctionCallParameters(context,(yyvsp[(1) - (3)]), nseel_createCompiledValue(context,0.0), 0, 0, 0,&err))) + { + if (err == 0) yyerror(&yylsp[-2], context, ""); // function not found + else yyerror(&yylsp[0], context, ""); // parameter count wrong + YYERROR; + } + } + break; + + case 11: +#line 104 "eel2.y" + { + int err; + if (!((yyval) = nseel_setCompiledFunctionCallParameters(context,(yyvsp[(1) - (6)]), (yyvsp[(3) - (6)]), (yyvsp[(5) - (6)]), 0, 0,&err))) + { + if (err == 0) yyerror(&yylsp[-5], context, ""); + else if (err == 2) yyerror(&yylsp[0], context, ""); // needs more than 2 parameters + else yyerror(&yylsp[-2], context, ""); // less than 2 + YYERROR; + } + } + break; + + case 12: +#line 115 "eel2.y" + { + int err; + if (!((yyval) = nseel_setCompiledFunctionCallParameters(context,(yyvsp[(1) - (8)]), (yyvsp[(3) - (8)]), (yyvsp[(5) - (8)]), (yyvsp[(7) - (8)]), 0, &err))) + { + if (err == 0) yyerror(&yylsp[-7], context, ""); + else if (err==2) yyerror(&yylsp[0], context, ""); // needs more parameters + else if (err==4) yyerror(&yylsp[-4], context, ""); // needs single parameter + else yyerror(&yylsp[-2], context, ""); // less parm + YYERROR; + } + } + break; + + case 13: +#line 127 "eel2.y" + { + (yyval) = nseel_createMemoryAccess(context,(yyvsp[(1) - (3)]),0); + } + break; + + case 14: +#line 131 "eel2.y" + { + (yyval) = nseel_createMemoryAccess(context,(yyvsp[(1) - (4)]),(yyvsp[(3) - (4)])); + } + break; + + case 17: +#line 140 "eel2.y" + { + (yyval) = nseel_eelMakeOpcodeFromStringSegments(context,(struct eelStringSegmentRec *)(yyvsp[(1) - (1)])); + } + break; + + case 20: +#line 150 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_ASSIGN,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 21: +#line 154 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_ADD_OP,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 22: +#line 158 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_SUB_OP,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 23: +#line 162 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_MOD_OP,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 24: +#line 166 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_OR_OP,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 25: +#line 170 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_AND_OP,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 26: +#line 174 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_XOR_OP,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 27: +#line 178 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_DIV_OP,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 28: +#line 182 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_MUL_OP,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 29: +#line 186 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_POW_OP,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 30: +#line 190 "eel2.y" + { + (yyval) = nseel_createFunctionByName(context,"strcpy",2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)]),NULL); + } + break; + + case 31: +#line 194 "eel2.y" + { + (yyval) = nseel_createFunctionByName(context,"strcat",2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)]),NULL); + } + break; + + case 33: +#line 202 "eel2.y" + { + (yyval) = (yyvsp[(2) - (2)]); + } + break; + + case 34: +#line 206 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_UMINUS,1,(yyvsp[(2) - (2)]),0); + } + break; + + case 35: +#line 210 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_NOT,1,(yyvsp[(2) - (2)]),0); + } + break; + + case 37: +#line 218 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_POW,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 39: +#line 226 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_MOD,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 40: +#line 230 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_SHL,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 41: +#line 234 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_SHR,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 43: +#line 242 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_DIVIDE,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 45: +#line 251 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_MULTIPLY,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 47: +#line 260 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_SUB,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 49: +#line 268 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_ADD,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 51: +#line 276 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_AND,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 52: +#line 280 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_OR,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 53: +#line 284 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_XOR,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 55: +#line 292 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_LT,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 56: +#line 296 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_GT,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 57: +#line 300 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_LTE,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 58: +#line 304 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_GTE,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 59: +#line 308 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_EQ,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 60: +#line 312 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_EQ_EXACT,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 61: +#line 316 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_NE,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 62: +#line 320 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_NE_EXACT,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 64: +#line 328 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_AND,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 65: +#line 332 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_OR,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 67: +#line 340 "eel2.y" + { + (yyval) = nseel_createIfElse(context, (yyvsp[(1) - (5)]), (yyvsp[(3) - (5)]), (yyvsp[(5) - (5)])); + } + break; + + case 68: +#line 344 "eel2.y" + { + (yyval) = nseel_createIfElse(context, (yyvsp[(1) - (4)]), 0, (yyvsp[(4) - (4)])); + } + break; + + case 69: +#line 348 "eel2.y" + { + (yyval) = nseel_createIfElse(context, (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)]), 0); + } + break; + + case 71: +#line 357 "eel2.y" + { + (yyval) = nseel_createSimpleCompiledFunction(context,FN_JOIN_STATEMENTS,2,(yyvsp[(1) - (3)]),(yyvsp[(3) - (3)])); + } + break; + + case 72: +#line 361 "eel2.y" + { + (yyval) = (yyvsp[(1) - (2)]); + } + break; + + case 73: +#line 369 "eel2.y" + { + if ((yylsp[(1) - (1)]).first_line) { } + context->result = (yyvsp[(1) - (1)]); + } + break; + + +/* Line 1267 of yacc.c. */ +#line 1965 "y.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, context, YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (&yylloc, context, yymsg); + } + else + { + yyerror (&yylloc, context, YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + yyerror_range[0] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[0] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[0] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + yyerror_range[1] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the look-ahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, context); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 376 "eel2.y" + + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/y.tab.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/y.tab.h new file mode 100644 index 000000000..c02daceff --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/eel2/y.tab.h @@ -0,0 +1,117 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + VALUE = 258, + IDENTIFIER = 259, + TOKEN_SHL = 260, + TOKEN_SHR = 261, + TOKEN_LTE = 262, + TOKEN_GTE = 263, + TOKEN_EQ = 264, + TOKEN_EQ_EXACT = 265, + TOKEN_NE = 266, + TOKEN_NE_EXACT = 267, + TOKEN_LOGICAL_AND = 268, + TOKEN_LOGICAL_OR = 269, + TOKEN_ADD_OP = 270, + TOKEN_SUB_OP = 271, + TOKEN_MOD_OP = 272, + TOKEN_OR_OP = 273, + TOKEN_AND_OP = 274, + TOKEN_XOR_OP = 275, + TOKEN_DIV_OP = 276, + TOKEN_MUL_OP = 277, + TOKEN_POW_OP = 278, + STRING_LITERAL = 279, + STRING_IDENTIFIER = 280 + }; +#endif +/* Tokens. */ +#define VALUE 258 +#define IDENTIFIER 259 +#define TOKEN_SHL 260 +#define TOKEN_SHR 261 +#define TOKEN_LTE 262 +#define TOKEN_GTE 263 +#define TOKEN_EQ 264 +#define TOKEN_EQ_EXACT 265 +#define TOKEN_NE 266 +#define TOKEN_NE_EXACT 267 +#define TOKEN_LOGICAL_AND 268 +#define TOKEN_LOGICAL_OR 269 +#define TOKEN_ADD_OP 270 +#define TOKEN_SUB_OP 271 +#define TOKEN_MOD_OP 272 +#define TOKEN_OR_OP 273 +#define TOKEN_AND_OP 274 +#define TOKEN_XOR_OP 275 +#define TOKEN_DIV_OP 276 +#define TOKEN_MUL_OP 277 +#define TOKEN_POW_OP 278 +#define STRING_LITERAL 279 +#define STRING_IDENTIFIER 280 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/fft.c b/source/modules/ysfx/thirdparty/WDL/source/WDL/fft.c new file mode 100644 index 000000000..8b15d6ca3 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/fft.c @@ -0,0 +1,1199 @@ +/* + WDL - fft.cpp + Copyright (C) 2006 and later Cockos Incorporated + Copyright 1999 D. J. Bernstein + + 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. + + + + This file implements the WDL FFT library. These routines are based on the + DJBFFT library, which are Copyright 1999 D. J. Bernstein, djb@pobox.com + + The DJB FFT web page is: http://cr.yp.to/djbfft.html + +*/ + + +// this is based on djbfft + +#include +#include "fft.h" + + +#define FFT_MAXBITLEN 15 + +#ifdef _MSC_VER +#define inline __inline +#endif + +#define PI 3.1415926535897932384626433832795 + +static WDL_FFT_COMPLEX d16[3]; +static WDL_FFT_COMPLEX d32[7]; +static WDL_FFT_COMPLEX d64[15]; +static WDL_FFT_COMPLEX d128[31]; +static WDL_FFT_COMPLEX d256[63]; +static WDL_FFT_COMPLEX d512[127]; +static WDL_FFT_COMPLEX d1024[127]; +static WDL_FFT_COMPLEX d2048[255]; +static WDL_FFT_COMPLEX d4096[511]; +static WDL_FFT_COMPLEX d8192[1023]; +static WDL_FFT_COMPLEX d16384[2047]; +static WDL_FFT_COMPLEX d32768[4095]; + + +#define sqrthalf (d16[1].re) + +#define VOL *(volatile WDL_FFT_REAL *)& + +#define TRANSFORM(a0,a1,a2,a3,wre,wim) { \ + t6 = a2.re; \ + t1 = a0.re - t6; \ + t6 += a0.re; \ + a0.re = t6; \ + t3 = a3.im; \ + t4 = a1.im - t3; \ + t8 = t1 - t4; \ + t1 += t4; \ + t3 += a1.im; \ + a1.im = t3; \ + t5 = wre; \ + t7 = t8 * t5; \ + t4 = t1 * t5; \ + t8 *= wim; \ + t2 = a3.re; \ + t3 = a1.re - t2; \ + t2 += a1.re; \ + a1.re = t2; \ + t1 *= wim; \ + t6 = a2.im; \ + t2 = a0.im - t6; \ + t6 += a0.im; \ + a0.im = t6; \ + t6 = t2 + t3; \ + t2 -= t3; \ + t3 = t6 * wim; \ + t7 -= t3; \ + a2.re = t7; \ + t6 *= t5; \ + t6 += t8; \ + a2.im = t6; \ + t5 *= t2; \ + t5 -= t1; \ + a3.im = t5; \ + t2 *= wim; \ + t4 += t2; \ + a3.re = t4; \ + } + +#define TRANSFORMHALF(a0,a1,a2,a3) { \ + t1 = a2.re; \ + t5 = a0.re - t1; \ + t1 += a0.re; \ + a0.re = t1; \ + t4 = a3.im; \ + t8 = a1.im - t4; \ + t1 = t5 - t8; \ + t5 += t8; \ + t4 += a1.im; \ + a1.im = t4; \ + t3 = a3.re; \ + t7 = a1.re - t3; \ + t3 += a1.re; \ + a1.re = t3; \ + t8 = a2.im; \ + t6 = a0.im - t8; \ + t2 = t6 + t7; \ + t6 -= t7; \ + t8 += a0.im; \ + a0.im = t8; \ + t4 = t6 + t5; \ + t3 = sqrthalf; \ + t4 *= t3; \ + a3.re = t4; \ + t6 -= t5; \ + t6 *= t3; \ + a3.im = t6; \ + t7 = t1 - t2; \ + t7 *= t3; \ + a2.re = t7; \ + t2 += t1; \ + t2 *= t3; \ + a2.im = t2; \ + } + +#define TRANSFORMZERO(a0,a1,a2,a3) { \ + t5 = a2.re; \ + t1 = a0.re - t5; \ + t5 += a0.re; \ + a0.re = t5; \ + t8 = a3.im; \ + t4 = a1.im - t8; \ + t7 = a3.re; \ + t6 = t1 - t4; \ + a2.re = t6; \ + t1 += t4; \ + a3.re = t1; \ + t8 += a1.im; \ + a1.im = t8; \ + t3 = a1.re - t7; \ + t7 += a1.re; \ + a1.re = t7; \ + t6 = a2.im; \ + t2 = a0.im - t6; \ + t7 = t2 + t3; \ + a2.im = t7; \ + t2 -= t3; \ + a3.im = t2; \ + t6 += a0.im; \ + a0.im = t6; \ + } + +#define UNTRANSFORM(a0,a1,a2,a3,wre,wim) { \ + t6 = VOL wre; \ + t1 = VOL a2.re; \ + t1 *= t6; \ + t8 = VOL wim; \ + t3 = VOL a2.im; \ + t3 *= t8; \ + t2 = VOL a2.im; \ + t4 = VOL a2.re; \ + t5 = VOL a3.re; \ + t5 *= t6; \ + t7 = VOL a3.im; \ + t1 += t3; \ + t7 *= t8; \ + t5 -= t7; \ + t3 = t5 + t1; \ + t5 -= t1; \ + t2 *= t6; \ + t6 *= a3.im; \ + t4 *= t8; \ + t2 -= t4; \ + t8 *= a3.re; \ + t6 += t8; \ + t1 = a0.re - t3; \ + t3 += a0.re; \ + a0.re = t3; \ + t7 = a1.im - t5; \ + t5 += a1.im; \ + a1.im = t5; \ + t4 = t2 - t6; \ + t6 += t2; \ + t8 = a1.re - t4; \ + t4 += a1.re; \ + a1.re = t4; \ + t2 = a0.im - t6; \ + t6 += a0.im; \ + a0.im = t6; \ + a2.re = t1; \ + a3.im = t7; \ + a3.re = t8; \ + a2.im = t2; \ + } + + +#define UNTRANSFORMHALF(a0,a1,a2,a3) { \ + t6 = sqrthalf; \ + t1 = a2.re; \ + t2 = a2.im - t1; \ + t2 *= t6; \ + t1 += a2.im; \ + t1 *= t6; \ + t4 = a3.im; \ + t3 = a3.re - t4; \ + t3 *= t6; \ + t4 += a3.re; \ + t4 *= t6; \ + t8 = t3 - t1; \ + t7 = t2 - t4; \ + t1 += t3; \ + t2 += t4; \ + t4 = a1.im - t8; \ + a3.im = t4; \ + t8 += a1.im; \ + a1.im = t8; \ + t3 = a1.re - t7; \ + a3.re = t3; \ + t7 += a1.re; \ + a1.re = t7; \ + t5 = a0.re - t1; \ + a2.re = t5; \ + t1 += a0.re; \ + a0.re = t1; \ + t6 = a0.im - t2; \ + a2.im = t6; \ + t2 += a0.im; \ + a0.im = t2; \ + } + +#define UNTRANSFORMZERO(a0,a1,a2,a3) { \ + t2 = a3.im; \ + t3 = a2.im - t2; \ + t2 += a2.im; \ + t1 = a2.re; \ + t4 = a3.re - t1; \ + t1 += a3.re; \ + t5 = a0.re - t1; \ + a2.re = t5; \ + t6 = a0.im - t2; \ + a2.im = t6; \ + t7 = a1.re - t3; \ + a3.re = t7; \ + t8 = a1.im - t4; \ + a3.im = t8; \ + t1 += a0.re; \ + a0.re = t1; \ + t2 += a0.im; \ + a0.im = t2; \ + t3 += a1.re; \ + a1.re = t3; \ + t4 += a1.im; \ + a1.im = t4; \ + } + +static void c2(register WDL_FFT_COMPLEX *a) +{ + register WDL_FFT_REAL t1; + + t1 = a[1].re; + a[1].re = a[0].re - t1; + a[0].re += t1; + + t1 = a[1].im; + a[1].im = a[0].im - t1; + a[0].im += t1; +} + +static inline void c4(register WDL_FFT_COMPLEX *a) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + + t5 = a[2].re; + t1 = a[0].re - t5; + t7 = a[3].re; + t5 += a[0].re; + t3 = a[1].re - t7; + t7 += a[1].re; + t8 = t5 + t7; + a[0].re = t8; + t5 -= t7; + a[1].re = t5; + t6 = a[2].im; + t2 = a[0].im - t6; + t6 += a[0].im; + t5 = a[3].im; + a[2].im = t2 + t3; + t2 -= t3; + a[3].im = t2; + t4 = a[1].im - t5; + a[3].re = t1 + t4; + t1 -= t4; + a[2].re = t1; + t5 += a[1].im; + a[0].im = t6 + t5; + t6 -= t5; + a[1].im = t6; +} + +static void c8(register WDL_FFT_COMPLEX *a) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + + t7 = a[4].im; + t4 = a[0].im - t7; + t7 += a[0].im; + a[0].im = t7; + + t8 = a[6].re; + t5 = a[2].re - t8; + t8 += a[2].re; + a[2].re = t8; + + t7 = a[6].im; + a[6].im = t4 - t5; + t4 += t5; + a[4].im = t4; + + t6 = a[2].im - t7; + t7 += a[2].im; + a[2].im = t7; + + t8 = a[4].re; + t3 = a[0].re - t8; + t8 += a[0].re; + a[0].re = t8; + + a[4].re = t3 - t6; + t3 += t6; + a[6].re = t3; + + t7 = a[5].re; + t3 = a[1].re - t7; + t7 += a[1].re; + a[1].re = t7; + + t8 = a[7].im; + t6 = a[3].im - t8; + t8 += a[3].im; + a[3].im = t8; + t1 = t3 - t6; + t3 += t6; + + t7 = a[5].im; + t4 = a[1].im - t7; + t7 += a[1].im; + a[1].im = t7; + + t8 = a[7].re; + t5 = a[3].re - t8; + t8 += a[3].re; + a[3].re = t8; + + t2 = t4 - t5; + t4 += t5; + + t6 = t1 - t4; + t8 = sqrthalf; + t6 *= t8; + a[5].re = a[4].re - t6; + t1 += t4; + t1 *= t8; + a[5].im = a[4].im - t1; + t6 += a[4].re; + a[4].re = t6; + t1 += a[4].im; + a[4].im = t1; + + t5 = t2 - t3; + t5 *= t8; + a[7].im = a[6].im - t5; + t2 += t3; + t2 *= t8; + a[7].re = a[6].re - t2; + t2 += a[6].re; + a[6].re = t2; + t5 += a[6].im; + a[6].im = t5; + + c4(a); +} + +static void c16(register WDL_FFT_COMPLEX *a) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + + TRANSFORMZERO(a[0],a[4],a[8],a[12]); + TRANSFORM(a[1],a[5],a[9],a[13],d16[0].re,d16[0].im); + TRANSFORMHALF(a[2],a[6],a[10],a[14]); + TRANSFORM(a[3],a[7],a[11],a[15],d16[0].im,d16[0].re); + c4(a + 8); + c4(a + 12); + + c8(a); +} + +/* a[0...8n-1], w[0...2n-2]; n >= 2 */ +static void cpass(register WDL_FFT_COMPLEX *a,register const WDL_FFT_COMPLEX *w,register unsigned int n) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + register WDL_FFT_COMPLEX *a1; + register WDL_FFT_COMPLEX *a2; + register WDL_FFT_COMPLEX *a3; + + a2 = a + 4 * n; + a1 = a + 2 * n; + a3 = a2 + 2 * n; + --n; + + TRANSFORMZERO(a[0],a1[0],a2[0],a3[0]); + TRANSFORM(a[1],a1[1],a2[1],a3[1],w[0].re,w[0].im); + + for (;;) { + TRANSFORM(a[2],a1[2],a2[2],a3[2],w[1].re,w[1].im); + TRANSFORM(a[3],a1[3],a2[3],a3[3],w[2].re,w[2].im); + if (!--n) break; + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + w += 2; + } +} + +static void c32(register WDL_FFT_COMPLEX *a) +{ + cpass(a,d32,4); + c8(a + 16); + c8(a + 24); + c16(a); +} + +static void c64(register WDL_FFT_COMPLEX *a) +{ + cpass(a,d64,8); + c16(a + 32); + c16(a + 48); + c32(a); +} + +static void c128(register WDL_FFT_COMPLEX *a) +{ + cpass(a,d128,16); + c32(a + 64); + c32(a + 96); + c64(a); +} + +static void c256(register WDL_FFT_COMPLEX *a) +{ + cpass(a,d256,32); + c64(a + 128); + c64(a + 192); + c128(a); +} + +static void c512(register WDL_FFT_COMPLEX *a) +{ + cpass(a,d512,64); + c128(a + 384); + c128(a + 256); + c256(a); +} + +/* a[0...8n-1], w[0...n-2]; n even, n >= 4 */ +static void cpassbig(register WDL_FFT_COMPLEX *a,register const WDL_FFT_COMPLEX *w,register unsigned int n) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + register WDL_FFT_COMPLEX *a1; + register WDL_FFT_COMPLEX *a2; + register WDL_FFT_COMPLEX *a3; + register unsigned int k; + + a2 = a + 4 * n; + a1 = a + 2 * n; + a3 = a2 + 2 * n; + k = n - 2; + + TRANSFORMZERO(a[0],a1[0],a2[0],a3[0]); + TRANSFORM(a[1],a1[1],a2[1],a3[1],w[0].re,w[0].im); + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + + do { + TRANSFORM(a[0],a1[0],a2[0],a3[0],w[1].re,w[1].im); + TRANSFORM(a[1],a1[1],a2[1],a3[1],w[2].re,w[2].im); + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + w += 2; + } while (k -= 2); + + TRANSFORMHALF(a[0],a1[0],a2[0],a3[0]); + TRANSFORM(a[1],a1[1],a2[1],a3[1],w[0].im,w[0].re); + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + + k = n - 2; + do { + TRANSFORM(a[0],a1[0],a2[0],a3[0],w[-1].im,w[-1].re); + TRANSFORM(a[1],a1[1],a2[1],a3[1],w[-2].im,w[-2].re); + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + w -= 2; + } while (k -= 2); +} + + +static void c1024(register WDL_FFT_COMPLEX *a) +{ + cpassbig(a,d1024,128); + c256(a + 768); + c256(a + 512); + c512(a); +} + +static void c2048(register WDL_FFT_COMPLEX *a) +{ + cpassbig(a,d2048,256); + c512(a + 1536); + c512(a + 1024); + c1024(a); +} + +static void c4096(register WDL_FFT_COMPLEX *a) +{ + cpassbig(a,d4096,512); + c1024(a + 3072); + c1024(a + 2048); + c2048(a); +} + +static void c8192(register WDL_FFT_COMPLEX *a) +{ + cpassbig(a,d8192,1024); + c2048(a + 6144); + c2048(a + 4096); + c4096(a); +} + +static void c16384(register WDL_FFT_COMPLEX *a) +{ + cpassbig(a,d16384,2048); + c4096(a + 8192 + 4096); + c4096(a + 8192); + c8192(a); +} + +static void c32768(register WDL_FFT_COMPLEX *a) +{ + cpassbig(a,d32768,4096); + c8192(a + 16384 + 8192); + c8192(a + 16384); + c16384(a); +} + + +/* n even, n > 0 */ +void WDL_fft_complexmul(WDL_FFT_COMPLEX *a,WDL_FFT_COMPLEX *b,int n) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + if (n<2 || (n&1)) return; + + do { + t1 = a[0].re * b[0].re; + t2 = a[0].im * b[0].im; + t3 = a[0].im * b[0].re; + t4 = a[0].re * b[0].im; + t5 = a[1].re * b[1].re; + t6 = a[1].im * b[1].im; + t7 = a[1].im * b[1].re; + t8 = a[1].re * b[1].im; + t1 -= t2; + t3 += t4; + t5 -= t6; + t7 += t8; + a[0].re = t1; + a[1].re = t5; + a[0].im = t3; + a[1].im = t7; + a += 2; + b += 2; + } while (n -= 2); +} + +void WDL_fft_complexmul2(WDL_FFT_COMPLEX *c, WDL_FFT_COMPLEX *a, WDL_FFT_COMPLEX *b, int n) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + if (n<2 || (n&1)) return; + + do { + t1 = a[0].re * b[0].re; + t2 = a[0].im * b[0].im; + t3 = a[0].im * b[0].re; + t4 = a[0].re * b[0].im; + t5 = a[1].re * b[1].re; + t6 = a[1].im * b[1].im; + t7 = a[1].im * b[1].re; + t8 = a[1].re * b[1].im; + t1 -= t2; + t3 += t4; + t5 -= t6; + t7 += t8; + c[0].re = t1; + c[1].re = t5; + c[0].im = t3; + c[1].im = t7; + a += 2; + b += 2; + c += 2; + } while (n -= 2); +} +void WDL_fft_complexmul3(WDL_FFT_COMPLEX *c, WDL_FFT_COMPLEX *a, WDL_FFT_COMPLEX *b, int n) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + if (n<2 || (n&1)) return; + + do { + t1 = a[0].re * b[0].re; + t2 = a[0].im * b[0].im; + t3 = a[0].im * b[0].re; + t4 = a[0].re * b[0].im; + t5 = a[1].re * b[1].re; + t6 = a[1].im * b[1].im; + t7 = a[1].im * b[1].re; + t8 = a[1].re * b[1].im; + t1 -= t2; + t3 += t4; + t5 -= t6; + t7 += t8; + c[0].re += t1; + c[1].re += t5; + c[0].im += t3; + c[1].im += t7; + a += 2; + b += 2; + c += 2; + } while (n -= 2); +} + + +static inline void u4(register WDL_FFT_COMPLEX *a) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + + t1 = VOL a[1].re; + t3 = a[0].re - t1; + t6 = VOL a[2].re; + t1 += a[0].re; + t8 = a[3].re - t6; + t6 += a[3].re; + a[0].re = t1 + t6; + t1 -= t6; + a[2].re = t1; + + t2 = VOL a[1].im; + t4 = a[0].im - t2; + t2 += a[0].im; + t5 = VOL a[3].im; + a[1].im = t4 + t8; + t4 -= t8; + a[3].im = t4; + + t7 = a[2].im - t5; + t5 += a[2].im; + a[1].re = t3 + t7; + t3 -= t7; + a[3].re = t3; + a[0].im = t2 + t5; + t2 -= t5; + a[2].im = t2; +} + +static void u8(register WDL_FFT_COMPLEX *a) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + + u4(a); + + t1 = a[5].re; + a[5].re = a[4].re - t1; + t1 += a[4].re; + + t3 = a[7].re; + a[7].re = a[6].re - t3; + t3 += a[6].re; + + t8 = t3 - t1; + t1 += t3; + + t6 = a[2].im - t8; + t8 += a[2].im; + a[2].im = t8; + + t5 = a[0].re - t1; + a[4].re = t5; + t1 += a[0].re; + a[0].re = t1; + + t2 = a[5].im; + a[5].im = a[4].im - t2; + t2 += a[4].im; + + t4 = a[7].im; + a[7].im = a[6].im - t4; + t4 += a[6].im; + + a[6].im = t6; + + t7 = t2 - t4; + t2 += t4; + + t3 = a[2].re - t7; + a[6].re = t3; + t7 += a[2].re; + a[2].re = t7; + + t6 = a[0].im - t2; + a[4].im = t6; + t2 += a[0].im; + a[0].im = t2; + + t6 = sqrthalf; + + t1 = a[5].re; + t2 = a[5].im - t1; + t2 *= t6; + t1 += a[5].im; + t1 *= t6; + t4 = a[7].im; + t3 = a[7].re - t4; + t3 *= t6; + t4 += a[7].re; + t4 *= t6; + + t8 = t3 - t1; + t1 += t3; + t7 = t2 - t4; + t2 += t4; + + t4 = a[3].im - t8; + a[7].im = t4; + t5 = a[1].re - t1; + a[5].re = t5; + t3 = a[3].re - t7; + a[7].re = t3; + t6 = a[1].im - t2; + a[5].im = t6; + + t8 += a[3].im; + a[3].im = t8; + t1 += a[1].re; + a[1].re = t1; + t7 += a[3].re; + a[3].re = t7; + t2 += a[1].im; + a[1].im = t2; +} + +static void u16(register WDL_FFT_COMPLEX *a) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + + u8(a); + u4(a + 8); + u4(a + 12); + + UNTRANSFORMZERO(a[0],a[4],a[8],a[12]); + UNTRANSFORMHALF(a[2],a[6],a[10],a[14]); + UNTRANSFORM(a[1],a[5],a[9],a[13],d16[0].re,d16[0].im); + UNTRANSFORM(a[3],a[7],a[11],a[15],d16[0].im,d16[0].re); +} + +/* a[0...8n-1], w[0...2n-2] */ +static void upass(register WDL_FFT_COMPLEX *a,register const WDL_FFT_COMPLEX *w,register unsigned int n) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + register WDL_FFT_COMPLEX *a1; + register WDL_FFT_COMPLEX *a2; + register WDL_FFT_COMPLEX *a3; + + a2 = a + 4 * n; + a1 = a + 2 * n; + a3 = a2 + 2 * n; + n -= 1; + + UNTRANSFORMZERO(a[0],a1[0],a2[0],a3[0]); + UNTRANSFORM(a[1],a1[1],a2[1],a3[1],w[0].re,w[0].im); + + for (;;) { + UNTRANSFORM(a[2],a1[2],a2[2],a3[2],w[1].re,w[1].im); + UNTRANSFORM(a[3],a1[3],a2[3],a3[3],w[2].re,w[2].im); + if (!--n) break; + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + w += 2; + } +} + +static void u32(register WDL_FFT_COMPLEX *a) +{ + u16(a); + u8(a + 16); + u8(a + 24); + upass(a,d32,4); +} + +static void u64(register WDL_FFT_COMPLEX *a) +{ + u32(a); + u16(a + 32); + u16(a + 48); + upass(a,d64,8); +} + +static void u128(register WDL_FFT_COMPLEX *a) +{ + u64(a); + u32(a + 64); + u32(a + 96); + upass(a,d128,16); +} + +static void u256(register WDL_FFT_COMPLEX *a) +{ + u128(a); + u64(a + 128); + u64(a + 192); + upass(a,d256,32); +} + +static void u512(register WDL_FFT_COMPLEX *a) +{ + u256(a); + u128(a + 256); + u128(a + 384); + upass(a,d512,64); +} + + +/* a[0...8n-1], w[0...n-2]; n even, n >= 4 */ +static void upassbig(register WDL_FFT_COMPLEX *a,register const WDL_FFT_COMPLEX *w,register unsigned int n) +{ + register WDL_FFT_REAL t1, t2, t3, t4, t5, t6, t7, t8; + register WDL_FFT_COMPLEX *a1; + register WDL_FFT_COMPLEX *a2; + register WDL_FFT_COMPLEX *a3; + register unsigned int k; + + a2 = a + 4 * n; + a1 = a + 2 * n; + a3 = a2 + 2 * n; + k = n - 2; + + UNTRANSFORMZERO(a[0],a1[0],a2[0],a3[0]); + UNTRANSFORM(a[1],a1[1],a2[1],a3[1],w[0].re,w[0].im); + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + + do { + UNTRANSFORM(a[0],a1[0],a2[0],a3[0],w[1].re,w[1].im); + UNTRANSFORM(a[1],a1[1],a2[1],a3[1],w[2].re,w[2].im); + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + w += 2; + } while (k -= 2); + + UNTRANSFORMHALF(a[0],a1[0],a2[0],a3[0]); + UNTRANSFORM(a[1],a1[1],a2[1],a3[1],w[0].im,w[0].re); + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + + k = n - 2; + do { + UNTRANSFORM(a[0],a1[0],a2[0],a3[0],w[-1].im,w[-1].re); + UNTRANSFORM(a[1],a1[1],a2[1],a3[1],w[-2].im,w[-2].re); + a += 2; + a1 += 2; + a2 += 2; + a3 += 2; + w -= 2; + } while (k -= 2); +} + + + +static void u1024(register WDL_FFT_COMPLEX *a) +{ + u512(a); + u256(a + 512); + u256(a + 768); + upassbig(a,d1024,128); +} + +static void u2048(register WDL_FFT_COMPLEX *a) +{ + u1024(a); + u512(a + 1024); + u512(a + 1536); + upassbig(a,d2048,256); +} + + +static void u4096(register WDL_FFT_COMPLEX *a) +{ + u2048(a); + u1024(a + 2048); + u1024(a + 3072); + upassbig(a,d4096,512); +} + +static void u8192(register WDL_FFT_COMPLEX *a) +{ + u4096(a); + u2048(a + 4096); + u2048(a + 6144); + upassbig(a,d8192,1024); +} + +static void u16384(register WDL_FFT_COMPLEX *a) +{ + u8192(a); + u4096(a + 8192); + u4096(a + 8192 + 4096); + upassbig(a,d16384,2048); +} + +static void u32768(register WDL_FFT_COMPLEX *a) +{ + u16384(a); + u8192(a + 16384); + u8192(a + 16384 + 8192 ); + upassbig(a,d32768,4096); +} + + +static void __fft_gen(WDL_FFT_COMPLEX *buf, const WDL_FFT_COMPLEX *buf2, int sz, int isfull) +{ + int x; + double div=PI*0.25/(sz+1); + + if (isfull) div*=2.0; + + for (x = 0; x < sz; x ++) + { + if (!(x & 1) || !buf2) + { + buf[x].re = (WDL_FFT_REAL) cos((x+1)*div); + buf[x].im = (WDL_FFT_REAL) sin((x+1)*div); + } + else + { + buf[x].re = buf2[x >> 1].re; + buf[x].im = buf2[x >> 1].im; + } + } +} + +#ifndef WDL_FFT_NO_PERMUTE + +static unsigned int fftfreq_c(unsigned int i,unsigned int n) +{ + unsigned int m; + + if (n <= 2) return i; + + m = n >> 1; + if (i < m) return fftfreq_c(i,m) << 1; + + i -= m; + m >>= 1; + if (i < m) return (fftfreq_c(i,m) << 2) + 1; + i -= m; + return ((fftfreq_c(i,m) << 2) - 1) & (n - 1); +} + +static int _idxperm[2<> 1, quart = half >> 1, eighth = quart >> 1; + const int *permute = WDL_fft_permute_tab(half); + unsigned int i, j; + + WDL_FFT_COMPLEX *p, *q, tw, sum, diff; + WDL_FFT_REAL tw1, tw2; + + if (!isInverse) + { + WDL_fft((WDL_FFT_COMPLEX*)buf, half, isInverse); + r2(buf); + } + else + { + v2(buf); + } + + /* Source: http://www.katjaas.nl/realFFT/realFFT2.html */ + + for (i = 1; i < quart; ++i) + { + p = (WDL_FFT_COMPLEX*)buf + permute[i]; + q = (WDL_FFT_COMPLEX*)buf + permute[half - i]; + +/* tw.re = cos(2*PI * i / len); + tw.im = sin(2*PI * i / len); */ + + if (i < eighth) + { + j = i - 1; + tw.re = d[j].re; + tw.im = d[j].im; + } + else if (i > eighth) + { + j = quart - i - 1; + tw.re = d[j].im; + tw.im = d[j].re; + } + else + { + tw.re = tw.im = sqrthalf; + } + + if (!isInverse) tw.re = -tw.re; + + sum.re = p->re + q->re; + sum.im = p->im + q->im; + diff.re = p->re - q->re; + diff.im = p->im - q->im; + + tw1 = tw.re * sum.im + tw.im * diff.re; + tw2 = tw.im * sum.im - tw.re * diff.re; + + p->re = sum.re - tw1; + p->im = diff.im - tw2; + q->re = sum.re + tw1; + q->im = -(diff.im + tw2); + } + + p = (WDL_FFT_COMPLEX*)buf + permute[i]; + p->re *= 2; + p->im *= -2; + + if (isInverse) WDL_fft((WDL_FFT_COMPLEX*)buf, half, isInverse); +} + +void WDL_real_fft(WDL_FFT_REAL* buf, int len, int isInverse) +{ + switch (len) + { + case 2: if (!isInverse) r2(buf); else v2(buf); break; + case 4: case 8: two_for_one(buf, 0, len, isInverse); break; +#define TMP(x) case x: two_for_one(buf, d##x, len, isInverse); break; + TMP(16) + TMP(32) + TMP(64) + TMP(128) + TMP(256) + TMP(512) + TMP(1024) + TMP(2048) + TMP(4096) + TMP(8192) + TMP(16384) + TMP(32768) +#undef TMP + } +} diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/fft.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/fft.h new file mode 100644 index 000000000..d2be245fb --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/fft.h @@ -0,0 +1,77 @@ +/* + WDL - fft.h + Copyright (C) 2006 and later Cockos Incorporated + + 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. + + + + This file defines the interface to the WDL FFT library. These routines are based on the + DJBFFT library, which are Copyright 1999 D. J. Bernstein, djb@pobox.com + + The DJB FFT web page is: http://cr.yp.to/djbfft.html + +*/ + +#ifndef _WDL_FFT_H_ +#define _WDL_FFT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WDL_FFT_REALSIZE +#define WDL_FFT_REALSIZE 4 +#endif + +#if WDL_FFT_REALSIZE == 4 +typedef float WDL_FFT_REAL; +#elif WDL_FFT_REALSIZE == 8 +typedef double WDL_FFT_REAL; +#else +#error invalid FFT item size +#endif + +typedef struct { + WDL_FFT_REAL re; + WDL_FFT_REAL im; +} WDL_FFT_COMPLEX; + +extern void WDL_fft_init(); + +extern void WDL_fft_complexmul(WDL_FFT_COMPLEX *dest, WDL_FFT_COMPLEX *src, int len); +extern void WDL_fft_complexmul2(WDL_FFT_COMPLEX *dest, WDL_FFT_COMPLEX *src, WDL_FFT_COMPLEX *src2, int len); +extern void WDL_fft_complexmul3(WDL_FFT_COMPLEX *destAdd, WDL_FFT_COMPLEX *src, WDL_FFT_COMPLEX *src2, int len); + +/* Expects WDL_FFT_COMPLEX input[0..len-1] scaled by 1.0/len, returns +WDL_FFT_COMPLEX output[0..len-1] order by WDL_fft_permute(len). */ +extern void WDL_fft(WDL_FFT_COMPLEX *, int len, int isInverse); + +/* Expects WDL_FFT_REAL input[0..len-1] scaled by 0.5/len, returns +WDL_FFT_COMPLEX output[0..len/2-1], for len >= 4 order by +WDL_fft_permute(len/2). Note that output[len/2].re is stored in +output[0].im. */ +extern void WDL_real_fft(WDL_FFT_REAL *, int len, int isInverse); + +extern int WDL_fft_permute(int fftsize, int idx); +extern int *WDL_fft_permute_tab(int fftsize); + +#ifdef __cplusplus +}; +#endif + +#endif \ No newline at end of file diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/has_strings.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/has_strings.h new file mode 100644 index 000000000..f581469ca --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/has_strings.h @@ -0,0 +1,222 @@ +#ifndef _WDL_HASSTRINGS_H_ +#define _WDL_HASSTRINGS_H_ + +#ifndef WDL_HASSTRINGS_EXPORT +#define WDL_HASSTRINGS_EXPORT +#endif + +WDL_HASSTRINGS_EXPORT bool hasStrings_isNonWordChar(int c) +{ + // treat as whitespace when searching for " foo " + switch (c) + { + case 0: + case 1: + case ' ': + case '\t': + case '.': + case '/': + case '\\': + return true; + + default: + return false; + } +} + +WDL_HASSTRINGS_EXPORT bool WDL_hasStringsEx(const char *name, const LineParser *lp, + int (*cmp_func)(const char *a, int apos, const char *b, int blen) // if set, returns length of matched string (0 if no match) + ) +{ + if (!lp) return true; + const int ntok = lp->getnumtokens(); + if (ntok<1) return true; + + char stack[1024]; // &1=not bit, 0x10 = ignoring subscopes, &2= state when 0x10 set + int stacktop = 0; + stack[0]=0; + + const int strlen_name = (int)strlen(name); + char matched_local=-1; // -1 = first eval for scope, 0=did not pass scope, 1=OK, 2=ignore rest of scope + for (int x = 0; x < ntok; x ++) + { + const char *n=lp->gettoken_str(x); + + if (n[0] == '(' && !n[1] && !lp->gettoken_quotingchar(x)) + { + if (!(matched_local&1)) + { + stack[stacktop] |= matched_local | 0x10; + matched_local=2; // ignore subscope + } + else + { + matched_local = -1; // new scope + } + + if (stacktop < (int)sizeof(stack) - 1) stack[++stacktop] = 0; + } + else if (stacktop && n[0] == ')' && !n[1] && !lp->gettoken_quotingchar(x)) + { + if (stack[--stacktop]&0x10) + { + // restore state + matched_local = stack[stacktop]&2; + } + else + { + matched_local = (matched_local != 0 ? 1 : 0) ^ (stack[stacktop]&1); + } + stack[stacktop] = 0; + } + else if (matched_local != 2 && !strcmp(n,"OR")) + { + matched_local = (matched_local > 0) ? 2 : -1; + stack[stacktop] = 0; + } + else if (matched_local&1) // matches 1, -1 + { + int ln; + if (!strcmp(n,"NOT")) + { + stack[stacktop]^=1; + } + else if (!strcmp(n,"AND") && !lp->gettoken_quotingchar(x)) + { + // ignore unquoted uppercase AND + } + else if ((ln=(int)strlen(n))>0) + { + int lt=strlen_name; + const char *t=name; + // ^foo -- string starts (or follows \1 separator with) foo + // foo$ -- string ends with foo (or is immediately followed by \1 separator) + // " foo ", "foo ", " foo" include end of string/start of string has whitespace + int wc_left = 0; // 1=require \1 or start of string, 2=require space or \1 or start + int wc_right = 0; // 1=require \1 or \0, 2 = require space or \1 or \0 + // perhaps wc_left/wc_right of 2 should also match non-alnum characters in addition to space? + if (ln>1) + { + switch (*n) + { + case ' ': + if (*++n != ' ') wc_left=2; + // else { multiple characters of whitespace = literal whitespace search (two spaces requires a single space, etc) } + + ln--; + break; + case '^': + ln--; + n++; + wc_left=1; + break; + } + } + if (ln>1) + { + switch (n[ln-1]) + { + case ' ': + if (n[--ln - 1] != ' ') wc_right=2; + // else { multiple characters of whitespace = literal whitespace search (two spaces requires a single space, etc) } + break; + case '$': + ln--; + wc_right++; + break; + } + } + + bool use_cmp_func = cmp_func != NULL && !(stack[stacktop]&1); + + if (!wc_left && !wc_right && *n) + { + switch (lp->gettoken_quotingchar(x)) + { + case '\'': + case '"': + { // if a quoted string has no whitespace in it, treat as whole word search + const char *p = n; + while (*p && *p != ' ' && *p != '\t') p++; + if (!*p) + { + wc_left=wc_right=2; + use_cmp_func = false; + } + } + break; + } + + } + + const int min_len = use_cmp_func ? 1 : ln; + if (wc_left>0) + { + unsigned char lastchar = 1; + while (lt>=min_len) + { + int lln; + if ((lastchar < 2 || (wc_left>1 && hasStrings_isNonWordChar(lastchar))) && (lln = use_cmp_func ? cmp_func(t,t-name,n,ln) : strnicmp(t,n,ln) ? 0 : ln)) + { + if (wc_right == 0) break; + const unsigned char nc=((const unsigned char*)t)[lln]; + if (nc < 2 || (wc_right > 1 && hasStrings_isNonWordChar(nc))) break; + } + lastchar = *(unsigned char*)t++; + lt--; + } + } + else + { + while (lt>=min_len) + { + const int lln = use_cmp_func ? cmp_func(t,t-name,n,ln) : strnicmp(t,n,ln) ? 0 : ln; + if (lln) + { + if (wc_right == 0) break; + const unsigned char nc=((const unsigned char*)t)[lln]; + if (nc < 2 || (wc_right > 1 && hasStrings_isNonWordChar(nc))) break; + } + t++; + lt--; + } + } + + matched_local = ((lt-min_len)>=0) ^ (stack[stacktop]&1); + stack[stacktop]=0; + } + } + } + while (stacktop > 0) + { + if (stack[--stacktop] & 0x10) matched_local=stack[stacktop]&2; + else matched_local = (matched_local > 0 ? 1 : 0) ^ (stack[stacktop]&1); + } + + return matched_local!=0; +} + +WDL_HASSTRINGS_EXPORT bool WDL_hasStrings(const char *name, const LineParser *lp) +{ + return WDL_hasStringsEx(name,lp,NULL); +} + +WDL_HASSTRINGS_EXPORT bool WDL_makeSearchFilter(const char *flt, LineParser *lp) +{ + if (WDL_NOT_NORMALLY(!lp)) return false; + + if (WDL_NOT_NORMALLY(!flt)) flt=""; + +#ifdef WDL_LINEPARSER_HAS_LINEPARSERINT + if (lp->parse_ex(flt,true,false,true)) // allow unterminated quotes +#else + if (lp->parse_ex(flt,true,false)) +#endif + { + if (*flt) lp->set_one_token(flt); // failed parsing search string, search as a single token + } + + return lp->getnumtokens()>0; +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/heapbuf.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/heapbuf.h new file mode 100644 index 000000000..ffaf40e8b --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/heapbuf.h @@ -0,0 +1,386 @@ +/* + WDL - heapbuf.h + Copyright (C) 2005 and later Cockos Incorporated + + 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. + +*/ + +/* + + This file provides the interface and implementation for WDL_HeapBuf, a simple + malloc() wrapper for resizeable blocks. + + Also in this file is WDL_TypedBuf which is a templated version WDL_HeapBuf + that manages type and type-size. + +*/ + +#ifndef _WDL_HEAPBUF_H_ +#define _WDL_HEAPBUF_H_ + +#ifndef WDL_HEAPBUF_IMPL_ONLY + +#ifdef WDL_HEAPBUF_TRACE +#include +#define WDL_HEAPBUF_TRACEPARM(x) ,(x) +#else +#define WDL_HEAPBUF_TRACEPARM(x) +#endif + +#include "wdltypes.h" + +class WDL_HeapBuf +{ + public: + // interface +#ifdef WDL_HEAPBUF_INTF_ONLY + void *Resize(int newsize, bool resizedown=true); + void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false); +#endif + void *Get() const { return m_size?m_buf:NULL; } // returns NULL if size is 0 + void *GetFast() const { return m_buf; } // returns last buffer if size is 0 + int GetSize() const { return m_size; } + void *GetAligned(int align) const { return (void *)(((UINT_PTR)Get() + (align-1)) & ~(UINT_PTR)(align-1)); } + + void SetGranul(int granul) { m_granul = granul; } + int GetGranul() const { return m_granul; } + + void *ResizeOK(int newsize, bool resizedown = true) { void *p=Resize(newsize, resizedown); return GetSize() == newsize ? p : NULL; } + + WDL_HeapBuf(const WDL_HeapBuf &cp) + { + m_buf=0; + CopyFrom(&cp,true); + } + WDL_HeapBuf &operator=(const WDL_HeapBuf &cp) + { + CopyFrom(&cp,false); + return *this; + } + + + + #ifndef WDL_HEAPBUF_TRACE + explicit WDL_HeapBuf(int granul=4096) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul) + { + } + ~WDL_HeapBuf() + { + free(m_buf); + } + #else + explicit WDL_HeapBuf(int granul=4096, const char *tracetype="WDL_HeapBuf" + ) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul) + { + m_tracetype = tracetype; + char tmp[512]; + wsprintf(tmp,"WDL_HeapBuf: created type: %s granul=%d\n",tracetype,granul); + OutputDebugString(tmp); + } + ~WDL_HeapBuf() + { + char tmp[512]; + wsprintf(tmp,"WDL_HeapBuf: destroying type: %s (alloc=%d, size=%d)\n",m_tracetype,m_alloc,m_size); + OutputDebugString(tmp); + free(m_buf); + } + #endif + +#endif // !WDL_HEAPBUF_IMPL_ONLY + + // implementation bits +#ifndef WDL_HEAPBUF_INTF_ONLY + #ifdef WDL_HEAPBUF_IMPL_ONLY + void *WDL_HeapBuf::Resize(int newsize, bool resizedown) + #else + void *Resize(int newsize, bool resizedown=true) + #endif + { + if (newsize<0) newsize=0; + #ifdef DEBUG_TIGHT_ALLOC // horribly slow, do not use for release builds + if (newsize == m_size) return m_buf; + + int a = newsize; + if (a > m_size) a=m_size; + void *newbuf = newsize ? malloc(newsize) : 0; + if (!newbuf && newsize) + { + #ifdef WDL_HEAPBUF_ONMALLOCFAIL + WDL_HEAPBUF_ONMALLOCFAIL(newsize) + #endif + return m_buf; + } + if (newbuf&&m_buf) memcpy(newbuf,m_buf,a); + m_size=m_alloc=newsize; + free(m_buf); + return m_buf=newbuf; + #endif + + if (newsize!=m_size || (resizedown && newsize < m_alloc/2)) + { + int resizedown_under = 0; + if (resizedown && newsize < m_size) + { + // shrinking buffer: only shrink if allocation decreases to min(alloc/2, alloc-granul*4) or 0 + resizedown_under = m_alloc - (m_granul<<2); + if (resizedown_under > m_alloc/2) resizedown_under = m_alloc/2; + if (resizedown_under < 1) resizedown_under=1; + } + + if (newsize > m_alloc || newsize < resizedown_under) + { + int granul=newsize/2; + int newalloc; + if (granul < m_granul) granul=m_granul; + + if (newsize<1) newalloc=0; + else if (m_granul<4096) newalloc=newsize+granul; + else + { + granul &= ~4095; + if (granul< 4096) granul=4096; + else if (granul>4*1024*1024) granul=4*1024*1024; + newalloc = ((newsize + granul + 96)&~4095)-96; + } + + if (newalloc != m_alloc) + { + + #ifdef WDL_HEAPBUF_TRACE + char tmp[512]; + wsprintf(tmp,"WDL_HeapBuf: type %s realloc(%d) from %d\n",m_tracetype,newalloc,m_alloc); + OutputDebugString(tmp); + #endif + if (newalloc <= 0) + { + free(m_buf); + m_buf=0; + m_alloc=0; + m_size=0; + return 0; + } + void *nbuf=realloc(m_buf,newalloc); + if (!nbuf) + { + if (!(nbuf=malloc(newalloc))) + { + #ifdef WDL_HEAPBUF_ONMALLOCFAIL + WDL_HEAPBUF_ONMALLOCFAIL(newalloc); + #endif + return m_size?m_buf:0; // failed, do not resize + } + + if (m_buf) + { + int sz=newsize0) memcpy(nbuf,m_buf,sz); + free(m_buf); + } + } + + m_buf=nbuf; + m_alloc=newalloc; + } // alloc size change + } // need size up or down + m_size=newsize; + } // size change + return m_size?m_buf:0; + } + + #ifdef WDL_HEAPBUF_IMPL_ONLY + void WDL_HeapBuf::CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig) + #else + void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false) + #endif + { + if (exactCopyOfConfig) // copy all settings + { + free(m_buf); + + #ifdef WDL_HEAPBUF_TRACE + m_tracetype = hb->m_tracetype; + #endif + m_granul = hb->m_granul; + + m_size=m_alloc=0; + m_buf=hb->m_buf && hb->m_alloc>0 ? malloc(m_alloc = hb->m_alloc) : NULL; + #ifdef WDL_HEAPBUF_ONMALLOCFAIL + if (!m_buf && m_alloc) { WDL_HEAPBUF_ONMALLOCFAIL(m_alloc) } ; + #endif + if (m_buf) memcpy(m_buf,hb->m_buf,m_size = hb->m_size); + else m_alloc=0; + } + else // copy just the data + size + { + const int newsz=hb->GetSize(); + Resize(newsz,true); + if (GetSize()!=newsz) Resize(0); + else memcpy(Get(),hb->Get(),newsz); + } + } + +#endif // ! WDL_HEAPBUF_INTF_ONLY + +#ifndef WDL_HEAPBUF_IMPL_ONLY + + private: + void *m_buf; + int m_alloc; + int m_size; + int m_granul; + + #if defined(_WIN64) || defined(__LP64__) + public: + int ___pad; // keep size 8 byte aligned + #endif + + #ifdef WDL_HEAPBUF_TRACE + const char *m_tracetype; + #endif + +}; + +template class WDL_TypedBuf +{ + public: + PTRTYPE *Get() const { return (PTRTYPE *) m_hb.Get(); } + PTRTYPE *GetFast() const { return (PTRTYPE *) m_hb.GetFast(); } + int GetSize() const { return m_hb.GetSize()/(unsigned int)sizeof(PTRTYPE); } + + PTRTYPE *Resize(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.Resize(newsize*sizeof(PTRTYPE),resizedown); } + PTRTYPE *ResizeOK(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.ResizeOK(newsize*sizeof(PTRTYPE), resizedown); } + + PTRTYPE *GetAligned(int align) const { return (PTRTYPE *) m_hb.GetAligned(align); } + + PTRTYPE *Add(PTRTYPE val) + { + const int sz=GetSize(); + PTRTYPE* p=ResizeOK(sz+1,false); + if (p) + { + p[sz]=val; + return p+sz; + } + return NULL; + } + PTRTYPE *Add(const PTRTYPE *buf, int bufsz) + { + if (bufsz>0) + { + const int sz=GetSize(); + PTRTYPE* p=ResizeOK(sz+bufsz,false); + if (p) + { + p+=sz; + if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE)); + else memset((char*)p,0,bufsz*sizeof(PTRTYPE)); + return p; + } + } + return NULL; + } + PTRTYPE *Set(const PTRTYPE *buf, int bufsz) + { + if (bufsz>=0) + { + PTRTYPE* p=ResizeOK(bufsz,false); + if (p) + { + if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE)); + else memset((char*)p,0,bufsz*sizeof(PTRTYPE)); + return p; + } + } + return NULL; + } + PTRTYPE* Insert(PTRTYPE val, int idx) + { + const int sz=GetSize(); + if (idx >= 0 && idx <= sz) + { + PTRTYPE* p=ResizeOK(sz+1,false); + if (p) + { + memmove(p+idx+1, p+idx, (sz-idx)*sizeof(PTRTYPE)); + p[idx]=val; + return p+idx; + } + } + return NULL; + } + + void Delete(int idx) + { + PTRTYPE* p=Get(); + const int sz=GetSize(); + if (idx >= 0 && idx < sz) + { + memmove(p+idx, p+idx+1, (sz-idx-1)*sizeof(PTRTYPE)); + Resize(sz-1,false); + } + } + + void SetGranul(int gran) { m_hb.SetGranul(gran); } + + int Find(PTRTYPE val) const + { + const PTRTYPE* p=Get(); + const int sz=GetSize(); + int i; + for (i=0; i < sz; ++i) if (p[i] == val) return i; + return -1; + } + +#ifndef WDL_HEAPBUF_TRACE + explicit WDL_TypedBuf(int granul=4096) : m_hb(granul) { } +#else + explicit WDL_TypedBuf(int granul=4096, const char *tracetype="WDL_TypedBuf") : m_hb(granul WDL_HEAPBUF_TRACEPARM(tracetype)) { } +#endif + ~WDL_TypedBuf() + { + } + + WDL_HeapBuf *GetHeapBuf() { return &m_hb; } + const WDL_HeapBuf *GetHeapBuf() const { return &m_hb; } + + int DeleteBatch(bool (*proc)(PTRTYPE *p, void *ctx), void *ctx=NULL) // proc returns true to delete item. returns number deleted + { + const int sz = GetSize(); + int cnt=0; + PTRTYPE *rd = Get(), *wr = rd; + for (int x = 0; x < sz; x ++) + { + if (!proc(rd,ctx)) + { + if (rd != wr) *wr=*rd; + wr++; + cnt++; + } + rd++; + } + if (cnt < sz) Resize(cnt,false); + return sz - cnt; + } + + private: + WDL_HeapBuf m_hb; +}; + +#endif // ! WDL_HEAPBUF_IMPL_ONLY + +#endif // _WDL_HEAPBUF_H_ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice.cpp new file mode 100644 index 000000000..d1a7c3000 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice.cpp @@ -0,0 +1,3058 @@ +/* + Cockos WDL - LICE - Lightweight Image Compositing Engine + Copyright (C) 2007 and later, Cockos Incorporated + File: lice.cpp (LICE core processing) + See lice.h for license and other information +*/ + + +#ifndef __LICE_CPP_IMPLEMENTED__ +#define __LICE_CPP_IMPLEMENTED__ + +#ifndef WDL_NO_DEFINE_MINMAX +#define WDL_NO_DEFINE_MINMAX +#endif +#include "lice.h" +#include +#include // only included in case we need to debug with sprintf etc + +#include "lice_combine.h" +#include "lice_extended.h" + +#ifndef _WIN32 +#include "../swell/swell.h" +#endif + +#define IGNORE_SCALING(mode) ((mode)&LICE_BLIT_IGNORE_SCALING) + +#define DO_RECT_SC(mode) \ + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); \ + if (__sc>0) { \ + if (!IGNORE_SCALING(mode)) { \ + __LICE_SC(x); \ + __LICE_SC(y); \ + __LICE_SCU(w); \ + __LICE_SCU(h); \ + } \ + __LICE_SCU(destbm_w); \ + __LICE_SCU(destbm_h); \ + } + + + +_LICE_ImageLoader_rec *LICE_ImageLoader_list; + +LICE_pixel LICE_CombinePixels(LICE_pixel dest, LICE_pixel src, float alpha, int mode) +{ + int r = LICE_GETR(src); + int g = LICE_GETG(src); + int b = LICE_GETB(src); + int a = LICE_GETA(src); + int al = (int)(alpha*256.0f); + +#define __LICE__ACTION(COMBFUNC) COMBFUNC::doPix((LICE_pixel_chan*)&dest,r, g, b, a, al) + __LICE_ACTION_SRCALPHA(mode, al,false); +#undef __LICE__ACTION + + return dest; +} + + +void LICE_CombinePixels2(LICE_pixel *destptr, int r, int g, int b, int a, int ia, int mode) +{ +#define __LICE__ACTION(COMBFUNC) COMBFUNC::doPix((LICE_pixel_chan*)destptr,r, g, b, a, ia) + __LICE_ACTION_SRCALPHA(mode, ia, false); +#undef __LICE__ACTION +} +void LICE_CombinePixels2Clamp(LICE_pixel *destptr, int r, int g, int b, int a, int ia, int mode) +{ +#define __LICE__ACTION(COMBFUNC) COMBFUNC::doPix((LICE_pixel_chan*)destptr,r, g, b, a, ia) + __LICE_ACTION_SRCALPHA(mode, ia, true); +#undef __LICE__ACTION +} + + +LICE_MemBitmap::LICE_MemBitmap(int w, int h, unsigned int linealign) +{ + m_allocsize=0; + m_fb=0; + m_width=0; + m_height=0; + m_linealign = linealign > 1 ? ((linealign & ~(linealign-1))-1) : 0; // force to be contiguous bits + if (m_linealign>16) m_linealign=16; + if (w>0&&h>0) __resize(w,h); +} + +LICE_MemBitmap::~LICE_MemBitmap() { free(m_fb); } + +bool LICE_MemBitmap::__resize(int w, int h) +{ + if (w!=m_width||h!=m_height) + { +#ifdef DEBUG_TIGHT_ALLOC // dont enable for anything you want to be even remotely fast + free(m_fb); + m_fb = (LICE_pixel *)malloc((m_allocsize = ((w+m_linealign)&~m_linealign)*h*sizeof(LICE_pixel)) + LICE_MEMBITMAP_ALIGNAMT); + m_width=m_fb?w:0; + m_height=m_fb?h:0; + return true; +#endif + int sz=(((m_width=w)+m_linealign)&~m_linealign)*(m_height=h)*sizeof(LICE_pixel); + + if (sz<=0||w<1||h<1) { free(m_fb); m_fb=0; m_allocsize=0; } + else if (!m_fb) m_fb=(LICE_pixel*)malloc((m_allocsize=sz) + LICE_MEMBITMAP_ALIGNAMT); + else + { + if (sz>m_allocsize) + { + void *op=m_fb; + if (!(m_fb=(LICE_pixel*)realloc(m_fb,(m_allocsize=sz+sz/4)+LICE_MEMBITMAP_ALIGNAMT))) + { + free(op); + m_fb=(LICE_pixel*)malloc((m_allocsize=sz)+LICE_MEMBITMAP_ALIGNAMT); + } + } + } + if (!m_fb) {m_width=m_height=0; } + + return true; + } + return false; +} + + + +#ifndef _LICE_NO_SYSBITMAPS_ + +LICE_SysBitmap::LICE_SysBitmap(int w, int h) +{ + m_allocw=m_alloch=0; +#ifdef _WIN32 + m_dc = CreateCompatibleDC(NULL); + m_bitmap = 0; + m_oldbitmap = 0; +#else + m_dc=0; +#endif + m_bits=0; + m_width=m_height=0; + m_adv_scaling=0; + m_draw_scaling=0; + + __resize(w,h); +} + + +LICE_SysBitmap::~LICE_SysBitmap() +{ +#ifdef _WIN32 + if (m_oldbitmap && m_dc) SelectObject(m_dc,m_oldbitmap); + if (m_bitmap) DeleteObject(m_bitmap); + if (m_dc) DeleteDC(m_dc); +#else + if (m_dc) + SWELL_DeleteGfxContext(m_dc); +#endif +} + +bool LICE_SysBitmap::__resize(int w, int h) +{ +#ifdef _WIN32 + if (!m_dc) { m_width=m_height=0; m_bits=0; return false; } +#endif + + if (m_width==w && m_height == h) return false; + + m_width=w; + m_height=h; + + if (m_draw_scaling > 0) + { + w = (w * m_draw_scaling) >> 8; + h = (h * m_draw_scaling) >> 8; + } + w = (w+3)&~3; // always keep backing store a multiple of 4px wide + +#ifndef DEBUG_TIGHT_ALLOC + // dont resize down bitmaps + if (w && h && w <= m_allocw && h <= m_alloch && m_bits) + { +#ifndef _WIN32 + if (isFlipped()) + { + m_bits=(LICE_pixel*)SWELL_GetCtxFrameBuffer(m_dc); + m_bits += (m_alloch-h)*m_allocw; + } +#endif + return true; + } +#endif//!DEBUG_TIGHT_ALLOC + + m_allocw=w; + m_alloch=h; + +#ifdef _WIN32 + if (m_oldbitmap) + { + SelectObject(m_dc,m_oldbitmap); + m_oldbitmap=0; + } + if (m_bitmap) DeleteObject(m_bitmap); + m_bitmap=0; + m_bits=0; + + + if (w<1 || h<1) return false; + + BITMAPINFO pbmInfo = {0,}; + pbmInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmInfo.bmiHeader.biWidth = w; + pbmInfo.bmiHeader.biHeight = isFlipped()?h:-h; + pbmInfo.bmiHeader.biPlanes = 1; + pbmInfo.bmiHeader.biBitCount = sizeof(LICE_pixel)*8; + pbmInfo.bmiHeader.biCompression = BI_RGB; + m_bitmap = CreateDIBSection( NULL, &pbmInfo, DIB_RGB_COLORS, (void **)&m_bits, NULL, 0); + + if (m_bitmap) m_oldbitmap=SelectObject(m_dc, m_bitmap); + else { m_width=m_height=0; m_bits=0; } +#else + if (m_dc) SWELL_DeleteGfxContext(m_dc); + m_dc=0; + m_bits=0; + + if (w<1 || h<1) return false; + + m_dc=SWELL_CreateMemContext(0,w,h); + if (!m_dc) { m_width=m_height=0; m_bits=0; } + else m_bits=(LICE_pixel*)SWELL_GetCtxFrameBuffer(m_dc); +#endif + + return true; +} + +#endif // _LICE_NO_SYSBITMAPS_ + + + +#ifndef LICE_NO_BLIT_SUPPORT +void LICE_Copy(LICE_IBitmap *dest, LICE_IBitmap *src) // resizes dest +{ + if (src&&dest) + { + dest->resize(src->getWidth(),src->getHeight()); + LICE_Blit(dest,src,0,0,NULL,1.0,LICE_BLIT_MODE_COPY); + } +} +#endif + +template class _LICE_Template_Blit0 // these always templated +{ + public: + static void solidBlitFAST(LICE_pixel *dest, int w, int h, + LICE_pixel color, + int dest_span) + { + while (h--) + { + LICE_pixel *pout=dest; + int n=w; + while (n--) + { + COMBFUNC::doPixFAST(pout,color); + ++pout; + } + dest+=dest_span; + } + } + + + + static void scaleBlitFAST(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, + int icurx, int icury, int idx, int idy, unsigned int clipright, unsigned int clipbottom, + int src_span, int dest_span) + { + LICE_pixel* destpx = (LICE_pixel*) dest; + int destpxspan = dest_span*(int)sizeof(LICE_pixel_chan)/(int)sizeof(LICE_pixel); + + while (h--) + { + const unsigned int cury = icury >> 16; + if (cury < clipbottom) + { + int curx=icurx; + const LICE_pixel_chan *inptr=src + cury * src_span; + LICE_pixel* pout = destpx; + int n=w; + while (n--) + { + const unsigned int offs=curx >> 16; + if (offs +#endif +class _LICE_Template_Blit1 // these controlled by LICE_FAVOR_SIZE_EXTREME +{ +#ifdef LICE_FAVOR_SIZE_EXTREME + #define DOPIX(pout,r,g,b,a,ia) combFunc(pout,r,g,b,a,ia); +#else + #define DOPIX(pout,r,g,b,a,ia) COMBFUNC::doPix(pout,r,g,b,a,ia); +#endif + public: + static void solidBlit(LICE_pixel_chan *dest, int w, int h, + int ir, int ig, int ib, int ia, + int dest_span +#ifdef LICE_FAVOR_SIZE_EXTREME + , LICE_COMBINEFUNC combFunc +#endif + ) + { + while (h--) + { + LICE_pixel_chan *pout=dest; + int n=w; + while (n--) + { + DOPIX(pout,ir,ig,ib,ia,ia); + pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan); + } + dest+=dest_span; + } + } + static void gradBlit(LICE_pixel_chan *dest, int w, int h, + int ir, int ig, int ib, int ia, + int drdx, int dgdx, int dbdx, int dadx, + int drdy, int dgdy, int dbdy, int dady, + int dest_span +#ifdef LICE_FAVOR_SIZE_EXTREME + , LICE_COMBINEFUNC combFunc +#endif + ) + { + while (h--) + { + int r=ir,g=ig,b=ib,a=ia; + ir+=drdy; ig+=dgdy; ib+=dbdy; ia+=dady; + LICE_pixel_chan *pout=dest; + int n=w; + while (n--) + { + const int aa=a/65536; + DOPIX(pout,r/65536,g/65536,b/65536,aa,aa); + pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan); + r+=drdx; g+=dgdx; b+=dbdx; a+=dadx; + } + dest+=dest_span; + } + } + +#undef DOPIX +}; + + +#ifndef LICE_FAVOR_SIZE +template +#endif +class _LICE_Template_Blit2 // these controlled by LICE_FAVOR_SIZE +{ +#ifdef LICE_FAVOR_SIZE + #define DOPIX(pout,r,g,b,a,ia) combFunc(pout,r,g,b,a,ia); +#else + #define DOPIX(pout,r,g,b,a,ia) COMBFUNC::doPix(pout,r,g,b,a,ia); +#endif + + public: + + static void blit(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, int src_span, int dest_span, int ia +#ifdef LICE_FAVOR_SIZE + , LICE_COMBINEFUNC combFunc +#endif + ) + { + while (h-->0) + { + int n=w; + const LICE_pixel_chan *pin=src; + LICE_pixel_chan *pout=dest; + while (n--) + { + + DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],ia); + + pin += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan); + pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan); + } + dest+=dest_span; + src += src_span; + } + } + + // this is only designed for filtering down an image approx 2:1 to 4:1 or so.. it'll work (poortly) for higher, and for less it's crap too. + // probably need to redo it using linear interpolation of the filter coefficients, but bleh I'm gonna go play some call of duty + static void scaleBlitFilterDown(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, + int icurx, int icury, int idx, int idy, int clipright, int clipbottom, + int src_span, int dest_span, int ia, const int *filter, int filt_start, int filtsz +#ifdef LICE_FAVOR_SIZE + , LICE_COMBINEFUNC combFunc +#endif + + ) + { + + while (h--) + { + int cury = icury >> 16; + int curx=icurx; + int n=w; + if (cury >= 0 && cury < clipbottom) + { + const LICE_pixel_chan *inptr=src + (cury+filt_start) * src_span; + LICE_pixel_chan *pout=dest; + while (n--) + { + int offs=curx >> 16; + if (offs>=0 && offs= clipbottom) break; + + if (ypos >= 0) + { + int xpos=offs + filt_start; + const LICE_pixel_chan *pin = rdptr; + int fx=filtsz; + while (fx--) + { + int tsc = *scaletab++; + if (xpos >= 0 && xpos < clipright) + { + r+=pin[LICE_PIXEL_R]*tsc; + g+=pin[LICE_PIXEL_G]*tsc; + b+=pin[LICE_PIXEL_B]*tsc; + a+=pin[LICE_PIXEL_A]*tsc; + sc+=tsc; + } + xpos++; + pin+=sizeof(LICE_pixel)/sizeof(LICE_pixel_chan); + } + } + else scaletab += filtsz; + + ypos++; + rdptr+=src_span; + } + if (sc>0) + { + DOPIX(pout,r/sc,g/sc,b/sc,a/sc,ia); + } + } + + pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan); + curx+=idx; + } + } + dest+=dest_span; + icury+=idy; + } + } + static void scaleBlit(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, + int icurx, int icury, int idx, int idy, unsigned int clipright, unsigned int clipbottom, + int src_span, int dest_span, int ia, int filtermode +#ifdef LICE_FAVOR_SIZE + , LICE_COMBINEFUNC combFunc +#endif + + ) + { + + if (filtermode == LICE_BLIT_FILTER_BILINEAR) + { + while (h--) + { + const unsigned int cury = icury >> 16; + const int yfrac=icury&65535; + int curx=icurx; + const LICE_pixel_chan *inptr=src + cury * src_span; + LICE_pixel_chan *pout=dest; + int n=w; + if (cury < clipbottom-1) + { + while (n--) + { + const unsigned int offs=curx >> 16; + const LICE_pixel_chan *pin = inptr + offs*sizeof(LICE_pixel); + if (offs> 16; + const LICE_pixel_chan *pin = inptr + offs*sizeof(LICE_pixel); + if (offs> 16; + if (cury < clipbottom) + { + int curx=icurx; + const LICE_pixel_chan *inptr=src + cury * src_span; + LICE_pixel_chan *pout=dest; + int n=w; + while (n--) + { + const unsigned int offs=curx >> 16; + if (offs +#endif +class _LICE_Template_Blit3 // stuff controlled by LICE_FAVOR_SPEED +{ +#ifndef LICE_FAVOR_SPEED + #define DOPIX(pout,r,g,b,a,ia) combFunc(pout,r,g,b,a,ia); +#else + #define DOPIX(pout,r,g,b,a,ia) COMBFUNC::doPix(pout,r,g,b,a,ia); +#endif + + public: + + static void deltaBlit(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, + int isrcx, int isrcy, int idsdx, int idtdx, int idsdy, int idtdy, + int idsdxdy, int idtdxdy, + unsigned int src_right, unsigned int src_bottom, + int src_span, int dest_span, int ia, int filtermode +#ifndef LICE_FAVOR_SPEED + , LICE_COMBINEFUNC combFunc +#endif + ) + { + if (filtermode == LICE_BLIT_FILTER_BILINEAR) + { + while (h--) + { + int thisx=isrcx; + int thisy=isrcy; + LICE_pixel_chan *pout=dest; + int n=w; + while (n--) + { + const unsigned int cury = thisy >> 16; + const unsigned int curx = thisx >> 16; + if (cury < src_bottom-1) + { + if (curx < src_right-1) + { + const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel); + int r,g,b,a; + + __LICE_BilinearFilterI(&r,&g,&b,&a,pin,pin+src_span,thisx&65535,thisy&65535); + + DOPIX(pout,r,g,b,a,ia); + } + else if (curx==src_right-1) + { + + const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel); + int r,g,b,a; + + __LICE_LinearFilterI(&r,&g,&b,&a,pin,pin+src_span,thisy&65535); + DOPIX(pout,r,g,b,a,ia); + } + } + else if (cury==src_bottom-1) + { + if (curx> 16; + const unsigned int curx = thisx >> 16; + if (cury < src_bottom && curx < src_right) + { + const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel); + + DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],ia); + } + + pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan); + thisx+=idsdx; + thisy+=idtdx; + } + idsdx+=idsdxdy; + idtdx+=idtdxdy; + isrcx+=idsdy; + isrcy+=idtdy; + dest+=dest_span; + } + } + } + +#undef DOPIX +}; + +#ifdef LICE_FAVOR_SPEED +template +#endif +class _LICE_Template_Blit4 // stuff controlled by LICE_FAVOR_SPEED +{ +#ifndef LICE_FAVOR_SPEED + #define DOPIX(pout,r,g,b,a,ia) combFunc(pout,r,g,b,a,ia); +#else + #define DOPIX(pout,r,g,b,a,ia) COMBFUNC::doPix(pout,r,g,b,a,ia); +#endif + + public: + + static void deltaBlitAlpha(LICE_pixel_chan *dest, const LICE_pixel_chan *src, int w, int h, + int isrcx, int isrcy, int idsdx, int idtdx, int idsdy, int idtdy, + int idsdxdy, int idtdxdy, + unsigned int src_right, unsigned int src_bottom, + int src_span, int dest_span, int ia, int idadx, int idady, int idadxdy, int filtermode +#ifndef LICE_FAVOR_SPEED + , LICE_COMBINEFUNC combFunc +#endif + ) + { + if (filtermode == LICE_BLIT_FILTER_BILINEAR) + { + while (h--) + { + int thisx=isrcx; + int thisy=isrcy; + int thisa=ia; + LICE_pixel_chan *pout=dest; + int n=w; + while (n--) + { + const unsigned int cury = thisy >> 16; + const unsigned int curx = thisx >> 16; + if (cury < src_bottom-1) + { + if (curx < src_right-1) + { + const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel); + int r,g,b,a; + + __LICE_BilinearFilterI(&r,&g,&b,&a,pin,pin+src_span,thisx&65535,thisy&65535); + + DOPIX(pout,r,g,b,a,thisa>>8); + } + else if (curx==src_right-1) + { + + const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel); + int r,g,b,a; + + __LICE_LinearFilterI(&r,&g,&b,&a,pin,pin+src_span,thisy&65535); + DOPIX(pout,r,g,b,a,thisa>>8); + } + } + else if (cury==src_bottom-1) + { + if (curx>8); + } + else if (curx==src_right-1) + { + const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel); + DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],thisa>>8); + } + } + + pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan); + thisx+=idsdx; + thisy+=idtdx; + thisa+=idadx; + } + idsdx+=idsdxdy; + idtdx+=idtdxdy; + idadx+=idadxdy; + isrcx+=idsdy; + isrcy+=idtdy; + ia += idady; + dest+=dest_span; + } + } + else + { + while (h--) + { + int thisx=isrcx; + int thisy=isrcy; + int thisa=ia; + LICE_pixel_chan *pout=dest; + int n=w; + while (n--) + { + const unsigned int cury = thisy >> 16; + const unsigned int curx = thisx >> 16; + if (cury < src_bottom && curx < src_right) + { + const LICE_pixel_chan *pin = src + cury * src_span + curx*sizeof(LICE_pixel); + + DOPIX(pout,pin[LICE_PIXEL_R],pin[LICE_PIXEL_G],pin[LICE_PIXEL_B],pin[LICE_PIXEL_A],thisa>>8); + } + + pout += sizeof(LICE_pixel)/sizeof(LICE_pixel_chan); + thisx+=idsdx; + thisy+=idtdx; + thisa+=idadx; + } + idsdx+=idsdxdy; + idtdx+=idtdxdy; + idadx+=idadxdy; + isrcx+=idsdy; + isrcy+=idtdy; + ia+=idady; + dest+=dest_span; + } + } + } + +#undef DOPIX +}; + + + +#ifndef LICE_NO_GRADIENT_SUPPORT + +void LICE_GradRect(LICE_IBitmap *dest, int dstx, int dsty, int dstw, int dsth, + float ir, float ig, float ib, float ia, + float drdx, float dgdx, float dbdx, float dadx, + float drdy, float dgdy, float dbdy, float dady, + int mode) +{ + if (!dest) return; + + ir*=255.0; ig*=255.0; ib*=255.0; ia*=256.0; + drdx*=255.0; dgdx*=255.0; dbdx*=255.0; dadx*=256.0; + drdy*=255.0; dgdy*=255.0; dbdy*=255.0; dady*=256.0; + // dont scale alpha + + // clip to output + if (dstx < 0) + { + ir-=dstx*drdx; ig-=dstx*dgdx; ib-=dstx*dbdx; ia-=dstx*dadx; + dstw+=dstx; + dstx=0; + } + if (dsty < 0) + { + ir -= dsty*drdy; ig-=dsty*dgdy; ib -= dsty*dbdy; ia -= dsty*dady; + dsth += dsty; + dsty=0; + } + + int dest_span=dest->getRowSpan()*sizeof(LICE_pixel); + LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits(); + + const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + + if (!pdest || !dest_span || dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return; + + if (dstw > destbm_w-dstx) dstw = destbm_w-dstx; + if (dsth > destbm_h-dsty) dsth = destbm_h-dsty; + + if (dest->isFlipped()) + { + pdest += (destbm_h-dsty - 1)*dest_span; + dest_span=-dest_span; + } + else + { + pdest += dsty*dest_span; + } + pdest+=dstx*sizeof(LICE_pixel); +#define TOFIX(a) ((int)((a)*65536.0)) + + int iir=TOFIX(ir), iig=TOFIX(ig), iib=TOFIX(ib), iia=TOFIX(ia), idrdx=TOFIX(drdx),idgdx=TOFIX(dgdx),idbdx=TOFIX(dbdx),idadx=TOFIX(dadx), + idrdy=TOFIX(drdy), idgdy=TOFIX(dgdy), idbdy=TOFIX(dbdy), idady=TOFIX(dady); + + +#ifdef LICE_FAVOR_SIZE_EXTREME + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc=comb::doPix; +#else + + #define __LICE__ACTION(comb) _LICE_Template_Blit1::gradBlit(pdest,dstw,dsth,iir,iig,iib,iia,idrdx,idgdx,idbdx,idadx,idrdy,idgdy,idbdy,idady,dest_span) +#endif + + // todo: could predict whether or not the colors will ever go out of 0.255 range and optimize + + if ((mode & LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY && iia==65536 && idady==0 && idadx == 0) + { + __LICE__ACTION(_LICE_CombinePixelsClobberClamp); + } + else + { + __LICE_ACTION_NOSRCALPHA(mode,256,true); + } + #undef __LICE__ACTION + +#ifdef LICE_FAVOR_SIZE_EXTREME + if (blitfunc) _LICE_Template_Blit1::gradBlit(pdest,dstw,dsth,iir,iig,iib,iia,idrdx,idgdx,idbdx,idadx,idrdy,idgdy,idbdy,idady,dest_span,blitfunc); +#endif + +#undef TOFIX +} + +#endif + + +#ifndef LICE_NO_BLIT_SUPPORT + +static void LICE_BlitInt(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, const RECT *srcrect, float alpha, int mode, bool allowSc) +{ + if (!dest || !src || !alpha) return; + + int srcbm_w = src->getWidth(), srcbm_h = src->getHeight(); + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + int __sc2 = (int)src->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc2 > 0) + { + const int __sc = __sc2; + __LICE_SCU(srcbm_w); + __LICE_SCU(srcbm_h); + } + + RECT sr={0,0,srcbm_w, srcbm_h}; + if (srcrect) + { + sr=*srcrect; + if (sr.left < 0) { dstx-=sr.left; sr.left=0; } + if (sr.top < 0) { dsty-=sr.top; sr.top=0; } + if (sr.right > srcbm_w) sr.right=srcbm_w; + if (sr.bottom > srcbm_h) sr.bottom=srcbm_h; + } + + int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (allowSc && __sc != __sc2 && !IGNORE_SCALING(mode)) + { + const int w = sr.right-sr.left, h=sr.bottom-sr.top; + LICE_ScaledBlit(dest,src,dstx,dsty,w,h, sr.left,sr.top,w,h,alpha,mode); + return; + } + if (__sc>0) + { + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(dstx); + __LICE_SC(dsty); + } + } + if (__sc2>0 && !IGNORE_SCALING(mode)) + { + __sc=__sc2; + __LICE_SC(sr.left); + __LICE_SC(sr.right); + __LICE_SC(sr.top); + __LICE_SC(sr.bottom); + } + + + // clip to output + if (dstx < 0) { sr.left -= dstx; dstx=0; } + if (dsty < 0) { sr.top -= dsty; dsty=0; } + + if (sr.left < 0 || sr.top < 0) return; // overflow check + if (sr.right <= sr.left || sr.bottom <= sr.top || dstx >= destbm_w || dsty >= destbm_h) return; + + if (sr.right > sr.left + (destbm_w-dstx)) sr.right = sr.left + (destbm_w-dstx); + if (sr.bottom > sr.top + (destbm_h-dsty)) sr.bottom = sr.top + (destbm_h-dsty); + + // ignore blits that are 0 + if (sr.right <= sr.left || sr.bottom <= sr.top) return; + +#ifndef DISABLE_LICE_EXTENSIONS + if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_BLIT_ACCEL)) + { + LICE_Ext_Blit_acceldata data(src, dstx, dsty, + sr.left, sr.top, + sr.right-sr.left, sr.bottom-sr.top, + alpha, mode); + if (dest->Extended(LICE_EXT_BLIT_ACCEL, &data)) return; + } +#endif + + + int dest_span=dest->getRowSpan()*sizeof(LICE_pixel); + int src_span=src->getRowSpan()*sizeof(LICE_pixel); + const LICE_pixel_chan *psrc = (LICE_pixel_chan *)src->getBits(); + LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits(); + if (!psrc || !pdest) return; + + if (src->isFlipped()) + { + psrc += (src->getHeight()-sr.top - 1)*src_span; + src_span=-src_span; + } + else psrc += sr.top*src_span; + psrc += sr.left*sizeof(LICE_pixel); + + if (dest->isFlipped()) + { + pdest += (destbm_h-dsty - 1)*dest_span; + dest_span=-dest_span; + } + else pdest += dsty*dest_span; + pdest+=dstx*sizeof(LICE_pixel); + + int i=sr.bottom-sr.top; + int cpsize=sr.right-sr.left; + + if ((mode&LICE_BLIT_MODE_MASK) >= LICE_BLIT_MODE_CHANCOPY && (mode&LICE_BLIT_MODE_MASK) < LICE_BLIT_MODE_CHANCOPY+0x10) + { + while (i-->0) + { + LICE_pixel_chan *o=pdest+((mode>>2)&3); + const LICE_pixel_chan *in=psrc+(mode&3); + int a=cpsize; + while (a--) + { + *o=*in; + o+=sizeof(LICE_pixel); + in+=sizeof(LICE_pixel); + } + pdest+=dest_span; + psrc += src_span; + } + } + // special fast case for copy with no source alpha and alpha=1.0 or 0.5 + else if ((mode&(LICE_BLIT_MODE_MASK|LICE_BLIT_USE_ALPHA))==LICE_BLIT_MODE_COPY && (alpha==1.0||alpha==0.5)) + { + if (alpha==0.5) + { + while (i-->0) + { + int a=cpsize; + const LICE_pixel *rd = (LICE_pixel *)psrc; + LICE_pixel *wr = (LICE_pixel *)pdest; + while (a-->0) + { + *wr = ((*wr>>1)&0x7f7f7f7f)+((*rd++>>1)&0x7f7f7f7f); + wr++; + } + + pdest+=dest_span; + psrc += src_span; + } + } + else + { + while (i-->0) + { + memmove(pdest,psrc,cpsize*sizeof(LICE_pixel)); + pdest+=dest_span; + psrc += src_span; + } + } + } + else + { + int ia=(int)(alpha*256.0); + #ifdef LICE_FAVOR_SIZE + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc=comb::doPix; + #else + #define __LICE__ACTION(comb) _LICE_Template_Blit2::blit(pdest,psrc,cpsize,i,src_span,dest_span,ia) + #endif + + __LICE_ACTION_SRCALPHA(mode,ia,false); + + #undef __LICE__ACTION + + #ifdef LICE_FAVOR_SIZE + if (blitfunc) _LICE_Template_Blit2::blit(pdest,psrc,cpsize,i,src_span,dest_span,ia,blitfunc); + #endif + } +} + +void LICE_Blit(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, const RECT *srcrect, float alpha, int mode) +{ + LICE_BlitInt(dest,src,dstx,dsty,srcrect,alpha,mode,true); +} + +void LICE_Blit(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int srcx, int srcy, int srcw, int srch, float alpha, int mode) +{ + RECT r={srcx,srcy,srcx+srcw,srcy+srch}; + LICE_BlitInt(dest,src,dstx,dsty,&r,alpha,mode,true); +} + +#endif + +#ifndef LICE_NO_BLUR_SUPPORT + +void LICE_Blur(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int srcx, int srcy, int srcw, int srch) // src and dest can overlap, however it may look fudgy if they do +{ + if (!dest || !src) return; + + RECT sr={srcx,srcy,srcx+srcw,srcy+srch}; + if (sr.left < 0) sr.left=0; + if (sr.top < 0) sr.top=0; + if (sr.right > src->getWidth()) sr.right=src->getWidth(); + if (sr.bottom > src->getHeight()) sr.bottom = src->getHeight(); + + // clip to output + if (dstx < 0) { sr.left -= dstx; dstx=0; } + if (dsty < 0) { sr.top -= dsty; dsty=0; } + + if (sr.left < 0 || sr.top < 0) return; // overflow check + + const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + if (sr.right <= sr.left || sr.bottom <= sr.top || dstx >= destbm_w || dsty >= destbm_h) return; + + if (sr.right > sr.left + (destbm_w-dstx)) sr.right = sr.left + (destbm_w-dstx); + if (sr.bottom > sr.top + (destbm_h-dsty)) sr.bottom = sr.top + (destbm_h-dsty); + + // ignore blits that are smaller than 2x2 + if (sr.right <= sr.left+1 || sr.bottom <= sr.top+1) return; + + int dest_span=dest->getRowSpan(); + int src_span=src->getRowSpan(); + const LICE_pixel *psrc = (LICE_pixel *)src->getBits(); + LICE_pixel *pdest = (LICE_pixel *)dest->getBits(); + if (!psrc || !pdest) return; + + if (src->isFlipped()) + { + psrc += (src->getHeight()-sr.top - 1)*src_span; + src_span=-src_span; + } + else psrc += sr.top*src_span; + psrc += sr.left; + + if (dest->isFlipped()) + { + pdest += (destbm_h-dsty - 1)*dest_span; + dest_span=-dest_span; + } + else pdest += dsty*dest_span; + pdest+=dstx; + + LICE_pixel *tmpbuf=NULL; + int w=sr.right-sr.left; + + // buffer to save the last unprocessed lines for the cases where blurring from a bitmap to itself + LICE_pixel turdbuf[2048]; + if (src==dest) + { + if (w <= (int) (sizeof(turdbuf)/sizeof(turdbuf[0])/2)) tmpbuf=turdbuf; + else tmpbuf=(LICE_pixel*)malloc(w*2*sizeof(LICE_pixel)); + } + + int i; + for (i = sr.top; i < sr.bottom; i ++) + { + if (tmpbuf) + memcpy(tmpbuf+((i&1)?w:0),psrc,w*sizeof(LICE_pixel)); + + if (i==sr.top || i==sr.bottom-1) + { + const LICE_pixel *psrc2=psrc+(i==sr.top ? src_span : -src_span); + + LICE_pixel lp; + + pdest[0] = LICE_PIXEL_HALF(lp=psrc[0]) + + LICE_PIXEL_QUARTER(psrc[1]) + + LICE_PIXEL_QUARTER(psrc2[0]); + int x; + for (x = 1; x < w-1; x ++) + { + LICE_pixel tp; + pdest[x] = LICE_PIXEL_HALF(tp=psrc[x]) + + LICE_PIXEL_QUARTER(psrc2[x]) + + LICE_PIXEL_EIGHTH(psrc[x+1]) + + LICE_PIXEL_EIGHTH(lp); + lp=tp; + } + pdest[x] = LICE_PIXEL_HALF(psrc[x]) + + LICE_PIXEL_QUARTER(lp) + + LICE_PIXEL_QUARTER(psrc2[x]); + } + else + { + const LICE_pixel *psrc2=psrc-src_span; + const LICE_pixel *psrc3=psrc+src_span; + if (tmpbuf) + psrc2=tmpbuf + ((i&1) ? 0 : w); + + LICE_pixel lp; + pdest[0] = LICE_PIXEL_HALF(lp=psrc[0]) + + LICE_PIXEL_QUARTER(psrc[1]) + + LICE_PIXEL_EIGHTH(psrc2[0]) + + LICE_PIXEL_EIGHTH(psrc3[0]); + int x; + for (x = 1; x < w-1; x ++) + { + LICE_pixel tp; + pdest[x] = LICE_PIXEL_HALF(tp=psrc[x]) + + LICE_PIXEL_EIGHTH(psrc[x+1]) + + LICE_PIXEL_EIGHTH(lp) + + LICE_PIXEL_EIGHTH(psrc2[x]) + + LICE_PIXEL_EIGHTH(psrc3[x]); + lp=tp; + } + pdest[x] = LICE_PIXEL_HALF(psrc[x]) + + LICE_PIXEL_QUARTER(lp) + + LICE_PIXEL_EIGHTH(psrc2[x]) + + LICE_PIXEL_EIGHTH(psrc3[x]); + } + pdest+=dest_span; + psrc += src_span; + } + if (tmpbuf && tmpbuf != turdbuf) + free(tmpbuf); +} + +#endif + +#ifndef LICE_NO_BLIT_SUPPORT +void LICE_ScaledBlit(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + float srcx, float srcy, float srcw, float srch, + float alpha, int mode) +{ + if (!dest || !src || !dstw || !dsth || !alpha) return; + + int srcbm_w = src->getWidth(), srcbm_h = src->getHeight(); + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + const float srcx_orig = srcx, srcy_orig = srcy, srcw_orig = srcw, srch_orig = srch; + const int dstx_orig = dstx, dsty_orig = dsty; + if (__sc>0) + { + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(dstx); + __LICE_SC(dsty); + __LICE_SC(dstw); + __LICE_SC(dsth); + } + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + } + __sc = (int)src->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(srcx); + __LICE_SC(srcy); + __LICE_SC(srcw); + __LICE_SC(srch); + } + __LICE_SCU(srcbm_w); + __LICE_SCU(srcbm_h); + } + + // non-scaling optimized omde + if (fabs(srcw-dstw)<0.001 && fabs(srch-dsth)<0.001) + { + // and if not bilinear filtering, or + // the source coordinates are near their integer counterparts + if ((mode&LICE_BLIT_FILTER_MASK)!=LICE_BLIT_FILTER_BILINEAR || + (fabs(srcx-floor(srcx+0.5f))<0.03 && fabs(srcy-floor(srcy+0.5f))<0.03)) + { + RECT sr={(int)(srcx_orig+0.5f),(int)(srcy_orig+0.5f),}; + sr.right=sr.left+(int) (srcw_orig+0.5); + sr.bottom=sr.top+(int) (srch_orig+0.5); + LICE_BlitInt(dest,src,dstx_orig,dsty_orig,&sr,alpha,mode,false); + return; + } + } + +#ifndef DISABLE_LICE_EXTENSIONS + if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_SCALEDBLIT_ACCEL)) + { + LICE_Ext_ScaledBlit_acceldata data(src, dstx, dsty, dstw, dsth, srcx, srcy, srcw, srch, alpha, mode); + if (dest->Extended(LICE_EXT_SCALEDBLIT_ACCEL, &data)) return; + } +#endif + + if (dstw<0) + { + dstw=-dstw; + dstx-=dstw; + srcx+=srcw; + srcw=-srcw; + } + if (dsth<0) + { + dsth=-dsth; + dsty-=dsth; + srcy+=srch; + srch=-srch; + } + + double xadvance = srcw / dstw; + double yadvance = srch / dsth; + + int clip_r=(int)(srcx+lice_max(srcw,0)+0.999999); // this rounding logic is shit, but we're stuck with it + int clip_b=(int)(srcy+lice_max(srch,0)+0.999999); + if (clip_r>srcbm_w) clip_r=srcbm_w; + if (clip_b>srcbm_h) clip_b=srcbm_h; + + + if (dstx < 0) { srcx -= (float) (dstx*xadvance); dstw+=dstx; dstx=0; } + if (dsty < 0) { srcy -= (float) (dsty*yadvance); dsth+=dsty; dsty=0; } + + if (dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return; + + if (dstw > destbm_w-dstx) dstw=destbm_w-dstx; + if (dsth > destbm_h-dsty) dsth=destbm_h-dsty; + + const double fidx=floor(xadvance*65536.0), fidy=floor(yadvance*65536.0); + + double ficurx=floor(srcx*65536.0), ficury=floor(srcy*65536.0); + + // the clip area calculations need to be done fixed point so the results match runtime + + if (fidx>0) + { + if (ficurx < 0) // increase dstx, decrease dstw + { + int n = (int)((fidx-1-ficurx)/fidx); + dstw-=n; + dstx+=n; + ficurx+=fidx*n; + } + if ((ficurx + fidx*(dstw-1)) >= srcbm_w*65536.0) + { + int neww = (int)(((srcbm_w-1)*65536.0 - ficurx)/fidx); + if (neww < dstw) dstw=neww; + } + } + else if (fidx<0) + { + // todo: optimize source-clipping with reversed X axis + } + + if (fidy > 0) + { + if (ficury < 0) // increase dsty, decrease dsth + { + int n = (int) ((fidy-1-ficury)/fidy); + dsth-=n; + dsty+=n; + ficury+=fidy*n; + } + if ((ficury + fidy*(dsth-1)) >= srcbm_h*65536.0) + { + int newh = (int)(((srcbm_h-1)*65536.0 - ficury)/fidy); + if (newh < dsth) dsth=newh; + } + } + else if (fidy<0) + { + // todo: optimize source-clipping with reversed Y axis (check icury against src->getHeight(), etc) + } + if (dstw<1 || dsth<1) return; + + int dest_span=dest->getRowSpan()*sizeof(LICE_pixel); + int src_span=src->getRowSpan()*sizeof(LICE_pixel); + + const LICE_pixel_chan *psrc = (const LICE_pixel_chan *)src->getBits(); + LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits(); + if (!psrc || !pdest) return; + + const int srcoffs_x = (int)((ficurx + (fidx<0 ? fidx*dstw : 0))*(1.0/65536.0)); + const int srcoffs_y = (int)((ficury + (fidy<0 ? fidy*dsth : 0))*(1.0/65536.0)); + + if (src->isFlipped()) + { + psrc += (srcbm_h-1-srcoffs_y)*src_span; + src_span=-src_span; + } + else psrc += srcoffs_y * src_span; + psrc += srcoffs_x * sizeof(LICE_pixel); + + if (dest->isFlipped()) + { + pdest += (destbm_h-dsty - 1)*dest_span; + dest_span=-dest_span; + } + else pdest += dsty*dest_span; + pdest+=dstx*sizeof(LICE_pixel); + + clip_r -= srcoffs_x; + clip_b -= srcoffs_y; + + const int icurx = (int) (ficurx - srcoffs_x*65536.0); + const int icury = (int) (ficury - srcoffs_y*65536.0); + const int idx = (int) fidx, idy = (int) fidy; + + if (clip_r<1||clip_b<1) return; + + int ia=(int)(alpha*256.0); + + if ((mode&(LICE_BLIT_FILTER_MASK|LICE_BLIT_MODE_MASK|LICE_BLIT_USE_ALPHA))==LICE_BLIT_MODE_COPY && (ia==128 || ia==256)) + { + if (ia==128) + { + _LICE_Template_Blit0<_LICE_CombinePixelsHalfMixFAST>::scaleBlitFAST(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span); + } + else + { + _LICE_Template_Blit0<_LICE_CombinePixelsClobberFAST>::scaleBlitFAST(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span); + } + } + else + { + if (xadvance>=1.7 && yadvance >=1.7 && (mode&LICE_BLIT_FILTER_MASK)==LICE_BLIT_FILTER_BILINEAR) + { + int msc = lice_max(idx,idy); + const int filtsz=msc>(3<<16) ? 5 : 3; + const int filt_start = - (filtsz/2); + + int filter[25]; // 5x5 max + { + int y; + // char buf[4096]; + // sprintf(buf,"filter, msc=%f: ",msc); + int *p=filter; + for(y=0;y1.0) *p++=65536; + else *p++=(int)(v*65536.0); + } + } + } +// OutputDebugString(buf); + } + + #ifdef LICE_FAVOR_SIZE + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc=comb::doPix; + #else + #define __LICE__ACTION(comb) _LICE_Template_Blit2::scaleBlitFilterDown(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span,ia,filter,filt_start,filtsz) + #endif + __LICE_ACTION_SRCALPHA(mode,ia,false); + #undef __LICE__ACTION + + #ifdef LICE_FAVOR_SIZE + if (blitfunc) _LICE_Template_Blit2::scaleBlitFilterDown(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span,ia,filter,filt_start,filtsz,blitfunc); + #endif + + } + else + { + #ifdef LICE_FAVOR_SIZE + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc=comb::doPix; + #else + #define __LICE__ACTION(comb) _LICE_Template_Blit2::scaleBlit(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK) + #endif + __LICE_ACTION_SRCALPHA(mode,ia,false); + #undef __LICE__ACTION + #ifdef LICE_FAVOR_SIZE + if (blitfunc) _LICE_Template_Blit2::scaleBlit(pdest,psrc,dstw,dsth,icurx,icury,idx,idy,clip_r,clip_b,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK,blitfunc); + #endif + } + } +} + +void LICE_DeltaBlit(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + float srcx, float srcy, float srcw, float srch, + double dsdx, double dtdx, double dsdy, double dtdy, + double dsdxdy, double dtdxdy, + bool cliptosourcerect, float alpha, int mode) +{ + if (!dest || !src || !dstw || !dsth) return; + + int srcbm_w = src->getWidth(), srcbm_h = src->getHeight(); + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + + int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(dstx); + __LICE_SC(dsty); + __LICE_SC(dstw); + __LICE_SC(dsth); + } + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + + } + const int __scdest = __sc; + __sc = (int)src->Extended(LICE_EXT_GET_SCALING,NULL); + + if (__sc>0) + { + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(srcx); + __LICE_SC(srcy); + __LICE_SC(srcw); + __LICE_SC(srch); + } + __LICE_SCU(srcbm_w); + __LICE_SCU(srcbm_h); + } + + if (__scdest != __sc && !IGNORE_SCALING(mode)) + { + const double adj = (__sc>0 ? __sc : 256.0) / (double) (__scdest>0 ? __scdest : 256.0); + dsdx *= adj; + dtdx *= adj; + dsdy *= adj; + dtdy *= adj; + dsdxdy *= adj; + dtdxdy *= adj; + } + + double src_top=0.0,src_left=0.0,src_right=srcbm_w,src_bottom=srcbm_h; + + if (cliptosourcerect) + { + if (srcx > src_left) src_left=srcx; + if (srcy > src_top) src_top=srcy; + if (srcx+srcw < src_right) src_right=srcx+srcw; + if (srcy+srch < src_bottom) src_bottom=srcy+srch; + } + + if (dstw<0) + { + dstw=-dstw; + dstx-=dstw; + srcx+=srcw; + } + if (dsth<0) + { + dsth=-dsth; + dsty-=dsth; + srcy+=srch; + } + + + if (dstx < 0) + { + srcx -= (float) (dstx*dsdx); + srcy -= (float) (dstx*dtdx); + dstw+=dstx; + dstx=0; + } + if (dsty < 0) + { + srcy -= (float) (dsty*dtdy); + srcx -= (float) (dsty*dsdy); + dsth+=dsty; + dsty=0; + } + + if (dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return; + + if (dstw > destbm_w-dstx) dstw=destbm_w-dstx; + if (dsth > destbm_h-dsty) dsth=destbm_h-dsty; + + + int dest_span=dest->getRowSpan()*sizeof(LICE_pixel); + int src_span=src->getRowSpan()*sizeof(LICE_pixel); + + const LICE_pixel_chan *psrc = (LICE_pixel_chan *)src->getBits(); + LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits(); + if (!psrc || !pdest) return; + + if (src->isFlipped()) + { + psrc += (srcbm_h-1)*src_span; + src_span=-src_span; + } + + if (dest->isFlipped()) + { + pdest += (destbm_h-dsty - 1)*dest_span; + dest_span=-dest_span; + } + else pdest += dsty*dest_span; + pdest+=dstx*sizeof(LICE_pixel); + + int sl=(int)(src_left); + int sr=(int)(src_right); + int st=(int)(src_top); + int sb=(int)(src_bottom); + + sr -= sl; + sb -= st; + if (sr < 1 || sb < 1) return; + + psrc += src_span * st + sl * sizeof(LICE_pixel); + + int ia=(int)(alpha*256.0); + int isrcx=(int)(srcx*65536.0); + int isrcy=(int)(srcy*65536.0); + int idsdx=(int)(dsdx*65536.0); + int idtdx=(int)(dtdx*65536.0); + int idsdy=(int)(dsdy*65536.0); + int idtdy=(int)(dtdy*65536.0); + int idsdxdy=(int)(dsdxdy*65536.0); + int idtdxdy=(int)(dtdxdy*65536.0); + +#ifndef LICE_FAVOR_SPEED + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc = comb::doPix; +#else + #define __LICE__ACTION(comb) _LICE_Template_Blit3::deltaBlit(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,idsdxdy,idtdxdy,sr,sb,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK) +#endif + __LICE_ACTION_SRCALPHA(mode,ia,false); + #undef __LICE__ACTION + +#ifndef LICE_FAVOR_SPEED + if (blitfunc) _LICE_Template_Blit3::deltaBlit(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,idsdxdy,idtdxdy,sr,sb,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK,blitfunc); +#endif + +} + +void LICE_DeltaBlitAlpha(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + float srcx, float srcy, float srcw, float srch, + double dsdx, double dtdx, double dsdy, double dtdy, + double dsdxdy, double dtdxdy, + bool cliptosourcerect, float alpha, int mode, double dadx, double dady, double dadxdy) +{ + if (!dest || !src || !dstw || !dsth) return; + + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + int srcbm_w = src->getWidth(), srcbm_h = src->getHeight(); + int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(dstx); + __LICE_SC(dsty); + __LICE_SC(dstw); + __LICE_SC(dsth); + } + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + + } + const int __scdest = __sc; + __sc = (int)src->Extended(LICE_EXT_GET_SCALING,NULL); + + if (__sc>0) + { + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(srcx); + __LICE_SC(srcy); + __LICE_SC(srcw); + __LICE_SC(srch); + } + __LICE_SCU(srcbm_w); + __LICE_SCU(srcbm_h); + } + + if (lice_max(__scdest,0) != lice_max(__sc,0)) + { + const double adj = (__sc>0 ? __sc : 256.0) / (double) (__scdest>0 ? __scdest : 256.0); + dsdx *= adj; + dtdx *= adj; + dadx *= adj; + dsdy *= adj; + dtdy *= adj; + dady *= adj; + dsdxdy *= adj; + dtdxdy *= adj; + dadxdy *= adj; + } + + const double eps = 0.0001; + if (fabs(dadx*dstw) < eps && fabs(dady*dsth) < eps && fabs(dadxdy*dsth) < eps) + { + LICE_DeltaBlit(dest, src, dstx, dsty, dstw, dsth, srcx, srcy, srcw, srch, dsdx, dtdx, dsdy, dtdy, dsdxdy, dtdxdy, cliptosourcerect, alpha, mode); + return; + } + + double src_top=0.0,src_left=0.0,src_right=srcbm_w,src_bottom=srcbm_h; + + if (cliptosourcerect) + { + if (srcx > src_left) src_left=srcx; + if (srcy > src_top) src_top=srcy; + if (srcx+srcw < src_right) src_right=srcx+srcw; + if (srcy+srch < src_bottom) src_bottom=srcy+srch; + } + + if (dstw<0) + { + dstw=-dstw; + dstx-=dstw; + srcx+=srcw; + } + if (dsth<0) + { + dsth=-dsth; + dsty-=dsth; + srcy+=srch; + } + + + if (dstx < 0) + { + alpha -= (float) (dstx*dadx); + srcx -= (float) (dstx*dsdx); + srcy -= (float) (dstx*dtdx); + dstw+=dstx; + dstx=0; + } + if (dsty < 0) + { + alpha -= (float) (dsty*dady); + srcy -= (float) (dsty*dtdy); + srcx -= (float) (dsty*dsdy); + dsth+=dsty; + dsty=0; + } + + if (dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return; + + if (dstw > destbm_w-dstx) dstw=destbm_w-dstx; + if (dsth > destbm_h-dsty) dsth=destbm_h-dsty; + + + int dest_span=dest->getRowSpan()*sizeof(LICE_pixel); + int src_span=src->getRowSpan()*sizeof(LICE_pixel); + + const LICE_pixel_chan *psrc = (LICE_pixel_chan *)src->getBits(); + LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits(); + if (!psrc || !pdest) return; + + if (src->isFlipped()) + { + psrc += (srcbm_h-1)*src_span; + src_span=-src_span; + } + + if (dest->isFlipped()) + { + pdest += (destbm_h-dsty - 1)*dest_span; + dest_span=-dest_span; + } + else pdest += dsty*dest_span; + pdest+=dstx*sizeof(LICE_pixel); + + int sl=(int)(src_left); + int sr=(int)(src_right); + int st=(int)(src_top); + int sb=(int)(src_bottom); + + sr -= sl; + sb -= st; + if (sr < 1 || sb < 1) return; + + psrc += src_span * st + sl * sizeof(LICE_pixel); + + int ia=(int)(alpha*65536.0); + int isrcx=(int)(srcx*65536.0); + int isrcy=(int)(srcy*65536.0); + int idsdx=(int)(dsdx*65536.0); + int idtdx=(int)(dtdx*65536.0); + int idsdy=(int)(dsdy*65536.0); + int idtdy=(int)(dtdy*65536.0); + int idsdxdy=(int)(dsdxdy*65536.0); + int idtdxdy=(int)(dtdxdy*65536.0); + + int idadx=(int)(dadx*65536.0); + int idady=(int)(dady*65536.0); + int idadxdy=(int)(dadxdy*65536.0); + +#ifndef LICE_FAVOR_SPEED + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc = comb::doPix; +#else + #define __LICE__ACTION(comb) _LICE_Template_Blit4::deltaBlitAlpha(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,idsdxdy,idtdxdy,sr,sb,src_span,dest_span,ia,idadx,idady,idadxdy,mode&LICE_BLIT_FILTER_MASK) +#endif + __LICE_ACTION_NOSRCALPHA(mode,256,true); + #undef __LICE__ACTION + +#ifndef LICE_FAVOR_SPEED + if (blitfunc) _LICE_Template_Blit4::deltaBlitAlpha(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,idsdxdy,idtdxdy,sr,sb,src_span,dest_span,ia,idadx,idady,idadxdy,mode&LICE_BLIT_FILTER_MASK,blitfunc); +#endif + +} + + + + +void LICE_RotatedBlit(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + float srcx, float srcy, float srcw, float srch, + float angle, + bool cliptosourcerect, float alpha, int mode, float rotxcent, float rotycent) +{ + if (!dest || !src || !dstw || !dsth) return; + + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + int srcbm_w = src->getWidth(), srcbm_h = src->getHeight(); + int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(dstx); + __LICE_SC(dsty); + __LICE_SC(dstw); + __LICE_SC(dsth); + } + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + + } + __sc = (int)src->Extended(LICE_EXT_GET_SCALING,NULL); + + if (__sc>0) + { + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(srcx); + __LICE_SC(srcy); + __LICE_SC(srcw); + __LICE_SC(srch); + } + __LICE_SCU(srcbm_w); + __LICE_SCU(srcbm_h); + } + double src_top=0.0,src_left=0.0,src_right=srcbm_w,src_bottom=srcbm_h; + + if (cliptosourcerect) + { + if (srcx > src_left) src_left=srcx; + if (srcy > src_top) src_top=srcy; + if (srcx+srcw < src_right) src_right=srcx+srcw; + if (srcy+srch < src_bottom) src_bottom=srcy+srch; + } + + if (dstw<0) + { + dstw=-dstw; + dstx-=dstw; + srcx+=srcw; + srcw=-srcw; + } + if (dsth<0) + { + dsth=-dsth; + dsty-=dsth; + srcy+=srch; + srch=-srch; + } + + double cosa=cos(angle); + double sina=sin(angle); + + double xsc=srcw / dstw; + double ysc=srch / dsth; + + double dsdx = xsc * cosa; + double dtdy = ysc * cosa; + double dsdy = xsc * sina; + double dtdx = ysc * -sina; + + srcx -= (float) (0.5 * (dstw*dsdx + dsth*dsdy - srcw) - rotxcent); + srcy -= (float) (0.5 * (dsth*dtdy + dstw*dtdx - srch) - rotycent); + + if (dstx < 0) + { + srcx -= (float) (dstx*dsdx); + srcy -= (float) (dstx*dtdx); + dstw+=dstx; + dstx=0; + } + if (dsty < 0) + { + srcy -= (float) (dsty*dtdy); + srcx -= (float) (dsty*dsdy); + dsth+=dsty; + dsty=0; + } + + if (dstw < 1 || dsth < 1 || dstx >= destbm_w || dsty >= destbm_h) return; + + if (dstw > destbm_w-dstx) dstw=destbm_w-dstx; + if (dsth > destbm_h-dsty) dsth=destbm_h-dsty; + + + int dest_span=dest->getRowSpan()*sizeof(LICE_pixel); + int src_span=src->getRowSpan()*sizeof(LICE_pixel); + + const LICE_pixel_chan *psrc = (LICE_pixel_chan *)src->getBits(); + LICE_pixel_chan *pdest = (LICE_pixel_chan *)dest->getBits(); + if (!psrc || !pdest) return; + + if (src->isFlipped()) + { + psrc += (srcbm_h-1)*src_span; + src_span=-src_span; + } + + if (dest->isFlipped()) + { + pdest += (destbm_h-dsty - 1)*dest_span; + dest_span=-dest_span; + } + else pdest += dsty*dest_span; + pdest+=dstx*sizeof(LICE_pixel); + + int sl=(int)(src_left); + int sr=(int)(src_right); + int st=(int)(src_top); + int sb=(int)(src_bottom); + + sr -= sl; + sb -= st; + srcx -= sl; + srcy -= st; + if (sr < 1 || sb < 1) return; + + psrc += src_span * st + sl * sizeof(LICE_pixel); + + int ia=(int)(alpha*256.0); + int isrcx=(int)(srcx*65536.0); + int isrcy=(int)(srcy*65536.0); + int idsdx=(int)(dsdx*65536.0); + int idtdx=(int)(dtdx*65536.0); + int idsdy=(int)(dsdy*65536.0); + int idtdy=(int)(dtdy*65536.0); + +#ifndef LICE_FAVOR_SPEED + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc = comb::doPix; +#else + #define __LICE__ACTION(comb) _LICE_Template_Blit3::deltaBlit(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,0,0,sr,sb,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK) +#endif + __LICE_ACTION_SRCALPHA(mode,ia,false); + #undef __LICE__ACTION + +#ifndef LICE_FAVOR_SPEED + if (blitfunc) _LICE_Template_Blit3::deltaBlit(pdest,psrc,dstw,dsth,isrcx,isrcy,idsdx,idtdx,idsdy,idtdy,0,0,sr,sb,src_span,dest_span,ia,mode&LICE_BLIT_FILTER_MASK,blitfunc); +#endif +} + +#endif + + +void LICE_Clear(LICE_IBitmap *dest, LICE_pixel color) +{ + if (!dest) return; + +#ifndef DISABLE_LICE_EXTENSIONS + if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_CLEAR_ACCEL)) + { + if (dest->Extended(LICE_EXT_CLEAR_ACCEL, &color)) return; + } +#endif + + LICE_pixel *p=dest->getBits(); + int h=dest->getHeight(); + int w=dest->getWidth(); + const int sp=dest->getRowSpan(); + + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + } + + if (!p || w<1 || h<1 || !sp) return; + + while (h-->0) + { + int n=w; + while (n--) *p++ = color; + p+=sp-w; + } +} + + + + +void LICE_MultiplyAddRect(LICE_IBitmap *dest, int x, int y, int w, int h, + float rsc, float gsc, float bsc, float asc, + float radd, float gadd, float badd, float aadd) +{ + if (!dest) return; + + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + DO_RECT_SC(0) + + if (x<0) { w+=x; x=0; } + if (y<0) { h+=y; y=0; } + + LICE_pixel *p=dest->getBits(); + const int sp=dest->getRowSpan(); + if (!p || !sp || w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return; + + if (w>destbm_w-x) w=destbm_w-x; + if (h>destbm_h-y) h=destbm_h-y; + + if (dest->isFlipped()) + { + p+=(destbm_h - y - h)*sp; + } + else + { + p+=sp*y; + } + + p += x; + + int ir=(int)(rsc*256.0); + int ig=(int)(gsc*256.0); + int ib=(int)(bsc*256.0); + int ia=(int)(asc*256.0); + int ir2=(int)(radd*256.0); + int ig2=(int)(gadd*256.0); + int ib2=(int)(badd*256.0); + int ia2=(int)(aadd*256.0); + + while (h-->0) + { + int n=w; + while (n--) + { + LICE_pixel_chan *ptr=(LICE_pixel_chan *)p++; + _LICE_MakePixelClamp(ptr,(ptr[LICE_PIXEL_R]*ir+ir2)>>8, + (ptr[LICE_PIXEL_G]*ig+ig2)>>8, + (ptr[LICE_PIXEL_B]*ib+ib2)>>8, + (ptr[LICE_PIXEL_A]*ia+ia2)>>8); + } + p+=sp-w; + } +} + +void LICE_ProcessRect(LICE_IBitmap *dest, int x, int y, int w, int h, void (*procFunc)(LICE_pixel *p, void *parm), void *parm) +{ + if (!dest||!procFunc) return; + + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + DO_RECT_SC(0) + + if (x<0) { w+=x; x=0; } + if (y<0) { h+=y; y=0; } + + LICE_pixel *p=dest->getBits(); + const int sp=dest->getRowSpan(); + + if (!p || !sp || w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return; + + if (w>destbm_w-x) w=destbm_w-x; + if (h>destbm_h-y) h=destbm_h-y; + + if (dest->isFlipped()) + { + p+=(destbm_h - y - h)*sp; + } + else + { + p+=sp*y; + } + + p += x; + + while (h--) + { + LICE_pixel *pout=p; + int n=w; + while (n-->0) procFunc(pout++,parm); + p+=sp; + } + + +} + +void LICE_FillRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha, int mode) +{ + if (!dest) return; + + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + DO_RECT_SC(mode) + +#ifndef DISABLE_LICE_EXTENSIONS + if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_FILLRECT_ACCEL)) + { + LICE_Ext_FillRect_acceldata data(x, y, w, h, color, alpha, mode); + if (dest->Extended(LICE_EXT_FILLRECT_ACCEL, &data)) return; + } +#endif + + if (mode & LICE_BLIT_USE_ALPHA) alpha *= LICE_GETA(color)/255.0f; + + LICE_pixel *p=dest->getBits(); + const int sp=dest->getRowSpan(); + + if (x<0) { w+=x; x=0; } + if (y<0) { h+=y; y=0; } + if (!alpha || !p || !sp || w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return; + + if (w>destbm_w-x) w=destbm_w-x; + if (h>destbm_h-y) h=destbm_h-y; + + if (dest->isFlipped()) + { + p+=(destbm_h - y - h)*sp; + } + else + { + p+=sp*y; + } + + p += x; + + const int ia=(int)(alpha*256.0); + // copy, alpha=1, alpha=0.5, 0.25, 0.75 optimizations + if ((mode&LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY) + { + if (ia==256) + { + _LICE_Template_Blit0<_LICE_CombinePixelsClobberFAST>::solidBlitFAST(p,w,h,color,sp); + return; + } + if (ia==128) + { + // we use _LICE_CombinePixelsHalfMix2 because we pre-halve color here + _LICE_Template_Blit0<_LICE_CombinePixelsHalfMix2FAST>::solidBlitFAST(p,w,h,(color>>1)&0x7f7f7f7f,sp); + return; + } + if (ia==64) + { + _LICE_Template_Blit0<_LICE_CombinePixelsQuarterMix2FAST>::solidBlitFAST(p,w,h,(color>>2)&0x3f3f3f3f,sp); + return; + } + if (ia==192) + { + _LICE_Template_Blit0<_LICE_CombinePixelsThreeQuarterMix2FAST>::solidBlitFAST(p,w,h, + ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f),sp); + return; + } + } + +#ifdef LICE_FAVOR_SIZE_EXTREME + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc=comb::doPix; +#else + #define __LICE__ACTION(comb) _LICE_Template_Blit1::solidBlit((LICE_pixel_chan*)p,w,h,LICE_GETR(color),LICE_GETG(color),LICE_GETB(color),ia,sp*sizeof(LICE_pixel)) +#endif + + // we use __LICE_ACTION_NOSRCALPHA even though __LICE_ACTION_CONSTANTALPHA + // is valid, sinc we optimized the 1.0f/0.5f cases above + __LICE_ACTION_NOSRCALPHA(mode,ia,false); + #undef __LICE__ACTION + +#ifdef LICE_FAVOR_SIZE_EXTREME + if (blitfunc) _LICE_Template_Blit1::solidBlit((LICE_pixel_chan*)p,w,h,LICE_GETR(color),LICE_GETG(color),LICE_GETB(color),ia,sp*sizeof(LICE_pixel),blitfunc); +#endif +} + +void LICE_ClearRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel mask, LICE_pixel orbits) +{ + if (!dest) return; + LICE_pixel *p=dest->getBits(); + + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + DO_RECT_SC(0) + + if (x<0) { w+=x; x=0; } + if (y<0) { h+=y; y=0; } + + const int sp=dest->getRowSpan(); + if (!p || !sp || w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return; + + if (w>destbm_w-x) w=destbm_w-x; + if (h>destbm_h-y) h=destbm_h-y; + + if (dest->isFlipped()) + { + p+=(destbm_h - y - h)*sp; + } + else p+=sp*y; + + p += x; + while (h-->0) + { + int n=w; + while (n--) + { + *p = (*p&mask)|orbits; + p++; + } + p+=sp-w; + } +} + + +LICE_pixel LICE_GetPixel(LICE_IBitmap *bm, int x, int y) +{ + if (!bm) return 0; + + int w = bm->getWidth(), h = bm->getHeight(); + const int __sc = (int)bm->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + __LICE_SC(x); + __LICE_SC(y); + } +#ifndef DISABLE_LICE_EXTENSIONS + if (bm->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_GETPIXEL_ACCEL)) + { + LICE_Ext_GetPixel_acceldata data(x, y); + if (bm->Extended(LICE_EXT_GETPIXEL_ACCEL, &data)) return data.px; + } +#endif + + const LICE_pixel *px; + + if (!(px=bm->getBits()) || x < 0 || y < 0 || x >= w || y>= h) return 0; + if (bm->isFlipped()) return px[(h-1-y) * bm->getRowSpan() + x]; + return px[y * bm->getRowSpan() + x]; +} + +void LICE_PutPixel(LICE_IBitmap *bm, int x, int y, LICE_pixel color, float alpha, int mode) +{ + if (!bm) return; + + const int __sc = (int)bm->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + if (!IGNORE_SCALING(mode)) + { + LICE_FillRect(bm,x,y,1,1,color,alpha,mode); + return; + } + + } + + int w = bm->getWidth(), h = bm->getHeight(); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + } +#ifndef DISABLE_LICE_EXTENSIONS + if (bm->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_PUTPIXEL_ACCEL)) + { + LICE_Ext_PutPixel_acceldata data(x, y, color, alpha, mode); + if (bm->Extended(LICE_EXT_PUTPIXEL_ACCEL, &data)) return; + } +#endif + + LICE_pixel *px; + if (!(px=bm->getBits()) || x < 0 || y < 0 || x >= w || y >= h) return; + + if (bm->isFlipped()) px+=x+(h-1-y)*bm->getRowSpan(); + else px+=x+y*bm->getRowSpan(); + + int ia = (int)(alpha * 256.0f); + if ((mode&LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY) + { + if (ia==256) + { + *px = color; + return; + } + if (ia==128) + { + *px = ((*px>>1)&0x7f7f7f7f) + ((color>>1)&0x7f7f7f7f); + return; + } + if (ia==64) + { + *px = ((*px>>1)&0x7f7f7f7f) + ((*px>>2)&0x3f3f3f3f) + ((color>>2)&0x3f3f3f3f); + return; + } + if (ia==192) + { + *px = ((*px>>2)&0x3f3f3f3f) + ((color>>1)&0x7f7f7f7f) + ((color>>2)&0x3f3f3f3f); + return; + } + } +#define __LICE__ACTION(comb) comb::doPix((LICE_pixel_chan *)px, LICE_GETR(color),LICE_GETG(color),LICE_GETB(color),LICE_GETA(color), ia) + __LICE_ACTION_NOSRCALPHA(mode,ia,false); +#undef __LICE__ACTION +} + + +#ifndef LICE_NO_BLIT_SUPPORT + +template class LICE_TransformBlit_class +{ + public: + static void blit(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + const T *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates + float alpha, int mode) +{ + if (!dest || !src || dstw<1 || dsth<1 || div_w<2 || div_h<2) return; + + int cypos=dsty; + double ypos=dsty; + double dxpos=dstw/(float)(div_w-1); + double dypos=dsth/(float)(div_h-1); + int y; + const T *curpoints=srcpoints; + for (y = 0; y < div_h-1; y ++) + { + int nypos=(int)((ypos+=dypos) + 0.5); + int x; + double xpos=dstx; + int cxpos=dstx; + + if (nypos != cypos) + { + double iy=1.0/(double)(nypos-cypos); + for (x = 0; x < div_w-1; x ++) + { + int nxpos=(int) ((xpos+=dxpos) + 0.5); + if (nxpos != cxpos) + { + int offs=x*2; + double sx=curpoints[offs]; + double sy=curpoints[offs+1]; + double sw=curpoints[offs+2]-sx; + double sh=curpoints[offs+3]-sy; + + offs+=div_w*2; + double sxdiff=curpoints[offs]-sx; + double sydiff=curpoints[offs+1]-sy; + double sw3=curpoints[offs+2]-curpoints[offs]; + double sh3=curpoints[offs+3]-curpoints[offs+1]; + + double ix=1.0/(double)(nxpos-cxpos); + double dsdx=sw*ix; + double dtdx=sh*ix; + double dsdx2=sw3*ix; + double dtdx2=sh3*ix; + double dsdy=sxdiff*iy; + double dtdy=sydiff*iy; + double dsdxdy = (dsdx2-dsdx)*iy; + double dtdxdy = (dtdx2-dtdx)*iy; + + LICE_DeltaBlit(dest,src,cxpos,cypos,nxpos-cxpos,nypos-cypos, + (float)sx,(float)sy,(float)sw,(float)sh, + dsdx,dtdx,dsdy,dtdy,dsdxdy,dtdxdy,false,alpha,mode); + } + + cxpos=nxpos; + } + } + curpoints+=div_w*2; + cypos=nypos; + } +} +}; + +template class LICE_TransformBlitAlpha_class +{ + public: + static void blit(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + const T *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates + int mode) +{ + if (!dest || !src || dstw<1 || dsth<1 || div_w<2 || div_h<2) return; + + int cypos=dsty; + double ypos=dsty; + double dxpos=dstw/(float)(div_w-1); + double dypos=dsth/(float)(div_h-1); + int y; + const T *curpoints=srcpoints; + for (y = 0; y < div_h-1; y ++) + { + int nypos=(int)((ypos+=dypos)+0.5); + int x; + double xpos=dstx; + int cxpos=dstx; + + if (nypos != cypos) + { + double iy=1.0/(double)(nypos-cypos); + for (x = 0; x < div_w-1; x ++) + { + int nxpos=(int) ((xpos+=dxpos)+0.5); + if (nxpos != cxpos) + { + int offs=x*3; + double sx=curpoints[offs]; + double sy=curpoints[offs+1]; + double sa=curpoints[offs+2]; + double sw=curpoints[offs+3]-sx; + double sh=curpoints[offs+4]-sy; + double sa2 = curpoints[offs+5]-sa; + + offs+=div_w*3; + double sxdiff=curpoints[offs]-sx; + double sydiff=curpoints[offs+1]-sy; + double sadiff=curpoints[offs+2]-sa; + double sw3=curpoints[offs+3]-curpoints[offs]; + double sh3=curpoints[offs+4]-curpoints[offs+1]; + double sa3=curpoints[offs+5]-curpoints[offs+2]; + + double ix=1.0/(double)(nxpos-cxpos); + double dsdx=sw*ix; + double dtdx=sh*ix; + double dadx=sa2*ix; + double dsdx2=sw3*ix; + double dtdx2=sh3*ix; + double dadx2=sa3*ix; + double dsdy=sxdiff*iy; + double dtdy=sydiff*iy; + double dady=sadiff*iy; + double dsdxdy = (dsdx2-dsdx)*iy; + double dtdxdy = (dtdx2-dtdx)*iy; + double dadxdy = (dadx2-dadx)*iy; + + LICE_DeltaBlitAlpha(dest,src,cxpos,cypos,nxpos-cxpos,nypos-cypos, + (float)sx,(float)sy,(float)sw,(float)sh, + dsdx,dtdx,dsdy,dtdy,dsdxdy,dtdxdy,false,sa,mode,dadx,dady,dadxdy); + } + + cxpos=nxpos; + } + } + curpoints+=div_w*3; + cypos=nypos; + } +} +}; + +void LICE_TransformBlit(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + const float *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates + float alpha, int mode) +{ + LICE_TransformBlit_class::blit(dest,src,dstx,dsty,dstw,dsth,srcpoints,div_w,div_h,alpha,mode); +} +void LICE_TransformBlit2(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + const double *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates + float alpha, int mode) +{ + LICE_TransformBlit_class::blit(dest,src,dstx,dsty,dstw,dsth,srcpoints,div_w,div_h,alpha,mode); +} + + +void LICE_TransformBlit2Alpha(LICE_IBitmap *dest, LICE_IBitmap *src, + int dstx, int dsty, int dstw, int dsth, + const double *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*3 long, and be in source image coordinates+alpha + int mode) +{ + LICE_TransformBlitAlpha_class::blit(dest,src,dstx,dsty,dstw,dsth,srcpoints,div_w,div_h,mode); +} +#endif + +#ifndef LICE_NO_MISC_SUPPORT + +void LICE_SetAlphaFromColorMask(LICE_IBitmap *dest, LICE_pixel color) +{ + if (!dest) return; + LICE_pixel *p=dest->getBits(); + int h=dest->getHeight(); + int w=dest->getWidth(); + int sp=dest->getRowSpan(); + if (!p || w<1 || h<1 || sp<1) return; + + while (h-->0) + { + int n=w; + while (n--) + { + if ((*p&LICE_RGBA(255,255,255,0)) == color) *p&=LICE_RGBA(255,255,255,0); + else *p|=LICE_RGBA(0,0,0,255); + p++; + } + p+=sp-w; + } +} + + +void LICE_SimpleFill(LICE_IBitmap *dest, int x, int y, LICE_pixel newcolor, + LICE_pixel comparemask, + LICE_pixel keepmask) +{ + if (!dest) return; + int dw=dest->getWidth(); + int dh=dest->getHeight(); + int rowsize=dest->getRowSpan(); + if (x<0||x>=dw||y<0||y>=dh) return; + LICE_pixel *ptr=dest->getBits(); + if (!ptr) return; + ptr += rowsize*y; + + LICE_pixel compval=ptr[x]&comparemask; + int ay; + for (ay=y;ay=0) + { + if ((ptr[ax]&comparemask)!=compval) break; + ptr[ax]=(ptr[ax]&keepmask)|newcolor; + } + + ptr+=rowsize; + } + ptr =dest->getBits()+rowsize*y; + + ay=y; + while (--ay>=0) + { + ptr-=rowsize; + if ((ptr[x]&comparemask)!=compval) break; + ptr[x]=(ptr[x]&keepmask)|newcolor; + + int ax; + for(ax=x+1;ax=0) + { + if ((ptr[ax]&comparemask)!=compval) break; + ptr[ax]=(ptr[ax]&keepmask)|newcolor; + } + } +} + +// VS6 instantiates this wrong as a template function, needs to be a template class +template class GlyphDrawImpl +{ +public: + static void DrawGlyph(const LICE_pixel_chan* srcalpha, LICE_pixel* destpx, int src_w, int src_h, LICE_pixel color, int span, int src_span, int aa) + { + const int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + int xi, yi; + for (yi = 0; yi < src_h; ++yi, srcalpha += src_span, destpx += span) { + const LICE_pixel_chan* tsrc = srcalpha; + LICE_pixel* tdest = destpx; + for (xi = 0; xi < src_w; ++xi, ++tsrc, ++tdest) { + const LICE_pixel_chan v = *tsrc; + if (v) { // glyphs should be expected to have a lot of "holes" + COMBFUNC::doPix((LICE_pixel_chan*) tdest, r, g, b, a, v*aa/256); + } + } + } + } + static void DrawGlyphScale(const LICE_pixel_chan* srcalpha, LICE_pixel* destpx, int src_w, int src_h, LICE_pixel color, int span, int src_span, int aa, int scale) + { + const int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + int xi, yi; + int ysum = 0; + for (yi = 0; yi < src_h; ++yi, srcalpha += src_span) { + ysum += scale; + while (ysum > 255) + { + const LICE_pixel_chan* tsrc = srcalpha; + LICE_pixel* tdest = destpx; + ysum -= 256; + destpx += span; + int sum = 0; + for (xi = 0; xi < src_w; ++xi, ++tsrc) { + const LICE_pixel_chan v = *tsrc; + sum += scale; + if (v) { // glyphs should be expected to have a lot of "holes" + while (sum>255) + { + COMBFUNC::doPix((LICE_pixel_chan*) tdest, r, g, b, a, v*aa/256); + tdest++; + sum -= 256; + } + } + else + { + tdest += sum/256; + sum &= 255; + } + } + } + } + } + static void DrawGlyphMono(const unsigned char * srcalpha, LICE_pixel* destpx, int src_w, int src_h, LICE_pixel color, int span, int src_span, int aa) + { + const int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + int xi, yi; + for (yi = 0; yi < src_h; ++yi, srcalpha += src_span, destpx += span) { + const unsigned char *tsrc = srcalpha; + LICE_pixel* tdest = destpx; + unsigned char cv=0; + for (xi = 0; xi < src_w; ++xi, ++tdest) { + if (!(xi&7)) cv = *tsrc++; + const LICE_pixel_chan v = (cv&128)?255:0; + cv<<=1; + if (v) { // glyphs should be expected to have a lot of "holes" + COMBFUNC::doPix((LICE_pixel_chan*) tdest, r, g, b, a, v*aa/256); + } + } + } + } + static void DrawGlyphMonoScale(const unsigned char * srcalpha, LICE_pixel* destpx, int src_w, int src_h, LICE_pixel color, int span, int src_span, int aa, int scale) + { + const int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + int xi, yi; + int ysum=0; + for (yi = 0; yi < src_h; ++yi, srcalpha += src_span) { + ysum += scale; + while (ysum > 255) + { + const unsigned char *tsrc = srcalpha; + LICE_pixel* tdest = destpx; + unsigned char cv=0; + int sum=0; + ysum -= 256; + destpx++; + for (xi = 0; xi < src_w; ++xi) { + if (!(xi&7)) cv = *tsrc++; + const LICE_pixel_chan v = (cv&128)?255:0; + cv<<=1; + sum += scale; + if (v) { // glyphs should be expected to have a lot of "holes" + while (sum > 255) + { + COMBFUNC::doPix((LICE_pixel_chan*) tdest, r, g, b, a, v*aa/256); + tdest++; + sum -= 256; + } + } + else + { + tdest += sum/256; + sum &= 255; + } + } + } + } + } +}; + +void LICE_DrawGlyphEx(LICE_IBitmap* dest, int x, int y, LICE_pixel color, const LICE_pixel_chan* alphas, int glyph_w, int glyph_span, int glyph_h, float alpha, int mode) +{ + if (!dest) return; + +#ifndef DISABLE_LICE_EXTENSIONS + if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_DRAWGLYPH_ACCEL) && glyph_w == glyph_span) + { + LICE_Ext_DrawGlyph_acceldata data(x, y, color, alphas, glyph_w, glyph_h, alpha, mode); + if (dest->Extended(LICE_EXT_DRAWGLYPH_ACCEL, &data)) return; + } +#endif + + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0 && IGNORE_SCALING(mode)) + { + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + } + + if (glyph_span < 0) alphas += -glyph_span * (glyph_h-1); + + const int ia= (int)(alpha*256.0f); + + int src_x = 0, src_y = 0, src_w = glyph_w, src_h = glyph_h; + if (x <= -src_w || y <= -src_h) return; + + if (x < 0) { + src_x -= x; + if (src_x < 0) return; // overflow + src_w += x; + x = 0; + } + if (y < 0) { + src_y -= y; + if (src_y < 0) return; // overflow + src_h += y; + y = 0; + } + + if (src_w < 0 || src_h < 0 || x >= destbm_w || y >= destbm_h) return; + + if (src_h > destbm_h-y) src_h = destbm_h-y; + if (src_w > destbm_w-x) src_w = destbm_w-x; + + if (src_w < 1 || src_h < 1) return; + + if (__sc>0 && !IGNORE_SCALING(mode)) + { + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + __LICE_SC(x); + __LICE_SC(y); + } + + LICE_pixel* destpx = dest->getBits(); + int span = dest->getRowSpan(); + if (dest->isFlipped()) { + destpx += (destbm_h-y-1)*span+x; + span = -span; + } + else { + destpx += y*dest->getRowSpan()+x; + } + + const LICE_pixel_chan* srcalpha = alphas+src_y*glyph_span+src_x; + + if (__sc>0 && !IGNORE_SCALING(mode)) + { +#define __LICE__ACTION(COMBFUNC) GlyphDrawImpl::DrawGlyphScale(srcalpha,destpx, src_w, src_h, color,span,glyph_span,ia,__sc) + __LICE_ACTION_NOSRCALPHA(mode, ia, false); +#undef __LICE__ACTION + } + else + { +#define __LICE__ACTION(COMBFUNC) GlyphDrawImpl::DrawGlyph(srcalpha,destpx, src_w, src_h, color,span,glyph_span,ia) + __LICE_ACTION_NOSRCALPHA(mode, ia, false); +#undef __LICE__ACTION + } +} + +void LICE_DrawGlyph(LICE_IBitmap* dest, int x, int y, LICE_pixel color, const LICE_pixel_chan* alphas, int glyph_w, int glyph_h, float alpha, int mode) +{ + LICE_DrawGlyphEx(dest,x,y,color,alphas,glyph_w,glyph_w,glyph_h,alpha,mode); +} + + +void LICE_DrawMonoGlyph(LICE_IBitmap* dest, int x, int y, LICE_pixel color, const unsigned char *alphabits, int glyph_w, int glyph_span, int glyph_h, float alpha, int mode) +{ + if (!dest) return; + + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0 && IGNORE_SCALING(mode)) + { + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + } + + if (glyph_span < 0) alphabits += -glyph_span * (glyph_h-1); + + const int ia= (int)(alpha*256.0f); + + int src_x = 0, src_y = 0, src_w = glyph_w, src_h = glyph_h; + if (x <= -src_w || y <= -src_h) return; + + if (x < 0) { + src_x -= x; + src_w += x; + x = 0; + } + if (y < 0) { + src_y -= y; + src_h += y; + y = 0; + } + + if (src_w < 0 || src_h < 0 || x >= destbm_w || y >= destbm_h) return; + + if (src_h > destbm_h-y) src_h = destbm_h-y; + if (src_w > destbm_w-x) src_w = destbm_w-x; + + if (src_w < 1 || src_h < 1) return; + + if (__sc>0 && !IGNORE_SCALING(mode)) + { + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + __LICE_SC(x); + __LICE_SC(y); + } + + LICE_pixel* destpx = dest->getBits(); + int span = dest->getRowSpan(); + if (dest->isFlipped()) { + destpx += (destbm_h-y-1)*span+x; + span = -span; + } + else { + destpx += y*dest->getRowSpan()+x; + } + + const unsigned char * srcalpha = alphabits+src_y*glyph_span+src_x; + if (__sc>0 && !IGNORE_SCALING(mode)) + { +#define __LICE__ACTION(COMBFUNC) GlyphDrawImpl::DrawGlyphMonoScale(srcalpha,destpx, src_w, src_h, color,span,glyph_span,ia,__sc) + __LICE_ACTION_NOSRCALPHA(mode, ia, false); +#undef __LICE__ACTION + } + else + { +#define __LICE__ACTION(COMBFUNC) GlyphDrawImpl::DrawGlyphMono(srcalpha,destpx, src_w, src_h, color,span,glyph_span,ia) + __LICE_ACTION_NOSRCALPHA(mode, ia, false); +#undef __LICE__ACTION + } +} + +void LICE_HalveBlitAA(LICE_IBitmap *dest, LICE_IBitmap *src) +{ + if (!dest||!src) return; + int w = dest->getWidth(); + if (w > src->getWidth()/2) w=src->getWidth()/2; + int h = dest->getHeight(); + if (h > src->getHeight()/2) h=src->getHeight()/2; + int src_span = src->getRowSpan(); + int dest_span = dest->getRowSpan(); + const LICE_pixel *srcptr = src->getBits(); + LICE_pixel *destptr = dest->getBits(); + + while (h--) + { + const LICE_pixel *sp = srcptr; + const LICE_pixel *sp2 = srcptr+src_span; + LICE_pixel *dp = destptr; + int x=w/2; + + // this is begging for SSE intrinsics, but we never use this function so leave it as-is :) + + // perhaps we should use more precision rather than chopping each src pixel to 6 bits, but oh well + while (x--) // unroll 2px at a time, about 5% faster on core2 and ICC + { + int r1=((sp[0]>>2)&0x3f3f3f3f); + int r2=((sp[2]>>2)&0x3f3f3f3f); + r1 += ((sp[1]>>2)&0x3f3f3f3f); + r2 += ((sp[3]>>2)&0x3f3f3f3f); + dp[0] = r1 + + ((sp2[0]>>2)&0x3f3f3f3f) + + ((sp2[1]>>2)&0x3f3f3f3f); + dp[1] = r2 + + ((sp2[2]>>2)&0x3f3f3f3f) + + ((sp2[3]>>2)&0x3f3f3f3f); + sp+=4; + sp2+=4; + dp+=2; + } + if (w&1) + { + *dp = + ((sp[0]>>2)&0x3f3f3f3f) + + ((sp[1]>>2)&0x3f3f3f3f) + + ((sp2[0]>>2)&0x3f3f3f3f) + + ((sp2[1]>>2)&0x3f3f3f3f); + } + srcptr+=src_span*2; + destptr+=dest_span; + } +} + +#endif // LICE_NO_MISC_SUPPORT + +int LICE_BitmapCmp(LICE_IBitmap* a, LICE_IBitmap* b, int *coordsOut) +{ + return LICE_BitmapCmpEx(a,b,LICE_RGBA(255,255,255,255),coordsOut); +} + +int LICE_BitmapCmpEx(LICE_IBitmap* a, LICE_IBitmap* b, LICE_pixel mask, int *coordsOut) +{ + if (!a || !b) { + if (!a && b) return -1; + if (a && !b) return 1; + return 0; + } + + int aw = a->getWidth(), bw = b->getWidth(); + if (aw != bw) return bw-aw; + int ah = a->getHeight(), bh = b->getHeight(); + if (ah != bh) return bh-ah; + + //coordsOut + const LICE_pixel *px1 = a->getBits(); + const LICE_pixel *px2 = b->getBits(); + int span1 = a->getRowSpan(); + int span2 = b->getRowSpan(); + if (a->isFlipped()) + { + px1+=span1*(ah-1); + span1=-span1; + } + if (b->isFlipped()) + { + px2+=span2*(ah-1); + span2=-span2; + } + + int y; + if (!coordsOut) + { + if (mask == (LICE_pixel)LICE_RGBA(255,255,255,255)) + for (y=0; y < ah; y ++) + { + const int dv = memcmp(px1,px2,aw*sizeof(LICE_pixel)); + if (dv) return dv; + px1+=span1; + px2+=span2; + } + else + for (y=0; y < ah; y ++) + { + const LICE_pixel *ptr1 = px1, *ptr2 = px2; + int x=aw; + while (x--) + if ((*ptr1++ ^ *ptr2++) & mask) return true; + px1+=span1; + px2+=span2; + } + } + else + { + int x; + + // find first row that differs + for (y=0; y < ah; y ++) + { + // check left side + for (x=0;x=ah) + { + memset(coordsOut,0,4*sizeof(int)); + return 0; // no differences + } + + int miny=y; + int minx=x; + // scan right edge of top differing row + for (x=aw-1;x>minx && !((px1[x]^px2[x])&mask);x--); + int maxx=x; + + // find last row that differs + px1+=span1 * (ah-1-y); + px2+=span2 * (ah-1-y); + for (y = ah-1; y > miny; y --) + { + // check left side + for (x=0;x miny) + { + // scan right edge of bottom row that differs + for (x=aw-1;x>maxx && !((px1[x]^px2[x])&mask);x--); + maxx=x; + } + + + // find min/max x that differ + px1+=span1 * (miny+1-y); + px2+=span2 * (miny+1-y); + for (y=miny+1;y0 || maxxmaxx && !((px1[x]^px2[x])&mask);x--); + maxx=x; + + px1+=span1; + px2+=span2; + } + + coordsOut[0]=minx; + coordsOut[1]=miny; + coordsOut[2]=maxx-minx+1; + coordsOut[3]=maxy-miny+1; + + return 1; + } + + return 0; +} + +unsigned short _LICE_RGB2HSV_invtab[256]={ // 65536/idx - 1 + 0, 0xffff, 0x7fff, 0x5554, 0x3fff, 0x3332, 0x2aa9, 0x2491, + 0x1fff, 0x1c70, 0x1998, 0x1744, 0x1554, 0x13b0, 0x1248, 0x1110, + 0x0fff, 0x0f0e, 0x0e37, 0x0d78, 0x0ccb, 0x0c2f, 0x0ba1, 0x0b20, + 0x0aa9, 0x0a3c, 0x09d7, 0x097a, 0x0923, 0x08d2, 0x0887, 0x0841, + 0x07ff, 0x07c0, 0x0786, 0x074f, 0x071b, 0x06ea, 0x06bb, 0x068f, + 0x0665, 0x063d, 0x0617, 0x05f3, 0x05d0, 0x05af, 0x058f, 0x0571, + 0x0554, 0x0538, 0x051d, 0x0504, 0x04eb, 0x04d3, 0x04bc, 0x04a6, + 0x0491, 0x047c, 0x0468, 0x0455, 0x0443, 0x0431, 0x0420, 0x040f, + 0x03ff, 0x03ef, 0x03df, 0x03d1, 0x03c2, 0x03b4, 0x03a7, 0x039a, + 0x038d, 0x0380, 0x0374, 0x0368, 0x035d, 0x0352, 0x0347, 0x033c, + 0x0332, 0x0328, 0x031e, 0x0314, 0x030b, 0x0302, 0x02f9, 0x02f0, + 0x02e7, 0x02df, 0x02d7, 0x02cf, 0x02c7, 0x02bf, 0x02b8, 0x02b0, + 0x02a9, 0x02a2, 0x029b, 0x0294, 0x028e, 0x0287, 0x0281, 0x027b, + 0x0275, 0x026f, 0x0269, 0x0263, 0x025d, 0x0258, 0x0252, 0x024d, + 0x0248, 0x0242, 0x023d, 0x0238, 0x0233, 0x022f, 0x022a, 0x0225, + 0x0221, 0x021c, 0x0218, 0x0213, 0x020f, 0x020b, 0x0207, 0x0203, + 0x01ff, 0x01fb, 0x01f7, 0x01f3, 0x01ef, 0x01eb, 0x01e8, 0x01e4, + 0x01e0, 0x01dd, 0x01d9, 0x01d6, 0x01d3, 0x01cf, 0x01cc, 0x01c9, + 0x01c6, 0x01c2, 0x01bf, 0x01bc, 0x01b9, 0x01b6, 0x01b3, 0x01b1, + 0x01ae, 0x01ab, 0x01a8, 0x01a5, 0x01a3, 0x01a0, 0x019d, 0x019b, + 0x0198, 0x0196, 0x0193, 0x0191, 0x018e, 0x018c, 0x0189, 0x0187, + 0x0185, 0x0182, 0x0180, 0x017e, 0x017c, 0x0179, 0x0177, 0x0175, + 0x0173, 0x0171, 0x016f, 0x016d, 0x016b, 0x0169, 0x0167, 0x0165, + 0x0163, 0x0161, 0x015f, 0x015d, 0x015b, 0x0159, 0x0157, 0x0156, + 0x0154, 0x0152, 0x0150, 0x014f, 0x014d, 0x014b, 0x0149, 0x0148, + 0x0146, 0x0145, 0x0143, 0x0141, 0x0140, 0x013e, 0x013d, 0x013b, + 0x013a, 0x0138, 0x0137, 0x0135, 0x0134, 0x0132, 0x0131, 0x012f, + 0x012e, 0x012d, 0x012b, 0x012a, 0x0128, 0x0127, 0x0126, 0x0124, + 0x0123, 0x0122, 0x0120, 0x011f, 0x011e, 0x011d, 0x011b, 0x011a, + 0x0119, 0x0118, 0x0117, 0x0115, 0x0114, 0x0113, 0x0112, 0x0111, + 0x0110, 0x010e, 0x010d, 0x010c, 0x010b, 0x010a, 0x0109, 0x0108, + 0x0107, 0x0106, 0x0105, 0x0104, 0x0103, 0x0102, 0x0101, 0x0100, +}; + + +#endif//__LICE_CPP_IMPLEMENTED__ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice.h new file mode 100644 index 000000000..8e4c9273b --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice.h @@ -0,0 +1,639 @@ +#ifndef _LICE_H +#define _LICE_H + +/* + Cockos WDL - LICE - Lightweight Image Compositing Engine + + Copyright (C) 2007 and later, Cockos Incorporated + Portions Copyright (C) 2007 "schwa" + + 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. + +*/ + +#ifdef _WIN32 +#include +#else +#include "../swell/swell-types.h" // use SWELL on other systems +#endif + + +// one of these can be defined in your project if you choose: +//#define LICE_FAVOR_SPEED // optimizes some stuff that doesnt seem to benefit much (like LICE_DeltaBlit/LICE_RotatedBlit/LICE_TransformBlit) +// (nothing) default probably good overall +//#define LICE_FAVOR_SIZE // reduces code size of normal/scaled blit functions +//#define LICE_FAVOR_SIZE_EXTREME // same as LICE_FAVOR_SIZE w/ smaller gains with bigger perf penalties (solid fills etc) + +#ifdef LICE_FAVOR_SPEED + #ifdef LICE_FAVOR_SIZE_EXTREME + #undef LICE_FAVOR_SIZE_EXTREME + #endif + #ifdef LICE_FAVOR_SIZE + #undef LICE_FAVOR_SIZE + #endif +#endif +#if defined(LICE_FAVOR_SIZE_EXTREME) && !defined(LICE_FAVOR_SIZE) +#define LICE_FAVOR_SIZE +#endif + + +typedef unsigned int LICE_pixel; +typedef unsigned char LICE_pixel_chan; + +#define LICE_RGBA(r,g,b,a) (((b)&0xff)|(((g)&0xff)<<8)|(((r)&0xff)<<16)|(((a)&0xff)<<24)) +#define LICE_GETB(v) ((v)&0xff) +#define LICE_GETG(v) (((v)>>8)&0xff) +#define LICE_GETR(v) (((v)>>16)&0xff) +#define LICE_GETA(v) (((v)>>24)&0xff) + +#if defined(__APPLE__) && defined(__ppc__) +#define LICE_PIXEL_A 0 +#define LICE_PIXEL_R 1 +#define LICE_PIXEL_G 2 +#define LICE_PIXEL_B 3 +#else +#define LICE_PIXEL_B 0 +#define LICE_PIXEL_G 1 +#define LICE_PIXEL_R 2 +#define LICE_PIXEL_A 3 +#endif + + +static inline LICE_pixel LICE_RGBA_FROMNATIVE(LICE_pixel col, int alpha=0) +{ + return LICE_RGBA(GetRValue(col),GetGValue(col),GetBValue(col),alpha); +} + +// bitmap interface, and some built-in types (memory bitmap and system bitmap) + +class LICE_IBitmap +{ +public: + virtual ~LICE_IBitmap() { } + + virtual LICE_pixel *getBits()=0; + virtual int getWidth()=0; + virtual int getHeight()=0; + virtual int getRowSpan()=0; // includes any off-bitmap data + virtual bool isFlipped() { return false; } + virtual bool resize(int w, int h)=0; + + virtual HDC getDC() { return 0; } // only sysbitmaps have to implement this + + + virtual INT_PTR Extended(int id, void* data) { return 0; } +}; + +#define LICE_EXT_SET_SCALING 0x2000 // data = int *, scaling is .8 fixed point. returns true if supported. affects LICE_*() draw operations +#define LICE_EXT_GET_SCALING 0x2001 // data ignored, returns .8 fixed point, returns 0 if unscaled +#define LICE_EXT_SET_ADVISORY_SCALING 0x2002 // data = int *, scaling is .8 fixed point. returns true if supported. does not affect draw operations +#define LICE_EXT_GET_ADVISORY_SCALING 0x2003 // data ignored, returns .8 fixed point. returns 0 if unscaled +#define LICE_EXT_GET_ANY_SCALING 0x2004 // data ignored, returns .8 fixed point, 0 if unscaled + +#define LICE_MEMBITMAP_ALIGNAMT 63 + +class LICE_MemBitmap : public LICE_IBitmap +{ +public: + LICE_MemBitmap(int w=0, int h=0, unsigned int linealign=4); + virtual ~LICE_MemBitmap(); + + + // LICE_IBitmap interface + virtual LICE_pixel *getBits() + { + const UINT_PTR extra=LICE_MEMBITMAP_ALIGNAMT; + return (LICE_pixel *) (((UINT_PTR)m_fb + extra)&~extra); + } + virtual int getWidth() { return m_width; } + virtual int getHeight() { return m_height; } + virtual int getRowSpan() { return (m_width+m_linealign)&~m_linealign; } + virtual bool resize(int w, int h) { return __resize(w,h); } // returns TRUE if a resize occurred + + // todo: LICE_EXT_SET_SCALING ? + +private: + bool __resize(int w, int h); + LICE_pixel *m_fb; + int m_width, m_height; + int m_allocsize; + unsigned int m_linealign; +}; + +class LICE_SysBitmap : public LICE_IBitmap +{ +public: + LICE_SysBitmap(int w=0, int h=0); + virtual ~LICE_SysBitmap(); + + // LICE_IBitmap interface + virtual LICE_pixel *getBits() { return m_bits; } + virtual int getWidth() { return m_width; } + virtual int getHeight() { return m_height; } + virtual int getRowSpan() { return m_allocw; }; + virtual bool resize(int w, int h) { return __resize(w,h); } // returns TRUE if a resize occurred + + virtual INT_PTR Extended(int id, void* data) + { + switch (id) + { + case LICE_EXT_SET_ADVISORY_SCALING: + { + int sc = data && *(int*)data != 256 ? *(int *)data : 0; + if (sc < 0) sc = 0; + m_adv_scaling = sc; + } + return 1; + case LICE_EXT_SET_SCALING: + { + int sc = data && *(int*)data != 256 ? *(int *)data : 0; + if (sc < 0) sc = 0; + if (m_draw_scaling != sc) + { + const int tmp=m_width; + m_draw_scaling = sc; + m_width=0; + resize(tmp,m_height); + } + } + return 1; + case LICE_EXT_GET_SCALING: + return m_draw_scaling; + case LICE_EXT_GET_ADVISORY_SCALING: + return m_adv_scaling; + case LICE_EXT_GET_ANY_SCALING: + if (m_draw_scaling > 0) + { + if (m_adv_scaling > 0) + return (m_adv_scaling * m_draw_scaling) >> 8; + return m_draw_scaling; + } + return m_adv_scaling; + } + return 0; + } + + // sysbitmap specific calls + virtual HDC getDC() { return m_dc; } + + +private: + bool __resize(int w, int h); + int m_width, m_height; + + HDC m_dc; + LICE_pixel *m_bits; + int m_allocw, m_alloch; +#ifdef _WIN32 + HBITMAP m_bitmap; + HGDIOBJ m_oldbitmap; +#endif + int m_draw_scaling, m_adv_scaling; +}; + +class LICE_WrapperBitmap : public LICE_IBitmap +{ + public: + LICE_WrapperBitmap(LICE_pixel *buf, int w, int h, int span, bool flipped) + { + m_buf=buf; + m_w=w; + m_h=h; + m_span=span; + m_flipped=flipped; + } + virtual ~LICE_WrapperBitmap() {} + + virtual bool resize(int w, int h) { return false; } + virtual LICE_pixel *getBits() { return m_buf; } + virtual int getWidth() { return m_w; } + virtual int getHeight() { return m_h; } + virtual int getRowSpan() { return m_span; } + + virtual HDC getDC() { return NULL; } + virtual bool isFlipped() { return m_flipped; } + + + LICE_pixel *m_buf; + int m_w,m_h,m_span; + bool m_flipped; +}; + + +class LICE_SubBitmap : public LICE_IBitmap // note: you should only keep these around as long as they are needed, and don't resize the parent while this is allocated +{ + public: + LICE_SubBitmap(LICE_IBitmap *parent, int x, int y, int w, int h) + { + m_parent=parent; + if(x<0)x=0; + if(y<0)y=0; + m_x=x;m_y=y; + __resize(w,h); + } + virtual ~LICE_SubBitmap() { } + + virtual bool resize(int w, int h) { return __resize(w,h); } + + bool __resize(int w, int h) + { + m_w=0;m_h=0; + if (m_parent && m_x >= 0 && m_y >= 0 && m_x < m_parent->getWidth() && m_y < m_parent->getHeight()) + { + if (w > m_parent->getWidth()-m_x) w=m_parent->getWidth()-m_x; + if (h > m_parent->getHeight()-m_y) h=m_parent->getHeight()-m_y; + + m_w=w; + m_h=h; + } + + return true; + } + + virtual bool isFlipped() { return m_parent && m_parent->isFlipped(); } + + virtual LICE_pixel *getBits() + { + if (!m_parent) return 0; + + int xc = m_x, yc = m_y, h = m_h; + const int scale = (int)m_parent->Extended(LICE_EXT_GET_SCALING,NULL); + if (scale > 0) + { + xc = (xc*scale)>>8; + yc = (yc*scale)>>8; + h = (h*scale)>>8; + } + + LICE_pixel* parentptr=m_parent->getBits(); + if (m_parent->isFlipped()) parentptr += (m_parent->getHeight() - (yc+h))*m_parent->getRowSpan()+xc; + else parentptr += yc*m_parent->getRowSpan()+xc; + + return parentptr; + } + + enum { + LICE_GET_SUBBITMAP_VERSION = 0x51b7000, + LICE_SUBBITMAP_VERSION = 0x1000 // if we change any of this struct, then we *must* increment this version. + }; + + virtual INT_PTR Extended(int id, void* data) + { + if (id == LICE_GET_SUBBITMAP_VERSION) return LICE_SUBBITMAP_VERSION; + + if (!m_parent) return 0; + return m_parent->Extended(id, data); + } + + virtual int getWidth() { return m_w; } + virtual int getHeight() { return m_h; } + virtual int getRowSpan() { return m_parent ? m_parent->getRowSpan() : 0; } + + virtual HDC getDC() { return NULL; } + + int m_w,m_h,m_x,m_y; + LICE_IBitmap *m_parent; +}; + + +// flags that most blit functions can take + +#define LICE_BLIT_MODE_MASK 0xff +#define LICE_BLIT_MODE_COPY 0 +#define LICE_BLIT_MODE_ADD 1 +#define LICE_BLIT_MODE_DODGE 2 +#define LICE_BLIT_MODE_MUL 3 +#define LICE_BLIT_MODE_OVERLAY 4 +#define LICE_BLIT_MODE_HSVADJ 5 + +#define LICE_BLIT_MODE_CHANCOPY 0xf0 // in this mode, only available for LICE_Blit(), the low nibble is 2 bits of source channel (low 2), 2 bits of dest channel (high 2) + +#define LICE_BLIT_FILTER_MASK 0xff00 +#define LICE_BLIT_FILTER_NONE 0 +#define LICE_BLIT_FILTER_BILINEAR 0x100 // currently pretty slow! ack +#define LICE_BLIT_IGNORE_SCALING 0x20000 + + +#define LICE_BLIT_USE_ALPHA 0x10000 // use source's alpha channel + +#ifndef lice_max +#define lice_max(x,y) ((x)<(y)?(y):(x)) +#define lice_min(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifdef _MSC_VER + #include + #define lice_isfinite(x) _finite(x) +#else + #define lice_isfinite(x) isfinite(x) +#endif + +// Reaper exports most LICE functions, so the function declarations below +// will collide with reaper_plugin.h +#ifndef LICE_PROVIDED_BY_APP + + +// bitmap loaders + +// dispatch to a linked loader implementation based on file extension +LICE_IBitmap* LICE_LoadImage(const char* filename, LICE_IBitmap* bmp=NULL, bool tryIgnoreExtension=false); +char *LICE_GetImageExtensionList(bool wantAllSup=true, bool wantAllFiles=true); // returns doublenull terminated GetOpenFileName() style list -- free() when done. +bool LICE_ImageIsSupported(const char *filename); // must be a filename that ends in .jpg, etc. if you want to check the extension, pass .ext + + +// pass a bmp if you wish to load it into that bitmap. note that if it fails bmp will not be deleted. +LICE_IBitmap *LICE_LoadPNG(const char *filename, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success +LICE_IBitmap *LICE_LoadPNGFromMemory(const void *data_in, int buflen, LICE_IBitmap *bmp=NULL); +LICE_IBitmap *LICE_LoadPNGFromResource(HINSTANCE hInst, const char *resid, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success +#ifndef _WIN32 +LICE_IBitmap *LICE_LoadPNGFromNamedResource(const char *name, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success +#endif + +LICE_IBitmap *LICE_LoadBMP(const char *filename, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success +LICE_IBitmap *LICE_LoadBMPFromResource(HINSTANCE hInst, const char *resid, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success + +LICE_IBitmap *LICE_LoadIcon(const char *filename, int reqiconsz=16, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success +LICE_IBitmap *LICE_LoadIconFromResource(HINSTANCE hInst, const char *resid, int reqiconsz=16, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success + +LICE_IBitmap *LICE_LoadJPG(const char *filename, LICE_IBitmap *bmp=NULL); +LICE_IBitmap *LICE_LoadJPGFromMemory(const void *data_in, int buflen, LICE_IBitmap *bmp = NULL); +LICE_IBitmap* LICE_LoadJPGFromResource(HINSTANCE hInst, const char *resid, LICE_IBitmap* bmp = 0); + +LICE_IBitmap *LICE_LoadGIF(const char *filename, LICE_IBitmap *bmp=NULL, int *nframes=NULL); // if nframes set, will be set to number of images (stacked vertically), otherwise first frame used + +LICE_IBitmap *LICE_LoadPCX(const char *filename, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success + +// bitmap saving +bool LICE_WritePNG(const char *filename, LICE_IBitmap *bmp, bool wantalpha=true); +bool LICE_WriteJPG(const char *filename, LICE_IBitmap *bmp, int quality=95, bool force_baseline=true); +bool LICE_WriteGIF(const char *filename, LICE_IBitmap *bmp, int transparent_alpha=0, bool dither=true); // if alphaExtended(LICE_EXT_GET_SCALING,NULL); \ + if (rsc>0) \ + StretchBlt(hdc,_x,_y,_w,_h,(src)->getDC(),(_sx*rsc)/256,(_sy*rsc)/256,(_w*rsc)>>8,(_h*rsc)>>8,_mode); \ + else BitBlt(hdc,_x,_y,_w,_h,(src)->getDC(),_sx,_sy,_mode); \ +} while (0) +#else +#define LICE_Scale_BitBlt(hdc, x,y,w,h, src, sx,sy, mode) BitBlt(hdc,x,y,w,h,(src)->getDC(),sx,sy,mode) +#endif + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_arc.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_arc.cpp new file mode 100644 index 000000000..231389b10 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_arc.cpp @@ -0,0 +1,727 @@ +#ifndef WDL_NO_DEFINE_MINMAX +#define WDL_NO_DEFINE_MINMAX +#endif +#include "lice.h" +#include "lice_combine.h" +#include + +#define _PI 3.141592653589793238f + +#define IGNORE_SCALING(mode) ((mode)&LICE_BLIT_IGNORE_SCALING) +template inline void _SWAP(T& a, T& b) { T tmp = a; a = b; b = tmp; } + +#define A(x) ((LICE_pixel_chan)((x)*255.0+0.5)) +#define AF(x) (255) + +#define DEF_ALPHAS(dim) \ + static const LICE_pixel_chan alphas_unfill[] = { __ALPHAS__(A) }; \ + static const LICE_pixel_chan alphas_fill[] = { __ALPHAS__(AF) }; \ + const LICE_pixel_chan * const alphas = fill ? alphas_fill : alphas_unfill; \ + ((void)sizeof(char[1 - 2*(sizeof(alphas_unfill) != dim*dim)])); \ + ((void)sizeof(char[1 - 2*(sizeof(alphas_fill) != dim*dim)])); + +static bool CachedCircle(LICE_IBitmap* dest, float cx, float cy, float r, LICE_pixel color, float alpha, int mode, bool aa, bool fill) +{ + // fast draw for some small circles + if (r == 1.5f) + { + if (aa) + { +#define __ALPHAS__(B) \ + A(0.31), A(1.00), A(1.00), A(0.31), \ + A(1.00), B(0.06), B(0.06), A(1.00), \ + A(1.00), B(0.06), B(0.06), A(1.00), \ + A(0.31), A(1.00), A(1.00), A(0.31), + + DEF_ALPHAS(4) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 4, 4, alpha, mode); + } + else + { +#define __ALPHAS__(B) \ + A(0.00), A(1.00), A(1.00), A(0.00), \ + A(1.00), B(0.00), B(0.00), A(1.00), \ + A(1.00), B(0.00), B(0.00), A(1.00), \ + A(0.00), A(1.00), A(1.00), A(0.00), + + DEF_ALPHAS(4) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 4, 4, alpha, mode); + } + return true; + } + else if (r == 2.0f) + { + if (aa) + { +#define __ALPHAS__(B) \ + A(0.06), A(0.75), A(1.00), A(0.75), A(0.06), \ + A(0.75), A(0.82), B(0.31), A(0.82), A(0.75), \ + A(1.00), B(0.31), B(0.00), B(0.31), A(1.00), \ + A(0.75), A(0.82), B(0.31), A(0.82), A(0.75), \ + A(0.06), A(0.75), A(1.00), A(0.75), A(0.06) + + DEF_ALPHAS(5) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 5, 5, alpha, mode); + } + else + { +#define __ALPHAS__(B) \ + A(0.00), A(0.00), A(1.00), A(0.00), A(0.00), \ + A(0.00), A(1.00), B(0.00), A(1.00), A(0.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(0.00), A(1.00), B(0.00), A(1.00), A(0.00), \ + A(0.00), A(0.00), A(1.00), A(0.00), A(0.00) + + DEF_ALPHAS(5) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 5, 5, alpha, mode); + } + return true; + } + else if (r == 2.5f) { + if (aa) { +#define __ALPHAS__(B) \ + A(0.06), A(0.75), A(1.00), A(1.00), A(0.75), A(0.06), \ + A(0.75), A(0.82), B(0.31), B(0.31), A(0.82), A(0.75), \ + A(1.00), B(0.31), B(0.00), B(0.00), B(0.31), A(1.00), \ + A(1.00), B(0.31), B(0.00), B(0.00), B(0.31), A(1.00), \ + A(0.75), A(0.82), B(0.31), B(0.31), A(0.82), A(0.75), \ + A(0.06), A(0.75), A(1.00), A(1.00), A(0.75), A(0.06) + + DEF_ALPHAS(6) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 6, 6, alpha, mode); + } + else { +#define __ALPHAS__(B) \ + A(0.00), A(0.00), A(1.00), A(1.00), A(0.00), A(0.00), \ + A(0.00), A(1.00), B(0.00), B(0.00), A(1.00), A(0.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(0.00), A(1.00), B(0.00), B(0.00), A(1.00), A(0.00), \ + A(0.00), A(0.00), A(1.00), A(1.00), A(0.00), A(0.00) + + DEF_ALPHAS(6) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 6, 6, alpha, mode); + } + return true; + } + else if (r == 3.0f) { + if (aa) { +#define __ALPHAS__(B) \ + A(0.00), A(0.56), A(1.00), A(1.00), A(1.00), A(0.56), A(0.00), \ + A(0.56), A(1.00), B(0.38), B(0.25), B(0.38), A(1.00), A(0.56), \ + A(1.00), B(0.44), B(0.00), B(0.00), B(0.00), B(0.44), A(1.00), \ + A(1.00), B(0.19), B(0.00), B(0.00), B(0.00), B(0.19), A(1.00), \ + A(1.00), B(0.44), B(0.00), B(0.00), B(0.00), B(0.44), A(1.00), \ + A(0.56), A(1.00), B(0.38), B(0.25), B(0.38), A(1.00), A(0.56), \ + A(0.00), A(0.56), A(1.00), A(1.00), A(1.00), A(0.56), A(0.00) + + DEF_ALPHAS(7) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 7, 7, alpha, mode); + } + else { +#define __ALPHAS__(B) \ + A(0.00), A(0.00), A(1.00), A(1.00), A(1.00), A(0.00), A(0.00), \ + A(0.00), A(1.00), B(0.00), B(0.00), B(0.00), A(1.00), A(0.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(0.00), A(1.00), B(0.00), B(0.00), B(0.00), A(1.00), A(0.00), \ + A(0.00), A(0.00), A(1.00), A(1.00), A(1.00), A(0.00), A(0.00) + + DEF_ALPHAS(7) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 7, 7, alpha, mode); + } + return true; + } + else if (r == 3.5f) { + if (aa) { +#define __ALPHAS__(B) \ + A(0.00), A(0.31), A(0.87), A(1.00), A(1.00), A(0.87), A(0.31), A(0.00), \ + A(0.31), A(1.00), A(0.69), B(0.25), B(0.25), A(0.69), A(1.00), A(0.31), \ + A(0.87), A(0.69), B(0.00), B(0.00), B(0.00), B(0.00), A(0.69), A(0.87), \ + A(1.00), B(0.25), B(0.00), B(0.00), B(0.00), B(0.00), B(0.25), A(1.00), \ + A(1.00), B(0.25), B(0.00), B(0.00), B(0.00), B(0.00), B(0.25), A(1.00), \ + A(0.87), A(0.69), B(0.00), B(0.00), B(0.00), B(0.00), A(0.69), A(0.87), \ + A(0.31), A(1.00), A(0.69), B(0.25), B(0.25), A(0.69), A(1.00), A(0.31), \ + A(0.00), A(0.31), A(0.87), A(1.00), A(1.00), A(0.87), A(0.31), A(0.00) + + DEF_ALPHAS(8) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 8, 8, alpha, mode); + } + else { +#define __ALPHAS__(B) \ + A(0.00), A(0.00), A(1.00), A(1.00), A(1.00), A(1.00), A(0.00), A(0.00), \ + A(0.00), A(1.00), A(1.00), B(0.00), B(0.00), A(1.00), A(1.00), A(0.00), \ + A(1.00), A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), A(1.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(1.00), A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), A(1.00), \ + A(0.00), A(1.00), A(1.00), B(0.00), B(0.00), A(1.00), A(1.00), A(0.00), \ + A(0.00), A(0.00), A(1.00), A(1.00), A(1.00), A(1.00), A(0.00), A(0.00) + + DEF_ALPHAS(8) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 8, 8, alpha, mode); + } + return true; + } + else if (r == 4.0f) { + if (aa) { +#define __ALPHAS__(B) \ + A(0.00), A(0.12), A(0.69), A(1.00), A(1.00), A(1.00), A(0.69), A(0.12), A(0.00), \ + A(0.12), A(0.94), A(0.82), B(0.31), B(0.25), B(0.31), A(0.82), A(0.94), A(0.12), \ + A(0.69), A(0.82), B(0.06), B(0.00), B(0.00), B(0.00), B(0.06), A(0.82), A(0.69), \ + A(1.00), B(0.31), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.31), A(1.00), \ + A(1.00), B(0.19), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.19), A(1.00), \ + A(1.00), B(0.31), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.31), A(1.00), \ + A(0.69), A(0.82), B(0.06), B(0.00), B(0.00), B(0.00), B(0.06), A(0.82), A(0.69), \ + A(0.12), A(0.94), A(0.82), B(0.31), B(0.25), B(0.31), A(0.82), A(0.94), A(0.12), \ + A(0.00), A(0.12), A(0.69), A(1.00), A(1.00), A(1.00), A(0.69), A(0.12), A(0.00) + + DEF_ALPHAS(9) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 9, 9, alpha, mode); + } + else { +#define __ALPHAS__(B) \ + A(0.00), A(0.00), A(1.00), A(1.00), A(1.00), A(1.00), A(1.00), A(0.00), A(0.00), \ + A(0.00), A(1.00), A(1.00), B(0.00), B(0.00), B(0.00), A(1.00), A(1.00), A(0.00), \ + A(1.00), A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), A(1.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(1.00), A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), A(1.00), \ + A(0.00), A(1.00), A(1.00), B(0.00), B(0.00), B(0.00), A(1.00), A(1.00), A(0.00), \ + A(0.00), A(0.00), A(1.00), A(1.00), A(1.00), A(1.00), A(1.00), A(0.00), A(0.00) + + DEF_ALPHAS(9) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 9, 9, alpha, mode); + } + return true; + } + else if (r == 5.0f) + { + if (aa) { +#define __ALPHAS__(B) \ + A(0.00), A(0.00), A(0.00), A(0.58), A(0.90), A(1.00), A(0.90), A(0.58), A(0.00), A(0.00), A(0.00), \ + A(0.00), A(0.00), A(1.00), B(0.42), B(0.10), B(0.00), B(0.10), B(0.42), A(1.00), A(0.00), A(0.00), \ + A(0.00), A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), A(0.00), \ + A(0.58), B(0.42), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.42), A(0.58), \ + A(0.90), B(0.10), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.10), A(0.90), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(0.90), B(0.10), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.10), A(0.90), \ + A(0.58), B(0.42), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.42), A(0.58), \ + A(0.00), A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), A(0.00), \ + A(0.00), A(0.00), A(1.00), B(0.42), B(0.10), B(0.00), B(0.10), B(0.42), A(1.00), A(0.00), A(0.00), \ + A(0.00), A(0.00), A(0.00), A(0.58), A(0.90), A(1.00), A(0.90), A(0.58), A(0.00), A(0.00), A(0.00) + + DEF_ALPHAS(11) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 11, 11, alpha, mode); + return true; + } + } + else if (r == 6.0f) + { + if (aa) { +#define __ALPHAS__(B) \ + A(0.00), A(0.00), A(0.00), A(0.20), A(0.66), A(0.92), A(1.00), A(0.92), A(0.66), A(0.20), A(0.00), A(0.00), A(0.00), \ + A(0.00), A(0.00), A(0.47), A(0.81), B(0.35), B(0.09), B(0.00), B(0.09), B(0.35), A(0.81), A(0.47), A(0.00), A(0.00), \ + A(0.00), A(0.47), B(0.53), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.53), A(0.47), A(0.00), \ + A(0.20), A(0.81), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(0.81), A(0.20), \ + A(0.66), B(0.35), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.35), A(0.66), \ + A(0.92), B(0.09), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.09), A(0.92), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(0.92), B(0.09), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.09), A(0.92), \ + A(0.66), B(0.35), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.35), A(0.66), \ + A(0.20), A(0.81), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(0.81), A(0.20), \ + A(0.00), A(0.47), B(0.53), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.53), A(0.47), A(0.00), \ + A(0.00), A(0.00), A(0.47), A(0.81), B(0.35), B(0.09), B(0.00), B(0.09), B(0.35), A(0.81), A(0.47), A(0.00), A(0.00), \ + A(0.00), A(0.00), A(0.00), A(0.20), A(0.66), A(0.92), A(1.00), A(0.92), A(0.66), A(0.20), A(0.00), A(0.00), A(0.00), + + DEF_ALPHAS(13) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 13, 13, alpha, mode); + return true; + } + } + else if (r == 7.0f) + { + if (aa) { +#define __ALPHAS__(B) \ + A(0.00), A(0.00), A(0.00), A(0.00), A(0.33), A(0.71), A(0.93), A(1.00), A(0.93), A(0.71), A(0.33), A(0.00), A(0.00), A(0.00), A(0.00), \ + A(0.00), A(0.00), A(0.00), A(0.75), A(0.68), B(0.29), B(0.07), B(0.00), B(0.07), B(0.29), A(0.68), A(0.75), A(0.00), A(0.00), A(0.00), \ + A(0.00), A(0.00), A(0.90), B(0.26), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.34), A(0.90), A(0.00), A(0.00), \ + A(0.00), A(0.75), B(0.34), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.26), A(0.75), A(0.00), \ + A(0.33), A(0.68), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(0.68), A(0.33), \ + A(0.71), B(0.29), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.29), A(0.71), \ + A(0.93), B(0.07), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.07), A(0.93), \ + A(1.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(1.00), \ + A(0.93), B(0.07), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.07), A(0.93), \ + A(0.71), B(0.29), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.29), A(0.71), \ + A(0.33), A(0.68), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), A(0.68), A(0.33), \ + A(0.00), A(0.75), B(0.34), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.26), A(0.75), A(0.00), \ + A(0.00), A(0.00), A(0.90), B(0.26), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.00), B(0.34), A(0.90), A(0.00), A(0.00), \ + A(0.00), A(0.00), A(0.00), A(0.75), A(0.68), B(0.29), B(0.07), B(0.00), B(0.07), B(0.29), A(0.68), A(0.75), A(0.00), A(0.00), A(0.00), \ + A(0.00), A(0.00), A(0.00), A(0.00), A(0.33), A(0.71), A(0.93), A(1.00), A(0.93), A(0.71), A(0.33), A(0.00), A(0.00), A(0.00), A(0.00), + + DEF_ALPHAS(15) +#undef __ALPHAS__ + LICE_DrawGlyph(dest, cx-r, cy-r, color, alphas, 15, 15, alpha, mode); + return true; + } + } + + return false; +} + + +template class _LICE_CircleDrawer +{ +public: + + static void DrawClippedPt(LICE_IBitmap* dest, int x, int y, const int *clip, + int r, int g, int b, int a, int alpha, bool doclip) + { + if (doclip && (x < clip[0] || x >= clip[2] || y < clip[1] || y >= clip[3])) return; + LICE_pixel* px = dest->getBits()+y*dest->getRowSpan()+x; + COMBFUNC::doPix((LICE_pixel_chan*)px, r, g, b, a, alpha); + } + + static void DrawClippedHorzLine(LICE_IBitmap* dest, int y, int xlo, int xhi, const int *clip, + int r, int g, int b, int a, int alpha, bool doclip) + { + if (doclip) + { + if (y < clip[1] || y >= clip[3]) return; + xlo = lice_max(xlo, clip[0]); + xhi = lice_min(xhi, clip[2]-1); + } + LICE_pixel* px = dest->getBits()+y*dest->getRowSpan()+xlo; + while (xlo <= xhi) + { + COMBFUNC::doPix((LICE_pixel_chan*)px, r, g, b, a, alpha); + ++px; + ++xlo; + } + } + + static void DrawClippedVertLine(LICE_IBitmap* dest, int x, int ylo, int yhi, const int *clip, + int r, int g, int b, int a, int alpha, bool doclip) + { + if (doclip) + { + if (x < clip[0] || x >= clip[2]) return; + ylo = lice_max(ylo, clip[1]); + yhi = lice_min(yhi, clip[3]-1); + } + int span=dest->getRowSpan(); + LICE_pixel* px = dest->getBits()+ylo*span+x; + while (ylo <= yhi) + { + COMBFUNC::doPix((LICE_pixel_chan*)px, r, g, b, a, alpha); + px += span; + ++ylo; + } + } + + static void DrawClippedCircleAA(LICE_IBitmap* dest, float cx, float cy, float rad, + const int *clip, LICE_pixel color, int ai, bool filled, bool doclip) + { + int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + const int cx0=(int)(cx+0.5f); + const int cy0=(int)(cy+0.5f); + + int y=(int)rad; + double w=rad-floor(rad); + int wa=(int)((double)ai*w); + + DrawClippedPt(dest, cx0, cy0-y-1, clip, r, g, b, a, wa, doclip); + DrawClippedPt(dest, cx0, cy0+y+1, clip, r, g, b, a, wa, doclip); + DrawClippedPt(dest, cx0-y-1, cy0, clip, r, g, b, a, wa, doclip); + DrawClippedPt(dest, cx0+y+1, cy0, clip, r, g, b, a, wa, doclip); + + if (filled) + { + DrawClippedVertLine(dest, cx0, cy0-y, cy0-1, clip, r, g, b, a, ai, doclip); + DrawClippedVertLine(dest, cx0, cy0+1, cy0+y, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0, cx0-y, cx0+y, clip, r, g, b, a, ai, doclip); + } + else + { + int iwa=ai-wa; + DrawClippedPt(dest, cx0, cy0-y, clip, r, g, b, a, iwa, doclip); + DrawClippedPt(dest, cx0+y, cy0, clip, r, g, b, a, iwa, doclip); + DrawClippedPt(dest, cx0, cy0+y, clip, r, g, b, a, iwa, doclip); + DrawClippedPt(dest, cx0-y, cy0, clip, r, g, b, a, iwa, doclip); + } + + double r2=rad*rad; + double yf=sqrt(r2-1.0); + int yl=(int)(yf+0.5); + + int x=1; + while (x <= yl) + { + y=(int)yf; + w=yf-floor(yf); + wa=(int)((double)ai*w); + + DrawClippedPt(dest, cx0-x, cy0-y-1, clip, r, g, b, a, wa, doclip); + DrawClippedPt(dest, cx0-x, cy0+y+1, clip, r, g, b, a, wa, doclip); + DrawClippedPt(dest, cx0+x, cy0-y-1, clip, r, g, b, a, wa, doclip); + DrawClippedPt(dest, cx0+x, cy0+y+1, clip, r, g, b, a, wa, doclip); + if (x != yl) + { + DrawClippedPt(dest, cx0-y-1, cy0-x, clip, r, g, b, a, wa, doclip); + DrawClippedPt(dest, cx0+y+1, cy0-x, clip, r, g, b, a, wa, doclip); + DrawClippedPt(dest, cx0-y-1, cy0+x, clip, r, g, b, a, wa, doclip); + DrawClippedPt(dest, cx0+y+1, cy0+x, clip, r, g, b, a, wa, doclip); + } + + if (filled) + { + DrawClippedVertLine(dest, cx0-x, cy0-y, cy0-x-1, clip, r, g, b, a, ai, doclip); + DrawClippedVertLine(dest, cx0-x, cy0+x+1, cy0+y, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0-x, cx0-y, cx0-x, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0-x, cx0+x, cx0+y, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0+x, cx0-y, cx0-x, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0+x, cx0+x, cx0+y, clip, r, g, b, a, ai, doclip); + DrawClippedVertLine(dest, cx0+x, cy0-y, cy0-x-1, clip, r, g, b, a, ai, doclip); + DrawClippedVertLine(dest, cx0+x, cy0+x+1, cy0+y, clip, r, g, b, a, ai, doclip); + } + else + { + int iwa=ai-wa; + DrawClippedPt(dest, cx0-y, cy0-x, clip, r, g, b, a, iwa, doclip); + DrawClippedPt(dest, cx0+y, cy0-x, clip, r, g, b, a, iwa, doclip); + DrawClippedPt(dest, cx0-x, cy0+y, clip, r, g, b, a, iwa, doclip); + DrawClippedPt(dest, cx0+x, cy0+y, clip, r, g, b, a, iwa, doclip); + if (x != yl) + { + DrawClippedPt(dest, cx0-x, cy0-y, clip, r, g, b, a, iwa, doclip); + DrawClippedPt(dest, cx0+x, cy0-y, clip, r, g, b, a, iwa, doclip); + DrawClippedPt(dest, cx0-y, cy0+x, clip, r, g, b, a, iwa, doclip); + DrawClippedPt(dest, cx0+y, cy0+x, clip, r, g, b, a, iwa, doclip); + } + } + + ++x; + yf=sqrt(r2-(double)(x*x)); + yl=(int)(yf+0.5); + } + } + + static void DrawClippedCircle(LICE_IBitmap* dest, float cx, float cy, float rad, + const int *clip, LICE_pixel color, int ai, bool filled, bool doclip) + { + const int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + const int cx0=(int)(cx+0.5f); + const int cy0=(int)(cy+0.5f); + const int r0=(int)(rad+0.5f); + + if (filled) + { + DrawClippedVertLine(dest, cx0, cy0-r0, cy0-1, clip, r, g, b, a, ai, doclip); + DrawClippedVertLine(dest, cx0, cy0+1, cy0+r0, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0, cx0-r0, cx0+r0, clip, r, g, b, a, ai, doclip); + } + else + { + DrawClippedPt(dest, cx0, cy0-r0, clip, r, g, b, a, ai, doclip); + DrawClippedPt(dest, cx0+r0, cy0, clip, r, g, b, a, ai, doclip); + DrawClippedPt(dest, cx0, cy0+r0, clip, r, g, b, a, ai, doclip); + DrawClippedPt(dest, cx0-r0, cy0, clip, r, g, b, a, ai, doclip); + } + + int x=0; + int y=r0; + int e=-r0; + while (++x < y) + { + if (e < 0) + { + e += 2*x+1; + } + else + { + --y; + e += 2*(x-y)+1; + } + + if (filled) + { + DrawClippedVertLine(dest, cx0-x, cy0-y, cy0-x-1, clip, r, g, b, a, ai, doclip); + DrawClippedVertLine(dest, cx0-x, cy0+x+1, cy0+y, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0-x, cx0-y, cx0-x, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0-x, cx0+x, cx0+y, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0+x, cx0-y, cx0-x, clip, r, g, b, a, ai, doclip); + DrawClippedHorzLine(dest, cy0+x, cx0+x, cx0+y, clip, r, g, b, a, ai, doclip); + DrawClippedVertLine(dest, cx0+x, cy0-y, cy0-x-1, clip, r, g, b, a, ai, doclip); + DrawClippedVertLine(dest, cx0+ x, cy0+x+1, cy0+y, clip, r, g, b, a, ai, doclip); + } + else + { + DrawClippedPt(dest, cx0-x, cy0-y, clip, r, g, b, a, ai, doclip); + DrawClippedPt(dest, cx0-x, cy0+y, clip, r, g, b, a, ai, doclip); + DrawClippedPt(dest, cx0+x, cy0-y, clip, r, g, b, a, ai, doclip); + DrawClippedPt(dest, cx0+x, cy0+y, clip, r, g, b, a, ai, doclip); + if (x != y) + { + DrawClippedPt(dest, cx0-y, cy0-x, clip, r, g, b, a, ai, doclip); + DrawClippedPt(dest, cx0-y, cy0+x, clip, r, g, b, a, ai, doclip); + DrawClippedPt(dest, cx0+y, cy0-x, clip, r, g, b, a, ai, doclip); + DrawClippedPt(dest, cx0+y, cy0+x, clip, r, g, b, a, ai, doclip); + } + } + } + } + +}; + + +static void __DrawCircleClipped(LICE_IBitmap* dest, float cx, float cy, float rad, + LICE_pixel color, int ia, bool aa, bool filled, int mode, const int *clip, bool doclip) +{ + // todo: more clipped/filled versions (to optimize constants out?) + if (aa) + { + #define __LICE__ACTION(COMBFUNC) _LICE_CircleDrawer::DrawClippedCircleAA(dest, cx, cy, rad, clip, color, ia, filled, doclip) + __LICE_ACTION_NOSRCALPHA(mode, ia,false) + #undef __LICE__ACTION + } + else + { + #define __LICE__ACTION(COMBFUNC) _LICE_CircleDrawer::DrawClippedCircle(dest, cx, cy, rad, clip, color, ia, filled, doclip) + __LICE_ACTION_CONSTANTALPHA(mode,ia,false) + #undef __LICE__ACTION + } +} + + +static void __DrawArc(int w, int h, LICE_IBitmap* dest, float cx, float cy, float rad, double anglo, double anghi, + LICE_pixel color, int ialpha, bool aa, int mode) +{ + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(cx); + __LICE_SC(cy); + __LICE_SC(rad); + } + } + // -2PI <= anglo <= anghi <= 2PI + anglo += 2.0*_PI; + anghi += 2.0*_PI; + + // 0 <= anglo <= anghi <= 4PI + + double next_ang = anglo - fmod(anglo,0.5*_PI); + + int ly = (int)(cy - rad*cos(anglo) + 0.5); + int lx = (int)(cx + rad*sin(anglo) + 0.5); + + while (anglo < anghi) + { + next_ang += 0.5*_PI; + if (next_ang > anghi) next_ang = anghi; + + int yhi = (int) (cy-rad*cos(next_ang)+0.5); + int xhi = (int) (cx+rad*sin(next_ang)+0.5); + int ylo = ly; + int xlo = lx; + + ly = yhi; + lx = xhi; + + if (yhi < ylo) { int tmp = ylo; ylo = yhi; yhi=tmp; } + if (xhi < xlo) { int tmp = xlo; xlo = xhi; xhi=tmp; } + + anglo = next_ang; + + if (xhi != cx) xhi++; + if (yhi != cy) yhi++; + + const int clip[4]={lice_max(xlo,0),lice_max(0, ylo),lice_min(w,xhi+1),lice_min(h, yhi+1)}; + + __DrawCircleClipped(dest,cx,cy,rad,color,ialpha,aa,false,mode,clip,true); + } +} + +void LICE_Arc(LICE_IBitmap* dest, float cx, float cy, float r, float minAngle, float maxAngle, + LICE_pixel color, float alpha, int mode, bool aa) +{ + if (!dest) return; + + if (dest->isFlipped()) { cy=dest->getHeight()-1-cy; minAngle=_PI-minAngle; maxAngle=_PI-maxAngle; } + + if (maxAngle < minAngle) + { + float tmp=maxAngle; + maxAngle=minAngle; + minAngle=tmp; + } + + if (maxAngle - minAngle >= 2.0f*_PI) + { + LICE_Circle(dest,cx,cy,r,color,alpha,mode,aa); + return; + } + + if (maxAngle >= 2.0f*_PI) + { + float tmp = fmod(maxAngle,2.0f*_PI); + minAngle -= maxAngle - tmp; // reduce by factors of 2PI + maxAngle = tmp; + } + else if (minAngle <= -2.0f*_PI) + { + float tmp = fmod(minAngle,2.0f*_PI); + maxAngle -= minAngle - tmp; // toward zero by factors of 2pi + minAngle = tmp; + } + + // -2PI <= minAngle <= maxAngle <= 2PI + + int ia = (int) (alpha*256.0f); + if (!ia) return; + + __DrawArc(dest->getWidth(),dest->getHeight(),dest,cx,cy,r,minAngle,maxAngle,color,ia,aa,mode); +} + + + + +void LICE_Circle(LICE_IBitmap* dest, float cx, float cy, float r, LICE_pixel color, float alpha, int mode, bool aa) +{ + if (!dest) return; + + int w = dest->getWidth(), h = dest->getHeight(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(cx); + __LICE_SC(cy); + __LICE_SC(r); + } + } + + const int clip[4] = { 0, 0, w, h }; + if (w < 1 || h <1 || r<0 || + (int)cx+(int)r < -2 || (int)cy + (int)r < - 2 || + (int)cx-(int)r > w + 2 || (int)cy - (int)r > h + 2 + ) return; + + int ia = (int) (alpha*256.0f); + if (!ia) return; + + if (CachedCircle(dest, cx, cy, r, color, alpha, mode|LICE_BLIT_IGNORE_SCALING, aa, false)) return; + + if (dest->isFlipped()) cy=h-1-cy; + + const bool doclip = !(cx-r-2 >= 0 && cy-r-2 >= 0 && cx+r+2 < w && cy+r+2 < h); + + __DrawCircleClipped(dest,cx,cy,r,color,ia,aa,false,mode,clip,doclip); +} + +void LICE_FillCircle(LICE_IBitmap* dest, float cx, float cy, float r, LICE_pixel color, float alpha, int mode, bool aa) +{ + if (!dest) return; + + int w = dest->getWidth(), h = dest->getHeight(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(cx); + __LICE_SC(cy); + __LICE_SC(r); + } + } + + if (w < 1 || h < 1 || r < 0.0 || + (int)cx+(int)r < -2 || (int)cy + (int)r < - 2 || + (int)cx-(int)r > w + 2 || (int)cy - (int)r > h + 2 + ) return; + + const int ia = (int) (alpha*256.0f); + if (!ia) return; + + if (CachedCircle(dest, cx, cy, r, color, alpha, mode|LICE_BLIT_IGNORE_SCALING, aa, true)) return; + + if (dest->isFlipped()) cy=h-1-cy; + + const int clip[4] = { 0, 0, w, h }; + + const bool doclip = !(cx-r-2 >= 0 && cy-r-2 >= 0 && cx+r+2 < w && cy+r+2 < h); + __DrawCircleClipped(dest,cx,cy,r,color,ia,aa,true,mode,clip,doclip); +} + + +void LICE_RoundRect(LICE_IBitmap *drawbm, float xpos, float ypos, float w, float h, int cornerradius, + LICE_pixel col, float alpha, int mode, bool aa) +{ + if (cornerradius>0) + { + float cr=cornerradius; + if (cr > w*0.5) cr=w*0.5; + if (cr > h*0.5) cr=h*0.5; + cr=floor(cr); + + if (cr>=2) + { + double adj = 0.0; + const int __sc = IGNORE_SCALING(mode) ? 0 : drawbm ? (int)drawbm->Extended(LICE_EXT_GET_SCALING,NULL) : 0; + if (__sc>0) + { + adj = 1.0 - 256.0/__sc; + + LICE_FLine(drawbm,xpos+cr+adj,ypos+adj,xpos+w-cr,ypos+adj,col,alpha,mode,true); + LICE_FLine(drawbm,xpos+cr-1+adj,ypos+h-adj,xpos+w-cr-adj,ypos+h-adj,col,alpha,mode,true); + LICE_FLine(drawbm,xpos+w-adj,ypos+cr+adj,xpos+w-adj,ypos+h-cr-adj,col,alpha,mode,true); + LICE_FLine(drawbm,xpos+adj,ypos+cr-1+adj,xpos+adj,ypos+h-cr-adj,col,alpha,mode,true); +// aa=true; + } + else + { + LICE_Line(drawbm,xpos+cr,ypos,xpos+w-cr,ypos,col,alpha,mode,aa); + LICE_Line(drawbm,xpos+cr-1,ypos+h,xpos+w-cr,ypos+h,col,alpha,mode,aa); + LICE_Line(drawbm,xpos+w,ypos+cr,xpos+w,ypos+h-cr,col,alpha,mode,aa); + LICE_Line(drawbm,xpos,ypos+cr-1,xpos,ypos+h-cr,col,alpha,mode,aa); + } + + LICE_Arc(drawbm,xpos+cr+adj,ypos+cr+adj,cr,-_PI*0.5f,0,col,alpha,mode,aa); + LICE_Arc(drawbm,xpos+w-cr-adj,ypos+cr+adj,cr,0,_PI*0.5f,col,alpha,mode,aa); + LICE_Arc(drawbm,xpos+w-cr-adj,ypos+h-cr-adj,cr,_PI*0.5f,_PI,col,alpha,mode,aa); + LICE_Arc(drawbm,xpos+cr+adj,ypos+h-cr-adj,cr,_PI,_PI*1.5f,col,alpha,mode,aa); + + return; + } + } + + LICE_DrawRect(drawbm, xpos, ypos, w, h, col, alpha, mode); +} + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_bezier.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_bezier.h new file mode 100644 index 000000000..4089ca52d --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_bezier.h @@ -0,0 +1,300 @@ +#ifndef _LICE_BEZIER_ +#define _LICE_BEZIER_ + +#include "lice.h" +#include + +// Returns quadratic bezier x, y for a given t in [0,1]. +template +void LICE_Bezier(T ctrl_x1, T ctrl_x2, T ctrl_x3, + T ctrl_y1, T ctrl_y2, T ctrl_y3, double t, T* pX, T* pY) +{ + double it = 1.0 - t; + double a = it * it; + double b = 2.0 * it * t; + double c = t * t; + *pX = (T) (a * (double) ctrl_x1 + b * (double) ctrl_x2 + c * (double) ctrl_x3); + *pY = (T) (a * (double) ctrl_y1 + b * (double) ctrl_y2 + c * (double) ctrl_y3); +} + +template +void LICE_CBezier_GetCoeffs(T ctrl_x1, T ctrl_x2, T ctrl_x3, T ctrl_x4, + T ctrl_y1, T ctrl_y2, T ctrl_y3, T ctrl_y4, + double* pAX, double* pBX, double* pCX, + double* pAY, double* pBY, double* pCY) +{ + double cx = *pCX = 3.0 * (double) (ctrl_x2 - ctrl_x1); + double bx = *pBX = 3.0 * (double) (ctrl_x3 - ctrl_x2) - cx; + *pAX = (double) (ctrl_x4 - ctrl_x1) - cx - bx; + double cy = *pCY = 3.0 * (double) (ctrl_y2 - ctrl_y1); + double by = *pBY = 3.0 * (double) (ctrl_y3 - ctrl_y2) - cy; + *pAY = (double) (ctrl_y4 - ctrl_y1) - cy - by; +} + +// Returns cubic bezier x, y for a given t in [0,1]. +template +void LICE_CBezier(T ctrl_x1, T ctrl_x2, T ctrl_x3, T ctrl_x4, + T ctrl_y1, T ctrl_y2, T ctrl_y3, T ctrl_y4, double t, T* pX, T* pY) +{ + double ax, bx, cx, ay, by, cy; + LICE_CBezier_GetCoeffs(ctrl_x1, ctrl_x2, ctrl_x3, ctrl_x4, + ctrl_y1, ctrl_y2, ctrl_y3, ctrl_y4, + &ax, &bx, &cx, &ay, &by, &cy); + + double t2 = t * t; + double t3 = t * t2; + *pX = (T) (ax * t3 + bx * t2 + cx * t) + ctrl_x1; + *pY = (T) (ay * t3 + by * t2 + cy * t) + ctrl_y1; +} + +// Returns quadratic bezier y for a given x in [x1, x3] (for rasterizing). +// ctrl_x1 < ctrl_x3 required. +template +T LICE_Bezier_GetY(T ctrl_x1, T ctrl_x2, T ctrl_x3, T ctrl_y1, T ctrl_y2, T ctrl_y3, T x, double* pt=0) +{ + if (x <= ctrl_x1) + { + if (pt) *pt = 0.0; + return ctrl_y1; + } + if (x >= ctrl_x3) + { + if (pt) *pt = 1.0; + return ctrl_y3; + } + + double t, a = (double) ctrl_x1 - (double) (2 * ctrl_x2) + (double) ctrl_x3; + if (a == 0.0) + { + t=(ctrl_x1 == ctrl_x3) ? 0.0 : (x-ctrl_x1)/(ctrl_x3-ctrl_x1); + } + else + { + t = (double) (ctrl_x2 - ctrl_x1); + t = (-t + sqrt(t * t - a * (ctrl_x1 - x))) / a; + } + const double it = 1.0 - t; + + if (pt) *pt = t; + return (T) (it * it * (double) ctrl_y1 + t * (2.0*it*(double)ctrl_y2 + t * (double) ctrl_y3)); +} + +// Special case for x = y = [0,1] +template +void LICE_Bezier_Norm(T ctrl_x2, T ctrl_y2, double t, T* pX, T* pY) +{ + double b = 2.0 * (1.0 - t) * t; + double c = t * t; + *pX = (T) (b * (double) ctrl_x2 + c); + *pY = (T) (b * (double) ctrl_y2 + c); +} + +// special case for x = y = [0,1]. +template +T LICE_Bezier_GetY_Norm(T ctrl_x2, T ctrl_y2, T x) +{ + if (x < (T) 0.0) { + return (T) 0.0; + } + if (x >= (T) 1.0) { + return (T) 1.0; + } + if (ctrl_x2 == (T) 0.5) { // linear + return x; + } + + +/* + // this causes ICC 11.0 to produce bad results on OSX/386 + double b = (double) (2 * ctrl_x2); + double a = 1.0 - b; + double c = (double) -x; + double t = (-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a); + + b = 2.0 * (1.0 - t) * t; + c = t * t; + return (T) (b * (double) ctrl_y2 + c); + + // the simplified math below works properly +*/ + + + const double t = (-ctrl_x2 + sqrt(ctrl_x2 * (ctrl_x2 - 2.0*x) + x)) / (1.0-2.0*ctrl_x2); + + return (T) (((2.0 * (1.0-t)) * ctrl_y2 + t)*t); +} + +// Finds the cardinal bezier control points surrounding x2. +// Cubic bezier over (x1,x1b,x2a,x2), (y1,y1b,y2a,y2) or +// quadratic bezier over (x1,x1b,mid(x1b,x2a)), (y1,y1b,mid(y1b,y2a)) +// will smoothly interpolate between (x1,y1) and (x2,y2) while preserving all existing values. +// The lower alpha is, the more tame the bezier curve will be (0.25 = subtle). +template +void LICE_Bezier_FindCardinalCtlPts(double alpha, T x1, T x2, T x3, T y1, T y2, T y3, + T* ctrl_x2a, T* ctrl_x2b, T* ctrl_y2a, T* ctrl_y2b) +{ + double dxa = alpha * (double) (x2 - x1); + double dxb = alpha * (double) (x3 - x2); + if (ctrl_x2a) *ctrl_x2a = x2 - (T) dxa; + if (ctrl_x2b) *ctrl_x2b = x2 + (T) dxb; + + if (x1 == x3) + { + if (ctrl_y2a) *ctrl_y2a = y2; + if (ctrl_y2b) *ctrl_y2b = y2; + } + else + { + double m = (double) (y3 - y1) / (double) (x3 - x1); + if (ctrl_y2a) *ctrl_y2a = y2 - (T) (m * dxa); + if (ctrl_y2b) *ctrl_y2b = y2 + (T) (m * dxb); + } +} + +// Basic quadratic nurbs. Given a set of n (x,y) pairs, +// populate pDest with the unit-spaced nurbs curve. +// pDest must be passed in with size (int) (*(pX+n-1) - *pX). +// pX must be monotonically increasing and no duplicates. +template +inline void LICE_QNurbs(T* pDest, int pDest_sz, int *pX, T* pY, int n) +{ + int x1 = *pX++, x2 = *pX++; + T y1 = *pY++, y2 = *pY++; + double xm1, xm2 = 0.5 * (x1 + x2); + double ym1, ym2 = 0.5 * (y1 + y2); + + double yi = y1, m = (y2 - y1) / (double) (x2 - x1); + int xi = x1, iend = (int)floor(xm2+0.5); // this (and below) was previously ceil(), but can't see any reason why it should matter (this should be more correct, I'd imagine) + for (; xi < iend; xi++, yi += m) + { + if (--pDest_sz<0) return; + *pDest++ = (T) yi; + } + + for (int i = 2; i < n; ++i) + { + x1 = x2; + x2 = *pX++; + y1 = y2; + y2 = *pY++; + + xm1 = xm2; + xm2 = 0.5 * (x1 + x2); + ym1 = ym2; + ym2 = 0.5 * (y1 + y2); + + iend = (int)floor(xm2+0.5); + if (ym1 == ym2 && y1 == ym1) + { + for (; xi < iend; xi++) + { + if (--pDest_sz<0) return; + *pDest++ = (T) y1; + } + } + else + { + for (; xi < iend; xi++) + { + if (--pDest_sz<0) return; + *pDest++ = (T) LICE_Bezier_GetY(xm1, (double)x1, xm2, ym1, (double)y1, ym2, (double)xi); + } + } + } + + m = (y2 - y1) / (double) (x2 - x1); + yi = ym2; + for (; xi < x2; xi++, yi += m) + { + if (--pDest_sz<0) return; + *pDest++ = (T) yi; + } +} + +#define CBEZ_ITERS 8 + +#define EVAL_CBEZ(tx,a,b,c,d,t) \ +{ \ + double _t2=t*t; \ + tx=(a*t*_t2+b*_t2+c*t+d); \ +} + +#define EVAL_CBEZXY(tx, ty, ax, bx, cx, dx, ay, by, cy, dy, t) \ +{ \ + double _t2=t*t; \ + double _t3=t*_t2; \ + tx=ax*_t3+bx*_t2+cx*t+dx; \ + ty=ay*_t3+by*_t2+cy*t+dy; \ +} + +template +T LICE_CBezier_GetY(T ctrl_x1, T ctrl_x2, T ctrl_x3, T ctrl_x4, + T ctrl_y1, T ctrl_y2, T ctrl_y3, T ctrl_y4, T x, + T* pNextX = 0, T* pdYdX = 0, double* ptLo = 0, double* ptHi = 0) +{ + if (x < ctrl_x1) + { + if (pNextX) *pNextX = ctrl_x1; + if (pdYdX) *pdYdX = (T) 0.0; + return ctrl_y1; + } + if (x >= ctrl_x4) + { + if (pNextX) *pNextX = ctrl_x4; + if (pdYdX) *pdYdX = (T) 0.0; + return ctrl_y4; + } + + double ax, bx, cx, ay, by, cy; + LICE_CBezier_GetCoeffs(ctrl_x1, ctrl_x2, ctrl_x3, ctrl_x4, + ctrl_y1, ctrl_y2, ctrl_y3, ctrl_y4, + &ax, &bx, &cx, &ay, &by, &cy); + + double tx, t, tLo = 0.0, tHi = 1.0; + double xLo=0.0, xHi=0.0, yLo, yHi; + int i; + for (i = 0; i < CBEZ_ITERS; ++i) + { + t = 0.5 * (tLo + tHi); + EVAL_CBEZ(tx, ax, bx, cx, (double) ctrl_x1, t); + if (tx < (double) x) + { + tLo = t; + xLo = tx; + } + else if (tx > (double) x) + { + tHi = t; + xHi = tx; + } + else + { + tLo = t; + xLo = tx; + tHi = t + 1.0/pow(2.0,CBEZ_ITERS); + if (tHi > 1.0) tHi = 1.0; // floating point error + EVAL_CBEZ(xHi, ax, bx, cx, (double) ctrl_x1, tHi); + break; + } + } + + if (tLo == 0.0) EVAL_CBEZ(xLo, ax, bx, cx, (double) ctrl_x1, 0.0); + if (tHi == 1.0) EVAL_CBEZ(xHi, ax, bx, cx, (double) ctrl_x1, 1.0); + + EVAL_CBEZ(yLo, ay, by, cy, (double) ctrl_y1, tLo); + EVAL_CBEZ(yHi, ay, by, cy, (double) ctrl_y1, tHi); + + double dYdX = (xLo == xHi ? 0.0 : (yHi - yLo) / (xHi - xLo)); + double y = yLo + ((double) x - xLo) * dYdX; + + if (pNextX) *pNextX = (T) xHi; + if (pdYdX) *pdYdX = (T) dYdX; + + if (ptLo) *ptLo = tLo; + if (ptHi) *ptHi = tHi; + + return (T) y; +} + +#endif + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_colorspace.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_colorspace.cpp new file mode 100644 index 000000000..f8acb0da9 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_colorspace.cpp @@ -0,0 +1,151 @@ +#ifndef WDL_NO_DEFINE_MINMAX +#define WDL_NO_DEFINE_MINMAX +#endif +#include "lice.h" +#include + +#define LICE_COMBINE_IMPLEMENT_HSV +#include "lice_combine.h" + + +LICE_pixel LICE_AlterColorHSV_int(LICE_pixel color, int dH, int dS, int dV) // H is rolled over [0,384), S and V are clamped [0,255) +{ + int h, s, v; + LICE_RGB2HSV(LICE_GETR(color), LICE_GETG(color), LICE_GETB(color), &h, &s, &v); + + h += dH; + s += dS; + v += dV; + + if (h < 0) h += 384; + else if (h >= 384) h -= 384; + + if (s & ~255) + { + if (s<0) s = 0; + else s = 255; + } + + if (v&~255) + { + if (v < 0) v = 0.; + else v = 255; + } + + return LICE_HSV2Pix(h, s, v, LICE_GETA(color)); +} + +LICE_pixel LICE_AlterColorHSV(LICE_pixel color, float dH, float dS, float dV) // H is rolled over, S and V are clamped, all [0,1) +{ + int dHi = (int)(dH*384.0f); + int dSi = (int)(dS*255.0f); + int dVi = (int)(dV*255.0f); + return LICE_AlterColorHSV_int(color, dHi, dSi, dVi); +} + +void LICE_AlterBitmapHSV(LICE_IBitmap* src, float dH, float dS, float dV) // H is rolled over, S and V are clamped +{ + if (src) LICE_AlterRectHSV(src,0,0,src->getWidth(),src->getHeight(),dH,dS,dV); +} + +void LICE_AlterRectHSV(LICE_IBitmap* src, int xpos, int ypos, int w, int h, float dH, float dS, float dV, int mode) // H is rolled over, S and V are clamped +{ + if (!src) return; + + int destbm_w = src->getWidth(), destbm_h = src->getHeight(); + const int __sc = (int)src->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + if (!(mode & LICE_BLIT_IGNORE_SCALING)) + { + __LICE_SC(w); + __LICE_SC(h); + __LICE_SC(xpos); + __LICE_SC(ypos); + } + } + + if (xpos < 0) { + w += xpos; + xpos = 0; + } + if (ypos < 0) { + h += ypos; + ypos = 0; + } + + const int span = src->getRowSpan(); + if (span < 1 || w < 1 || h < 1 || xpos >= destbm_w || ypos >= destbm_h) return; + + if (w > destbm_w - xpos) w = destbm_w - xpos; + if (h > destbm_h - ypos) h = destbm_h - ypos; + + LICE_pixel* px = src->getBits()+ypos*span+xpos; + + int dHi = (int)(dH*384.0f); + int dSi = (int)(dS*255.0f); + int dVi = (int)(dV*255.0f); + if (dHi > 383) dHi=383; + else if (dHi < -383) dHi=-383; + + + if (!dHi && !dSi && !dVi) return; // no mod + + if (w*h > 8192) + { + // generate a table of HSV translations with clip/clamp + unsigned char stab[256], vtab[256]; + short htab[384]; + int x; + for(x=0;x<256;x++) + { + int a=x+dSi; + if(a<0)a=0; else if (a>255)a=255; + stab[x]=a; + + a=x+dVi; + if(a<0)a=0; else if (a>255)a=255; + vtab[x]=a; + + a=x+dHi; + if(a<0)a+=384; else if (a>=384)a-=384; + htab[x]=a; + } + for(;x<384;x++) + { + int a=x+dHi; + if(a<0)a+=384; else if (a>=384)a-=384; + htab[x]=a; + } + + while (h-->0) + { + LICE_pixel* tpx = px; + px+=span; + int xi=w; + while (xi-->0) + { + LICE_pixel color = *tpx; + int hh,s,v; + LICE_RGB2HSV(LICE_GETR(color), LICE_GETG(color), LICE_GETB(color), &hh, &s, &v); + *tpx++ = LICE_HSV2Pix(htab[hh],stab[s],vtab[v],LICE_GETA(color)); + } + } + } + else + { + while (h-->0) + { + LICE_pixel* tpx = px; + px+=span; + int xi=w; + while (xi-->0) + { + *tpx = LICE_AlterColorHSV_int(*tpx, dHi, dSi, dVi); + tpx++; + } + } + } +} diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_combine.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_combine.h new file mode 100644 index 000000000..602af311a --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_combine.h @@ -0,0 +1,826 @@ +#ifndef _LICE_COMBINE_H_ +#define _LICE_COMBINE_H_ + + +#if defined(_MSC_VER) +#pragma warning(disable:4244) // float-to-int +#endif + +#define __LICE_BOUND(x,lo,hi) ((x)<(lo)?(lo):((x)>(hi)?(hi):(x))) + + +#define LICE_PIXEL_HALF(x) (((x)>>1)&0x7F7F7F7F) +#define LICE_PIXEL_QUARTER(x) (((x)>>2)&0x3F3F3F3F) +#define LICE_PIXEL_EIGHTH(x) (((x)>>3)&0x1F1F1F1F) + + +static inline void __LICE_BilinearFilterI(int *r, int *g, int *b, int *a, const LICE_pixel_chan *pin, const LICE_pixel_chan *pinnext, unsigned int xfrac, unsigned int yfrac) +{ + const unsigned int f4=(xfrac*yfrac)>>16; + const unsigned int f3=yfrac-f4; // (1.0-xfrac)*yfrac; + const unsigned int f2=xfrac-f4; // xfrac*(1.0-yfrac); + const unsigned int f1=65536-yfrac-xfrac+f4; // (1.0-xfrac)*(1.0-yfrac); + #define DOCHAN(output, inchan) \ + (output)=(pin[(inchan)]*f1 + pin[4+(inchan)]*f2 + pinnext[(inchan)]*f3 + pinnext[4+(inchan)]*f4)>>16; + DOCHAN(*r,LICE_PIXEL_R) + DOCHAN(*g,LICE_PIXEL_G) + DOCHAN(*b,LICE_PIXEL_B) + DOCHAN(*a,LICE_PIXEL_A) + #undef DOCHAN +} + +static inline void __LICE_BilinearFilterIPixOut(LICE_pixel_chan *out, const LICE_pixel_chan *pin, const LICE_pixel_chan *pinnext, unsigned int xfrac, unsigned int yfrac) +{ + const unsigned int f4=(xfrac*yfrac)>>16; + const unsigned int f3=yfrac-f4; // (1.0-xfrac)*yfrac; + const unsigned int f2=xfrac-f4; // xfrac*(1.0-yfrac); + const unsigned int f1=65536-yfrac-xfrac+f4; // (1.0-xfrac)*(1.0-yfrac); + #define DOCHAN(inchan) \ + (out[inchan])=(pin[(inchan)]*f1 + pin[4+(inchan)]*f2 + pinnext[(inchan)]*f3 + pinnext[4+(inchan)]*f4)>>16; + DOCHAN(LICE_PIXEL_R) + DOCHAN(LICE_PIXEL_G) + DOCHAN(LICE_PIXEL_B) + DOCHAN(LICE_PIXEL_A) + #undef DOCHAN +} + + +static inline void __LICE_BilinearFilterI_2(int *r, int *g, int *b, int *a, const LICE_pixel_chan *pin, const LICE_pixel_chan *pinnext, int npoffs, unsigned int xfrac, unsigned int yfrac) +{ + const unsigned int f4=(xfrac*yfrac)>>16; + const unsigned int f3=yfrac-f4; // (1.0-xfrac)*yfrac; + const unsigned int f2=xfrac-f4; // xfrac*(1.0-yfrac); + const unsigned int f1=65536-yfrac-xfrac+f4; // (1.0-xfrac)*(1.0-yfrac); + *r=(pin[LICE_PIXEL_R]*f1 + pin[npoffs+LICE_PIXEL_R]*f2 + pinnext[LICE_PIXEL_R]*f3 + pinnext[npoffs+LICE_PIXEL_R]*f4)>>16; + *g=(pin[LICE_PIXEL_G]*f1 + pin[npoffs+LICE_PIXEL_G]*f2 + pinnext[LICE_PIXEL_G]*f3 + pinnext[npoffs+LICE_PIXEL_G]*f4)>>16; + *b=(pin[LICE_PIXEL_B]*f1 + pin[npoffs+LICE_PIXEL_B]*f2 + pinnext[LICE_PIXEL_B]*f3 + pinnext[npoffs+LICE_PIXEL_B]*f4)>>16; + *a=(pin[LICE_PIXEL_A]*f1 + pin[npoffs+LICE_PIXEL_A]*f2 + pinnext[LICE_PIXEL_A]*f3 + pinnext[npoffs+LICE_PIXEL_A]*f4)>>16; +} + + +static inline void __LICE_LinearFilterI(int *r, int *g, int *b, int *a, const LICE_pixel_chan *pin, const LICE_pixel_chan *pinnext, unsigned int frac) +{ + const unsigned int f=65536-frac; + *r=(pin[LICE_PIXEL_R]*f + pinnext[LICE_PIXEL_R]*frac)>>16; + *g=(pin[LICE_PIXEL_G]*f + pinnext[LICE_PIXEL_G]*frac)>>16; + *b=(pin[LICE_PIXEL_B]*f + pinnext[LICE_PIXEL_B]*frac)>>16; + *a=(pin[LICE_PIXEL_A]*f + pinnext[LICE_PIXEL_A]*frac)>>16; +} +static inline void __LICE_LinearFilterIPixOut(LICE_pixel_chan *out, const LICE_pixel_chan *pin, const LICE_pixel_chan *pinnext, unsigned int frac) +{ + const unsigned int f=65536-frac; + out[LICE_PIXEL_R]=(pin[LICE_PIXEL_R]*f + pinnext[LICE_PIXEL_R]*frac)>>16; + out[LICE_PIXEL_G]=(pin[LICE_PIXEL_G]*f + pinnext[LICE_PIXEL_G]*frac)>>16; + out[LICE_PIXEL_B]=(pin[LICE_PIXEL_B]*f + pinnext[LICE_PIXEL_B]*frac)>>16; + out[LICE_PIXEL_A]=(pin[LICE_PIXEL_A]*f + pinnext[LICE_PIXEL_A]*frac)>>16; +} + +static void inline _LICE_MakePixelClamp(LICE_pixel_chan *out, int r, int g, int b, int a) +{ +#define LICE_PIX_MAKECHAN(a,b) out[a] = (b&~0xff) ? (b<0?0:255) : b; + LICE_PIX_MAKECHAN(LICE_PIXEL_B,b) + LICE_PIX_MAKECHAN(LICE_PIXEL_G,g) + LICE_PIX_MAKECHAN(LICE_PIXEL_R,r) + LICE_PIX_MAKECHAN(LICE_PIXEL_A,a) +#undef LICE_PIX_MAKECHAN +} + +static void inline _LICE_MakePixelNoClamp(LICE_pixel_chan *out, LICE_pixel_chan r, LICE_pixel_chan g, LICE_pixel_chan b, LICE_pixel_chan a) +{ +#define LICE_PIX_MAKECHAN(a,b) out[a] = b; + LICE_PIX_MAKECHAN(LICE_PIXEL_B,b) + LICE_PIX_MAKECHAN(LICE_PIXEL_G,g) + LICE_PIX_MAKECHAN(LICE_PIXEL_R,r) + LICE_PIX_MAKECHAN(LICE_PIXEL_A,a) +#undef LICE_PIX_MAKECHAN +} + + + +#define HSV_P v*(256-s)/256 +#define HSV_Q(hval) v*(16384-(hval)*s)/16384 +#define HSV_T(hval) v*(16384-(64-(hval))*s)/16384 +#define HSV_X v +extern unsigned short _LICE_RGB2HSV_invtab[256]; // 65536/idx - 1 + +#ifdef LICE_COMBINE_IMPLEMENT_HSV + LICE_pixel LICE_HSV2Pix(int h, int s, int v, int alpha) + #define __LICE_HSV2Pix LICE_HSV2Pix +#else + static inline LICE_pixel __LICE_HSV2Pix(int h, int s, int v, int alpha) +#endif +{ + if (h<192) + { + if (h<64) return LICE_RGBA(HSV_X,HSV_T(h),HSV_P,alpha); + if (h<128) return LICE_RGBA(HSV_Q(h-64),HSV_X,HSV_P,alpha); + return LICE_RGBA(HSV_P,HSV_X,HSV_T(h-128),alpha); + } + if (h < 256) return LICE_RGBA(HSV_P,HSV_Q(h-192),HSV_X,alpha); + if (h < 320) return LICE_RGBA(HSV_T(h-256),HSV_P,HSV_X,alpha); + return LICE_RGBA(HSV_X,HSV_P,HSV_Q(h-320),alpha); +} + +#ifdef LICE_COMBINE_IMPLEMENT_HSV +void LICE_HSV2RGB(int h, int s, int v, int* r, int* g, int* b) +#define __LICE_HSV2RGB LICE_HSV2RGB +#else +static inline void __LICE_HSV2RGB(int h, int s, int v, int* r, int* g, int* b) +#endif +{ + if (h<192) + { + if (h<64) + { + *r = HSV_X; *g = HSV_T(h); *b = HSV_P; + } + else if (h<128) + { + *r = HSV_Q(h-64); *g = HSV_X; *b = HSV_P; + } + else + { + *r = HSV_P; *g = HSV_X; *b = HSV_T(h-128); + } + } + else + { + if (h < 256) + { + *r = HSV_P; *g = HSV_Q(h-192); *b = HSV_X; + } + else if (h < 320) + { + *r = HSV_T(h-256); *g = HSV_P; *b = HSV_X; + } + else + { + *r = HSV_X; *g = HSV_P; *b = HSV_Q(h-320); + } + } +} + + +#define LICE_RGB2HSV_USE_TABLE +// h = [0,384), s and v = [0,256) + +#ifdef LICE_COMBINE_IMPLEMENT_HSV + void LICE_RGB2HSV(int r, int g, int b, int* h, int* s, int* v) + #define __LICE_RGB2HSV LICE_RGB2HSV +#else + static inline void __LICE_RGB2HSV(int r, int g, int b, int* h, int* s, int* v) +#endif +{ + + // this makes it just 3 conditional branches per call + int df,d,maxrgb; + int degoffs; + if (g > r) + { + if (g>b) // green max + { + maxrgb=g; + degoffs=128; + df = maxrgb - lice_min(b,r); + d=b-r; + } + else // blue max + { + maxrgb=b; + degoffs=256; + df = maxrgb - lice_min(g,r); + d=r-g; + } + } + else // r >= g + { + if (r > b) // red max + { + maxrgb=r; + + if (g>1, + (dest[LICE_PIXEL_G]+g)>>1, + (dest[LICE_PIXEL_B]+b)>>1, + (dest[LICE_PIXEL_A]+a)>>1); + } +}; + +class _LICE_CombinePixelsHalfMixFAST +{ +public: + static inline void doPixFAST(LICE_pixel *dest, LICE_pixel src) // src is full range + { + *dest = ((*dest>>1) &0x7f7f7f7f) + ((src>>1)&0x7f7f7f7f); + } +}; + +class _LICE_CombinePixelsHalfMixClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + _LICE_MakePixelClamp(dest, + (dest[LICE_PIXEL_R]+r)>>1, + (dest[LICE_PIXEL_G]+g)>>1, + (dest[LICE_PIXEL_B]+b)>>1, + (dest[LICE_PIXEL_A]+a)>>1); + } + +}; + + +class _LICE_CombinePixelsHalfMix2FAST +{ +public: + static inline void doPixFAST(LICE_pixel *dest, LICE_pixel src) // src is pre-halfed and masked + { + *dest = ((*dest>>1) &0x7f7f7f7f) + src; + } +}; + +class _LICE_CombinePixelsQuarterMix2FAST +{ +public: + static inline void doPixFAST(LICE_pixel *dest, LICE_pixel src) // src is pre-quartered and masked + { + LICE_pixel tmp = *dest; + *dest = ((tmp>>1) &0x7f7f7f7f) + ((tmp>>2) &0x3f3f3f3f) + src; + } +}; + +class _LICE_CombinePixelsThreeEighthMix2FAST +{ +public: + static inline void doPixFAST(LICE_pixel *dest, LICE_pixel src) // src is pre-three-eighthed and masked + { + LICE_pixel tmp = *dest; + *dest = ((tmp>>1) &0x7f7f7f7f) + ((tmp>>3) &0x1f1f1f1f) + src; + } +}; + +class _LICE_CombinePixelsThreeQuarterMix2FAST +{ +public: + static inline void doPixFAST(LICE_pixel *dest, LICE_pixel src) // src is pre-three-quartered and masked + { + *dest = ((*dest>>2) &0x3f3f3f3f) + src; + } +}; + +class _LICE_CombinePixelsCopyNoClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + const int sc=(256-alpha); + + // don't check alpha=0 here, since the caller should (since alpha is usually used for static alphas) + _LICE_MakePixelNoClamp(dest, + r + ((dest[LICE_PIXEL_R]-r)*sc)/256, + g + ((dest[LICE_PIXEL_G]-g)*sc)/256, + b + ((dest[LICE_PIXEL_B]-b)*sc)/256, + a + ((dest[LICE_PIXEL_A]-a)*sc)/256); + } +}; + +class _LICE_CombinePixelsCopyClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + const int sc=(256-alpha); + + // don't check alpha=0 here, since the caller should (since alpha is usually used for static alphas) + _LICE_MakePixelClamp(dest, + r + ((dest[LICE_PIXEL_R]-r)*sc)/256, + g + ((dest[LICE_PIXEL_G]-g)*sc)/256, + b + ((dest[LICE_PIXEL_B]-b)*sc)/256, + a + ((dest[LICE_PIXEL_A]-a)*sc)/256); + } +}; + +class _LICE_CombinePixelsCopySourceAlphaNoClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + if (a) + { + const int sc2=(alpha*(a+1))/256; + const int sc = 256 - sc2; + + _LICE_MakePixelNoClamp(dest, + r + ((dest[LICE_PIXEL_R]-r)*sc)/256, + g + ((dest[LICE_PIXEL_G]-g)*sc)/256, + b + ((dest[LICE_PIXEL_B]-b)*sc)/256, + lice_min(255,sc2 + dest[LICE_PIXEL_A])); + } + } +}; + +class _LICE_CombinePixelsCopySourceAlphaClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + if (a) + { + const int sc2=(alpha*(a+1))/256; + const int sc = 256 - sc2; + + _LICE_MakePixelClamp(dest, + r + ((dest[LICE_PIXEL_R]-r)*sc)/256, + g + ((dest[LICE_PIXEL_G]-g)*sc)/256, + b + ((dest[LICE_PIXEL_B]-b)*sc)/256, + sc2 + dest[LICE_PIXEL_A]); + } + } +}; +class _LICE_CombinePixelsCopySourceAlphaIgnoreAlphaParmNoClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + if (a) + { + if (a==255) + { + _LICE_MakePixelNoClamp(dest,r,g,b,a); + } + else + { + const int sc=(255-a); + + _LICE_MakePixelNoClamp(dest, + r + ((dest[LICE_PIXEL_R]-r)*sc)/256, + g + ((dest[LICE_PIXEL_G]-g)*sc)/256, + b + ((dest[LICE_PIXEL_B]-b)*sc)/256, + lice_min(255,a + dest[LICE_PIXEL_A])); + } + } + } +}; +class _LICE_CombinePixelsCopySourceAlphaIgnoreAlphaParmClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + if (a) + { + if (a==255) + { + _LICE_MakePixelClamp(dest,r,g,b,a); + } + else + { + const int sc=(255-a); + + _LICE_MakePixelClamp(dest, + r + ((dest[LICE_PIXEL_R]-r)*sc)/256, + g + ((dest[LICE_PIXEL_G]-g)*sc)/256, + b + ((dest[LICE_PIXEL_B]-b)*sc)/256, + a + dest[LICE_PIXEL_A]); + } + } + } +}; + +#ifndef LICE_DISABLE_BLEND_ADD + +class _LICE_CombinePixelsAdd +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + // don't check alpha=0 here, since the caller should (since alpha is usually used for static alphas) + + _LICE_MakePixelClamp(dest, + dest[LICE_PIXEL_R]+(r*alpha)/256, + dest[LICE_PIXEL_G]+(g*alpha)/256, + dest[LICE_PIXEL_B]+(b*alpha)/256, + dest[LICE_PIXEL_A]+(a*alpha)/256); + + } +}; +class _LICE_CombinePixelsAddSourceAlpha +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + if (a) + { + alpha=(alpha*(a+1))/256; + _LICE_MakePixelClamp(dest, + dest[LICE_PIXEL_R]+(r*alpha)/256, + dest[LICE_PIXEL_G]+(g*alpha)/256, + dest[LICE_PIXEL_B]+(b*alpha)/256, + dest[LICE_PIXEL_A]+(a*alpha)/256); + } + } +}; + +#else // !LICE_DISABLE_BLEND_ADD +#define _LICE_CombinePixelsAddSourceAlpha _LICE_CombinePixelsCopySourceAlphaClamp +#define _LICE_CombinePixelsAdd _LICE_CombinePixelsCopyClamp +#endif + +#ifndef LICE_DISABLE_BLEND_DODGE + +class _LICE_CombinePixelsColorDodge +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + const int src_r = 256-r*alpha/256; + const int src_g = 256-g*alpha/256; + const int src_b = 256-b*alpha/256; + const int src_a = 256-a*alpha/256; + + _LICE_MakePixelClamp(dest, + src_r > 1 ? 256*dest[LICE_PIXEL_R] / src_r : 256*dest[LICE_PIXEL_R], + src_g > 1 ? 256*dest[LICE_PIXEL_G] / src_g : 256*dest[LICE_PIXEL_G], + src_b > 1 ? 256*dest[LICE_PIXEL_B] / src_b : 256*dest[LICE_PIXEL_B], + src_a > 1 ? 256*dest[LICE_PIXEL_A] / src_a : 256*dest[LICE_PIXEL_A]); + } +}; + +class _LICE_CombinePixelsColorDodgeSourceAlpha +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + const int ualpha=(alpha*(a+1))/256; + + const int src_r = 256-r*ualpha/256; + const int src_g = 256-g*ualpha/256; + const int src_b = 256-b*ualpha/256; + const int src_a = 256-a*ualpha/256; + + _LICE_MakePixelClamp(dest, + src_r > 1 ? 256*dest[LICE_PIXEL_R] / src_r : 256*dest[LICE_PIXEL_R], + src_g > 1 ? 256*dest[LICE_PIXEL_G] / src_g : 256*dest[LICE_PIXEL_G], + src_b > 1 ? 256*dest[LICE_PIXEL_B] / src_b : 256*dest[LICE_PIXEL_B], + src_a > 1 ? 256*dest[LICE_PIXEL_A] / src_a : 256*dest[LICE_PIXEL_A]); + } +}; + +#else // !LICE_DISABLE_BLEND_DODGE +#define _LICE_CombinePixelsColorDodgeSourceAlpha _LICE_CombinePixelsCopySourceAlphaClamp +#define _LICE_CombinePixelsColorDodge _LICE_CombinePixelsCopyClamp +#endif + + +#ifndef LICE_DISABLE_BLEND_MUL + +class _LICE_CombinePixelsMulNoClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + // we could check alpha=0 here, but the caller should (since alpha is usually used for static alphas) + + const int da=(256-alpha)*256; + _LICE_MakePixelNoClamp(dest, + (dest[LICE_PIXEL_R]*(da + (r*alpha)))>>16, + (dest[LICE_PIXEL_G]*(da + (g*alpha)))>>16, + (dest[LICE_PIXEL_B]*(da + (b*alpha)))>>16, + (dest[LICE_PIXEL_A]*(da + (a*alpha)))>>16); + + } +}; +class _LICE_CombinePixelsMulClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + // we could check alpha=0 here, but the caller should (since alpha is usually used for static alphas) + + const int da=(256-alpha)*256; + _LICE_MakePixelClamp(dest, + (dest[LICE_PIXEL_R]*(da + (r*alpha)))>>16, + (dest[LICE_PIXEL_G]*(da + (g*alpha)))>>16, + (dest[LICE_PIXEL_B]*(da + (b*alpha)))>>16, + (dest[LICE_PIXEL_A]*(da + (a*alpha)))>>16); + + } +}; +class _LICE_CombinePixelsMulSourceAlphaNoClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + if (a) + { + const int ualpha=(alpha*(a+1))/256; + const int da=(256-ualpha)*256; + _LICE_MakePixelNoClamp(dest, + (dest[LICE_PIXEL_R]*(da + (r*ualpha)))>>16, + (dest[LICE_PIXEL_G]*(da + (g*ualpha)))>>16, + (dest[LICE_PIXEL_B]*(da + (b*ualpha)))>>16, + (dest[LICE_PIXEL_A]*(da + (a*ualpha)))>>16); + + } + } +}; +class _LICE_CombinePixelsMulSourceAlphaClamp +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + if (a) + { + const int ualpha=(alpha*(a+1))/256; + const int da=(256-ualpha)*256; + _LICE_MakePixelClamp(dest, + (dest[LICE_PIXEL_R]*(da + (r*ualpha)))>>16, + (dest[LICE_PIXEL_G]*(da + (g*ualpha)))>>16, + (dest[LICE_PIXEL_B]*(da + (b*ualpha)))>>16, + (dest[LICE_PIXEL_A]*(da + (a*ualpha)))>>16); + + } + } +}; + +#else // !LICE_DISABLE_BLEND_MUL +#define _LICE_CombinePixelsMulSourceAlphaNoClamp _LICE_CombinePixelsCopySourceAlphaNoClamp +#define _LICE_CombinePixelsMulSourceAlphaClamp _LICE_CombinePixelsCopySourceAlphaClamp +#define _LICE_CombinePixelsMulNoClamp _LICE_CombinePixelsCopyNoClamp +#define _LICE_CombinePixelsMulClamp _LICE_CombinePixelsCopyClamp +#endif + +//#define LICE_DISABLE_BLEND_OVERLAY +#ifndef LICE_DISABLE_BLEND_OVERLAY + +class _LICE_CombinePixelsOverlay +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + // we could check alpha=0 here, but the caller should (since alpha is usually used for static alphas) + + int destr = dest[LICE_PIXEL_R], destg = dest[LICE_PIXEL_G], destb = dest[LICE_PIXEL_B], desta = dest[LICE_PIXEL_A]; + +#if 0 + int srcr = r*alpha, srcg = g*alpha, srcb = b*alpha, srca = a*alpha; + int da=(256-alpha)*256; + int mr = (destr*(da+srcr))/65536; + int mg = (destg*(da+srcg))/65536; + int mb = (destb*(da+srcb))/65536; + int ma = (desta*(da+srca))/65536; + int sr = 256-(65536-srcr)*(256-destr)/65536; + int sg = 256-(65536-srcg)*(256-destg)/65536; + int sb = 256-(65536-srcb)*(256-destb)/65536; + int sa = 256-(65536-srca)*(256-desta)/65536; + + destr = (destr*sr+(256-destr)*mr)/256; + destg = (destg*sg+(256-destg)*mg)/256; + destb = (destb*sb+(256-destb)*mb)/256; + desta = (desta*sa+(256-desta)*ma)/256; +#else + // can produce slightly diff (+-1) results from above due to rounding + const int da=(256-alpha)*128; + const int srcr = r*alpha+da, srcg = g*alpha+da, srcb = b*alpha+da, srca = a*alpha + da; + destr = ( destr*( (destr*(32768-srcr))/256 + srcr ) ) >> 15; + destg = ( destg*( (destg*(32768-srcg))/256 + srcg ) ) >> 15; + destb = ( destb*( (destb*(32768-srcb))/256 + srcb ) ) >> 15; + desta = ( desta*( (desta*(32768-srca))/256 + srca ) ) >> 15; + +#endif + + _LICE_MakePixelClamp(dest, destr, destg, destb, desta); + } +}; + +class _LICE_CombinePixelsOverlaySourceAlpha +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + _LICE_CombinePixelsOverlay::doPix(dest, r, g, b, a, (alpha*(a+1))/256); + } +}; + +#else // !LICE_DISABLE_BLEND_OVERLAY +#define _LICE_CombinePixelsOverlaySourceAlpha _LICE_CombinePixelsCopySourceAlphaClamp +#define _LICE_CombinePixelsOverlay _LICE_CombinePixelsCopyClamp +#endif + + +//#define LICE_DISABLE_BLEND_HSVADJ +#ifndef LICE_DISABLE_BLEND_HSVADJ + +class _LICE_CombinePixelsHSVAdjust +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + int h,s,v; + __LICE_RGB2HSV(dest[LICE_PIXEL_R],dest[LICE_PIXEL_G],dest[LICE_PIXEL_B],&h,&s,&v); + h+=(((r+r/2) - 192) * alpha)/256; + if (h<0)h+=384; + else if (h>=384) h-=384; + s+=((g-128)*alpha)/128; + if (s&~0xff) + { + if (s<0)s=0; + else s=255; + } + v+=((b-128)*alpha)/128; + if (v&~0xff) + { + if (v<0)v=0; + else v=255; + } + + *(LICE_pixel *)dest = __LICE_HSV2Pix(h,s,v,a); + } +}; + +class _LICE_CombinePixelsHSVAdjustSourceAlpha +{ +public: + static inline void doPix(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha) + { + _LICE_CombinePixelsHSVAdjust::doPix(dest, r, g, b, a, (alpha*(a+1))/256); + } +}; + +#else // !LICE_DISABLE_BLEND_HSVADJ +#define _LICE_CombinePixelsHSVAdjustSourceAlpha _LICE_CombinePixelsCopySourceAlphaClamp +#define _LICE_CombinePixelsHSVAdjust _LICE_CombinePixelsCopyClamp +#endif + +// note: the "clamp" parameter would generally be false, unless you're working with +// input colors that need to be clamped (i.e. if you have a r value of >255 or <0, etc. +// if your input is LICE_pixel only then use false, and it will clamp as needed depending +// on the blend mode.. + +//#define __LICE__ACTION(comb) templateclass::function(parameters) +//__LICE_ACTION_SRCALPHA(mode,alpha,clamp); +//#undef __LICE__ACTION + + +// use this for paths that support LICE_BLIT_USE_ALPHA (source-alpha combining), but +// otherwise have constant alpha +#define __LICE_ACTION_SRCALPHA(mode,ia,clamp) \ + if ((ia)!=0) switch ((mode)&(LICE_BLIT_MODE_MASK|LICE_BLIT_USE_ALPHA)) { \ + case LICE_BLIT_MODE_COPY: if ((ia)>0) { \ + if (clamp) { \ + if ((ia)==256) { __LICE__ACTION(_LICE_CombinePixelsClobberClamp); } \ + else { __LICE__ACTION(_LICE_CombinePixelsCopyClamp); } \ + } else { \ + if ((ia)==256) { __LICE__ACTION(_LICE_CombinePixelsClobberNoClamp); } \ + else { __LICE__ACTION(_LICE_CombinePixelsCopyNoClamp); } \ + } \ + } \ + break; \ + case LICE_BLIT_MODE_ADD: __LICE__ACTION(_LICE_CombinePixelsAdd); break; \ + case LICE_BLIT_MODE_DODGE: __LICE__ACTION(_LICE_CombinePixelsColorDodge); break; \ + case LICE_BLIT_MODE_MUL: \ + if (clamp) { __LICE__ACTION(_LICE_CombinePixelsMulClamp); } \ + else { __LICE__ACTION(_LICE_CombinePixelsMulNoClamp); } \ + break; \ + case LICE_BLIT_MODE_OVERLAY: __LICE__ACTION(_LICE_CombinePixelsOverlay); break; \ + case LICE_BLIT_MODE_HSVADJ: __LICE__ACTION(_LICE_CombinePixelsHSVAdjust); break; \ + case LICE_BLIT_MODE_COPY|LICE_BLIT_USE_ALPHA: \ + if (clamp) { \ + if ((ia)==256) { __LICE__ACTION(_LICE_CombinePixelsCopySourceAlphaIgnoreAlphaParmClamp);} \ + else { __LICE__ACTION(_LICE_CombinePixelsCopySourceAlphaClamp); } \ + } else { \ + if ((ia)==256) { __LICE__ACTION(_LICE_CombinePixelsCopySourceAlphaIgnoreAlphaParmNoClamp); } \ + else { __LICE__ACTION(_LICE_CombinePixelsCopySourceAlphaNoClamp); } \ + } \ + break; \ + case LICE_BLIT_MODE_ADD|LICE_BLIT_USE_ALPHA: \ + __LICE__ACTION(_LICE_CombinePixelsAddSourceAlpha); \ + break; \ + case LICE_BLIT_MODE_DODGE|LICE_BLIT_USE_ALPHA: \ + __LICE__ACTION(_LICE_CombinePixelsColorDodgeSourceAlpha); \ + break; \ + case LICE_BLIT_MODE_MUL|LICE_BLIT_USE_ALPHA: \ + if (clamp) { __LICE__ACTION(_LICE_CombinePixelsMulSourceAlphaClamp); } \ + else { __LICE__ACTION(_LICE_CombinePixelsMulSourceAlphaNoClamp); } \ + break; \ + case LICE_BLIT_MODE_OVERLAY|LICE_BLIT_USE_ALPHA: \ + __LICE__ACTION(_LICE_CombinePixelsOverlaySourceAlpha); \ + break; \ + case LICE_BLIT_MODE_HSVADJ|LICE_BLIT_USE_ALPHA: \ + __LICE__ACTION(_LICE_CombinePixelsHSVAdjustSourceAlpha); \ + break; \ + } + + +// use this for paths that can have per pixel alpha, but calculate it themselves +#define __LICE_ACTION_NOSRCALPHA(mode, ia,clamp) \ + if ((ia)!=0) switch ((mode)&LICE_BLIT_MODE_MASK) { \ + case LICE_BLIT_MODE_COPY: if ((ia)>0) { if (clamp) { __LICE__ACTION(_LICE_CombinePixelsCopyClamp); } else { __LICE__ACTION(_LICE_CombinePixelsCopyNoClamp); } } break; \ + case LICE_BLIT_MODE_ADD: __LICE__ACTION(_LICE_CombinePixelsAdd); break; \ + case LICE_BLIT_MODE_DODGE: __LICE__ACTION(_LICE_CombinePixelsColorDodge); break; \ + case LICE_BLIT_MODE_MUL: if (clamp) { __LICE__ACTION(_LICE_CombinePixelsMulClamp); } else { __LICE__ACTION(_LICE_CombinePixelsMulNoClamp); } break; \ + case LICE_BLIT_MODE_OVERLAY: __LICE__ACTION(_LICE_CombinePixelsOverlay); break; \ + case LICE_BLIT_MODE_HSVADJ: __LICE__ACTION(_LICE_CombinePixelsHSVAdjust); break; \ + } + +// For drawing where there is constant alpha and no per-pixel alpha. +#define __LICE_ACTION_CONSTANTALPHA(mode,ia,clamp) \ + if ((ia)!=0) switch ((mode)&LICE_BLIT_MODE_MASK) { \ + case LICE_BLIT_MODE_COPY: \ + if ((ia)==256) { if (clamp) { __LICE__ACTION(_LICE_CombinePixelsClobberClamp); } else { __LICE__ACTION(_LICE_CombinePixelsClobberNoClamp); } } \ + else if ((ia)==128) { if (clamp) { __LICE__ACTION(_LICE_CombinePixelsHalfMixClamp); } else { __LICE__ACTION(_LICE_CombinePixelsHalfMixNoClamp); } } \ + else if ((ia)>0) { if (clamp) { __LICE__ACTION(_LICE_CombinePixelsCopyClamp); } else { __LICE__ACTION(_LICE_CombinePixelsCopyNoClamp); } } \ + break; \ + case LICE_BLIT_MODE_ADD: __LICE__ACTION(_LICE_CombinePixelsAdd); break; \ + case LICE_BLIT_MODE_DODGE: __LICE__ACTION(_LICE_CombinePixelsColorDodge); break; \ + case LICE_BLIT_MODE_MUL: if (clamp) { __LICE__ACTION(_LICE_CombinePixelsMulClamp); } else { __LICE__ACTION(_LICE_CombinePixelsMulNoClamp); } break; \ + case LICE_BLIT_MODE_OVERLAY: __LICE__ACTION(_LICE_CombinePixelsOverlay); break; \ + case LICE_BLIT_MODE_HSVADJ: __LICE__ACTION(_LICE_CombinePixelsHSVAdjust); break; \ + } + +typedef void (*LICE_COMBINEFUNC)(LICE_pixel_chan *dest, int r, int g, int b, int a, int alpha); + +#define __LICE_SC(x) do { (x) = ((x)*(__sc))/256; } while (0) +#define __LICE_SCU(x) do { (x) = ((x)*(__sc))>>8; } while (0) + +#endif // _LICE_COMBINE_H_ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_extended.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_extended.h new file mode 100644 index 000000000..a453a77f0 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_extended.h @@ -0,0 +1,164 @@ +#ifndef _LICE_EXTENDED_ +#define _LICE_EXTENDED_ + +#include "lice.h" + +#define DISABLE_LICE_EXTENSIONS + +// stuff to pass to LICE_IBitmap::Extended + +enum // IDs +{ + LICE_EXT_SUPPORTS_ID, // data = ID, returns 1 if that extension ID is supported + LICE_EXT_CLEAR_ACCEL, + LICE_EXT_LINE_ACCEL, + LICE_EXT_FILLRECT_ACCEL, + LICE_EXT_DRAWCBEZIER_ACCEL, + LICE_EXT_DRAWGLYPH_ACCEL, + LICE_EXT_BLIT_ACCEL, + LICE_EXT_SCALEDBLIT_ACCEL, + LICE_EXT_GETFBOTEX_ACCEL, // if the bitmap is implemented as an openGL framebuffer object, get its texture backing store + LICE_EXT_DASHEDLINE_ACCEL, + LICE_EXT_GETPIXEL_ACCEL, + LICE_EXT_PUTPIXEL_ACCEL, + LICE_EXT_SETCLIP, // data == 0 to clear clip + LICE_EXT_WINDOW_BLIT, + LICE_EXT_FORGET, // optimizations can sometimes happen if a bitmap can be told it doesn't need to retain data after it's accessed + LICE_EXT_DRAWTRIANGLE_ACCEL, +}; + +struct LICE_Ext_Line_acceldata +{ + float x1, y1, x2, y2; + LICE_pixel color; + float alpha; + int mode; + bool aa; + + LICE_Ext_Line_acceldata(float _x1, float _y1, float _x2, float _y2, LICE_pixel _color, float _alpha, int _mode, bool _aa) + : x1(_x1), y1(_y1), x2(_x2), y2(_y2), color(_color), alpha(_alpha), mode(_mode), aa(_aa) {} +}; + +struct LICE_Ext_FillRect_acceldata +{ + int x, y, w, h; + LICE_pixel color; + float alpha; + int mode; + + LICE_Ext_FillRect_acceldata(int _x, int _y, int _w, int _h, LICE_pixel _color, float _alpha, int _mode) + : x(_x), y(_y), w(_w), h(_h), color(_color), alpha(_alpha), mode(_mode) {} +}; + +struct LICE_Ext_DrawCBezier_acceldata +{ + float xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend; + LICE_pixel color; + float alpha; + int mode; + bool aa; + + LICE_Ext_DrawCBezier_acceldata(float _xstart, float _ystart, float _xctl1, float _yctl1, float _xctl2, float _yctl2, float _xend, float _yend, + LICE_pixel _color, float _alpha, int _mode, bool _aa) + : xstart(_xstart), ystart(_ystart), xctl1(_xctl1), yctl1(_yctl1), xctl2(_xctl2), yctl2(_yctl2), xend(_xend), yend(_yend), + color(_color), alpha(_alpha), mode(_mode), aa(_aa) {} +}; + +struct LICE_Ext_DrawGlyph_acceldata +{ + int x; + int y; + LICE_pixel color; + const LICE_pixel_chan* alphas; + int glyph_w, glyph_h; + float alpha; + int mode; + + LICE_Ext_DrawGlyph_acceldata(int _x, int _y, LICE_pixel _color, LICE_pixel_chan* _alphas, int _glyph_w, int _glyph_h, float _alpha, int _mode) + : x(_x), y(_y), color(_color), alphas(_alphas), glyph_w(_glyph_w), glyph_h(_glyph_h), alpha(_alpha), mode(_mode) {} +}; + +struct LICE_Ext_Blit_acceldata +{ + LICE_IBitmap* src; + int dstx, dsty, srcx, srcy, srcw, srch; + float alpha; + int mode; + + LICE_Ext_Blit_acceldata(LICE_IBitmap* _src, int _dstx, int _dsty, int _srcx, int _srcy, int _srcw, int _srch, float _alpha, int _mode) + : src(_src), dstx(_dstx), dsty(_dsty), srcx(_srcx), srcy(_srcy), srcw(_srcw), srch(_srch), alpha(_alpha), mode(_mode) {} +}; + +struct LICE_Ext_ScaledBlit_acceldata +{ + LICE_IBitmap* src; + int dstx, dsty, dstw, dsth; + float srcx, srcy, srcw, srch; + float alpha; + int mode; + + LICE_Ext_ScaledBlit_acceldata(LICE_IBitmap* _src, int _dstx, int _dsty, int _dstw, int _dsth, float _srcx, float _srcy, float _srcw, float _srch, float _alpha, int _mode) + : src(_src), dstx(_dstx), dsty(_dsty), dstw(_dstw), dsth(_dsth), srcx(_srcx), srcy(_srcy), srcw(_srcw), srch(_srch), alpha(_alpha), mode(_mode) {} +}; + +struct LICE_Ext_DashedLine_acceldata +{ + float x1, y1, x2, y2; + int pxon, pxoff; + LICE_pixel color; + float alpha; + int mode; + bool aa; + + LICE_Ext_DashedLine_acceldata(float _x1, float _y1, float _x2, float _y2, int _pxon, int _pxoff, LICE_pixel _color, float _alpha, int _mode, bool _aa) + : x1(_x1), y1(_y1), x2(_x2), y2(_y2), pxon(_pxon), pxoff(_pxoff), color(_color), alpha(_alpha), mode(_mode), aa(_aa) {} +}; + +struct LICE_Ext_GetPixel_acceldata +{ + int x, y; + LICE_pixel px; // return + + LICE_Ext_GetPixel_acceldata(int _x, int _y) + : x(_x), y(_y), px(0) {} +}; + +struct LICE_Ext_PutPixel_acceldata +{ + int x, y; + LICE_pixel color; + float alpha; + int mode; + + LICE_Ext_PutPixel_acceldata(int _x, int _y, LICE_pixel _color, float _alpha, int _mode) + : x(_x), y(_y), color(_color), alpha(_alpha), mode(_mode) {} +}; + +struct LICE_Ext_SetClip_data +{ + int x, y, w, h; + + LICE_Ext_SetClip_data(int _x, int _y, int _w, int _h) + : x(_x), y(_y), w(_w), h(_h) {} +}; + +class pl_Mat; + +struct LICE_Ext_DrawTriangle_acceldata +{ + pl_Mat *mat; // will need to include plush.h to access this + double VertexShades[3][3]; // for solid element + float scrx[3], scry[3], scrz[3]; // scrz = 1/Zdist + double mapping_coords[2][3][2]; // [texture or texture2][vertex][uv] +}; + +struct LICE_Ext_WindowBlit_data +{ + HWND hwnd; + int destx, desty, srcx, srcy, w, h; + + LICE_Ext_WindowBlit_data(HWND _hwnd, int _destx, int _desty, int _srcx, int _srcy, int _w, int _h) + : hwnd(_hwnd), destx(_destx), desty(_desty), srcx(_srcx), srcy(_srcy), w(_w), h(_h) {} +}; + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_image.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_image.cpp new file mode 100644 index 000000000..93bd063cf --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_image.cpp @@ -0,0 +1,147 @@ +#include "lice.h" + + + +LICE_IBitmap* LICE_LoadImage(const char* filename, LICE_IBitmap* bmp, bool tryIgnoreExtension) +{ + _LICE_ImageLoader_rec *hdr = LICE_ImageLoader_list; + while (hdr) + { + LICE_IBitmap *ret = hdr->loadfunc(filename,true,bmp); + if (ret) return ret; + hdr=hdr->_next; + } + if (tryIgnoreExtension) + { + hdr = LICE_ImageLoader_list; + while (hdr) + { + LICE_IBitmap *ret = hdr->loadfunc(filename,false,bmp); + if (ret) return ret; + hdr=hdr->_next; + } + } + + return 0; +} + + +static bool grow_buf(char **buf, int *bufsz, int *wrpos, const char *rd, int len) +{ + if (*wrpos + len > *bufsz) + { + char *nbuf=(char*)realloc(*buf,*bufsz = *wrpos+len+4096); + if (!nbuf) return true; // ugh + *buf = nbuf; + } + memcpy(*buf+*wrpos,rd,len); + *wrpos+=len; + return false; +} + +char *LICE_GetImageExtensionList(bool wantAllSup, bool wantAllFiles) +{ + _LICE_ImageLoader_rec *hdr = LICE_ImageLoader_list; + int bufsz=4096; + int wrpos=0; + char *buf=(char *)malloc(bufsz); + buf[0]=buf[1]=buf[2]=0; + + if (wantAllSup) + { + static const char af[]="All supported images"; + if (grow_buf(&buf,&bufsz,&wrpos,af,sizeof(af))) { free(buf); return NULL; } // fail + + int cnt=0; + while (hdr) + { + const char *rd = hdr->get_extlist(); + if (rd && *rd) + { + bool st=false; + const char *p=rd; + while (*p) + { + if (st) + { + if (cnt++) + if (grow_buf(&buf,&bufsz,&wrpos,";",1)) { free(buf); return NULL; } // fail + if (grow_buf(&buf,&bufsz,&wrpos,p,(int)strlen(p)+1)) { free(buf); return NULL; } // fail + wrpos--; + } + while (*p) p++; + p++; + st=!st; + } + } + hdr=hdr->_next; + } + + if (!cnt) + { + wrpos=0; // reset if nothing meaningful added + buf[0]=buf[1]=buf[2]=0; + } + else wrpos++; + + hdr = LICE_ImageLoader_list; + } + + while (hdr) + { + const char *rd = hdr->get_extlist(); + if (rd && *rd) + { + int len = 0; + while (rd[len] || rd[len+1]) len++; // doublenull terminated list + + if (len) + { + if (grow_buf(&buf, &bufsz, &wrpos, rd, len+2)) return buf; // fail + wrpos--; // remove doublenull on next round + } + } + hdr=hdr->_next; + } + if (wantAllFiles) + { + static const char af[]="All files (*.*)\0*.*\0"; + if (grow_buf(&buf,&bufsz,&wrpos,af,sizeof(af))) return buf; // fail + wrpos--; + } + + return buf; +} + +bool LICE_ImageIsSupported(const char *filename) +{ + const char *extension = filename; + while (*extension)extension++; + while (extension>=filename && *extension != '/' && *extension != '.' && *extension != '\\') extension--; + if (extensionget_extlist(); + if (el) + { + while (*el) el++; // skip desc + ++el; + + // scan rest of buffer + while (*el) + { + if (!strnicmp(el,extension,extlen)) + { + if (el[extlen] == 0|| el[extlen] == ';') return true; + } + el++; + } + } + hdr=hdr->_next; + } + return false; +} diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_import.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_import.h new file mode 100644 index 000000000..17876a138 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_import.h @@ -0,0 +1,84 @@ +#define LICE_TEXT_NO_DECLARE_CACHEDFONT + +#ifdef LICE_IMPORT_INTERFACE_ONLY +#define LICE_FUNC_DEF_DECL extern +#else +#define LICE_FUNC_DEF_DECL +#endif + +#include "lice_text.h" +LICE_FUNC_DEF_DECL LICE_IBitmap *(*__LICE_CreateBitmap)(int, int, int); +LICE_FUNC_DEF_DECL void (*__LICE_PutPixel)(LICE_IBitmap* dest, int x, int y, LICE_pixel color, float alpha, int mode); +LICE_FUNC_DEF_DECL void (*__LICE_Line)(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, LICE_pixel color, float alpha, int mode, bool aa); +LICE_FUNC_DEF_DECL void (*__LICE_FLine)(LICE_IBitmap *dest, float x1, float y1, float x2, float y2, LICE_pixel color, float alpha, int mode, bool aa); +LICE_FUNC_DEF_DECL void (*__LICE_DashedLine)(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, int on, int off, LICE_pixel color, float alpha, int mode, bool aa); +LICE_FUNC_DEF_DECL void (*__LICE_FillRect)(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha , int mode); +LICE_FUNC_DEF_DECL void (*__LICE_DrawRect)(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha , int mode); +LICE_FUNC_DEF_DECL void (*__LICE_BorderedRect)(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel bgcolor, LICE_pixel fgcolor, float alpha, int mode); +LICE_FUNC_DEF_DECL void (*__LICE_Circle)(LICE_IBitmap* dest, float cx, float cy, float r, LICE_pixel color, float alpha, int mode, bool aa); +LICE_FUNC_DEF_DECL void (*__LICE_FillCircle)(LICE_IBitmap* dest, float cx, float cy, float r, LICE_pixel color, float alpha, int mode, bool aa); +LICE_FUNC_DEF_DECL void (*__LICE_Clear)(LICE_IBitmap *dest, LICE_pixel color); +LICE_FUNC_DEF_DECL void (*__LICE_Blit)(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int srcx, int srcy, int srcw, int srch, float alpha, int mode); +LICE_FUNC_DEF_DECL void (*__LICE_RotatedBlit)(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int dstw, int dsth, float srcx, float srcy, float srcw, float srch, float angle, bool cliptosourcerect, float alpha, int mode, float rotxcent, float rotycent); +LICE_FUNC_DEF_DECL void (*__LICE_DrawGlyph)(LICE_IBitmap* dest, int x, int y, LICE_pixel color, LICE_pixel_chan* glyph, int glyph_w, int glyph_h, float alpha, int mode); +LICE_FUNC_DEF_DECL void (*__LICE_FillTriangle)(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, int x3, int y3, LICE_pixel color, float alpha, int mode); +LICE_FUNC_DEF_DECL void (*__LICE_Arc)(LICE_IBitmap* dest, float cx, float cy, float r, float alo, float ahi, LICE_pixel color, float alpha, int mode, bool aa); +LICE_FUNC_DEF_DECL void (*__LICE_FillTrapezoid)(LICE_IBitmap* dest, int x1a, int x1b, int y1, int x2a, int x2b, int y2, LICE_pixel color, float alpha, int mode); +LICE_FUNC_DEF_DECL void (*__LICE_FillConvexPolygon)(LICE_IBitmap* dest, int* x, int* y, int npoints, LICE_pixel color, float alpha, int mode); +LICE_FUNC_DEF_DECL void (*__LICE_Copy)(LICE_IBitmap* dest, LICE_IBitmap* src); +LICE_FUNC_DEF_DECL void (*__LICE_DrawText)(LICE_IBitmap *bm, int x, int y, const char *string, LICE_pixel color, float alpha, int mode); +LICE_FUNC_DEF_DECL void (*__LICE_MeasureText)(const char *string, int *w, int *h); +LICE_FUNC_DEF_DECL void (*__LICE_ScaledBlit)(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int dstw, int dsth, float srcx, float srcy, float srcw, float srch, float alpha, int mode); + +LICE_FUNC_DEF_DECL void * (*LICE_CreateFont)(); +#define LICE_PutPixel __LICE_PutPixel +#define LICE_Line __LICE_Line +#define LICE_FLine __LICE_FLine +#define LICE_DashedLine __LICE_DashedLine +#define LICE_FillRect __LICE_FillRect +#define LICE_DrawRect __LICE_DrawRect +#define LICE_Circle __LICE_Circle +#define LICE_Clear __LICE_Clear +#define LICE_Blit __LICE_Blit +#define LICE_RotatedBlit __LICE_RotatedBlit +#define LICE_DrawGlyph __LICE_DrawGlyph +#define LICE_FillCircle __LICE_FillCircle +#define LICE_BorderedRect __LICE_BorderedRect +#define LICE_FillTriangle __LICE_FillTriangle +#define LICE_Arc __LICE_Arc +#define LICE_FillTrapezoid __LICE_FillTrapezoid +#define LICE_FillConvexPolygon __LICE_FillConvexPolygon +#define LICE_Copy __LICE_Copy +#define LICE_DrawText __LICE_DrawText +#define LICE_MeasureText __LICE_MeasureText +#define LICE_ScaledBlit __LICE_ScaledBlit +#define LICE_CreateMemBitmap(w,h) (__LICE_CreateBitmap ? __LICE_CreateBitmap(0,w,h) : 0) +#define LICE_CreateSysBitmap(w,h) (__LICE_CreateBitmap ? __LICE_CreateBitmap(1,w,h) : 0) +#define LICE_CreateTextCache() ((LICE_IFont*)(LICE_CreateFont?LICE_CreateFont():0)) +#undef LICE_FUNC_DEF_DECL + +#define IMPORT_LICE_FUNCS(IMPORT_FUNC) \ + IMPORT_FUNC(__LICE_CreateBitmap,"LICE_CreateBitmap") \ + IMPORT_FUNC(__LICE_PutPixel,"LICE_PutPixel") \ + IMPORT_FUNC(__LICE_Line,"LICE_LineInt") \ + IMPORT_FUNC(__LICE_FLine,"LICE_Line") \ + IMPORT_FUNC(__LICE_DashedLine, "LICE_DashedLine") \ + IMPORT_FUNC(__LICE_Circle,"LICE_Circle") \ + IMPORT_FUNC(__LICE_FillCircle,"LICE_FillCircle") \ + IMPORT_FUNC(__LICE_FillRect,"LICE_FillRect") \ + IMPORT_FUNC(__LICE_DrawRect,"LICE_DrawRect") \ + IMPORT_FUNC(__LICE_BorderedRect,"LICE_BorderedRect") \ + IMPORT_FUNC(__LICE_Clear,"LICE_Clear") \ + IMPORT_FUNC(__LICE_Blit,"LICE_Blit") \ + IMPORT_FUNC(__LICE_RotatedBlit,"LICE_RotatedBlit") \ + IMPORT_FUNC(__LICE_DrawGlyph,"LICE_DrawGlyph") \ + IMPORT_FUNC(LICE_CreateFont,"LICE_CreateFont") \ + IMPORT_FUNC(LICE_FillTriangle,"LICE_FillTriangle") \ + IMPORT_FUNC(LICE_Arc,"LICE_Arc") \ + IMPORT_FUNC(LICE_FillTrapezoid,"LICE_FillTrapezoid") \ + IMPORT_FUNC(LICE_FillConvexPolygon,"LICE_FillConvexPolygon") \ + IMPORT_FUNC(LICE_Copy,"LICE_Copy") \ + IMPORT_FUNC(__LICE_ScaledBlit,"LICE_ScaledBlit") \ + IMPORT_FUNC(__LICE_MeasureText,"LICE_MeasureText") \ + IMPORT_FUNC(__LICE_DrawText,"LICE_DrawText") + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_line.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_line.cpp new file mode 100644 index 000000000..5314da1c4 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_line.cpp @@ -0,0 +1,2031 @@ +#ifndef WDL_NO_DEFINE_MINMAX +#define WDL_NO_DEFINE_MINMAX +#endif +#include "lice.h" +#include "lice_combine.h" +#include "lice_extended.h" +#include +#include +//#include + +#define IGNORE_SCALING(mode) ((mode)&LICE_BLIT_IGNORE_SCALING) + +template inline void SWAP(T& a, T& b) { T tmp = a; a = b; b = tmp; } + +enum { eOK = 0, eXLo = 1, eXHi = 2, eYLo = 4, eYHi = 8 }; + +static int OffscreenTest(int x, int y, int nX, int nY) +{ + int e = eOK; + if (x < 0) e |= eXLo; + else if (x >= nX) e |= eXHi; + if (y < 0) e |= eYLo; + else if (y >= nY) e |= eYHi; + return e; +} + +// Cohen-Sutherland. Returns false if the line is entirely offscreen. +static bool ClipLine(int* pX1, int* pY1, int* pX2, int* pY2, int nX, int nY) +{ + int x1 = *pX1, y1 = *pY1, x2 = *pX2, y2 = *pY2; + int e1 = OffscreenTest(x1, y1, nX, nY); + int e2 = OffscreenTest(x2, y2, nX, nY); + int timeout = 32; + bool accept = false, done = false; + do + { + if (!(e1 | e2)) { + accept = done = true; + } + else + if (e1 & e2) { + done = true; // Line is entirely offscreen. + } + else { + int x, y; + int eOut = e1 ? e1 : e2; + if (eOut & eYHi) { + x = x1 + (int) ((double) (x2 - x1) * (double) (nY - 1 - y1) / (double) (y2 - y1)); + y = nY - 1; + } + else + if (eOut & eYLo) { + x = x1 + (int) ((double) (x2 - x1) * (double) -y1 / (double) (y2 - y1)); + y = 0; + } + else + if (eOut & eXHi) { + y = y1 + (int) ((double) (y2 - y1) * (double) (nX - 1 - x1) / (double) (x2 - x1)); + x = nX - 1; + } + else { + y = y1 + (int) ((double) (y2 - y1) * (double) -x1 / (double) (x2 - x1)); + x = 0; + } + + if (eOut == e1) { + x1 = x; + y1 = y; + e1 = OffscreenTest(x1, y1, nX, nY); + } + else { + x2 = x; + y2 = y; + e2 = OffscreenTest(x2, y2, nX, nY); + } + } + } + while (!done && timeout--); + + *pX1 = x1; + *pY1 = y1; + *pX2 = x2; + *pY2 = y2; + return accept; +} + +template static int OffscreenFTest(T x, T y, T w, T h) +{ + int e = eOK; + if (x < 0.0f) e |= eXLo; + else if (x >= w) e |= eXHi; + if (y < 0.0f) e |= eYLo; + else if (y >= h) e |= eYHi; + return e; +} + +template static bool ClipFLine(T * x1, T * y1, T * x2, T *y2, int w, int h) +{ + T tx1 = *x1, ty1 = *y1, tx2 = *x2, ty2 = *y2; + T tw = (T)(w-1), th = (T)(h-1); + if (!lice_isfinite(tx1) || !lice_isfinite(tx2) || + !lice_isfinite(ty1) || !lice_isfinite(ty2)) return false; + + int e1 = OffscreenFTest(tx1, ty1, tw, th); + int e2 = OffscreenFTest(tx2, ty2, tw, th); + + int timeout = 32; + bool accept = false, done = false; + do + { + if (!(e1|e2)) + { + accept = done = true; + } + else + if (e1&e2) + { + done = true; // Line is entirely offscreen. + } + else + { + T x, y; + int eOut = (e1 ? e1 : e2); + if (eOut&eYHi) + { + x = tx1+(tx2-tx1)*(th-ty1)/(ty2-ty1); + y = th-1.0f; + } + else if (eOut&eYLo) + { + x = tx1+(tx2-tx1)*ty1/(ty1-ty2); + y = 0.0f; + } + else if (eOut&eXHi) + { + y = ty1+(ty2-ty1)*(tw-tx1)/(tx2-tx1); + x = tw-1.0f; + } + else + { + y = ty1+(ty2-ty1)*tx1/(tx1-tx2); + x = 0.0f; + } + + if (eOut == e1) + { + tx1 = x; + ty1 = y; + e1 = OffscreenFTest(tx1, ty1, tw, th); + } + else + { + tx2 = x; + ty2 = y; + e2 = OffscreenFTest(tx2, ty2, tw, th); + } + } + } + while (!done && timeout--); + + *x1 = tx1; + *y1 = ty1; + *x2 = tx2; + *y2 = ty2; + return accept; +} + + +inline static void LICE_DiagLineFAST(LICE_pixel *px, int span, int n, int xstep, int ystep, LICE_pixel color, bool aa) +{ + int step = xstep+ystep; + if (aa) + { + LICE_pixel color75 = ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f); + LICE_pixel color25 = (color>>2)&0x3f3f3f3f; + while (n--) + { + _LICE_CombinePixelsThreeQuarterMix2FAST::doPixFAST(px, color75); + _LICE_CombinePixelsQuarterMix2FAST::doPixFAST(px+xstep, color25); + _LICE_CombinePixelsQuarterMix2FAST::doPixFAST(px+ystep, color25); + px += step; + } + _LICE_CombinePixelsThreeQuarterMix2FAST::doPixFAST(px, color75); + } + else + { + ++n; + while (n--) + { + *px = color; + px += step; + } + } +} + +inline static void LICE_DottedVertLineFAST(LICE_IBitmap* dest, int x, int y1, int y2, LICE_pixel color) +{ + int span = dest->getRowSpan(); + LICE_pixel* px = dest->getBits()+y1*span+x; + + int n = (y2-y1+1)/2; + while (n--) + { + *px = color; + px += 2*span; + } +} + +// this is the white-color table, doing this properly requires correcting the destination color specifically +#define DO_AA_GAMMA_CORRECT 0 +#if DO_AA_GAMMA_CORRECT +static unsigned char AA_GAMMA_CORRECT[256] = +{ + // 1.8 gamma + 0,11,17,21,25,28,31,34,37,39,42,44,46,48,50,52,54,56,58,60,61,63,65,67,68,70,71,73,74,76,77,79,80,81,83,84,85,87,88,89,91,92,93,94,96,97,98,99,100,101,103,104,105,106,107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,132,133,134,135,136,137,138,139,140,141,142,142,143,144,145,146,147,148,149,149,150,151,152,153,154,154,155,156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,179,179,180,181,182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193,194,194,195,196,196,197,198,198,199,200,200,201,202,202,203,204,204,205,206,206,207,208,208,209,210,210,211,212,212,213,214,214,215,215,216,217,217,218,219,219,220,220,221,222,222,223,224,224,225,225,226,227,227,228,228,229,230,230,231,231,232,233,233,234,234,235,236,236,237,237,238,239,239,240,240,241,241,242,243,243,244,244,245,245,246,247,247,248,248,249,249,250,251,251,252,252,253,253,254,255 + + // 2.0 gamma + //0,15,22,27,31,35,39,42,45,47,50,52,55,57,59,61,63,65,67,69,71,73,74,76,78,79,81,82,84,85,87,88,90,91,93,94,95,97,98,99,100,102,103,104,105,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,141,142,143,144,145,146,147,148,148,149,150,151,152,153,153,154,155,156,157,158,158,159,160,161,162,162,163,164,165,165,166,167,168,168,169,170,171,171,172,173,174,174,175,176,177,177,178,179,179,180,181,182,182,183,184,184,185,186,186,187,188,188,189,190,190,191,192,192,193,194,194,195,196,196,197,198,198,199,200,200,201,201,202,203,203,204,205,205,206,206,207,208,208,209,210,210,211,211,212,213,213,214,214,215,216,216,217,217,218,218,219,220,220,221,221,222,222,223,224,224,225,225,226,226,227,228,228,229,229,230,230,231,231,232,233,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,255 + + // 2.2 gamma + //0,20,28,33,38,42,46,49,52,55,58,61,63,65,68,70,72,74,76,78,80,81,83,85,87,88,90,91,93,94,96,97,99,100,102,103,104,106,107,108,109,111,112,113,114,115,117,118,119,120,121,122,123,124,125,126,128,129,130,131,132,133,134,135,136,136,137,138,139,140,141,142,143,144,145,146,147,147,148,149,150,151,152,153,153,154,155,156,157,158,158,159,160,161,162,162,163,164,165,165,166,167,168,168,169,170,171,171,172,173,174,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,190,191,192,192,193,194,194,195,196,196,197,197,198,199,199,200,200,201,202,202,203,203,204,205,205,206,206,207,208,208,209,209,210,210,211,212,212,213,213,214,214,215,216,216,217,217,218,218,219,219,220,220,221,222,222,223,223,224,224,225,225,226,226,227,227,228,228,229,229,230,230,231,231,232,232,233,233,234,234,235,235,236,236,237,237,238,238,239,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,249,250,250,251,251,252,252,253,253,254,254,255 + + // 2.6 gamma + //0,30,39,46,51,56,60,63,67,70,73,76,78,81,83,85,87,89,91,93,95,97,99,101,102,104,105,107,109,110,111,113,114,116,117,118,120,121,122,123,125,126,127,128,129,130,131,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,148,149,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,164,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,175,176,177,177,178,179,179,180,181,181,182,183,183,184,185,185,186,187,187,188,188,189,190,190,191,192,192,193,193,194,195,195,196,196,197,197,198,199,199,200,200,201,201,202,203,203,204,204,205,205,206,206,207,207,208,208,209,210,210,211,211,212,212,213,213,214,214,215,215,216,216,217,217,218,218,219,219,220,220,221,221,222,222,223,223,223,224,224,225,225,226,226,227,227,228,228,229,229,230,230,230,231,231,232,232,233,233,234,234,234,235,235,236,236,237,237,237,238,238,239,239,240,240,240,241,241,242,242,243,243,243,244,244,245,245,245,246,246,247,247,247,248,248,249,249,249,250,250,251,251,251,252,252,253,253,253,254,254,255 +}; +#endif + +static void GetAAPxWeight(int err, int alpha, int* wt, int* iwt) +{ + int i = err>>8; + int w = 255-i; + +#if DO_AA_GAMMA_CORRECT + w = AA_GAMMA_CORRECT[w]; + i = AA_GAMMA_CORRECT[i]; +#endif + + w = (alpha*w) >> 8; + i = (alpha*i) >> 8; + *wt = w; + *iwt = i; +} + +static void GetAAPxWeightFAST(int err, int* wt, int* iwt) +{ + int i = err>>8; + int w = 255-i; + +#if DO_AA_GAMMA_CORRECT + w = AA_GAMMA_CORRECT[w]; + i = AA_GAMMA_CORRECT[i]; +#endif + + *wt = w; + *iwt = i; +} +template class __LICE_LineClassSimple +{ +public: + static void LICE_VertLineFAST(LICE_pixel *px, int span, int len, LICE_pixel color) + { + while (len--) + { + COMBFUNC::doPixFAST(px, color); + px+=span; + } + } + + static void LICE_HorizLineFAST(LICE_pixel *px, int n, LICE_pixel color) + { + while (n--) + { + COMBFUNC::doPixFAST(px, color); + px++; + } + } + static void LICE_VertLine(LICE_pixel *px, int span, int len, int color, int aw) + { + int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + while (len--) + { + COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, aw); + px+=span; + } + } + + static void LICE_HorizLine(LICE_pixel *px, int n, LICE_pixel color, int aw) + { + int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + while (n--) + { + COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, aw); + px++; + } + } + static void LICE_DiagLine(LICE_pixel *px, int span, int n, int xstep, int ystep, LICE_pixel color, int aw) + { + int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + int step = xstep+ystep; + + for (int i = 0; i <= n; ++i, px += step) + { + COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, aw); + } + } + static void LICE_DiagLineAA(LICE_pixel *px, int span, int n, int xstep, int ystep, LICE_pixel color, int aw) + { + int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + int step = xstep+ystep; + +#if DO_AA_GAMMA_CORRECT + int iw = aw*AA_GAMMA_CORRECT[256*3/4]/256; + int dw = aw*AA_GAMMA_CORRECT[256/4]/256; +#else + int iw = aw*3/4; + int dw = aw/4; +#endif + for (int i = 0; i < n; ++i, px += step) + { + COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, iw); + COMBFUNC::doPix((LICE_pixel_chan*) (px+xstep), r, g, b, a, dw); + COMBFUNC::doPix((LICE_pixel_chan*) (px+ystep), r, g, b, a, dw); + } + COMBFUNC::doPix((LICE_pixel_chan*) px, r, g, b, a, iw); + } +}; + + +#ifndef LICE_FAVOR_SIZE +template +#endif +class __LICE_LineClass +{ +public: + +#ifdef LICE_FAVOR_SIZE + #define DOPIX(pout,r,g,b,a,ia) combFunc(pout,r,g,b,a,ia); +#else + #define DOPIX(pout,r,g,b,a,ia) COMBFUNC::doPix(pout,r,g,b,a,ia); +#endif + + static void DashedLine(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int pxon, int pxoff, LICE_pixel color, int aw +#ifdef LICE_FAVOR_SIZE + , LICE_COMBINEFUNC combFunc +#endif + ) + { + int span = dest->getRowSpan(); + LICE_pixel* px = dest->getBits()+y1*span+x1; + int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + if (x1 == x2) + { + int i, y; + for (y = y1; y < y2-pxon; y += pxon+pxoff) + { + for (i = 0; i < pxon; ++i, px += span) DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw) + px += pxoff*span; + } + for (i = 0; i < lice_min(pxon, y2-y); ++i, px += span) DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw) + } + else if (y1 == y2) + { + int i, x; + for (x = x1; x < x2-pxon; x += pxon+pxoff) + { + for (i = 0; i < pxon; ++i, ++px) DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw) + px += pxoff; + } + for (i = 0; i < lice_min(pxon, x2-x); ++i, ++px) DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw) + } + } + + + + + static void LICE_LineImpl(LICE_pixel *px, LICE_pixel *px2, int derr, int astep, int da, int bstep, LICE_pixel color, int aw, bool aa +#ifdef LICE_FAVOR_SIZE + , LICE_COMBINEFUNC combFunc +#endif + ) + { + int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + int err = 0; + int i; + int n = (da+1)/2; + + if (aa) + { + DOPIX((LICE_pixel_chan*) px, r, g, b, a, aw) + DOPIX((LICE_pixel_chan*) px2, r, g, b, a, aw) + px += astep; + px2 -= astep; + err = derr; + + if (aw == 256) + { + for (i = 1; i < n; ++i) + { + int wt, iwt; + GetAAPxWeightFAST(err, &wt, &iwt); + DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt) + DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt) + DOPIX((LICE_pixel_chan*)px2, r, g, b, a, wt) + DOPIX((LICE_pixel_chan*)(px2-bstep), r, g, b, a, iwt) + + err += derr; + if (err >= 65536) + { + px += bstep; + px2 -= bstep; + err -= 65536; + } + px += astep; + px2 -= astep; + } + } + else // alpha<256 + { + for (i = 1; i < n; ++i) + { + int wt, iwt; + GetAAPxWeight(err, aw, &wt, &iwt); + DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt) + DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt) + DOPIX((LICE_pixel_chan*)px2, r, g, b, a, wt) + DOPIX((LICE_pixel_chan*)(px2-bstep), r, g, b, a, iwt) + + err += derr; + if (err >= 65536) + { + px += bstep; + px2 -= bstep; + err -= 65536; + } + px += astep; + px2 -= astep; + } + } + if (!(da%2)) + { + int wt, iwt; + if (aw == 256) GetAAPxWeightFAST(err, &wt, &iwt); + else GetAAPxWeight(err, aw, &wt, &iwt); + DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt) + DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt) + } + } + else // not aa + { + for (i = 0; i < n; ++i) + { + DOPIX((LICE_pixel_chan*)px, r, g, b, a, aw) + DOPIX((LICE_pixel_chan*)px2, r, g, b, a, aw) + err += derr; + if (err >= 65536/2) + { + px += bstep; + px2 -= bstep; + err -= 65536; + } + + px += astep; + px2 -= astep; + } + if (!(da%2)) + { + DOPIX((LICE_pixel_chan*)px, r, g, b, a, aw) + } + } + } + + static void LICE_FLineImpl(LICE_pixel *px, int n , int err, int derr, int astep, int bstep, LICE_pixel color, int aw +#ifdef LICE_FAVOR_SIZE + , LICE_COMBINEFUNC combFunc +#endif + + ) // only does AA + { + int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + + int wt, iwt; + int i; + + if (aw == 256) + { + for (i = 0; i <= n; ++i) + { + GetAAPxWeightFAST(err, &wt, &iwt); + DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt) + DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt) + + err += derr; + if (err >= 65536) + { + px += bstep; + err -= 65536; + } + px += astep; + } + } + else // alpha != 256 + { + for (i = 0; i <= n; ++i) + { + GetAAPxWeight(err, aw, &wt, &iwt); + DOPIX((LICE_pixel_chan*)px, r, g, b, a, wt) + DOPIX((LICE_pixel_chan*)(px+bstep), r, g, b, a, iwt) + + err += derr; + if (err >= 65536) + { + px += bstep; + err -= 65536; + } + px += astep; + } + } + } + + static void LICE_FLineImplFill(LICE_pixel *px, int n , int err, int derr, int astep, int bstep, LICE_pixel color, int aw, + int fill_sz, int b_pos, unsigned int b_max +#ifdef LICE_FAVOR_SIZE + , LICE_COMBINEFUNC combFunc +#endif + ) + { + // fill_sz always >= 2 + int r = LICE_GETR(color), g = LICE_GETG(color), b = LICE_GETB(color), a = LICE_GETA(color); + + int wt, iwt; + int i; + + const int dbpos = bstep < 0 ? -1 : 1; + + const int b_adj = -(fill_sz/2); + b_pos += b_adj*dbpos; + px += b_adj*bstep; + + fill_sz--; // fill size of 2 has one extra pixel in the middle, 2 AA pixels + + if (aw == 256) + { + for (i = 0; i <= n; ++i) + { + GetAAPxWeightFAST(err, &wt, &iwt); + LICE_pixel *wr = px; + unsigned int bp = b_pos; + if (bp= 65536) + { + px += bstep; + b_pos += dbpos; + err -= 65536; + } + px += astep; + } + } + else // alpha != 256 + { + for (i = 0; i <= n; ++i) + { + GetAAPxWeight(err, aw, &wt, &iwt); + LICE_pixel *wr = px; + unsigned int bp = b_pos; + if (bp= 65536) + { + px += bstep; + b_pos += dbpos; + err -= 65536; + } + px += astep; + } + } + } + +#undef DOPIX +}; + + +void LICE_Line(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, LICE_pixel color, float alpha, int mode, bool aa) +{ + if (!dest) return; + + int w = dest->getWidth(); + int h = dest->getHeight(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(x1); + __LICE_SC(y1); + __LICE_SC(x2); + __LICE_SC(y2); + } + } + +#ifndef DISABLE_LICE_EXTENSIONS + if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_LINE_ACCEL)) + { + LICE_Ext_Line_acceldata data(x1, y1, x2, y2, color, alpha, mode, aa); + if (dest->Extended(LICE_EXT_LINE_ACCEL, &data)) return; + } +#endif + + if (dest->isFlipped()) + { + y1 = h-1-y1; + y2 = h-1-y2; + } + + if (ClipLine(&x1, &y1, &x2, &y2, w, h)) + { + int xdiff = x2-x1; + if (y1 == y2) // horizontal line optimizations + { + if (x1 > x2) SWAP(x1, x2); + int span = dest->getRowSpan(); + LICE_pixel* px = dest->getBits()+y1*span+x1; + int n=x2-x1+1; + + if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 1.0f) + { + __LICE_LineClassSimple<_LICE_CombinePixelsClobberFAST>::LICE_HorizLineFAST(px, n, color); + } + else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.5f) + { + color = (color>>1)&0x7f7f7f7f; + __LICE_LineClassSimple<_LICE_CombinePixelsHalfMix2FAST>::LICE_HorizLineFAST(px, n, color); + } + else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.25f) + { + color = (color>>2)&0x3f3f3f3f; + __LICE_LineClassSimple<_LICE_CombinePixelsQuarterMix2FAST>::LICE_HorizLineFAST(px, n, color); + } + else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.75f) + { + color = ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f); + __LICE_LineClassSimple<_LICE_CombinePixelsThreeQuarterMix2FAST>::LICE_HorizLineFAST(px, n, color); + } + else + { + int aw = (int)(256.0f*alpha); +#define __LICE__ACTION(COMBFUNC) __LICE_LineClassSimple::LICE_HorizLine(px, n, color, aw) + __LICE_ACTION_CONSTANTALPHA(mode, aw, false); +#undef __LICE__ACTION + } + } + else if (!xdiff) // vertical line optimizations + { + if (y1 > y2) SWAP(y1, y2); + int len=y2+1-y1; + int span = dest->getRowSpan(); + LICE_pixel* px = dest->getBits()+y1*span+x1; + int aw = (int)(256.0f*alpha); + if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 1.0f) + { + __LICE_LineClassSimple<_LICE_CombinePixelsClobberFAST>::LICE_VertLineFAST(px, span, len, color); + } + else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.5f) + { + color = (color>>1)&0x7f7f7f7f; + __LICE_LineClassSimple<_LICE_CombinePixelsHalfMix2FAST>::LICE_VertLineFAST(px, span, len, color); + } + else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.25f) + { + color = (color>>2)&0x3f3f3f3f; + __LICE_LineClassSimple<_LICE_CombinePixelsQuarterMix2FAST>::LICE_VertLineFAST(px, span, len, color); + } + else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 0.75f) + { + color = ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f); + __LICE_LineClassSimple<_LICE_CombinePixelsThreeQuarterMix2FAST>::LICE_VertLineFAST(px, span, len, color); + } + else + { +#define __LICE__ACTION(COMBFUNC) __LICE_LineClassSimple::LICE_VertLine(px, span, len, color,aw) + __LICE_ACTION_CONSTANTALPHA(mode, aw, false); +#undef __LICE__ACTION + } + } + else if ((xdiff=abs(xdiff)) == abs(y2-y1)) // diagonal line optimizations + { + int span = dest->getRowSpan(); + LICE_pixel* px = dest->getBits()+y1*span+x1; + int aw = (int)(256.0f*alpha); + int xstep = (x2 > x1 ? 1 : -1); + int ystep = (y2 > y1 ? span : -span); + if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 1.0f) + { + LICE_DiagLineFAST(px,span, xdiff, xstep, ystep, color, aa); + } + else + { + if (aa) + { +#define __LICE__ACTION(COMBFUNC) __LICE_LineClassSimple::LICE_DiagLineAA(px,span, xdiff, xstep, ystep, color, aw) + __LICE_ACTION_NOSRCALPHA(mode, aw, false); +#undef __LICE__ACTION + } + else + { +#define __LICE__ACTION(COMBFUNC) __LICE_LineClassSimple::LICE_DiagLine(px,span, xdiff, xstep, ystep, color, aw) + __LICE_ACTION_CONSTANTALPHA(mode, aw, false); +#undef __LICE__ACTION + } + } + } + else + { + + // common set-up for normal line draws + + int span = dest->getRowSpan(); + int aw = (int)(256.0f*alpha); + LICE_pixel* px = dest->getBits()+y1*span+x1; + LICE_pixel* px2 = dest->getBits()+y2*span+x2; + + int da, db; + int astep, bstep; + int dx = x2-x1; + int dy = y2-y1; + + if (abs(dx) > abs(dy)) + { + da = dx; + db = dy; + astep = 1; + bstep = span; + } + else + { + da = dy; + db = dx; + astep = span; + bstep = 1; + } + + if (da < 0) + { + da = -da; + db = -db; + SWAP(px, px2); + } + if (db < 0) + { + db = -db; + bstep = -bstep; + } + + double dbda = (double)db/(double)da; + + int derr = (int)(dbda*65536.0); + +#ifdef LICE_FAVOR_SIZE + + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc=comb::doPix; + +#else + #define __LICE__ACTION(COMBFUNC) __LICE_LineClass::LICE_LineImpl(px,px2, derr, astep, da, bstep, color, aw, aa) +#endif + if (aa) + { + __LICE_ACTION_NOSRCALPHA(mode, aw, false); + } + else + { + __LICE_ACTION_CONSTANTALPHA(mode, aw, false); + } + + #undef __LICE__ACTION + +#ifdef LICE_FAVOR_SIZE + if (blitfunc) __LICE_LineClass::LICE_LineImpl(px,px2, derr, astep, da, bstep, color, aw, aa, blitfunc); +#endif + } + } +} + +void LICE_FLine(LICE_IBitmap* dest, float x1, float y1, float x2, float y2, LICE_pixel color, float alpha, int mode, bool aa) +{ + if (!dest) return; + if (!aa) + { + LICE_Line(dest,(int)x1,(int)y1,(int)x2,(int)y2,color,alpha,mode,false); + return; + } + + int w = dest->getWidth(); + int h = dest->getHeight(); + if (dest->isFlipped()) + { + y1 = (float)(h-1)-y1; + y2 = (float)(h-1)-y2; + } + + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(x1); + __LICE_SC(x2); + __LICE_SC(y1); + __LICE_SC(y2); + } + } + + if (ClipFLine(&x1, &y1, &x2, &y2, w, h)) + { + if (x1 != x2 || y1 != y2) + { + int span = dest->getRowSpan(); + int aw = (int)(256.0f*alpha); + + float a1, a2, b1, b2, da, db; + int astep, bstep; + float dx = x2-x1; + float dy = y2-y1; + + if (fabs(dx) > fabs(dy)) + { + a1 = x1; + a2 = x2; + b1 = y1; + b2 = y2; + da = dx; + db = dy; + astep = 1; + bstep = span; + } + else + { + a1 = y1; + a2 = y2; + b1 = x1; + b2 = x2; + da = dy; + db = dx; + astep = span; + bstep = 1; + } + + if (da < 0.0f) + { + da = -da; + db = -db; + SWAP(a1, a2); + SWAP(b1, b2); + } + if (db < 0.0f) + { + bstep = -bstep; + } + + int n = (int)(floor(a2)-ceil(a1)); + float dbda = db/da; + + float ta = ceil(a1); + float tb = b1+(ta-a1)*dbda; + float bf = tb-floor(tb); + int err = (int)(bf*65536.0f); + if (bstep < 0) err = 65535-err; + int derr = (int)(fabs(dbda)*65536.0f); + + LICE_pixel* px = dest->getBits()+(int)ta*astep+(int)tb*abs(bstep); + + if (bstep < 0) px -= bstep; + +#ifdef LICE_FAVOR_SIZE + + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc=comb::doPix; + +#else + + #define __LICE__ACTION(COMBFUNC) __LICE_LineClass::LICE_FLineImpl(px,n,err,derr,astep,bstep, color, aw) +#endif + + __LICE_ACTION_NOSRCALPHA(mode, aw, false); + +#ifdef LICE_FAVOR_SIZE + if (blitfunc) __LICE_LineClass::LICE_FLineImpl(px,n,err,derr,astep,bstep, color, aw, blitfunc); +#endif + + #undef __LICE__ACTION + } + } +} + +void LICE_DashedLine(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int pxon, int pxoff, LICE_pixel color, float alpha, int mode, bool aa) +{ + if (!dest) return; + + int w = dest->getWidth(); + int h = dest->getHeight(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(x1); + __LICE_SC(y1); + __LICE_SC(x2); + __LICE_SC(y2); + __LICE_SCU(pxon); + __LICE_SCU(pxoff); + } + } + +#ifndef DISABLE_LICE_EXTENSIONS + if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_DASHEDLINE_ACCEL)) + { + LICE_Ext_DashedLine_acceldata data(x1, y1, x2, y2, pxon, pxoff, color, alpha, mode, aa); + if (dest->Extended(LICE_EXT_DASHEDLINE_ACCEL, &data)) return; + } +#endif + + if (ClipLine(&x1, &y1, &x2, &y2, w, h)) + { + if (y1 > y2) SWAP(y1, y2); + if (pxon == 1 && pxoff == 1 && x1 == x2 && (mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && alpha == 1.0f) + { + LICE_DottedVertLineFAST(dest, x1, y1, y2, color); + } + else + { + int aw = (int)(256.0f*alpha); + if (x1 > x2) SWAP(x1, x2); + +#ifdef LICE_FAVOR_SIZE + + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc=comb::doPix; + +#else + + #define __LICE__ACTION(COMBFUNC) __LICE_LineClass::DashedLine(dest, x1, y1, x2, y2, pxon, pxoff, color, aw); +#endif + + __LICE_ACTION_CONSTANTALPHA(mode, aw, false); + +#ifdef LICE_FAVOR_SIZE + if (blitfunc) __LICE_LineClass::DashedLine(dest, x1, y1, x2, y2, pxon, pxoff, color, aw, blitfunc); +#endif + +#undef __LICE__ACTION + } + } +} + +bool LICE_ClipLine(int* pX1, int* pY1, int* pX2, int* pY2, int xLo, int yLo, int xHi, int yHi) +{ + int x1 = *pX1-xLo; + int y1 = *pY1-yLo; + int x2 = *pX2-xLo; + int y2 = *pY2-yLo; + bool onscreen = ClipLine(&x1, &y1, &x2, &y2, xHi-xLo, yHi-yLo); + *pX1 = x1+xLo; + *pY1 = y1+yLo; + *pX2 = x2+xLo; + *pY2 = y2+yLo; + return onscreen; +} + +bool LICE_ClipFLine(float* px1, float* py1, float* px2, float* py2, float xlo, float ylo, float xhi, float yhi) +{ + float x1 = *px1-xlo; + float y1 = *py1-ylo; + float x2 = *px2-xlo; + float y2 = *py2-ylo; + bool onscreen = ClipFLine(&x1, &y1, &x2, &y2, xhi-xlo, yhi-ylo); + *px1 = x1+xlo; + *py1 = y1+ylo; + *px2 = x2+xlo; + *py2 = y2+ylo; + return onscreen; +} + + +#include "lice_bezier.h" + +static void DoBezierFillSegment(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int yfill, LICE_pixel color, float alpha, int mode) +{ + if (x2 < x1) return; + if (x2 == x1) + { + if (y1 > y2) SWAP(y1,y2); + int ylo = lice_min(y1,yfill); + int yhi = lice_max(y2,yfill+1); + LICE_FillRect(dest, x1, ylo, 1, yhi-ylo+1, color, alpha, mode); + return; + } + + if ((y1 < yfill) == (y2 < yfill)) + { + if (y1 < yfill) ++yfill; + int x[4] = { x1, x1, x2, x2 }; + int y[4] = { y1, yfill, y2, yfill }; + LICE_FillConvexPolygon(dest, x, y, 4, color, alpha, mode); + } + else + { + int x = x1+(int)((double)(yfill-y1)*(double)(x2-x1)/(double)(y2-y1)); + int yf = yfill; + if (y1 < yfill) ++yf; + LICE_FillTriangle(dest, x1, y1, x1, yf, x, yf, color, alpha, mode); + yf = yfill; + if (y2 < yfill) ++yf; + LICE_FillTriangle(dest, x, yf, x2, yf, x2, y2, color, alpha, mode); + } +} + +static void DoBezierFillSegmentX(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int xfill, LICE_pixel color, float alpha, int mode) +{ + if (y2 < y1) return; + if (y2 == y1) + { + if (x1 > x2) SWAP(x1,x2); + int xlo = lice_min(x1,xfill); + int xhi = lice_max(x2,xfill+1); + LICE_FillRect(dest, xlo, y1, xhi-xlo+1, 1, color, alpha, mode); + return; + } + + if ((x1 < xfill) == (x2 < xfill)) + { + if (x1 < xfill) ++xfill; + int x[4] = { x1, xfill, x2, xfill }; + int y[4] = { y1, y1, y2+1, y2+1 }; + LICE_FillConvexPolygon(dest, x, y, 4, color, alpha, mode); + } + else + { + int y = y1+(int)((double)(xfill-x1)*(double)(y2-y1)/(double)(x2-x1)); + int xf = xfill; + if (x1 < xfill) ++xf; + LICE_FillTriangle(dest, x1, y1, xf, y1, xf, y, color, alpha, mode); + xf = xfill; + if (x2 < xfill) ++xf; + LICE_FillTriangle(dest, xf, y, xf, y2, x2, y2, color, alpha, mode); + } +} + + +// quadratic bezier ... NOT TESTED YET +// attempt to draw segments no longer than tol px +void LICE_DrawQBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl, double yctl, double xend, double yend, + LICE_pixel color, float alpha, int mode, bool aa, double tol) +{ + if (!dest) return; + + int w = dest->getWidth(); + + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc) + { + __LICE_SCU(w); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(xstart); + __LICE_SC(ystart); + __LICE_SC(xctl); + __LICE_SC(yctl); + __LICE_SC(xend); + __LICE_SC(yend); + } + mode|=LICE_BLIT_IGNORE_SCALING; + } + + + + + if (xstart > xend) + { + SWAP(xstart, xend); + SWAP(ystart, yend); + } + + double len = sqrt((xctl-xstart)*(xctl-xstart)+(yctl-ystart)*(yctl-ystart)); + len += sqrt((xend-xctl)*(xend-xctl)+(yend-yctl)*(yend-yctl)); + + double xlo = xstart; + double xhi = xend; + double ylo = ystart; + double yhi = yend; + double tlo = 0.0; + double thi = 1.0; + + if (xlo < 0.0f) + { + xlo = 0.0f; + ylo = LICE_Bezier_GetY(xstart, xctl, xend, ystart, yctl, yend, xlo, &tlo); + } + if (xhi >= (float)w) + { + xhi = (float)(w-1); + yhi = LICE_Bezier_GetY(xstart, xctl, xend, ystart, yctl, yend, xhi, &thi); + } + if (xlo > xhi) return; + + len *= (thi-tlo); + if (tol <= 0.0f) tol = 1.0f; + int nsteps = (int)(len/tol); + if (nsteps <= 0) nsteps = 1; + + double dt = (thi-tlo)/(double)nsteps; + double t = tlo+dt; + + double lastx = xlo; + double lasty = ylo; + double x, y; + int i; + for (i = 1; i < nsteps; ++i) + { + LICE_Bezier(xstart, xctl, xend, ystart, yctl, yend, t, &x, &y); + LICE_FLine(dest, lastx, lasty, x, y, color, alpha, mode, aa); + lastx = x; + lasty = y; + t += dt; + } + LICE_FLine(dest, lastx, lasty, xhi, yhi, color, alpha, mode, aa); + +} + +int LICE_CBezPrep(int dest_w, double xstart, double ystart, double xctl1, double yctl1, + double xctl2, double yctl2, double xend, double yend, double tol, bool xbasis, + double* ax, double* bx, double* cx, double* dx, double* ay, double* by, double* cy, double* dy, + double* xlo, double* xhi, double* ylo, double* yhi, double* tlo, double* thi) +{ + const int w = dest_w; + + if ((xbasis && xstart > xend) || (!xbasis && ystart > yend)) + { + SWAP(xstart, xend); + SWAP(xctl1, xctl2); + SWAP(ystart, yend); + SWAP(yctl1, yctl2); + } + + double len = sqrt((xctl1-xstart)*(xctl1-xstart)+(yctl1-ystart)*(yctl1-ystart)); + len += sqrt((xctl2-xctl1)*(xctl2-xctl1)+(yctl2-yctl1)*(yctl2-yctl1)); + len += sqrt((xend-xctl2)*(xend-xctl2)+(yend-yctl2)*(yend-yctl2)); + + LICE_CBezier_GetCoeffs(xstart, xctl1, xctl2, xend, ystart, yctl1, yctl2, yend, ax, bx, cx, ay, by, cy); + *dx = xstart; + *dy = ystart; + + *xlo = xstart; + *xhi = xend; + *ylo = ystart; + *yhi = yend; + *tlo = 0.0; + *thi = 1.0; + + if (*xlo < 0.0f) + { + *xlo = 0.0f; + *ylo = LICE_CBezier_GetY(xstart, xctl1, xctl2, xend, ystart, yctl1, yctl2, yend, *xlo, (double*)NULL, (double*)NULL, (double*)NULL, tlo); + } + if (*xhi > w) + { + *xhi = w; + *yhi = LICE_CBezier_GetY(xstart, xctl1, xctl2, xend, ystart, yctl1, yctl2, yend, *xhi, (double*)NULL, (double*)(double*)NULL, thi, (double*)NULL); + } + if ((xbasis && *xlo > *xhi) || (!xbasis && *ylo > *yhi)) + { + return 0; + } + + len *= (*thi-*tlo); + if (tol <= 0.0f) tol = 1.0f; + int nsteps = (int)(len/tol); + if (nsteps <= 0) nsteps = 1; + return nsteps; +} + +#define __LICE_SC_BEZ \ + __LICE_SC(destbm_w); \ + if (!IGNORE_SCALING(mode)) { \ + __LICE_SC(xstart); \ + __LICE_SC(ystart); \ + __LICE_SC(xctl1); \ + __LICE_SC(yctl1); \ + __LICE_SC(xctl2); \ + __LICE_SC(yctl2); \ + __LICE_SC(xend); \ + __LICE_SC(yend); \ + } + + +void LICE_DrawCBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1, + double xctl2, double yctl2, double xend, double yend, LICE_pixel color, float alpha, int mode, bool aa, double tol) +{ + if (!dest) return; + int destbm_w = dest->getWidth(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc) + { + __LICE_SC_BEZ + mode|=LICE_BLIT_IGNORE_SCALING; + } + +#ifndef DISABLE_LICE_EXTENSIONS + if (dest->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_DRAWCBEZIER_ACCEL)) + { + LICE_Ext_DrawCBezier_acceldata data(xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, color, alpha, mode, aa); + if (dest->Extended(LICE_EXT_DRAWCBEZIER_ACCEL, &data)) return; + } +#endif + + double ax, bx, cx, dx, ay, by, cy, dy; + double xlo, xhi, ylo, yhi; + double tlo, thi; + int nsteps = LICE_CBezPrep(destbm_w, xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, tol, true, + &ax, &bx, &cx, &dx, &ay, &by, &cy, &dy, &xlo, &xhi, &ylo, &yhi, &tlo, &thi); + if (!nsteps) return; + + double dt = (thi-tlo)/(double)nsteps; + double t = tlo+dt; + + double lastx = xlo; + double lasty = ylo; + double x, y; + int i; + for (i = 1; i < nsteps-1; ++i) + { + EVAL_CBEZXY(x, y, ax, bx, cx, dx, ay, by, cy, dy, t); + LICE_FLine(dest, lastx, lasty, x, y, color, alpha, mode, aa); + lastx = x; + lasty = y; + t += dt; + } + LICE_FLine(dest, lastx, lasty, xhi, yhi, color, alpha, mode, aa); +} + +void LICE_DrawThickCBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1, + double xctl2, double yctl2, double xend, double yend, LICE_pixel color, float alpha, int mode, int wid, double tol) +{ + if (!dest) return; + int destbm_w = dest->getWidth(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc) + { + __LICE_SC_BEZ + mode|=LICE_BLIT_IGNORE_SCALING; + } + + double ax, bx, cx, dx, ay, by, cy, dy; + double xlo, xhi, ylo, yhi; + double tlo, thi; + int nsteps = LICE_CBezPrep(destbm_w, xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, tol, true, + &ax, &bx, &cx, &dx, &ay, &by, &cy, &dy, &xlo, &xhi, &ylo, &yhi, &tlo, &thi); + if (!nsteps) return; + + double dt = (thi-tlo)/(double)nsteps; + double t = tlo+dt; + + double lastx = xlo; + double lasty = ylo; + double x, y; + bool last_xmaj=false; + int i; + for (i = 1; i < nsteps; ++i) + { + if (i == nsteps-1) + { + x = xhi; + y = yhi; + } + else + { + EVAL_CBEZXY(x, y, ax, bx, cx, dx, ay, by, cy, dy, t); + } + LICE_ThickFLine(dest, lastx, lasty, x, y, color, alpha, mode, wid); + + bool xmaj = fabs(x-lastx) > fabs(y-lasty); + if (i>1 && xmaj != last_xmaj) + { + //int color = LICE_RGBA(255,0,0,0); + if (wid>2) + { + // tested this with w=3, w=4, w=8 and all looked pretty decent + double r = wid*.5 - 1; + if (r<0) r=0; + LICE_FillCircle(dest,floor(lastx+0.5),floor(lasty+0.5),.5+r*.707,color,alpha,mode,true); + } + else + { + const int ix = (int)floor(lastx+0.5), iy = (int)floor(lasty); + const double da = lasty - iy; + LICE_PutPixel(dest,ix,iy,color,alpha * (1.0-da),mode); + LICE_PutPixel(dest,ix,iy+1,color,alpha*da,mode); + } + } + + last_xmaj = xmaj; + lastx = x; + lasty = y; + t += dt; + } +} + +void LICE_FillCBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1, + double xctl2, double yctl2, double xend, double yend, int yfill, LICE_pixel color, float alpha, int mode, double tol) +{ + if (!dest) return; + int destbm_w = dest->getWidth(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc) + { + __LICE_SC_BEZ + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(yfill); + mode|=LICE_BLIT_IGNORE_SCALING; + } + } + + + double ax, bx, cx, dx, ay, by, cy, dy; + double xlo, xhi, ylo, yhi; + double tlo, thi; + int nsteps = LICE_CBezPrep(destbm_w, xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, tol, true, + &ax, &bx, &cx, &dx, &ay, &by, &cy, &dy, &xlo, &xhi, &ylo, &yhi, &tlo, &thi); + if (!nsteps) return; + + double dt = (thi-tlo)/(double)nsteps; + double t = tlo+dt; + + int lastfillx = (int)xlo; + int lastfilly = (int)(ylo+0.5f); + double x, y; + int i; + for (i = 1; i < nsteps-1; ++i) + { + EVAL_CBEZXY(x, y, ax, bx, cx, dx, ay, by, cy, dy, t); + if ((int)x >= lastfillx) + { + int xi = (int)x; + int yi = (int)(y+0.5f); + DoBezierFillSegment(dest, lastfillx, lastfilly, xi, yi, yfill, color, alpha, mode); + lastfillx = xi+1; + lastfilly = yi; + } + t += dt; + } + if ((int)(xhi-1.0f) >= lastfillx) + { + DoBezierFillSegment(dest, lastfillx, lastfilly, (int)(xhi-1.0f),(int)(yhi+0.5f), yfill, color, alpha, mode); + } +} + +void LICE_FillCBezierX(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1, + double xctl2, double yctl2, double xend, double yend, int xfill, LICE_pixel color, float alpha, int mode, double tol) +{ + if (!dest) return; + + int destbm_w = dest->getWidth(); + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc) + { + __LICE_SC_BEZ + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(xfill); + mode|=LICE_BLIT_IGNORE_SCALING; + } + } + + double ax, bx, cx, dx, ay, by, cy, dy; + double xlo, xhi, ylo, yhi; + double tlo, thi; + int nsteps = LICE_CBezPrep(destbm_w, xstart, ystart, xctl1, yctl1, xctl2, yctl2, xend, yend, tol, false, + &ax, &bx, &cx, &dx, &ay, &by, &cy, &dy, &xlo, &xhi, &ylo, &yhi, &tlo, &thi); + if (!nsteps) return; + + double dt = (thi-tlo)/(double)nsteps; + double t = tlo+dt; + + int lastfillx = (int)(xlo+0.5f); + int lastfilly = (int)ylo; + double x, y; + int i; + for (i = 1; i < nsteps-1; ++i) + { + EVAL_CBEZXY(x, y, ax, bx, cx, dx, ay, by, cy, dy, t); + if ((int)y >= lastfilly) + { + int xi = (int)(x+0.5f); + int yi = (int)y; + DoBezierFillSegmentX(dest, lastfillx, lastfilly, xi, yi, xfill, color, alpha, mode); + lastfillx = xi; + lastfilly = yi+1; + } + t += dt; + } + if ((int)(yhi-1.0f) >= lastfilly) + { + DoBezierFillSegmentX(dest, lastfillx, lastfilly, (int)(xhi+0.5),(int)(yhi-1.0f), xfill, color, alpha, mode); + } +} + + +void LICE_DrawRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha, int mode) +{ + const int __sc = IGNORE_SCALING(mode) ? 0 : (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + double x1 = x, y1 = y, x2 = x+w, y2 = y+h; + const double amt = 1.0 - 256.0/__sc; + x1 += amt; + y1 += amt; + x2 -= amt; + y2 -= amt; + LICE_FLine(dest, x1, y1, x2, y1, color, alpha, mode, true); + LICE_FLine(dest, x2, y1, x2, y2, color, alpha, mode, true); + LICE_FLine(dest, x2, y2, x1, y2, color, alpha, mode, true); + LICE_FLine(dest, x1, y2, x1, y1, color, alpha, mode, true); + } + else + { + LICE_Line(dest, x, y, x+w, y, color, alpha, mode, false); + LICE_Line(dest, x+w, y, x+w, y+h, color, alpha, mode, false); + LICE_Line(dest, x+w, y+h, x, y+h, color, alpha, mode, false); + LICE_Line(dest, x, y+h, x, y, color, alpha, mode, false); + } +} + +void LICE_BorderedRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel bgcolor, LICE_pixel fgcolor, float alpha, int mode) +{ + LICE_FillRect(dest, x+1, y+1, w-1, h-1, bgcolor, alpha, mode); + LICE_DrawRect(dest, x, y, w, h, fgcolor, alpha, mode); +} + + +#ifndef LICE_FAVOR_SIZE_EXTREME +template +#endif +class _LICE_Fill +{ + +#ifdef LICE_FAVOR_SIZE_EXTREME + #define DOPIX(pout,r,g,b,a,ia) combFunc(pout,r,g,b,a,ia); +#else + #define DOPIX(pout,r,g,b,a,ia) COMBFUNC::doPix(pout,r,g,b,a,ia); +#endif + +public: + + // da, db are [0..65536] + static void FillClippedTrapezoid(int wid, int span, LICE_pixel *px, int y, int xa, int xb, int da, int db, int a, int b, int astep, int bstep, int cr, int cg, int cb, int ca, int aw +#ifdef LICE_FAVOR_SIZE_EXTREME + , LICE_COMBINEFUNC combFunc +#endif + ) + { + if (!da && !db) + { + while (y-->0) + { + LICE_pixel* xpx = px; + int x=xb; + while (x--) + { + DOPIX((LICE_pixel_chan*)xpx, cr, cg, cb, ca, aw) + ++xpx; + } + px += span; + } + return; + } + + + while (y-->0) + { + int x1=lice_max(xa,0); + int x2=lice_min(xb,wid); + LICE_pixel* xpx = px + x1; + int cnt=x2-x1; + while (cnt-->0) + { + DOPIX((LICE_pixel_chan*)xpx, cr, cg, cb, ca, aw) + ++xpx; + } + + a += da; + b += db; + if (a >= 65536) + { + int na = a>>16; + a &= 65535; + if (astep<0)na=-na; + xa += na; + } + if (b >= 65536) + { + int nb = b>>16; + b &= 65535; + if (bstep<0)nb=-nb; + xb += nb; + } + px += span; + } + } +}; + + +template class _LICE_FillFast +{ +public: + + // da, db are [0..65536] + static void FillClippedTrapezoidFAST(int wid, int span, LICE_pixel *px, int y, int xa, int xb, int da, int db, int a, int b, int astep, int bstep, LICE_pixel color) + { + if (!da && !db) + { + while (y-->0) + { + LICE_pixel* xpx = px; + int x=xb; + while (x--) + { + COMBFUNC::doPixFAST(xpx, color); + ++xpx; + } + px += span; + } + return; + } + + + while (y-->0) + { + int x1=lice_max(xa,0); + int x2=lice_min(xb,wid); + LICE_pixel* xpx = px + x1; + int cnt=x2-x1; + while (cnt-->0) + { + COMBFUNC::doPixFAST(xpx, color); + ++xpx; + } + + a += da; + b += db; + if (a >= 65536) + { + int na = a>>16; + a &= 65535; + if (astep<0)na=-na; + xa += na; + } + if (b >= 65536) + { + int nb = b>>16; + b &= 65535; + if (bstep<0)nb=-nb; + xb += nb; + } + px += span; + } + } +}; + +static double FindXOnSegment(int x1, int y1, int x2, int y2, int ty) +{ + if (y1 > y2) + { + SWAP(x1, x2); + SWAP(y1, y2); + } + if (ty <= y1) return x1; + if (ty >= y2) return x2; + const double dxdy = (x2-x1)/(double)(y2-y1); + return x1+(ty-y1)*dxdy; +} + +void LICE_FillTrapezoidF(LICE_IBitmap* dest, double fx1a, double fx1b, int y1, double fx2a, double fx2b, int y2, LICE_pixel color, float alpha, int mode) +{ + if (!dest) return; + if (y1 > y2) + { + SWAP(y1, y2); + SWAP(fx1a, fx2a); + SWAP(fx1b, fx2b); + } + if (fx1a > fx1b) SWAP(fx1a, fx1b); + if (fx2a > fx2b) SWAP(fx2a, fx2b); + + int w = dest->getWidth(); + int h = dest->getHeight(); + + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(fx1a); + __LICE_SC(fx1b); + __LICE_SC(fx2a); + __LICE_SC(fx2b); + __LICE_SC(y1); + __LICE_SC(y2); + } + } + + if (fx1b < 0 && fx2b < 0) return; + if (fx1a >= w && fx2a >= w) return; + + if (fx1a <= 0 && fx2a <= 0) fx1a = fx2a = 0; + if (fx1b >= w-1 && fx2b >= w-1) fx1b = fx2b = w-1; + + if (y2 < 0 || y1 >= h) return; + + int aw = (int)(alpha*256.0f); + + double idy = y2==y1 ? 0.0 : (65536.0/(y2-y1)); + + const double maxv=(double)(1<<29); + double tmp = (fx2a-fx1a)*idy; + if (tmp > maxv) tmp=maxv; + else if (tmp < -maxv) tmp=-maxv; + int dxady = (int)floor(tmp+0.5); + + tmp = ((fx2b-fx1b)*idy); + if (tmp > maxv) tmp=maxv; + else if (tmp < -maxv) tmp=-maxv; + int dxbdy = (int)floor(tmp+0.5); + + int astep = 1; + int bstep = 1; + if (dxady < 0) + { + dxady = -dxady; + astep = -1; + } + if (dxbdy < 0) + { + dxbdy = -dxbdy; + bstep = -1; + } + + int x1a = (int)floor(fx1a); + int x1b = (int)floor(fx1b); + int a = (int) floor((fx1a-x1a)*65536.0*astep+0.5); + int b = (int) floor((fx1b-x1b)*65536.0*bstep+0.5); + + if (y1<0) + { + a -= dxady*y1; + b -= dxbdy*y1; + y1=0; + } + if (a< 0 || a >= 65536) + { + int na = a>>16; + a &= 65535; + if (astep<0)na=-na; + x1a += na; + } + if (b < 0 || b >= 65536) + { + int nb = b>>16; + b &= 65535; + if (bstep<0)nb=-nb; + x1b += nb; + } + const int extra = __sc> 0 && !IGNORE_SCALING(mode) ? (__sc/256 - 1) : 0; + if (y2 > h-1-extra) y2 = h-1-extra; + + int wid = w; + int span = dest->getRowSpan(); + LICE_pixel* px = dest->getBits()+y1*span; + int y = y2-y1 + 1 + extra; + + x1b++; // from now on draw [x1a,x1b) + + if (!dxady && !dxbdy) + { + if (x1a<0)x1a=0; + x1b = lice_min(x1b,wid)-x1a; + px+=x1a; + if (x1b<1) return; + } + + if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && aw==256) + { + _LICE_FillFast<_LICE_CombinePixelsClobberFAST>::FillClippedTrapezoidFAST(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, color); + } + else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && aw==128) + { + color = (color>>1)&0x7f7f7f7f; + _LICE_FillFast<_LICE_CombinePixelsHalfMix2FAST>::FillClippedTrapezoidFAST(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, color); + } + else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && aw==64) + { + color = (color>>2)&0x3f3f3f3f; + _LICE_FillFast<_LICE_CombinePixelsQuarterMix2FAST>::FillClippedTrapezoidFAST(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, color); + } + else if ((mode&LICE_BLIT_MODE_MASK) == LICE_BLIT_MODE_COPY && aw==192) + { + color = ((color>>1)&0x7f7f7f7f)+((color>>2)&0x3f3f3f3f); + _LICE_FillFast<_LICE_CombinePixelsThreeQuarterMix2FAST>::FillClippedTrapezoidFAST(wid,span,px,y, x1a, x1b, dxady, dxbdy,a,b, astep,bstep, color); + } + else + { + int cr = LICE_GETR(color), cg = LICE_GETG(color), cb = LICE_GETB(color), ca = LICE_GETA(color); +#ifdef LICE_FAVOR_SIZE_EXTREME + + LICE_COMBINEFUNC blitfunc=NULL; +#define __LICE__ACTION(comb) blitfunc=comb::doPix; +#else +#define __LICE__ACTION(COMBFUNC) _LICE_Fill::FillClippedTrapezoid(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, cr,cg,cb,ca, aw); +#endif + + __LICE_ACTION_CONSTANTALPHA(mode, aw, false); + +#ifdef LICE_FAVOR_SIZE_EXTREME + if (blitfunc) _LICE_Fill::FillClippedTrapezoid(wid,span,px,y, x1a, x1b, dxady, dxbdy, a,b, astep,bstep, cr,cg,cb,ca, aw, blitfunc); +#endif + +#undef __LICE__ACTION + } +} + +void LICE_FillTrapezoid(LICE_IBitmap* dest, int x1a, int x1b, int y1, int x2a, int x2b, int y2, LICE_pixel color, float alpha, int mode) +{ + LICE_FillTrapezoidF(dest,x1a,x1b,y1,x2a,x2b,y2,color,alpha,mode); +} + +static int _ysort(const void* a, const void* b) +{ + int* xya = (int*)a; + int* xyb = (int*)b; + if (xya[1] < xyb[1]) return -1; + if (xya[1] > xyb[1]) return 1; + if (xya[0] < xyb[0]) return -1; + if (xya[0] > xyb[0]) return 1; + return 0; +} + +#define _X(i) xy[2*(i)] +#define _Y(i) xy[2*(i)+1] + +static int FindNextEdgeVertex(int* xy, int a, int n, int dir) +{ + bool init = false; + double dxdy_best = 0.0f; + int i, ilo = a; + + for (i = a+1; i < n; ++i) + { + if (_Y(i) == _Y(a)) continue; + const double dxdy = (_X(i)-_X(a))/(double)(_Y(i)-_Y(a)); + if (!init || dxdy == dxdy_best || (dir < 0 && dxdy < dxdy_best) || (dir > 0 && dxdy > dxdy_best)) + { + init = true; + ilo = i; + dxdy_best = dxdy; + } + } + return ilo; +} + +void LICE_FillConvexPolygon(LICE_IBitmap* dest, const int* x, const int* y, int npoints, LICE_pixel color, float alpha, int mode) +{ + if (!dest) return; + if (npoints < 3) return; + + int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + + if (IGNORE_SCALING(mode)) + { + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc) + { + __LICE_SCU(destbm_w); + __LICE_SCU(destbm_h); + } + } + + int* xy = 0; + int xyt[1024]; // use stack space if small + bool usestack = npoints <= (int) (sizeof(xyt)/sizeof(int)/2); + if (usestack) xy = xyt; + else xy = (int*)malloc(npoints*sizeof(int)*2); + + int i; + { + int min_x=destbm_w,max_x=0; + for (i = 0; i < npoints; ++i) + { + int tx = x[i], ty=y[i]; + if (tx < min_x) min_x=tx; + if (tx > max_x) max_x=tx; + _X(i) = tx; + if (dest->isFlipped()) ty = destbm_h-ty-1; + _Y(i) = ty; + } + qsort(xy, npoints, 2*sizeof(int), _ysort); // sorts by y, at same y sorts by x + + + int ty=_Y(0); + if (ty == _Y(npoints-1)) + { + // special case 1px high polygon + if (ty >= 0 && ty < dest->getHeight() && min_x <= max_x) + { + LICE_FillTrapezoid(dest,min_x,max_x,ty,min_x,max_x,ty,color,alpha,mode); + } + if (!usestack) free(xy); + + return; + } + } + + int a1, b1; // index of previous vertex L and R + int a2, b2; // index of next vertex L and R + int y1; // top and bottom of current trapezoid + + a1 = b1 = 0; + y1 = _Y(0); + + for (i = 1; i < npoints && _Y(i) == y1; ++i) + { + if (_X(i) == _X(0)) a1 = i; + b1 = i; + } + + a2 = FindNextEdgeVertex(xy, a1, npoints, -1); + b2 = FindNextEdgeVertex(xy, b1, npoints, 1); + + while (a1 != a2 || b1 != b2) + { + int y_a2 = _Y(a2); + int y_b2 = _Y(b2); + + int y2 = lice_min(y_a2, y_b2); + double x1a = FindXOnSegment(_X(a1), _Y(a1), _X(a2), y_a2, y1); + double x1b = FindXOnSegment(_X(b1), _Y(b1), _X(b2), y_b2, y1); + double x2a = FindXOnSegment(_X(a1), _Y(a1), _X(a2), y_a2, y2); + double x2b = FindXOnSegment(_X(b1), _Y(b1), _X(b2), y_b2, y2); + + LICE_FillTrapezoidF(dest, x1a, x1b, y1, x2a, x2b, y2, color, alpha, mode); + + bool dir = y1<=y2; // should always be true + + y1 = y2; + if (y_a2 == y1) + { + a1 = a2; + a2 = FindNextEdgeVertex(xy, a2, npoints, -1); + } + if (y_b2 == y1) + { + b1 = b2; + b2 = FindNextEdgeVertex(xy, b2, npoints, 1); + } + + if (dir) y1++; + else y1--; + } + + if (!usestack) free(xy); +} + +#undef _X +#undef _Y + + +void LICE_FillTriangle(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, int x3, int y3, LICE_pixel color, float alpha, int mode) +{ + if (!dest) return; + + int x[3] = { x1, x2, x3 }; + int y[3] = { y1, y2, y3 }; + LICE_FillConvexPolygon(dest, x, y, 3, color, alpha, mode); +} + +void LICE_ThickFLine(LICE_IBitmap* dest, double x1, double y1, double x2, double y2, LICE_pixel color, float alpha, int mode, int wid) // always AA. wid is not affected by scaling (1 is always normal line, 2 is always 2 physical pixels, etc) +{ + if (!dest || wid<1) return; + if (wid==1) + { + LICE_Line(dest,(float)x1,(float)y1,(float)x2,float(y2),color,alpha,mode,true); + return; + } + + int w = dest->getWidth(); + int h = dest->getHeight(); + if (dest->isFlipped()) + { + y1 = (h-1)-y1; + y2 = (h-1)-y2; + } + + const int __sc = (int)dest->Extended(LICE_EXT_GET_SCALING,NULL); + if (__sc>0) + { + __LICE_SCU(w); + __LICE_SCU(h); + if (!IGNORE_SCALING(mode)) + { + __LICE_SC(x1); + __LICE_SC(x2); + __LICE_SC(y1); + __LICE_SC(y2); + } + } + + bool steep = fabs(y2-y1) >= fabs(x2-x1); + + if (ClipFLine(&x1, &y1, &x2, &y2, w, h)) + { + if (x1 != x2 || y1 != y2) + { + int span = dest->getRowSpan(); + int aw = (int)(256.0f*alpha); + + double a1, a2, b1, b2, da, db; + int astep, bstep; + double dx = x2-x1; + double dy = y2-y1; + + int b_max; + if (!steep) + { + a1 = x1; + a2 = x2; + b1 = y1; + b2 = y2; + da = dx; + db = dy; + astep = 1; + bstep = span; + b_max = h; + } + else + { + a1 = y1; + a2 = y2; + b1 = x1; + b2 = x2; + da = dy; + db = dx; + astep = span; + bstep = 1; + b_max = w; + } + + if (da < 0.0) + { + da = -da; + db = -db; + SWAP(a1, a2); + SWAP(b1, b2); + } + if (db < 0.0) + { + bstep = -bstep; + } + + int n = (int)(floor(a2)-ceil(a1)); + double dbda = db/da; + + double ta = ceil(a1); + double tb = b1+(ta-a1)*dbda; + double bf = tb-floor(tb); + int err = (int)(bf*65536.0); + if (bstep < 0) err = 65535-err; + int derr = (int)(fabs(dbda)*65536.0); + + int b_pos = (int) tb; + LICE_pixel *px = dest->getBits() + (int)ta*astep+b_pos*abs(bstep); + + if (bstep < 0) { px -= bstep; b_pos++; } + +#ifdef LICE_FAVOR_SIZE + + LICE_COMBINEFUNC blitfunc=NULL; + #define __LICE__ACTION(comb) blitfunc=comb::doPix; + +#else + #define __LICE__ACTION(COMBFUNC) \ + __LICE_LineClass::LICE_FLineImplFill(px,n,err,derr,astep,bstep, color, aw, wid, b_pos, b_max) +#endif + + __LICE_ACTION_NOSRCALPHA(mode, aw, false); + +#ifdef LICE_FAVOR_SIZE + if (blitfunc) + { + __LICE_LineClass::LICE_FLineImplFill(px,n,err,derr,astep,bstep, color, aw, wid, b_pos, b_max, blitfunc); + } +#endif + + #undef __LICE__ACTION + } + } +} diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_palette.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_palette.cpp new file mode 100644 index 000000000..b3b2f43b4 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_palette.cpp @@ -0,0 +1,469 @@ +#include "lice.h" +#include "../ptrlist.h" +#include "../wdltypes.h" + + +#define OCTREE_DEPTH 5 // every depth level adds 3 bits of RGB colorspace (depth 8 => 24-bit RGB) + +struct ONode +{ + WDL_INT64 colorcount; // number of color instances at or below this node + WDL_INT64 sumrgb[3]; + int childflag; // 0=leaf, >0=index of single child, <0=branch + int leafidx; // populated at the end + ONode* next; // for OTree::branches + ONode* children[8]; +}; + +struct OTree +{ + int maxcolors; + int leafcount; + ONode* trunk; + ONode* branches[OCTREE_DEPTH]; // linked lists of branches for each level of the tree + ONode* spares; + LICE_pixel* palette; // populated at the end + bool palette_valid; +}; + + + +int LICE_BuildPalette(LICE_IBitmap* bmp, LICE_pixel* palette, int maxcolors) +{ + void* tree = LICE_CreateOctree(maxcolors); + LICE_BuildOctree(tree, bmp); + int sz = LICE_ExtractOctreePalette(tree, palette); + LICE_DestroyOctree(tree); + return sz; +} + + +static void AddColorToTree(OTree*, const LICE_pixel_chan *rgb); +static int FindColorInTree(OTree*, const LICE_pixel_chan *rgb); +static int PruneTree(OTree*); +static void DeleteNode(OTree*, ONode*, ONode **delete_to); +static int CollectLeaves(OTree*); +static int CollectNodeLeaves(ONode* node, LICE_pixel* palette, int colorcount); + + +void* LICE_CreateOctree(int maxcolors) +{ + OTree* tree = new OTree; + memset(tree, 0, sizeof(OTree)); + tree->maxcolors = maxcolors; + tree->trunk = new ONode; + memset(tree->trunk, 0, sizeof(ONode)); + tree->spares = NULL; + return tree; +} + + +void LICE_ResetOctree(void *octree, int maxc) +{ + OTree* tree = (OTree*)octree; + if (!tree) return; + + if (maxc > tree->maxcolors) + { + free(tree->palette); + tree->palette=0; + } + + DeleteNode(tree, tree->trunk, &tree->spares); + tree->leafcount = 0; + tree->maxcolors = maxc; + tree->palette_valid=false; + memset(tree->branches,0,sizeof(tree->branches)); + + tree->trunk=tree->spares; + if (tree->trunk) tree->spares = tree->trunk->next; + else tree->trunk = new ONode; + + memset(tree->trunk, 0, sizeof(ONode)); +} + +void LICE_DestroyOctree(void* octree) +{ + OTree* tree = (OTree*)octree; + if (!tree) return; + + DeleteNode(tree, tree->trunk, NULL); + + ONode *p = tree->spares; + while (p) + { + ONode *del = p; + p = p->next; + + delete del; + } + free(tree->palette); + delete tree; +} + + +int LICE_BuildOctree(void* octree, LICE_IBitmap* bmp) +{ + OTree* tree = (OTree*)octree; + if (!tree || !bmp) return 0; + + tree->palette_valid = false; + + int y; + const int h=bmp->getHeight(); + const int w=bmp->getWidth(); + const int rowspan = bmp->getRowSpan(); + const LICE_pixel *bits = bmp->getBits(); + for (y = 0; y < h; ++y) + { + const LICE_pixel *px = bits+y*rowspan; + int x=w; + while (x--) + { + AddColorToTree(tree, (const LICE_pixel_chan*)px); + if (tree->leafcount > tree->maxcolors) PruneTree(tree); + px++; + } + } + + return tree->leafcount; +} + +int LICE_BuildOctreeForAlpha(void* octree, LICE_IBitmap* bmp, unsigned int minalpha) +{ + OTree* tree = (OTree*)octree; + if (!tree || !bmp) return 0; + + tree->palette_valid = false; + + int y; + const int h=bmp->getHeight(); + const int w=bmp->getWidth(); + const int rowspan = bmp->getRowSpan(); + const LICE_pixel *bits = bmp->getBits(); + int pxcnt=0; + for (y = 0; y < h; ++y) + { + const LICE_pixel *px = bits+y*rowspan; + int x=w; + while (x--) + { + if (px[LICE_PIXEL_A] >= minalpha) + { + AddColorToTree(tree, (const LICE_pixel_chan*)px); + if (tree->leafcount > tree->maxcolors) PruneTree(tree); + pxcnt++; + } + px++; + } + } + + return pxcnt; +} + + +int LICE_BuildOctreeForDiff(void* octree, LICE_IBitmap* bmp, LICE_IBitmap* refbmp, LICE_pixel mask) +{ + OTree* tree = (OTree*)octree; + if (!tree || !bmp || !refbmp) return 0; + + tree->palette_valid=false; + + int y; + const int h=lice_min(bmp->getHeight(),refbmp->getHeight()); + const int w=lice_min(bmp->getWidth(),refbmp->getWidth()); + + int rowspan = bmp->getRowSpan(); + int rowspan2 = refbmp->getRowSpan(); + const LICE_pixel *bits = bmp->getBits(); + const LICE_pixel *bits2 = refbmp->getBits(); + + if (bmp->isFlipped()) + { + bits += rowspan * (bmp->getHeight()-1); + rowspan = -rowspan; + } + + if (refbmp->isFlipped()) + { + bits2 += rowspan2 * (refbmp->getHeight()-1); + rowspan2 = -rowspan2; + } + + int pxcnt=0; + for (y = 0; y < h; ++y) + { + const LICE_pixel * px = bits+y*rowspan; + const LICE_pixel * px2 = bits2+y*rowspan2; + int x=w; + while (x--) + { + if ((*px ^ *px2) & mask) + { + AddColorToTree(tree, (const LICE_pixel_chan *)px); + if (tree->leafcount > tree->maxcolors) PruneTree(tree); + pxcnt++; + } + px++; + px2++; + } + } + + return pxcnt; +} + + +int LICE_FindInOctree(void* octree, LICE_pixel color) +{ + OTree* tree = (OTree*)octree; + if (!tree) return 0; + + if (!tree->palette_valid) CollectLeaves(tree); + + return FindColorInTree(tree, (const LICE_pixel_chan *)&color); +} + + +int LICE_ExtractOctreePalette(void* octree, LICE_pixel* palette) +{ + OTree* tree = (OTree*)octree; + if (!tree || !palette) return 0; + + if (!tree->palette_valid) CollectLeaves(tree); + + if (tree->palette) memcpy(palette, tree->palette, tree->maxcolors*sizeof(LICE_pixel)); + + return tree->leafcount; +} + + + +void LICE_TestPalette(LICE_IBitmap* bmp, LICE_pixel* palette, int numcolors) +{ + int x, y; + for (y = 0; y < bmp->getHeight(); ++y) + { + LICE_pixel* px = bmp->getBits()+y*bmp->getRowSpan(); + for (x = 0; x < bmp->getWidth(); ++x) + { + const LICE_pixel col = px[x]; + const int rgb[3] = { (int)LICE_GETR(col), (int)LICE_GETG(col), (int)LICE_GETB(col) }; + + int minerr = 0x7FFFFFFF; + int bestcol=-1; + int i; + for (i = 0; i < numcolors; ++i) + { + const LICE_pixel palcol = palette[i]; + const int rerr[3] = { rgb[0]-(int)LICE_GETR(palcol), rgb[1]-(int)LICE_GETG(palcol), rgb[2]-(int)LICE_GETB(palcol) }; + const int err = rerr[0]*rerr[0]+rerr[1]*rerr[1]+rerr[2]*rerr[2]; + if (err < minerr) + { + bestcol=i; + minerr=err; + } + } + px[x] = palette[bestcol]; + } + } +} + + +void AddColorToTree(OTree* tree, const LICE_pixel_chan *rgb) +{ + ONode* p = tree->trunk; + p->colorcount++; + + int i; + const unsigned char r = rgb[LICE_PIXEL_R]; + const unsigned char g = rgb[LICE_PIXEL_G]; + const unsigned char b = rgb[LICE_PIXEL_B]; + for (i = OCTREE_DEPTH-1; i >= 0; --i) + { + const int j = i+8-OCTREE_DEPTH; + const unsigned char idx = (((r>>(j-2))&4))|(((g>>(j-1))&2))|((b>>j)&1); + + ONode* np = p->children[idx]; + bool isleaf = false; + + if (np) + { + isleaf = !np->childflag; + } + else // add node + { + if (!p->childflag) // first time down this path + { + p->childflag=idx+1; + } + else if (p->childflag > 0) // creating a new branch + { + p->childflag = -1; + p->next = tree->branches[i]; + tree->branches[i] = p; + } + // else multiple branch, which we don't care about + + np=tree->spares; + if (np) tree->spares = np->next; + else np = new ONode; + + p->children[idx] = np; + memset(np, 0, sizeof(ONode)); + } + + np->sumrgb[0] += r; + np->sumrgb[1] += g; + np->sumrgb[2] += b; + np->colorcount++; + + if (isleaf) return; + + p=np; // continue downward + } + + // p is a new leaf at the bottom + tree->leafcount++; +} + +int FindColorInTree(OTree* tree, const LICE_pixel_chan *rgb) +{ + ONode* p = tree->trunk; + + int i; + const unsigned char r=rgb[LICE_PIXEL_R]; + const unsigned char g=rgb[LICE_PIXEL_G]; + const unsigned char b=rgb[LICE_PIXEL_B]; + for (i = OCTREE_DEPTH-1; i >= 0; --i) + { + if (!p->childflag) break; + + const int j = i+8-OCTREE_DEPTH; + const unsigned char idx = (((r>>(j-2))&4))|(((g>>(j-1))&2))|((b>>j)&1); + + ONode* np = p->children[idx]; + if (!np) break; + + p = np; + } + + return p->leafidx; +} + + +int PruneTree(OTree* tree) +{ + ONode* branch=0; + int i; + for (i = 0; i < OCTREE_DEPTH; ++i) // prune at the furthest level from the trunk + { + branch = tree->branches[i]; + if (branch) + { + tree->branches[i] = branch->next; + branch->next=0; + break; + } + } + + if (branch) + { + for (i = 0; i < 8; ++i) + { + if (branch->children[i]) + { + DeleteNode(tree, branch->children[i],&tree->spares); + branch->children[i]=0; + } + } + branch->childflag=0; // now it's a leaf + tree->leafcount++; + } + + return tree->leafcount; +} + +int CollectLeaves(OTree* tree) +{ + if (!tree->palette) tree->palette = (LICE_pixel*)malloc(tree->maxcolors*sizeof(LICE_pixel)); + + if (!tree->palette) return 0; + + int sz = CollectNodeLeaves(tree->trunk, tree->palette, 0); + memset(tree->palette+sz, 0, (tree->maxcolors-sz)*sizeof(LICE_pixel)); + tree->palette_valid = true; + + return sz; +} + +int CollectNodeLeaves(ONode* p, LICE_pixel* palette, int colorcount) +{ + if (!p->childflag) + { + p->leafidx = colorcount; + int r = (int)((double)p->sumrgb[0]/(double)p->colorcount); + int g = (int)((double)p->sumrgb[1]/(double)p->colorcount); + int b = (int)((double)p->sumrgb[2]/(double)p->colorcount); + palette[colorcount++] = LICE_RGBA(r, g, b, 255); + } + else + { + if (p->childflag > 0) + { + colorcount = CollectNodeLeaves(p->children[p->childflag-1], palette, colorcount); + } + else + { + int i; + for (i = 0; i < 8; ++i) + { + if (p->children[i]) + { + colorcount = CollectNodeLeaves(p->children[i], palette, colorcount); + } + } + } + // this is a branch or passthrough node, record the index + // of any downtree leaf here so that we can return it for + // color lookups that want to diverge off this node + p->leafidx = colorcount-1; + } + + // colorcount should == leafcount + return colorcount; +} + + +void DeleteNode(OTree* tree, ONode* p, ONode **delete_to) +{ + if (!p->childflag) + { + tree->leafcount--; + } + else if (p->childflag > 0) + { + DeleteNode(tree, p->children[p->childflag-1],delete_to); + } + else + { + int i; + for (i = 0; i < 8; ++i) + { + if (p->children[i]) + { + DeleteNode(tree, p->children[i],delete_to); + } + } + } + + if (delete_to) + { + p->next = *delete_to; + *delete_to = p; + } + else + { + delete p; + } +} + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_texgen.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_texgen.cpp new file mode 100644 index 000000000..8dbf7afd8 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_texgen.cpp @@ -0,0 +1,351 @@ +/* + Cockos WDL - LICE - Lightweight Image Compositing Engine + Copyright (C) 2007 and later, Cockos Incorporated + File: lice_texgen.cpp (LICE texture generator routines) + See lice.h for license and other information +*/ + + +#ifndef WDL_NO_DEFINE_MINMAX +#define WDL_NO_DEFINE_MINMAX +#endif +#include "lice.h" +#include + +void LICE_TexGen_Marble(LICE_IBitmap *dest, const RECT *rect, float rv, float gv, float bv, float intensity) +{ + int span=dest->getRowSpan(); + int w = dest->getWidth(); + int h = dest->getHeight(); + int x = 0; + int y = 0; + if(rect) + { + x = rect->left; + y = rect->top; + w = rect->right - rect->left; + h = rect->bottom - rect->top; + } + + if (x<0) { w+=x; x=0; } + if (y<0) { h+=y; y=0; } + + const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + if (w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return; + + if (w>destbm_w-x) w=destbm_w-x; + if (h>destbm_h-y) h=destbm_h-y; + + + LICE_pixel *startp = dest->getBits(); + if (dest->isFlipped()) + { + startp += x + (dest->getHeight()-1-y)*span; + span=-span; + } + else startp += x + y*span; + + //simple 16bit marble noise generator + +#define ROL(x,y) ((x<<(y))|(((unsigned short)x)>>(16-(y)))) +#define ROR(x,y) ((((unsigned short)x)>>(y))|(x<<(16-(y)))) + + intensity/=1024.0f; + int maxc = 0; + { + LICE_pixel *p = startp; + short n1 = 0, n2 = 0; + for(int i=0;i0) + { + c = p[j-span]; + if(j==0) + c2 = p[(w-1)-span]; + else + c2 = p[(j-1)-span]; + } + + int pix = (((c + c2)/2) + val); + if(pix>maxc) maxc = pix; + p[j] = pix; + } + p+=span; + } + } + + //normalize values and apply gamma + { + LICE_pixel *p = startp; + float sc=255.0f/maxc; + + for(int i=0;igetRowSpan(); + int w = dest->getWidth(); + int h = dest->getHeight(); + int dx = 0; + int dy = 0; + if(rect) + { + dx = rect->left; + dy = rect->top; + w = rect->right - rect->left; + h = rect->bottom - rect->top; + } + + if (dx<0) { w+=dx; dx=0; } + if (dy<0) { h+=dy; dy=0; } + const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + if (w<1 || h < 1 || dx >= destbm_w || dy >= destbm_h) return; + + if (w>destbm_w-dx) w=destbm_w-dx; + if (h>destbm_h-dy) h=destbm_h-dy; + + LICE_pixel *startp = dest->getBits(); + if (dest->isFlipped()) + { + startp += dx + (dest->getHeight()-1-dy)*span; + span=-span; + } + else startp += dx + dy*span; + + { + LICE_pixel *p = startp; + for(int i=0;i=1) + { + switch(mode) + { + case NOISE_MODE_NORMAL: val += noise(x/size, y/size)*size; break; + case NOISE_MODE_WOOD: val += (float)cos( x/size + noise(x/size,y/size) )*size/2; break; + } + size /= 2; + } + float col = (float)fabs(val/smooth)*255; + if(col>255) col=255; + + p[j] = LICE_RGBA((int)(col*rv),(int)(col*gv),(int)(col*bv),255); + } + p+=span; + } + } +} + +static float turbulence(int x, int y, float size, float isize) +{ + float value = 0.0; + const float initialSize = isize; + while(size >= 1) + { + value += noise(x * isize, y * isize) * size; + size *= 0.5f; + isize *= 2.0f; + } + return(128.0f * value * initialSize); +} + +void LICE_TexGen_CircNoise(LICE_IBitmap *dest, const RECT *rect, float rv, float gv, float bv, float nrings, float power, int size) +{ + initNoise(); + + int span=dest->getRowSpan(); + int w = dest->getWidth(); + int h = dest->getHeight(); + int x = 0; + int y = 0; + if(rect) + { + x = rect->left; + y = rect->top; + w = rect->right - rect->left; + h = rect->bottom - rect->top; + } + + if (x<0) { w+=x; x=0; } + if (y<0) { h+=y; y=0; } + const int destbm_w = dest->getWidth(), destbm_h = dest->getHeight(); + if (w<1 || h < 1 || x >= destbm_w || y >= destbm_h) return; + + if (w>destbm_w-x) w=destbm_w-x; + if (h>destbm_h-y) h=destbm_h-y; + + LICE_pixel *startp = dest->getBits(); + if (dest->isFlipped()) + { + startp += x + (dest->getHeight()-1-y)*span; + span=-span; + } + else startp += x + y*span; + + float xyPeriod = nrings; + float turbPower = power; + const float iturbSize = 1.0f/(float)size; + const float turbSize = (float)size; + + { + LICE_pixel *p = startp; + for(int i=0;i' */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xc0, /* 11000000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xce, /* 11001110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xce, /* 11001110 */ + 0x7c, /* 01111100 */ + 0x0e, /* 00001110 */ + + /* 82 0x52 'R' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x8c, /* 10001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0xc0, /* 11000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + + /* 96 0x60 '`' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0xf8, /* 11111000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + + /* 104 0x68 'h' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + + /* 107 0x6b 'k' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x4c, /* 01001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ +}; + + +void LICE_DrawChar(LICE_IBitmap *bm, int x, int y, char c, + LICE_pixel color, float alpha, int mode) +{ + LICE_pixel *fb; + if (c<1 || !bm || !(fb=bm->getBits()))return; + unsigned char *font = LICE_deffont + ((c-1)*LICE_FONT_HEIGHT); + int len = LICE_FONT_HEIGHT; + int smask=128; + int xlen=8; + + if (y < 0) + { + font -= y; + len += y; + y = 0; + } + if (x<0) + { + smask >>= -x; + xlen+=x; + x=0; + } + + const int bm_w = bm->getWidth(), bm_h = bm->getHeight(); + if (xlen < 1 || len < 1 || x >= bm_w || y >= bm_h) return; + + if (xlen > bm_w - x) xlen = bm_w - x; + if (len > bm_h - y) len = bm_h - y; + + int rs=bm->getRowSpan(); + if (bm->isFlipped()) + { + fb += x+((bm->getHeight()-1-y)*rs); + rs=-rs; + } + else + fb += x+(y*rs); + + int red=LICE_GETR(color), green=LICE_GETG(color), blue=LICE_GETB(color), alp=LICE_GETA(color), ialpha=(int) (alpha * 256.0f); + + while (len-->0) + { + LICE_pixel *outmem = fb; + fb+=rs; + unsigned char ch = *font++; + int a=smask; + int xleft = xlen; + while (a && xleft--) + { + if (ch & a) + { + #define __LICE__ACTION(comb) comb::doPix((LICE_pixel_chan *)outmem, red,green,blue,alp,ialpha) + __LICE_ACTION_NOSRCALPHA(mode,ialpha, false); + #undef __LICE__ACTION + } + outmem++; + a >>= 1; + } + } +} + +void LICE_DrawText(LICE_IBitmap *bm, int x, int y, const char *string, + LICE_pixel color, float alpha, int mode) +{ + if (!bm) return; + int w=bm->getWidth(); + int h=bm->getHeight(); + int xx = x; + while (*string) + { + switch (*string) + { + case '\n': y += LICE_FONT_HEIGHT; xx = x; break; + case ' ': xx += 8; break; + case '\r': break; + case '\t': xx += 8*5; break; + default: + if (xx>=-8 && xx= -LICE_FONT_HEIGHT && y < h) + LICE_DrawChar(bm,xx,y,*string, color,alpha,mode); + xx += 8; + break; + } + string++; + } +} + +void LICE_MeasureText(const char *string, int *w, int *h) +{ + if (w) *w=0; + if (h) *h=0; + int x=0,y=LICE_FONT_HEIGHT; + while (*string) + { + switch (*string) + { + case '\n': y += LICE_FONT_HEIGHT; x = 0; break; + case '\r': break; + case '\t': x += 8*4; + default: + x += 8; + if (w && x > *w) *w=x; + if (h && y > *h) *h=y; + break; + } + string++; + } +} + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_text.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_text.h new file mode 100644 index 000000000..5f7fcc528 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_text.h @@ -0,0 +1,274 @@ +#ifndef _LICE_TEXT_H_ +#define _LICE_TEXT_H_ + +#include "lice.h" + +#ifndef _WIN32 +#include "../swell/swell.h" +#endif +#include "../heapbuf.h" + +#define LICE_FONT_FLAG_VERTICAL 1 // rotate text to vertical (do not set the windows font to vertical though) +#define LICE_FONT_FLAG_VERTICAL_BOTTOMUP 2 + +#define LICE_FONT_FLAG_PRECALCALL 4 +//#define LICE_FONT_FLAG_ALLOW_NATIVE 8 +#define LICE_FONT_FLAG_FORCE_NATIVE 1024 + +#define LICE_FONT_FLAG_FX_BLUR 16 +#define LICE_FONT_FLAG_FX_INVERT 32 +#define LICE_FONT_FLAG_FX_MONO 64 // faster but no AA/etc + +#define LICE_FONT_FLAG_FX_SHADOW 128 // these imply MONO +#define LICE_FONT_FLAG_FX_OUTLINE 256 + +#define LICE_FONT_FLAG_OWNS_HFONT 512 + +// could do a mask for these flags +#define LICE_FONT_FLAGS_HAS_FX(flag) \ + (flag&(LICE_FONT_FLAG_VERTICAL|LICE_FONT_FLAG_VERTICAL_BOTTOMUP| \ + LICE_FONT_FLAG_FX_BLUR|LICE_FONT_FLAG_FX_INVERT|LICE_FONT_FLAG_FX_MONO| \ + LICE_FONT_FLAG_FX_SHADOW|LICE_FONT_FLAG_FX_OUTLINE)) + +#define LICE_DT_NEEDALPHA 0x80000000 // include in DrawText() if the output alpha channel is important +#define LICE_DT_USEFGALPHA 0x40000000 // uses alpha channel in fg color + +class LICE_IFont +{ + public: + virtual ~LICE_IFont() {} + + virtual void SetFromHFont(HFONT font, int flags=0)=0; // hfont must REMAIN valid, unless LICE_FONT_FLAG_PRECALCALL or LICE_FONT_FLAG_OWNS_HFONT set (OWNS means LICE_IFont will clean up hfont on font change or exit) + + virtual LICE_pixel SetTextColor(LICE_pixel color)=0; + virtual LICE_pixel SetBkColor(LICE_pixel color)=0; + virtual LICE_pixel SetEffectColor(LICE_pixel color)=0; + virtual int SetBkMode(int bkmode)=0; + virtual void SetCombineMode(int combine, float alpha=1.0f)=0; + + virtual int DrawText(LICE_IBitmap *bm, const char *str, int strcnt, RECT *rect, UINT dtFlags)=0; + + virtual LICE_pixel GetTextColor()=0; + virtual HFONT GetHFont()=0; + virtual int GetLineHeight()=0; + virtual void SetLineSpacingAdjust(int amt)=0; +}; + + +#ifndef LICE_TEXT_NO_DECLARE_CACHEDFONT + +class LICE_CachedFont : public LICE_IFont +{ + public: + LICE_CachedFont(); + virtual ~LICE_CachedFont(); + + virtual void SetFromHFont(HFONT font, int flags=0); + + virtual LICE_pixel SetTextColor(LICE_pixel color) { LICE_pixel ret=m_fg; m_fg=color; return ret; } + virtual LICE_pixel SetBkColor(LICE_pixel color) { LICE_pixel ret=m_bg; m_bg=color; return ret; } + virtual LICE_pixel SetEffectColor(LICE_pixel color) { LICE_pixel ret=m_effectcol; m_effectcol=color; return ret; } + virtual int SetBkMode(int bkmode) { int bk = m_bgmode; m_bgmode=bkmode; return bk; } + virtual void SetCombineMode(int combine, float alpha=1.0f) { m_comb=combine; m_alpha=alpha; } + + virtual int DrawText(LICE_IBitmap *bm, const char *str, int strcnt, RECT *rect, UINT dtFlags) + { + return DrawTextImpl(bm,str,strcnt,rect,dtFlags); + } + + virtual LICE_pixel GetTextColor() { return m_fg; } + virtual HFONT GetHFont() { return m_font; } + virtual int GetLineHeight() { return m_line_height; } + + virtual void SetLineSpacingAdjust(int amt) { m_lsadj=amt; } + + protected: + + virtual bool DrawGlyph(LICE_IBitmap *bm, unsigned short c, int xpos, int ypos, RECT *clipR); + int DrawTextImpl(LICE_IBitmap *bm, const char *str, int strcnt, RECT *rect, UINT dtFlags); // cause swell defines DrawText to SWELL_DrawText etc + + bool RenderGlyph(unsigned short idx); + + const char *NextWordBreak(const char *str, int strcnt, int w); + + LICE_pixel m_fg,m_bg,m_effectcol; + int m_bgmode; + int m_comb; + float m_alpha; + int m_flags; + + int m_line_height,m_lsadj; + struct charEnt + { + int base_offset; // offset in m_cachestore+1, so 1=offset0, 0=unset, -1=failed to render + int width, height; + int advance; + int charid; // used by m_extracharlist + int left_extra; + }; + charEnt *findChar(unsigned short c); + + charEnt m_lowchars[128]; // first 128 chars cached here + WDL_TypedBuf m_extracharlist; + WDL_TypedBuf m_cachestore; + + static int _charSortFunc(const void *a, const void *b); + + HFONT m_font; + +}; + +#endif // !LICE_TEXT_NO_DECLARE_CACHEDFONT + +#ifndef LICE_TEXT_NO_MULTIDPI +class __LICE_dpiAwareFont : public LICE_IFont +{ + struct rec { + LICE_IFont *cache; + int sz; + }; + WDL_TypedBuf m_list; // used entries are at end of list, most recently used last. sz=0 for unused + + int (*m_getflags)(int); + int m_flags; + LICE_pixel m_fg, m_bg, m_effectcol; + int m_bgmode, m_comb; + float m_alpha; + int m_lsadj; + +public: + LOGFONT m_lf; + + + // LICE_IFont interface + virtual void SetFromHFont(HFONT font, int flags=0) { } + + virtual LICE_pixel SetTextColor(LICE_pixel color) { LICE_pixel ret=m_fg; m_fg=color; return ret; } + virtual LICE_pixel SetBkColor(LICE_pixel color) { LICE_pixel ret=m_bg; m_bg=color; return ret; } + virtual LICE_pixel SetEffectColor(LICE_pixel color) { LICE_pixel ret=m_effectcol; m_effectcol=color; return ret; } + virtual int SetBkMode(int bkmode) { int bk = m_bgmode; m_bgmode=bkmode; return bk; } + virtual void SetCombineMode(int combine, float alpha=1.0f) { m_comb=combine; m_alpha=alpha; } + + virtual int DrawText(LICE_IBitmap *bm, const char *str, int strcnt, RECT *rect, UINT dtFlags) + { + LICE_IFont *f = get(bm); + if (!f) return 0; + if (!(dtFlags & DT_CALCRECT)) + { + f->SetTextColor(m_fg); + f->SetBkColor(m_bg); + f->SetEffectColor(m_effectcol); + f->SetBkMode(m_bgmode); + f->SetCombineMode(m_comb,m_alpha); + f->SetLineSpacingAdjust(m_lsadj); + } + return f->DrawText(bm,str,strcnt,rect,dtFlags); + } + + virtual LICE_pixel GetTextColor() { return m_fg; } + virtual HFONT GetHFont() { return NULL; } + virtual int GetLineHeight() { return GetLineHeightDPI(NULL); } + + virtual void SetLineSpacingAdjust(int amt) { m_lsadj=amt; } + + __LICE_dpiAwareFont(int maxsz) + { + memset(&m_lf,0,sizeof(m_lf)); + m_getflags = NULL; + m_flags=0; + m_fg=m_bg=m_effectcol=0; + m_bgmode=TRANSPARENT; + m_comb=0; + m_alpha=1.0; + m_lsadj=0; + + rec *l = m_list.ResizeOK(maxsz); + if (l) memset(l,0,sizeof(*l)*maxsz); + } + ~__LICE_dpiAwareFont() + { + for (int x = 0; x < m_list.GetSize(); x ++) delete m_list.Get()[x].cache; + } + void SetFromLogFont(LOGFONT *lf, int (*get_flags)(int)) + { + m_lf = *lf; + m_getflags = get_flags; + m_flags = get_flags ? get_flags(0) : 0; + clear(); + } + + void clear() + { + int x = m_list.GetSize()-1; + rec *t = m_list.Get(); + while (x>=0 && t[x].sz) t[x--].sz=0; + } + + LICE_IFont *get(LICE_IBitmap *bm) + { + int use_flag = m_getflags ? m_getflags(0) & ~LICE_FONT_FLAG_PRECALCALL : 0; + if (m_flags != use_flag) + { + m_flags = use_flag; + clear(); + } + + int ht = m_lf.lfHeight, ht2 = m_lf.lfWidth; + if (bm) + { + int sz = (int)bm->Extended(LICE_EXT_GET_ANY_SCALING,NULL); + if (sz) + { + ht = (ht * sz) / 256; + ht2 = (ht2 * sz) / 256; + if (sz != 256) + use_flag |= LICE_FONT_FLAG_FORCE_NATIVE; + } + } + + int x = m_list.GetSize()-1; + rec *t = m_list.Get(); + while (x>=0 && t[x].sz != ht && t[x].sz) x--; + if (x<0) t[x=0].sz = 0; // if list full, use oldest item + + // move to end of list + if (x != m_list.GetSize()-1) + { + rec tmp = t[x]; + m_list.Delete(x); + m_list.Add(tmp); + } + + t = m_list.Get() + m_list.GetSize() - 1; + if (!t->cache) t->cache = __CreateFont(); + if (!t->sz && t->cache) + { + t->sz = ht; + LOGFONT lf = m_lf; + lf.lfHeight = ht; + lf.lfWidth = ht2; + #ifdef _WIN32 + if (!(m_flags & LICE_FONT_FLAG_FORCE_NATIVE) && abs(lf.lfHeight) <= 14) lf.lfQuality = NONANTIALIASED_QUALITY; + #endif + t->cache->SetFromHFont(CreateFontIndirect(&lf), LICE_FONT_FLAG_OWNS_HFONT | use_flag); + } + + return t->cache; + } + + int GetLineHeightDPI(LICE_IBitmap *bm) + { + LICE_IFont *f = get(bm); + return f ? f->GetLineHeight() : 10; + } + virtual LICE_IFont *__CreateFont()=0; +}; + +template class LICE_dpiAwareFont : public __LICE_dpiAwareFont { + public: + LICE_dpiAwareFont(int max) : __LICE_dpiAwareFont(max) { } + virtual LICE_IFont *__CreateFont() { return new BASEFONT; } +}; +#endif//LICE_TEXT_NO_MULTIDPI + +#endif//_LICE_TEXT_H_ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_textnew.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_textnew.cpp new file mode 100644 index 000000000..c8ace35ae --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lice/lice_textnew.cpp @@ -0,0 +1,1343 @@ +#ifndef WDL_NO_DEFINE_MINMAX +#define WDL_NO_DEFINE_MINMAX +#endif +#include "lice_text.h" +#include + +#include "lice_combine.h" +#include "lice_extended.h" + +#define IGNORE_SCALING(mode) ((mode)&LICE_BLIT_IGNORE_SCALING) + +#if defined(_WIN32) && defined(WDL_SUPPORT_WIN9X) +static char __1ifNT2if98=0; // 2 for iswin98 +#endif + +// for very large fonts, fallback to native and avoid making a huge glyph cache, which would be terrible for performance +#define USE_NATIVE_RENDERING_FOR_FONTS_HIGHER_THAN 256 + + +// if other methods fail, at this point just flat out refuse to render glyphs (since they would use a ridiculous amount of memory) +#ifndef ABSOLUTELY_NO_GLYPHS_HIGHER_THAN +#define ABSOLUTELY_NO_GLYPHS_HIGHER_THAN 1024 +#endif + + +static int utf8makechar(char *ptrout, unsigned short charIn) +{ + unsigned char *pout = (unsigned char *)ptrout; + if (charIn < 128) { *pout = (unsigned char)charIn; return 1; } + if (charIn < 2048) { pout[0] = 0xC0 + (charIn>>6); pout[1] = 0x80 + (charIn&0x3f); return 2; } + pout[0] = 0xE0 + (charIn>>12); + pout[1] = 0x80 + ((charIn>>6)&0x3f); + pout[2] = 0x80 + (charIn&0x3f); + return 3; +} + +static int utf8char(const char *ptr, unsigned short *charOut) // returns char length +{ + const unsigned char *p = (const unsigned char *)ptr; + unsigned char tc = *p; + + if (tc < 128) + { + if (charOut) *charOut = (unsigned short) tc; + return 1; + } + else if (tc < 0xC2) // invalid chars (subsequent in sequence, or overlong which we disable for) + { + } + else if (tc < 0xE0) // 2 char seq + { + if (p[1] >= 0x80 && p[1] <= 0xC0) + { + if (charOut) *charOut = ((tc&0x1f)<<6) | (p[1]&0x3f); + return 2; + } + } + else if (tc < 0xF0) // 3 char seq + { + if (p[1] >= 0x80 && p[1] <= 0xC0 && p[2] >= 0x80 && p[2] <= 0xC0) + { + if (charOut) *charOut = ((tc&0xf)<<12) | ((p[1]&0x3f)<<6) | ((p[2]&0x3f)); + return 3; + } + } + else if (tc < 0xF5) // 4 char seq + { + if (p[1] >= 0x80 && p[1] <= 0xC0 && p[2] >= 0x80 && p[2] <= 0xC0 && p[3] >= 0x80 && p[3] <= 0xC0) + { + if (charOut) *charOut = (unsigned short)' '; // dont support 4 byte sequences yet(ever?) + return 4; + } + } + if (charOut) *charOut = (unsigned short) tc; + return 1; +} + + + +//not threadsafe ---- +static LICE_SysBitmap *s_tempbitmap; // keep a sysbitmap around for rendering fonts +static LICE_SysBitmap *s_nativerender_tempbitmap; +static int s_tempbitmap_refcnt; + + +int LICE_CachedFont::_charSortFunc(const void *a, const void *b) +{ + charEnt *aa = (charEnt *)a; + charEnt *bb = (charEnt *)b; + return aa->charid - bb->charid; +} + +LICE_CachedFont::LICE_CachedFont() : m_cachestore(65536) +{ + s_tempbitmap_refcnt++; + m_fg=0; + m_effectcol=m_bg=LICE_RGBA(255,255,255,255); + m_comb=0; + m_alpha=1.0f; + m_bgmode = TRANSPARENT; + m_flags=0; + m_line_height=0; + m_lsadj=0; + m_font=0; + memset(m_lowchars,0,sizeof(m_lowchars)); +} + +LICE_CachedFont::~LICE_CachedFont() +{ + if ((m_flags&LICE_FONT_FLAG_OWNS_HFONT) && m_font) { + DeleteObject(m_font); + } + if (!--s_tempbitmap_refcnt) + { + delete s_tempbitmap; + s_tempbitmap=0; + delete s_nativerender_tempbitmap; + s_nativerender_tempbitmap=0; + } +} + +void LICE_CachedFont::SetFromHFont(HFONT font, int flags) +{ + if ((m_flags&LICE_FONT_FLAG_OWNS_HFONT) && m_font && m_font != font) + { + DeleteObject(m_font); + } + + m_flags=flags; + m_font=font; + if (font) + { + if (!s_tempbitmap) s_tempbitmap=new LICE_SysBitmap; + + if (s_tempbitmap->getWidth() < 256 || s_tempbitmap->getHeight() < 256) + { + s_tempbitmap->resize(256,256); + ::SetTextColor(s_tempbitmap->getDC(),RGB(255,255,255)); + ::SetBkMode(s_tempbitmap->getDC(),OPAQUE); + ::SetBkColor(s_tempbitmap->getDC(),RGB(0,0,0)); + } + + TEXTMETRIC tm; + HGDIOBJ oldFont = 0; + if (font) oldFont = SelectObject(s_tempbitmap->getDC(),font); + GetTextMetrics(s_tempbitmap->getDC(),&tm); + if (oldFont) SelectObject(s_tempbitmap->getDC(),oldFont); + + m_line_height = tm.tmHeight; + } + + memset(m_lowchars,0,sizeof(m_lowchars)); + m_extracharlist.Resize(0,false); + m_cachestore.Resize(0); + if (flags&LICE_FONT_FLAG_PRECALCALL) + { + int x; + for(x=0;x<128;x++) + RenderGlyph(x); + } +} + +bool LICE_CachedFont::RenderGlyph(unsigned short idx) // return TRUE if ok +{ + if (m_line_height >= ABSOLUTELY_NO_GLYPHS_HIGHER_THAN) return false; + + bool needSort=false; + charEnt *ent; + if (idx>=128) + { +#if defined(_WIN32) && defined(WDL_SUPPORT_WIN9X) + if (!__1ifNT2if98) __1ifNT2if98 = GetVersion()<0x80000000 ? 1 : 2; + + if (__1ifNT2if98==2) return false; +#endif + ent=findChar(idx); + if (!ent) + { + if (m_flags & LICE_FONT_FLAG_PRECALCALL) return false; + + int oldsz=m_extracharlist.GetSize(); + ent = m_extracharlist.Resize(oldsz+1) + oldsz; + memset(ent,0,sizeof(*ent)); + ent->charid = idx; + + needSort=true; + } + } + else ent = m_lowchars+idx; + + const int bmsz=lice_max(m_line_height,1) * 2 + 8; + + if (!s_tempbitmap) s_tempbitmap=new LICE_SysBitmap; + + if (s_tempbitmap->getWidth() < bmsz || s_tempbitmap->getHeight() < bmsz) + { + s_tempbitmap->resize(bmsz,bmsz); + ::SetTextColor(s_tempbitmap->getDC(),RGB(255,255,255)); + ::SetBkMode(s_tempbitmap->getDC(),OPAQUE); + ::SetBkColor(s_tempbitmap->getDC(),RGB(0,0,0)); + } + + HGDIOBJ oldFont=0; + if (m_font) oldFont = SelectObject(s_tempbitmap->getDC(),m_font); + RECT r={0,0,0,0,}; + int advance; + const int right_extra_pad = 2+(m_line_height>=16 ? m_line_height/16 : 0); // overrender right side by this amount, and check to see if it was drawn to + + const int left_extra_pad = right_extra_pad; // overrender on left side too + +#ifdef _WIN32 +#if defined(WDL_SUPPORT_WIN9X) + if (__1ifNT2if98==1) +#endif + { + WCHAR tmpstr[2]={(WCHAR)idx,0}; + ::DrawTextW(s_tempbitmap->getDC(),tmpstr,1,&r,DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX); + advance=r.right; + r.right += right_extra_pad+left_extra_pad; + LICE_FillRect(s_tempbitmap,0,0,r.right,r.bottom,0,1.0f,LICE_BLIT_MODE_COPY); + r.left+=left_extra_pad; + ::DrawTextW(s_tempbitmap->getDC(),tmpstr,1,&r,DT_SINGLELINE|DT_LEFT|DT_TOP|DT_NOPREFIX|DT_NOCLIP); + } + #if defined(WDL_SUPPORT_WIN9X) + else + #endif +#endif + +#if !defined(_WIN32) || defined(WDL_SUPPORT_WIN9X) + { + + char tmpstr[6]={(char)idx,0}; +#ifndef _WIN32 + if (idx>=128) utf8makechar(tmpstr,idx); +#endif + ::DrawText(s_tempbitmap->getDC(),tmpstr,-1,&r,DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX); + advance=r.right; + r.right += right_extra_pad+left_extra_pad; + LICE_FillRect(s_tempbitmap,0,0,r.right,r.bottom,0,1.0f,LICE_BLIT_MODE_COPY); + r.left += left_extra_pad; + ::DrawText(s_tempbitmap->getDC(),tmpstr,-1,&r,DT_SINGLELINE|DT_LEFT|DT_TOP|DT_NOPREFIX|DT_NOCLIP); + } +#endif + + if (advance > s_tempbitmap->getWidth()) advance=s_tempbitmap->getWidth(); + if (r.right > s_tempbitmap->getWidth()) r.right=s_tempbitmap->getWidth(); + if (r.bottom > s_tempbitmap->getHeight()) r.bottom=s_tempbitmap->getHeight(); + + if (oldFont) SelectObject(s_tempbitmap->getDC(),oldFont); + + if (advance < 1 || r.bottom < 1) + { + ent->base_offset=-1; + ent->left_extra=ent->advance=ent->width=ent->height=0; + } + else + { + ent->advance=advance; + int flags=m_flags; + if (flags&LICE_FONT_FLAG_FX_BLUR) + { + LICE_Blur(s_tempbitmap,s_tempbitmap,0,0,0,0,r.right,r.bottom); + } + LICE_pixel *srcbuf = s_tempbitmap->getBits(); + int span=s_tempbitmap->getRowSpan(); + + ent->base_offset=m_cachestore.GetSize()+1; + int newsz = ent->base_offset-1+r.right*r.bottom; + unsigned char *destbuf = m_cachestore.Resize(newsz) + ent->base_offset-1; + if (m_cachestore.GetSize() != newsz) + { + ent->base_offset=-1; + ent->advance=ent->width=ent->height=0; + } + else + { + if (s_tempbitmap->isFlipped()) + { + srcbuf += (s_tempbitmap->getHeight()-1)*span; + span=-span; + } + int x,y; + int min_x=left_extra_pad; + int max_x=advance + min_x; + + for(y=0;y 0) + { + const int neww = max_x-min_x; + const unsigned char *rdptr=destbuf + min_x; + // trim down destbuf + for (y=0;ybase_offset-1+r.right*r.bottom; + destbuf = m_cachestore.Resize(newsz,false) + ent->base_offset-1; + } + + if (flags&LICE_FONT_FLAG_VERTICAL) + { + int a=r.bottom; r.bottom=r.right; r.right=a; + + unsigned char *tmpbuf = (unsigned char *)s_tempbitmap->getBits(); + memcpy(tmpbuf,destbuf,r.right*r.bottom); + + int dptr=r.bottom; + int dtmpbuf=1; + if (!(flags&LICE_FONT_FLAG_VERTICAL_BOTTOMUP)) + { + tmpbuf += (r.right-1)*dptr; + dptr=-dptr; + } + else + { + tmpbuf+=r.bottom-1; + dtmpbuf=-1; + } + + for(y=0;y130 ? 255:0; + destbuf++; + } + + destbuf -= r.right*r.bottom; + } + if (flags&(LICE_FONT_FLAG_FX_SHADOW|LICE_FONT_FLAG_FX_OUTLINE)) + { + for(y=0;y130 ? 255:0; + destbuf++; + } + + destbuf -= r.right*r.bottom; + if (flags&LICE_FONT_FLAG_FX_SHADOW) + { + for(y=0;y0) + { + if (destbuf[-1]==255) *destbuf=128; + else if (y>0 && destbuf[-r.right-1]==255) *destbuf=128; + } + if (y>0 && destbuf[-r.right]==255) *destbuf=128; + } + destbuf++; + } + } + else + { + for(y=0;y0 && destbuf[-r.right]==255) *destbuf=128; + else if (y0) + { + if (destbuf[-1]==255) *destbuf=128; + // else if (y>0 && destbuf[-r.right-1]==255) *destbuf=128; + // else if (y0 && destbuf[-r.right+1]==255) *destbuf=128; + // else if (yleft_extra=left_extra_pad-min_x; + ent->width = r.right; + ent->height = r.bottom; + } + } + if (needSort&&m_extracharlist.GetSize()>1) qsort(m_extracharlist.Get(),m_extracharlist.GetSize(),sizeof(charEnt),_charSortFunc); + + return true; +} + +template class GlyphRenderer +{ +public: + static void Normal(unsigned char *gsrc, LICE_pixel *pout, + int src_span, int dest_span, int width, int height, + int red, int green, int blue, int a256) + { + int y; + if (a256==256) + { + for(y=0;y256)a=256; + T::doPix((unsigned char *)(pout+x),red,green,blue,255,a); + } + } + gsrc += src_span; + pout += dest_span; + } + } + } + static void Mono(unsigned char *gsrc, LICE_pixel *pout, + int src_span, int dest_span, int width, int height, + int red, int green, int blue, int alpha) + { + int y; + for(y=0;yleft_extra; + else + ypos += ch->left_extra; + } + else + { + xpos -= ch->left_extra; + } + + if (xpos >= clipR->right || + ypos >= clipR->bottom || + xpos+ch->width <= clipR->left || + ypos+ch->height <= clipR->top) return false; + + unsigned char *gsrc = m_cachestore.Get() + ch->base_offset-1; + int src_span = ch->width; + int width = ch->width; + int height = ch->height; + +#ifndef DISABLE_LICE_EXTENSIONS + if (bm->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_DRAWGLYPH_ACCEL)) + { + LICE_Ext_DrawGlyph_acceldata data(xpos, ypos, m_fg, gsrc, width, height, m_alpha, m_comb); + if (bm->Extended(LICE_EXT_DRAWGLYPH_ACCEL, &data)) return true; + } +#endif + + if (xpos < clipR->left) + { + width += (xpos-clipR->left); + gsrc += clipR->left-xpos; + xpos=clipR->left; + } + if (ypos < clipR->top) + { + gsrc += src_span*(clipR->top-ypos); + height += (ypos-clipR->top); + ypos=clipR->top; + } + int dest_span = bm->getRowSpan(); + LICE_pixel *pout = bm->getBits(); + + if (bm->isFlipped()) + { + int bm_h = bm->getHeight(); + const int __sc = bm ? (int)bm->Extended(LICE_EXT_GET_SCALING,NULL) : 0; + if (__sc>0) + { + __LICE_SCU(bm_h); + } + + pout += (bm_h-1)*dest_span; + dest_span=-dest_span; + } + + pout += xpos + ypos * dest_span; + + if (width >= clipR->right-xpos) width = clipR->right-xpos; + if (height >= clipR->bottom-ypos) height = clipR->bottom-ypos; + + if (width < 1 || height < 1) return false; // this could be an assert, it is guaranteed by the xpos/ypos checks at the top of this function + + int mode=m_comb&~LICE_BLIT_USE_ALPHA; + float alpha=m_alpha; + + if (m_bgmode==OPAQUE) + LICE_FillRect(bm,xpos,ypos,width,height,m_bg,alpha,mode|LICE_BLIT_IGNORE_SCALING); + + int red=LICE_GETR(m_fg); + int green=LICE_GETG(m_fg); + int blue=LICE_GETB(m_fg); + + if (m_flags&LICE_FONT_FLAG_FX_MONO) + { + if (alpha==1.0 && (mode&LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY) // fast simple + { + LICE_pixel col=m_fg; + int y; + for(y=0;y256)avalint=256; + + #define __LICE__ACTION(comb) GlyphRenderer::Mono(gsrc,pout,src_span,dest_span,width,height,red,green,blue,avalint) + __LICE_ACTION_NOSRCALPHA(mode,avalint, false); + #undef __LICE__ACTION + } + } + else if (m_flags&(LICE_FONT_FLAG_FX_SHADOW|LICE_FONT_FLAG_FX_OUTLINE)) + { + LICE_pixel col=m_fg; + LICE_pixel bkcol=m_effectcol; + + if (alpha==1.0 && (mode&LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY) + { + int y; + for(y=0;y256)avalint=256; + int r2=LICE_GETR(bkcol); + int g2=LICE_GETG(bkcol); + int b2=LICE_GETB(bkcol); + #define __LICE__ACTION(comb) GlyphRenderer::Effect(gsrc,pout,src_span,dest_span,width,height,red,green,blue,avalint,r2,g2,b2) + __LICE_ACTION_NOSRCALPHA(mode,avalint, false); + #undef __LICE__ACTION + } + } + else + { + int avalint = (int) (alpha*256.0); + #define __LICE__ACTION(comb) GlyphRenderer::Normal(gsrc,pout,src_span,dest_span,width,height,red,green,blue,avalint) + __LICE_ACTION_NOSRCALPHA(mode,avalint, false); + #undef __LICE__ACTION + } + + return true; // drew glyph at all (for updating max extents) +} + + +#ifndef LICE_TEXT_NONATIVE +static int LICE_Text_IsWine() +{ + static int isWine=-1; +#ifdef _WIN32 + if (isWine<0) + { + HKEY hk; + if (RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Wine",&hk)==ERROR_SUCCESS) + { + isWine=1; + RegCloseKey(hk); + } + else isWine=0; + } +#endif + return isWine>0; +} +#endif + +#ifdef _WIN32 +static BOOL LICE_Text_HasUTF8(const char *_str) +{ + const unsigned char *str = (const unsigned char *)_str; + if (!str) return FALSE; + while (*str) + { + unsigned char c = *str; + if (c >= 0xC2) // fuck overlongs + { + if (c <= 0xDF && str[1] >=0x80 && str[1] <= 0xBF) return TRUE; + else if (c <= 0xEF && str[1] >=0x80 && str[1] <= 0xBF && str[2] >=0x80 && str[2] <= 0xBF) return TRUE; + else if (c <= 0xF4 && str[1] >=0x80 && str[1] <= 0xBF && str[2] >=0x80 && str[2] <= 0xBF) return TRUE; + } + str++; + } + return FALSE; +} +#endif + + +#define __LICE_SC_DRAWTEXT_RESTORE_RECT \ + if (__sc > 0 && rect) { \ + rect->left = (rect->left * 256) / __sc; \ + rect->top = (rect->top * 256) / __sc; \ + rect->right = (rect->right * 256) / __sc; \ + rect->bottom = (rect->bottom * 256) / __sc; \ + } + + +static const char *adv_str(const char *str, int *strcnt, unsigned short *c) +{ + int charlen=utf8char(str, c); + if (strcnt && *strcnt > 0) *strcnt=wdl_max(*strcnt-charlen, 0); + return str+charlen; +} + +const char *LICE_CachedFont::NextWordBreak(const char *str, int strcnt, int w) +{ + // returns the first character of the next line + const char *next_break=NULL; + while (*str && strcnt) + { + unsigned short c; + str=adv_str(str, &strcnt, &c); + if (c == '\n') return str; + if (c != '\r') + { + charEnt *ent=findChar(c); + if (ent && ent->base_offset > 0 && ent->base_offset < m_cachestore.GetSize()) + { + w -= ent->advance; + if (w < 0) return next_break ? next_break : str; + } + } + if (c == ' ' || c == '\t' || c == '\r') next_break=str; + } + return str; +} + + +int LICE_CachedFont::DrawTextImpl(LICE_IBitmap *bm, const char *str, int strcnt, + RECT *rect, UINT dtFlags) +{ + if (!bm && !(dtFlags&DT_CALCRECT)) return 0; + + const int __sc = bm ? (int)bm->Extended(LICE_EXT_GET_SCALING,NULL) : 0; + int bm_w = bm ? bm->getWidth() : 0; + int bm_h = bm ? bm->getHeight() : 0; + + if (__sc>0 && rect) + { + if (!IGNORE_SCALING(m_comb)) + { + __LICE_SC(rect->left); + __LICE_SC(rect->top); + __LICE_SC(rect->right); + __LICE_SC(rect->bottom); + } + __LICE_SC(bm_w); + __LICE_SC(bm_h); + } + + bool forceWantAlpha=false; + + if (dtFlags & LICE_DT_NEEDALPHA) + { + forceWantAlpha=true; + dtFlags &= ~LICE_DT_NEEDALPHA; + } + + if (dtFlags&DT_SINGLELINE) dtFlags &= ~DT_WORDBREAK; + + // if using line-spacing adjustments (m_lsadj), don't allow native rendering + // todo: split rendering up into invidual lines and DrawText calls +#ifndef LICE_TEXT_NONATIVE + + int ret=0; + if (!bm || !bm->Extended('YUVx',NULL)) if (((m_flags&LICE_FONT_FLAG_FORCE_NATIVE) && m_font && !forceWantAlpha &&!LICE_Text_IsWine() && +#ifndef _WIN32 + // swell does not support DT_WORDBREAK at the moment + !(dtFlags & DT_WORDBREAK) && +#endif + !(dtFlags & LICE_DT_USEFGALPHA) && + !(m_flags&LICE_FONT_FLAG_PRECALCALL) && !LICE_FONT_FLAGS_HAS_FX(m_flags) && + (!m_lsadj || (dtFlags&DT_SINGLELINE))) || + (m_line_height >= USE_NATIVE_RENDERING_FOR_FONTS_HIGHER_THAN) ) + { + +#ifdef _WIN32 + WCHAR wtmpbuf[1024]; + WCHAR *wtmp=NULL; + #ifdef WDL_SUPPORT_WIN9X + static int win9x; + if (!win9x) win9x = GetVersion() < 0x80000000 ? -1 : 1; //>0 if win9x + if (win9x<0 && LICE_Text_HasUTF8(str)) + #else + if (LICE_Text_HasUTF8(str)) + #endif + { + int req = MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,str,strcnt,NULL,0); + if (req < 1000) + { + int cnt=0; + if ((cnt=MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,str,strcnt,wtmpbuf,1024))) + { + wtmp=wtmpbuf; + wtmp[cnt]=0; + } + } + else + { + wtmp = (WCHAR *)malloc((req + 32)*sizeof(WCHAR)); + int cnt=-1; + if (wtmp && !(cnt=MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,str,strcnt,wtmp,req+1))) + free(wtmp); + else if (cnt>0) wtmp[cnt]=0; + } + } +#endif + + HDC hdc = (bm ? bm->getDC() : 0); + HGDIOBJ oldfont = 0; + RECT dt_rect={0,}; + + bool isTmp=false; + RECT tmp_rect={0,}; + +#ifdef _WIN32 + HRGN rgn=NULL; +#endif + RECT nr_subbitmap_clip = {0,}; + bool nr_subbitmap_clip_use=false; + if (!hdc && bm) + { + LICE_IBitmap *bmt = bm; + while (bmt->Extended(LICE_SubBitmap::LICE_GET_SUBBITMAP_VERSION,NULL) == (INT_PTR)LICE_SubBitmap::LICE_SUBBITMAP_VERSION) + { + LICE_SubBitmap *sb = (LICE_SubBitmap *)bmt; + int sub_x = sb->m_x, sub_y = sb->m_y; + if (__sc>0) + { + __LICE_SC(sub_x); + __LICE_SC(sub_y); + } + nr_subbitmap_clip.left += sub_x; + nr_subbitmap_clip.top += sub_y; + bmt = sb->m_parent; + if (!bmt) break; // ran out of parents + + hdc=bmt->getDC(); + if (hdc) + { + int sub_w = ((LICE_SubBitmap*)bm)->m_w, sub_h = ((LICE_SubBitmap*)bm)->m_h; + if (__sc>0) + { + __LICE_SC(sub_w); + __LICE_SC(sub_h); + } + nr_subbitmap_clip.right = nr_subbitmap_clip.left + sub_w; + nr_subbitmap_clip.bottom = nr_subbitmap_clip.top + sub_h; + nr_subbitmap_clip_use=!(dtFlags & DT_CALCRECT); + bm = bmt; + break; + } + } + } + + if (!hdc) + { + // use temp buffer -- we could optionally enable this if non-1 alpha was desired, though this only works for BLIT_MODE_COPY anyway (since it will end up compositing the whole rectangle, ugh) + isTmp=true; + + if (!s_nativerender_tempbitmap) + s_nativerender_tempbitmap = new LICE_SysBitmap; + + if (s_nativerender_tempbitmap->getWidth() < 4 || s_nativerender_tempbitmap->getHeight() < 4) s_nativerender_tempbitmap->resize(4,4); + + hdc = s_nativerender_tempbitmap->getDC(); + if (!hdc) goto finish_up_native_render; + + oldfont = SelectObject(hdc, m_font); + + RECT text_size = {0,0}; + ret = +#ifdef _WIN32 + wtmp ? ::DrawTextW(hdc,wtmp,-1,&text_size,(dtFlags&~(DT_CENTER|DT_VCENTER|DT_TOP|DT_BOTTOM|DT_LEFT|DT_RIGHT))|DT_CALCRECT|DT_NOPREFIX) : +#endif + ::DrawText(hdc,str,strcnt,&text_size,(dtFlags&~(DT_CENTER|DT_VCENTER|DT_TOP|DT_BOTTOM|DT_LEFT|DT_RIGHT))|DT_CALCRECT|DT_NOPREFIX); + if (dtFlags & DT_CALCRECT) + { + rect->right = rect->left + text_size.right - text_size.left; + rect->bottom = rect->top + text_size.bottom - text_size.top; + goto finish_up_native_render; + } + if (!bm) goto finish_up_native_render; + + if (dtFlags & DT_RIGHT) tmp_rect.left = rect->right - text_size.right; + else if (dtFlags & DT_CENTER) tmp_rect.left = (rect->right+rect->left- text_size.right)/2; + else tmp_rect.left = rect->left; + tmp_rect.right = tmp_rect.left + text_size.right; + + if (dtFlags & DT_BOTTOM) tmp_rect.top = rect->bottom - text_size.bottom; + else if (dtFlags & DT_VCENTER) tmp_rect.top = (rect->bottom+rect->top- text_size.bottom)/2; + else tmp_rect.top = rect->top; + tmp_rect.bottom = tmp_rect.top + text_size.bottom; + + // tmp_rect is the desired rect of drawing, now clip to bitmap (adjusting dt_rect.top/left if starting offscreen) + if (tmp_rect.right > bm_w) tmp_rect.right=bm_w; + if (tmp_rect.bottom > bm_h) tmp_rect.bottom=bm_h; + + int lclip = 0, tclip = 0; + // clip tmp_rect to rect if not DT_NOCLIP + if (!(dtFlags&DT_NOCLIP)) + { + if (tmp_rect.right > rect->right) tmp_rect.right=rect->right; + if (tmp_rect.bottom > rect->bottom) tmp_rect.bottom=rect->bottom; + if (rect->left > 0) lclip = rect->left; + if (rect->top > 0) tclip = rect->top; + } + + if (tmp_rect.left < lclip) + { + dt_rect.left = tmp_rect.left - lclip; + tmp_rect.left = lclip; + } + if (tmp_rect.top < tclip) + { + dt_rect.top = tmp_rect.top - tclip; + tmp_rect.top = tclip; + } + + if (tmp_rect.bottom <= tmp_rect.top || tmp_rect.right <= tmp_rect.left) goto finish_up_native_render; + + dtFlags &= ~(DT_BOTTOM|DT_RIGHT|DT_CENTER|DT_VCENTER); + + int left_overrender = 2+(m_line_height>=16 ? m_line_height/16 : 0); + if (left_overrender>tmp_rect.left) left_overrender=tmp_rect.left; + tmp_rect.left -= left_overrender; + + if (tmp_rect.right-tmp_rect.left > s_nativerender_tempbitmap->getWidth() || + tmp_rect.bottom-tmp_rect.top > s_nativerender_tempbitmap->getHeight()) + { + SelectObject(hdc,oldfont); + s_nativerender_tempbitmap->resize(tmp_rect.right-tmp_rect.left, tmp_rect.bottom-tmp_rect.top); + hdc = s_nativerender_tempbitmap->getDC(); + oldfont = SelectObject(hdc, m_font); + } + + LICE_Blit(s_nativerender_tempbitmap, bm, 0, 0, &tmp_rect, 1.0f, LICE_BLIT_MODE_COPY); + + dt_rect.left += left_overrender; + dt_rect.right=tmp_rect.right; + dt_rect.bottom=tmp_rect.bottom; + } + else + { + oldfont = SelectObject(hdc, m_font); + dt_rect = *rect; + } + + ::SetTextColor(hdc,RGB(LICE_GETR(m_fg),LICE_GETG(m_fg),LICE_GETB(m_fg))); + ::SetBkMode(hdc,m_bgmode); + if (m_bgmode==OPAQUE) ::SetBkColor(hdc,RGB(LICE_GETR(m_bg),LICE_GETG(m_bg),LICE_GETB(m_bg))); + + if (nr_subbitmap_clip_use) + { +#ifdef _WIN32 + rgn=CreateRectRgn(0,0,0,0); + if (!GetClipRgn(hdc,rgn)) + { + DeleteObject(rgn); + rgn=NULL; + } + IntersectClipRect(hdc,nr_subbitmap_clip.left,nr_subbitmap_clip.top,nr_subbitmap_clip.right,nr_subbitmap_clip.bottom); +#else + SWELL_PushClipRegion(hdc); + SWELL_SetClipRegion(hdc,&nr_subbitmap_clip); +#endif + dt_rect.left += nr_subbitmap_clip.left; + dt_rect.top += nr_subbitmap_clip.top; + dt_rect.right += nr_subbitmap_clip.left; + dt_rect.bottom += nr_subbitmap_clip.top; + } + + ret = +#ifdef _WIN32 + wtmp ? ::DrawTextW(hdc,wtmp,-1,&dt_rect,dtFlags|DT_NOPREFIX) : +#endif + ::DrawText(hdc,str,strcnt,&dt_rect,dtFlags|DT_NOPREFIX); + + if (nr_subbitmap_clip_use) + { +#ifdef _WIN32 + SelectClipRgn(hdc,rgn); // if rgn is NULL, clears region + if (rgn) DeleteObject(rgn); +#else + SWELL_PopClipRegion(hdc); +#endif + } + + if (isTmp) + LICE_Blit(bm, s_nativerender_tempbitmap, tmp_rect.left, tmp_rect.top, + 0,0, tmp_rect.right-tmp_rect.left, tmp_rect.bottom-tmp_rect.top, + m_alpha, // this is a slight improvement over the non-tempbuffer version, but might not be necessary... + LICE_BLIT_MODE_COPY); + else if (dtFlags & DT_CALCRECT) *rect = dt_rect; + +finish_up_native_render: + if (hdc) SelectObject(hdc, oldfont); +#ifdef _WIN32 + if (wtmp!=wtmpbuf) free(wtmp); +#endif + + __LICE_SC_DRAWTEXT_RESTORE_RECT + if (__sc>0) ret = (ret * 256) / __sc; + + return ret; + } +#endif + + // ensure all glyphs rendered + const char *tstr=str; + int tcnt=strcnt; + while (*tstr && tcnt) + { + unsigned short c; + tstr=adv_str(tstr, &tcnt, &c); + + if (c != '\r' && c != '\n') + { + charEnt *ent=findChar(c); + if (!ent) + { + const int os=m_extracharlist.GetSize(); + RenderGlyph(c); + if (m_extracharlist.GetSize() != os) ent=findChar(c); + } + if (ent && ent->base_offset == 0) RenderGlyph(c); + } + } + + if (dtFlags & DT_CALCRECT) + { + int xpos=0; + int ypos=0; + int max_xpos=0; + int max_ypos=0; + const char *next_break=NULL; + while (*str && strcnt) + { + unsigned short c; + str=adv_str(str, &strcnt, &c); + + if (c == '\r') continue; + if (c == '\n') + { + if (dtFlags & DT_SINGLELINE) + { + c=' '; // different from win32 native behavior, which skips the character + } + else + { + if (m_flags&LICE_FONT_FLAG_VERTICAL) + { + xpos+=m_line_height+m_lsadj; + ypos=0; + } + else + { + ypos+=m_line_height+m_lsadj; + xpos=0; + } + if (dtFlags&DT_WORDBREAK) next_break=NULL; + continue; + } + } + + charEnt *ent = findChar(c); + if (ent && ent->base_offset > 0 && ent->base_offset < m_cachestore.GetSize()) + { + if (m_flags&LICE_FONT_FLAG_VERTICAL) + { + const int yext = ypos + ent->height - ent->left_extra; + ypos += ent->advance; + if (xpos+ent->width>max_xpos) max_xpos=xpos+ent->width; + if (ypos>max_ypos) max_ypos=ypos; + if (yext>max_ypos) max_ypos=yext; + } + else + { + const int xext = xpos + ent->width - ent->left_extra; + xpos += ent->advance; + if (ypos+ent->height>max_ypos) max_ypos=ypos+ent->height; + if (xpos>max_xpos) max_xpos=xpos; + if (xext>max_xpos) max_xpos=xext; + } + + if (dtFlags&DT_WORDBREAK) + { + if (m_flags&LICE_FONT_FLAG_VERTICAL) + { + if (str == next_break) + { + xpos += m_line_height+m_lsadj; + ypos=0; + next_break=NULL; + } + if (!next_break) + { + next_break=NextWordBreak(str, strcnt, rect->bottom-rect->top-ypos); + } + } + else + { + if (str == next_break) + { + ypos += m_line_height+m_lsadj; + xpos=0; + next_break=NULL; + } + if (!next_break) + { + next_break=NextWordBreak(str, strcnt, rect->right-rect->left-xpos); + } + } + } + } + } + + rect->right = rect->left+max_xpos; + rect->bottom = rect->top+max_ypos; + + + int retval = (m_flags&LICE_FONT_FLAG_VERTICAL) ? max_xpos : max_ypos; + __LICE_SC_DRAWTEXT_RESTORE_RECT + if (__sc>0) return (retval * 256) / __sc; + return retval; + } + float alphaSave = m_alpha; + + if (dtFlags & LICE_DT_USEFGALPHA) + { + m_alpha *= LICE_GETA(m_fg)/255.0; + } + + if (m_alpha==0.0) + { + m_alpha=alphaSave; + __LICE_SC_DRAWTEXT_RESTORE_RECT + return 0; + } + + + RECT use_rect=*rect; + int xpos=use_rect.left; + int ypos=use_rect.top; + + bool isVertRev = false; + if ((m_flags&(LICE_FONT_FLAG_VERTICAL|LICE_FONT_FLAG_VERTICAL_BOTTOMUP)) == (LICE_FONT_FLAG_VERTICAL|LICE_FONT_FLAG_VERTICAL_BOTTOMUP)) + isVertRev=true; + + if (dtFlags & (DT_CENTER|DT_VCENTER|DT_RIGHT|DT_BOTTOM)) + { + RECT tr={0,}; + DrawTextImpl(bm,str,strcnt,&tr,DT_CALCRECT|(dtFlags & DT_SINGLELINE)|(forceWantAlpha?LICE_DT_NEEDALPHA:0)); + if (__sc > 0) + { + __LICE_SC(tr.right); + __LICE_SC(tr.bottom); + } + if (dtFlags & DT_CENTER) + { + xpos += (use_rect.right-use_rect.left-tr.right)/2; + } + else if (dtFlags & DT_RIGHT) + { + xpos = use_rect.right - tr.right; + } + + if (dtFlags & DT_VCENTER) + { + ypos += (use_rect.bottom-use_rect.top-tr.bottom)/2; + } + else if (dtFlags & DT_BOTTOM) + { + ypos = use_rect.bottom - tr.bottom; + } + if (isVertRev) + ypos += tr.bottom; + } + else if (isVertRev) + { + RECT tr={0,}; + DrawTextImpl(bm,str,strcnt,&tr,DT_CALCRECT|(dtFlags & DT_SINGLELINE)|(forceWantAlpha?LICE_DT_NEEDALPHA:0)); + if (__sc > 0) __LICE_SC(tr.bottom); + ypos += tr.bottom; + } + + + int start_y=ypos; + int start_x=xpos; + int max_ypos=ypos; + int max_xpos=xpos; + + if (!(dtFlags & DT_NOCLIP)) + { + if (use_rect.left<0)use_rect.left=0; + if (use_rect.top<0) use_rect.top=0; + if (use_rect.right > bm_w) use_rect.right = bm_w; + if (use_rect.bottom > bm_h) use_rect.bottom = bm_h; + if (use_rect.right <= use_rect.left || use_rect.bottom <= use_rect.top) + { + m_alpha=alphaSave; + __LICE_SC_DRAWTEXT_RESTORE_RECT + return 0; + } + } + else + { + use_rect.left=use_rect.top=0; + use_rect.right = bm_w; + use_rect.bottom = bm_h; + } + + + // todo: handle DT_END_ELLIPSIS etc + // thought: calculate length of "...", then when pos+length+widthofnextchar >= right, switch + // might need to precalc size to make sure it's needed, though + + const char *next_break=NULL; + while (*str && strcnt) + { + unsigned short c; + str=adv_str(str, &strcnt, &c); + + if (c == '\r') continue; + if (c == '\n') + { + if (dtFlags & DT_SINGLELINE) + { + c=' '; // different from win32 native behavior, which skips the character + } + else + { + if (m_flags&LICE_FONT_FLAG_VERTICAL) + { + xpos+=m_line_height+m_lsadj; + ypos=start_y; + } + else + { + ypos+=m_line_height+m_lsadj; + xpos=start_x; + } + if (dtFlags&DT_WORDBREAK) next_break=NULL; + continue; + } + } + + charEnt *ent = findChar(c); + if (ent && ent->base_offset > 0 && ent->base_offset < m_cachestore.GetSize()) + { + if (isVertRev) ypos -= ent->height; + + bool drawn = DrawGlyph(bm,c,xpos,ypos,&use_rect); + + if (m_flags&LICE_FONT_FLAG_VERTICAL) + { + if (!isVertRev) + { + ypos += ent->advance; + } + else ypos += ent->height - ent->advance; + if (drawn && xpos+ent->width > max_xpos) max_xpos=xpos; + } + else + { + xpos += ent->advance; + if (drawn && ypos+ent->height>max_ypos) max_ypos=ypos+ent->height; + } + + if (dtFlags&DT_WORDBREAK) + { + if (m_flags&LICE_FONT_FLAG_VERTICAL) + { + if (str == next_break) + { + xpos += m_line_height+m_lsadj; + ypos=start_y; + next_break=NULL; + } + if (!next_break) + { + next_break=NextWordBreak(str, strcnt, use_rect.bottom-ypos); + } + } + else + { + if (str == next_break) + { + ypos += m_line_height+m_lsadj; + xpos=start_x; + next_break=NULL; + } + if (!next_break) + { + next_break=NextWordBreak(str, strcnt, use_rect.right-xpos); + } + } + } + } + } + + m_alpha=alphaSave; + int retv = (m_flags&LICE_FONT_FLAG_VERTICAL) ? max_xpos - start_x : max_ypos - start_y; + __LICE_SC_DRAWTEXT_RESTORE_RECT + if (__sc>0) return (retv*256)/__sc; + return retv; +} diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/lineparse.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/lineparse.h new file mode 100644 index 000000000..cd91f7571 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/lineparse.h @@ -0,0 +1,374 @@ +/* + WDL - lineparse.h + Copyright (C) 2005-2014 Cockos Incorporated + Copyright (C) 1999-2004 Nullsoft, Inc. + + 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. + +*/ + +/* + + This file provides a simple line parsing class. This class was derived from that of NSIS, + http://nsis.sf.net, but it is no longer compatible (escaped-encodings and multiline C-style comments + are ignored). + + In particular, it allows for multiple space delimited tokens + on a line, with a choice of three quotes (`bla`, 'bla', or "bla") to contain any + items that may have spaces. + +*/ + +#ifndef WDL_LINEPARSE_H_ +#define WDL_LINEPARSE_H_ + +#include "heapbuf.h" + +#ifndef WDL_LINEPARSER_HAS_LINEPARSERINT +#define WDL_LINEPARSER_HAS_LINEPARSERINT +#endif + +#ifdef WDL_LINEPARSE_ATOF + extern "C" double WDL_LINEPARSE_ATOF(const char *); +#else + #define WDL_LINEPARSE_ATOF atof +#endif + +#ifndef WDL_LINEPARSE_IMPL_ONLY +class LineParserInt // version which does not have any temporary space for buffers (requires use of parseDestroyBuffer) +{ + public: + int getnumtokens() const { return m_nt-m_eat; } + + #ifdef WDL_LINEPARSE_INTF_ONLY + // parse functions return <0 on error (-1=mem, -2=unterminated quotes), ignore_commentchars = true means don't treat #; as comments + int parseDestroyBuffer(char *line, bool ignore_commentchars = true, bool backtickquote = true, bool allowunterminatedquotes = false); + + double gettoken_float(int token, int *success=NULL) const; + int gettoken_int(int token, int *success=NULL) const; + unsigned int gettoken_uint(int token, int *success=NULL) const; + const char *gettoken_str(int token) const; + char gettoken_quotingchar(int token) const; + int gettoken_enum(int token, const char *strlist) const; // null seperated list + #endif + + void eattoken() { if (m_eat= (int) (sizeof(m_toklist_small)/sizeof(m_toklist_small[0]))) + { + m_tokens = m_toklist_big.ResizeOK(m_nt+1,false); + if (!m_tokens) + { + m_nt=0; + return -1; + } + if (m_nt == (int) (sizeof(m_toklist_small)/sizeof(m_toklist_small[0]))) + memcpy(m_tokens,m_toklist_small,m_nt*sizeof(const char *)); + } + m_tokens[m_nt++] = basep; + + if (!oldterm) + { + if (lstate && !allowunterminatedquotes) + { + m_nt = 0; + return -2; + } + return 0; + } + + line++; + while ((thischar=*line) == ' ' || thischar == '\t') line++; + if (!thischar) return 0; + } + } + + + double WDL_LINEPARSE_PREFIX gettoken_float(int token, int *success WDL_LINEPARSE_DEFPARM(NULL)) const + { + token+=m_eat; + if ((unsigned int)token >= m_nt) + { + if (success) *success=0; + return 0.0; + } + const char *t=m_tokens[token]; + if (success) + *success=*t?1:0; + + // todo: detect d or f prefix for double/float base64 encodings + char buf[512]; + int ot = 0; + while (*t&&ot<(int)sizeof(buf)-1) + { + char c=*t++; + if (c == ',') c = '.'; + else if (success && (c < '0' || c > '9') && c != '.') *success=0; + buf[ot++]=c; + } + buf[ot] = 0; + return WDL_LINEPARSE_ATOF(buf); + } + + int WDL_LINEPARSE_PREFIX gettoken_int(int token, int *success WDL_LINEPARSE_DEFPARM(NULL)) const + { + token+=m_eat; + const char *tok; + if ((unsigned int)token >= m_nt || !((tok=m_tokens[token])[0])) + { + if (success) *success=0; + return 0; + } + char *tmp; + int l; + if (tok[0] == '-') l=(int)strtol(tok,&tmp,0); + else l=(int)strtoul(tok,&tmp,0); + if (success) *success=! (int)(*tmp); + return l; + } + + unsigned int WDL_LINEPARSE_PREFIX gettoken_uint(int token, int *success WDL_LINEPARSE_DEFPARM(NULL)) const + { + token+=m_eat; + const char *tok; + if ((unsigned int)token >= m_nt || !((tok=m_tokens[token])[0])) + { + if (success) *success=0; + return 0; + } + char *tmp; + const char* p=tok; + if (p[0] == '-') ++p; + unsigned int val=(int)strtoul(p, &tmp, 0); + if (success) *success=! (int)(*tmp); + return val; + } + + const char * WDL_LINEPARSE_PREFIX gettoken_str(int token) const + { + token+=m_eat; + if ((unsigned int)token >= m_nt) return ""; + return m_tokens[token]; + } + + char WDL_LINEPARSE_PREFIX gettoken_quotingchar(int token) const + { + token+=m_eat; + if ((unsigned int)token >= m_nt) return 0; + + const char *tok = m_tokens[token]; + if (tok != m_tokenbasebuffer) switch (tok[-1]) + { + case '"': return '"'; + case '`': return '`'; + case '\'': return '\''; + } + return 0; + } + + int WDL_LINEPARSE_PREFIX gettoken_enum(int token, const char *strlist) const // null seperated list + { + token+=m_eat; + if ((unsigned int)token >= m_nt) return -1; + + int x=0; + const char *tt=m_tokens[token]; + if (*tt) while (*strlist) + { +#ifdef _WIN32 + if (!stricmp(tt,strlist)) return x; +#else + if (!strcasecmp(tt,strlist)) return x; +#endif + while (*strlist) strlist++; + strlist++; + x++; + } + return -1; + } + +#ifndef WDL_LINEPARSE_IMPL_ONLY + private: +#endif + + + #undef WDL_LINEPARSE_PREFIX + #undef WDL_LINEPARSE_DEFPARM +#endif // ! WDL_LINEPARSE_INTF_ONLY + +#ifndef WDL_LINEPARSE_IMPL_ONLY + protected: + + WDL_TypedBuf m_toklist_big; + + unsigned int m_nt, m_eat; + + const char *m_tokenbasebuffer; // points to (mangled) caller's buffer + const char **m_tokens; // points to m_toklist_small or m_toklist_big + + const char *m_toklist_small[64]; +}; +#endif//!WDL_LINEPARSE_IMPL_ONLY + + + + + + +// derived + +#ifndef WDL_LINEPARSE_IMPL_ONLY +class LineParser : public LineParserInt +{ + public: + int parse(const char *line) { return parse_ex(line,false); } // <0 on error, old style (;# starting tokens means comment to EOL) + + #ifdef WDL_LINEPARSE_INTF_ONLY + // parse functions return <0 on error (-1=mem, -2=unterminated quotes), ignore_commentchars = true means don't treat #; as comments + int parse_ex(const char *line, bool ignore_commentchars = true, bool backtickquote = true, bool allowunterminatedquotes = false); + void set_one_token(const char *ptr); + char *__get_tmpbuf(const char *line); + #endif + + + LineParser(bool ignoredLegacyValue=false) { } + +#endif // !WDL_LINEPARSE_IMPL_ONLY + + + +#ifndef WDL_LINEPARSE_INTF_ONLY + #ifdef WDL_LINEPARSE_IMPL_ONLY + #define WDL_LINEPARSE_PREFIX LineParser:: + #define WDL_LINEPARSE_DEFPARM(x) + #else + #define WDL_LINEPARSE_PREFIX + #define WDL_LINEPARSE_DEFPARM(x) =(x) + #endif + + int WDL_LINEPARSE_PREFIX parse_ex(const char *line, bool ignore_commentchars WDL_LINEPARSE_DEFPARM(true), bool backtickquote WDL_LINEPARSE_DEFPARM(true), bool allowunterminatedquotes WDL_LINEPARSE_DEFPARM(false)) + { + return parseDestroyBuffer(__get_tmpbuf(line), ignore_commentchars, backtickquote, allowunterminatedquotes); + } + + void WDL_LINEPARSE_PREFIX set_one_token(const char *line) + { + m_tokens=m_toklist_small; + m_tokens[0] = m_tokenbasebuffer = __get_tmpbuf(line); + m_eat=0; + m_nt=m_tokenbasebuffer?1:0; + } + + char * WDL_LINEPARSE_PREFIX __get_tmpbuf(const char *line) + { + int linelen = (int)strlen(line); + + char *usebuf=m_tmpbuf; + if (linelen >= (int)sizeof(m_tmpbuf)) + { + usebuf = (char *)m_tmpbuf_big.ResizeOK(linelen+1,false); + if (!usebuf) + { + m_nt=0; + return NULL; + } + } + memcpy(usebuf,line,linelen+1); + return usebuf; + } + + #undef WDL_LINEPARSE_PREFIX + #undef WDL_LINEPARSE_DEFPARM +#endif // ! WDL_LINEPARSE_INTF_ONLY + +#ifndef WDL_LINEPARSE_IMPL_ONLY + private: + + WDL_HeapBuf m_tmpbuf_big; + char m_tmpbuf[2048]; +}; +#endif//!WDL_LINEPARSE_IMPL_ONLY + + + + + + + +#endif//WDL_LINEPARSE_H_ + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/mergesort.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/mergesort.h new file mode 100644 index 000000000..7a8c86229 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/mergesort.h @@ -0,0 +1,66 @@ +#ifndef _WDL_MERGESORT_H_ +#define _WDL_MERGESORT_H_ + +#include "wdltypes.h" + +static void WDL_STATICFUNC_UNUSED WDL_mergesort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *), char *tmpspace) +{ + char *b1,*b2; + size_t n1, n2; + + if (nmemb < 2) return; + + n1 = nmemb / 2; + b1 = (char *) base; + n2 = nmemb - n1; + b2 = b1 + (n1 * size); + + if (nmemb>2) + { + WDL_mergesort(b1, n1, size, compar, tmpspace); + WDL_mergesort(b2, n2, size, compar, tmpspace); + } + + + do + { + if (compar(b1, b2) > 0) // out of order, go to full merge + { + size_t sofar = b1-(char*)base; + memcpy(tmpspace,base,sofar); + memcpy(tmpspace+sofar, b2, size); + b2 += size; + n2--; + + char *writeptr=tmpspace+sofar+size; + while (n1 > 0 && n2 > 0) + { + if (compar(b1, b2) > 0) + { + memcpy(writeptr, b2, size); + b2 += size; + n2--; + } + else + { + memcpy(writeptr, b1, size); + b1 += size; + n1--; + } + writeptr += size; + } + + if (n1 > 0) memcpy(writeptr, b1, n1 * size); + memcpy(base, tmpspace, (nmemb - n2) * size); + + break; + } + + // in order, just advance + b1 += size; + n1--; + } + while (n1 > 0 && n2 > 0); +} + +#endif//_WDL_MERGESORT_H_ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/mutex.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/mutex.h new file mode 100644 index 000000000..a83c24f47 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/mutex.h @@ -0,0 +1,237 @@ +/* + WDL - mutex.h + Copyright (C) 2005 and later, Cockos Incorporated + + 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. + +*/ + +/* + + + This file provides a simple class that abstracts a mutex or critical section object. + On Windows it uses CRITICAL_SECTION, on everything else it uses pthread's mutex library. + It simulates the Critical Section behavior on non-Windows, as well (meaning a thread can + safely Enter the mutex multiple times, provided it Leaves the same number of times) + +*/ + +#ifndef _WDL_MUTEX_H_ +#define _WDL_MUTEX_H_ + +#ifdef _WIN32 +#include +#else + +#include +// define this if you wish to use carbon critical sections on OS X +// #define WDL_MAC_USE_CARBON_CRITSEC + +#ifdef WDL_MAC_USE_CARBON_CRITSEC +#include +#else +#include +#endif + +#endif + +#include "wdltypes.h" +#include "wdlatomic.h" + +#ifdef _DEBUG +#include +#endif + +class WDL_Mutex { + public: + WDL_Mutex() + { +#ifdef _WIN32 + InitializeCriticalSection(&m_cs); +#elif defined( WDL_MAC_USE_CARBON_CRITSEC) + MPCreateCriticalRegion(&m_cr); +#elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER) && !defined(__linux__) + const pthread_mutex_t tmp = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + m_mutex = tmp; +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE); +#ifdef __linux__ + // todo: macos too? + pthread_mutexattr_setprotocol(&attr,PTHREAD_PRIO_INHERIT); +#endif + pthread_mutex_init(&m_mutex,&attr); + pthread_mutexattr_destroy(&attr); +#endif + } + ~WDL_Mutex() + { +#ifdef _WIN32 + DeleteCriticalSection(&m_cs); +#elif defined(WDL_MAC_USE_CARBON_CRITSEC) + MPDeleteCriticalRegion(m_cr); +#else + pthread_mutex_destroy(&m_mutex); +#endif + } + + void Enter() + { +#ifdef _WIN32 + EnterCriticalSection(&m_cs); +#elif defined(WDL_MAC_USE_CARBON_CRITSEC) + MPEnterCriticalRegion(m_cr,kDurationForever); +#else + pthread_mutex_lock(&m_mutex); +#endif + } + + void Leave() + { +#ifdef _WIN32 + LeaveCriticalSection(&m_cs); +#elif defined(WDL_MAC_USE_CARBON_CRITSEC) + MPExitCriticalRegion(m_cr); +#else + pthread_mutex_unlock(&m_mutex); +#endif + } + + private: +#ifdef _WIN32 + CRITICAL_SECTION m_cs; +#elif defined(WDL_MAC_USE_CARBON_CRITSEC) + MPCriticalRegionID m_cr; +#else + pthread_mutex_t m_mutex; +#endif + + // prevent callers from copying mutexes accidentally + WDL_Mutex(const WDL_Mutex &cp) + { +#ifdef _DEBUG + assert(sizeof(WDL_Mutex) == 0); +#endif + } + WDL_Mutex &operator=(const WDL_Mutex &cp) + { +#ifdef _DEBUG + assert(sizeof(WDL_Mutex) == 0); +#endif + return *this; + } + +} WDL_FIXALIGN; + +class WDL_MutexLock { +public: + WDL_MutexLock(WDL_Mutex *m) : m_m(m) { if (m) m->Enter(); } + ~WDL_MutexLock() { if (m_m) m_m->Leave(); } + + // the caller modifies this, make sure it unlocks the mutex first and locks the new mutex! + WDL_Mutex *m_m; +} WDL_FIXALIGN; + +class WDL_SharedMutex +{ + public: + WDL_SharedMutex() { m_sharedcnt=0; } + ~WDL_SharedMutex() { } + + void LockExclusive() // note: the calling thread must NOT have any shared locks, or deadlock WILL occur + { + m_mutex.Enter(); +#ifdef _WIN32 + while (m_sharedcnt>0) Sleep(1); +#else + while (m_sharedcnt>0) usleep(100); +#endif + } + void UnlockExclusive() { m_mutex.Leave(); } + + void LockShared() + { + m_mutex.Enter(); + wdl_atomic_incr(&m_sharedcnt); + m_mutex.Leave(); + } + void UnlockShared() + { + wdl_atomic_decr(&m_sharedcnt); + } + + void SharedToExclusive() // assumes a SINGLE shared lock by this thread! + { + m_mutex.Enter(); +#ifdef _WIN32 + while (m_sharedcnt>1) Sleep(1); +#else + while (m_sharedcnt>1) usleep(100); +#endif + UnlockShared(); + } + + void ExclusiveToShared() // assumes exclusive locked returns with shared locked + { + // already have exclusive lock + wdl_atomic_incr(&m_sharedcnt); + m_mutex.Leave(); + } + + private: + WDL_Mutex m_mutex; + volatile int m_sharedcnt; + + // prevent callers from copying accidentally + WDL_SharedMutex(const WDL_SharedMutex &cp) + { + #ifdef _DEBUG + assert(sizeof(WDL_SharedMutex) == 0); + #endif + } + WDL_SharedMutex &operator=(const WDL_SharedMutex &cp) + { + #ifdef _DEBUG + assert(sizeof(WDL_SharedMutex) == 0); + #endif + return *this; + } + + +} WDL_FIXALIGN; + + + +class WDL_MutexLockShared { + public: + WDL_MutexLockShared(WDL_SharedMutex *m) : m_m(m) { if (m) m->LockShared(); } + ~WDL_MutexLockShared() { if (m_m) m_m->UnlockShared(); } + private: + WDL_SharedMutex *m_m; +} WDL_FIXALIGN; + +class WDL_MutexLockExclusive { + public: + WDL_MutexLockExclusive(WDL_SharedMutex *m) : m_m(m) { if (m) m->LockExclusive(); } + ~WDL_MutexLockExclusive() { if (m_m) m_m->UnlockExclusive(); } + private: + WDL_SharedMutex *m_m; +} WDL_FIXALIGN; + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/ptrlist.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/ptrlist.h new file mode 100644 index 000000000..eec7a9824 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/ptrlist.h @@ -0,0 +1,273 @@ +/* + WDL - ptrlist.h + Copyright (C) 2005 and later, Cockos Incorporated + + 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. + +*/ + +/* + + This file provides a simple templated class for a list of pointers. By default this list + doesn't free any of the pointers, but you can call Empty(true) or Delete(x,true) to delete the pointer, + or you can use Empty(true,free) etc to call free (or any other function). + + Note: on certain compilers, instantiating with WDL_PtrList bla; will give a warning, since + the template will create code for "delete (void *)x;" which isn't technically valid. Oh well. + +*/ + +#ifndef _WDL_PTRLIST_H_ +#define _WDL_PTRLIST_H_ + +#include "heapbuf.h" + +template class WDL_PtrList +{ + public: + explicit WDL_PtrList(int defgran=4096) : m_hb(defgran WDL_HEAPBUF_TRACEPARM("WDL_PtrList")) + { + } + + ~WDL_PtrList() + { + } + + PTRTYPE **GetList() const { return (PTRTYPE**)m_hb.Get(); } + PTRTYPE *Get(INT_PTR index) const + { + PTRTYPE **list = (PTRTYPE**)m_hb.Get(); + if (list && (UINT_PTR)index < (UINT_PTR)(m_hb.GetSize()/sizeof(PTRTYPE *))) return list[index]; + return NULL; + } + + int GetSize(void) const { return m_hb.GetSize()/(unsigned int)sizeof(PTRTYPE *); } + + int Find(const PTRTYPE *p) const + { + if (p) + { + PTRTYPE **list=(PTRTYPE **)m_hb.Get(); + int x; + const int n = GetSize(); + for (x = 0; x < n; x ++) if (list[x] == p) return x; + } + return -1; + } + int FindR(const PTRTYPE *p) const + { + if (p) + { + PTRTYPE **list=(PTRTYPE **)m_hb.Get(); + int x = GetSize(); + while (--x >= 0) if (list[x] == p) return x; + } + return -1; + } + + PTRTYPE *Add(PTRTYPE *item) + { + const int s=GetSize(); + PTRTYPE **list=(PTRTYPE **)m_hb.ResizeOK((s+1)*(unsigned int)sizeof(PTRTYPE*),false); + if (list) + { + list[s]=item; + return item; + } + return NULL; + } + + PTRTYPE *Set(int index, PTRTYPE *item) + { + PTRTYPE **list=(PTRTYPE **)m_hb.Get(); + if (list && index >= 0 && index < GetSize()) return list[index]=item; + return NULL; + } + + PTRTYPE *Insert(int index, PTRTYPE *item) + { + int s=GetSize(); + PTRTYPE **list = (PTRTYPE **)m_hb.ResizeOK((s+1)*(unsigned int)sizeof(PTRTYPE*),false); + + if (!list) return item; + + if (index<0) index=0; + + int x; + for (x = s; x > index; x --) list[x]=list[x-1]; + return (list[x] = item); + } + int FindSorted(const PTRTYPE *p, int (*compar)(const PTRTYPE **a, const PTRTYPE **b)) const + { + bool m; + int i = LowerBound(p,&m,compar); + return m ? i : -1; + } + PTRTYPE *InsertSorted(PTRTYPE *item, int (*compar)(const PTRTYPE **a, const PTRTYPE **b)) + { + bool m; + return Insert(LowerBound(item,&m,compar),item); + } + + void Delete(int index) + { + PTRTYPE **list=GetList(); + int size=GetSize(); + if (list && index >= 0 && index < size) + { + if (index < --size) memmove(list+index,list+index+1,(unsigned int)sizeof(PTRTYPE *)*(size-index)); + m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*),false); + } + } + void Delete(int index, bool wantDelete, void (*delfunc)(void *)=NULL) + { + PTRTYPE **list=GetList(); + int size=GetSize(); + if (list && index >= 0 && index < size) + { + if (wantDelete) + { + if (delfunc) delfunc(Get(index)); + else delete Get(index); + } + if (index < --size) memmove(list+index,list+index+1,(unsigned int)sizeof(PTRTYPE *)*(size-index)); + m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*),false); + } + } + void Delete(int index, void (*delfunc)(PTRTYPE *)) + { + PTRTYPE **list=GetList(); + int size=GetSize(); + if (list && index >= 0 && index < size) + { + if (delfunc) delfunc(Get(index)); + if (index < --size) memmove(list+index,list+index+1,(unsigned int)sizeof(PTRTYPE *)*(size-index)); + m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*),false); + } + } + void DeletePtr(const PTRTYPE *p) { Delete(Find(p)); } + void DeletePtr(const PTRTYPE *p, bool wantDelete, void (*delfunc)(void *)=NULL) { Delete(Find(p),wantDelete,delfunc); } + void DeletePtr(const PTRTYPE *p, void (*delfunc)(PTRTYPE *)) { Delete(Find(p),delfunc); } + + void Empty() + { + m_hb.Resize(0,false); + } + void Empty(bool wantDelete, void (*delfunc)(void *)=NULL) + { + if (wantDelete) + { + int x; + for (x = GetSize()-1; x >= 0; x --) + { + PTRTYPE* p = Get(x); + if (p) + { + if (delfunc) delfunc(p); + else delete p; + } + m_hb.Resize(x*(unsigned int)sizeof(PTRTYPE *),false); + } + } + m_hb.Resize(0,false); + } + void Empty(void (*delfunc)(PTRTYPE *)) + { + int x; + for (x = GetSize()-1; x >= 0; x --) + { + PTRTYPE* p = Get(x); + if (delfunc && p) delfunc(p); + m_hb.Resize(x*(unsigned int)sizeof(PTRTYPE *),false); + } + } + void EmptySafe(bool wantDelete=false,void (*delfunc)(void *)=NULL) + { + if (!wantDelete) Empty(); + else + { + WDL_PtrList tmp; + int x; + for(x=0;x 0) a = b+1; + else if (cmp < 0) c = b; + else + { + *ismatch = true; + return b; + } + } + *ismatch = false; + return a; + } + + void Compact() { m_hb.Resize(m_hb.GetSize(),true); } + + + int DeleteBatch(bool (*proc)(PTRTYPE *p, void *ctx), void *ctx=NULL) // proc returns true to remove item. returns number removed + { + const int sz = GetSize(); + int cnt=0; + PTRTYPE **rd = GetList(), **wr = rd; + for (int x = 0; x < sz; x ++) + { + if (!proc(*rd,ctx)) + { + if (rd != wr) *wr=*rd; + wr++; + cnt++; + } + rd++; + } + if (cnt < sz) m_hb.Resize(cnt * sizeof(PTRTYPE*),false); + return sz - cnt; + } + + private: + WDL_HeapBuf m_hb; + +}; + + +template class WDL_PtrList_DeleteOnDestroy : public WDL_PtrList +{ +public: + explicit WDL_PtrList_DeleteOnDestroy(void (*delfunc)(void *)=NULL, int defgran=4096) : WDL_PtrList(defgran), m_delfunc(delfunc) { } + ~WDL_PtrList_DeleteOnDestroy() + { + WDL_PtrList::EmptySafe(true,m_delfunc); + } +private: + void (*m_delfunc)(void *); +}; + +#endif + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/queue.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/queue.h new file mode 100644 index 000000000..ec6b74ad3 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/queue.h @@ -0,0 +1,286 @@ +/* + WDL - queue.h + Copyright (C) 2005 and later, Cockos Incorporated + + 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. + +*/ + +/* + + This file provides a simple class for a FIFO queue of bytes. It uses a simple buffer, + so should not generally be used for large quantities of data (it can advance the queue + pointer, but Compact() needs to be called regularly to keep memory usage down, and when + it is called, there's a memcpy() penalty for the remaining data. oh well, is what it is). + + You may also wish to look at fastqueue.h or circbuf.h if these limitations aren't acceptable. + +*/ + +#ifndef _WDL_QUEUE_H_ +#define _WDL_QUEUE_H_ + +#include "heapbuf.h" + + +class WDL_Queue +{ +public: + WDL_Queue() : m_hb(4096 WDL_HEAPBUF_TRACEPARM("WDL_Queue")), m_pos(0) { } + WDL_Queue(int hbgran) : m_hb(hbgran WDL_HEAPBUF_TRACEPARM("WDL_Queue")), m_pos(0) { } + ~WDL_Queue() { } + + template void* AddT(T* buf) + { + return Add(buf, sizeof(T)); + } + + void *Add(const void *buf, int len) + { + int olen=m_hb.GetSize(); + if (m_pos >= olen) m_pos=olen=0; // if queue is empty then autoreset it + + char *newbuf=(char *)m_hb.ResizeOK(olen+len,false); + if (newbuf) + { + newbuf += olen; + if (buf) memcpy(newbuf,buf,len); + } + return newbuf; + } + + template T* GetT(T* val=0) + { + T* p = (T*) Get(sizeof(T)); + if (val && p) *val = *p; + return p; + } + + void* Get(int size) + { + void* p = Get(); + if (p) Advance(size); + return p; + } + + void *Get() const + { + if (m_pos >= 0 && m_pos < m_hb.GetSize()) return (char *)m_hb.Get()+m_pos; + return NULL; + } + + void* Rewind() + { + m_pos = 0; + return m_hb.Get(); + } + + int GetSize() const + { + return m_hb.GetSize()-m_pos; + } + int Available() const { return GetSize(); } + + void Clear() + { + m_pos=0; + m_hb.Resize(0,false); + } + + void Advance(int bytecnt) + { + m_pos+=bytecnt; + if (m_pos<0)m_pos=0; + else if (m_pos > m_hb.GetSize()) m_pos=m_hb.GetSize(); + } + + void Compact(bool allocdown=false, bool force=false) + { + int olen=m_hb.GetSize(); + if (m_pos > (force ? 0 : olen/2)) + { + olen -= m_pos; + if (olen > 0) + { + char *a=(char*)m_hb.Get(); + memmove(a,a+m_pos,olen); + } + else + { + olen = 0; + } + m_hb.Resize(olen,allocdown); + m_pos=0; + } + } + + void SetGranul(int granul) { m_hb.SetGranul(granul); } + + + + + // endian-management stuff + + static void WDL_Queue__bswap_buffer(void *buf, int len) + { + #ifdef __ppc__ + char *p=(char *)buf; + char *ep=p+len; + while ((len-=2) >= 0) + { + char tmp=*p; *p++=*--ep; *ep=tmp; + } + #endif + } + + // older API of static functions (that endedu p warning a bit anyway) +#define WDL_Queue__AddToLE(q, v) (q)->AddToLE(v) +#define WDL_Queue__AddDataToLE(q,d,ds,us) (q)->AddDataToLE(d,ds,us) +#define WDL_Queue__GetTFromLE(q,v) (q)->GetTFromLE(v) +#define WDL_Queue__GetDataFromLE(q,ds,us) (q)->GetDataFromLE(ds,us) + + template void AddToLE(T *val) + { + WDL_Queue__bswap_buffer(AddT(val),sizeof(T)); + } + void AddDataToLE(void *data, int datasize, int unitsize) + { + #ifdef __ppc__ + char *dout = (char *)Add(data,datasize); + while (datasize >= unitsize) + { + WDL_Queue__bswap_buffer(dout,unitsize); + dout+=unitsize; + datasize-=unitsize; + } + #else + Add(data,datasize); + #endif + } + + + // NOTE: these thrash the contents of the queue if on LE systems. So for example if you are going to rewind it later or use it elsewhere, + // then get ready to get unhappy. + template T *GetTFromLE(T* val=0) + { + T *p = GetT(val); + if (p) { + WDL_Queue__bswap_buffer(p,sizeof(T)); + if (val) *val = *p; + } + return p; + } + + void *GetDataFromLE(int datasize, int unitsize) + { + void *data=Get(datasize); + #ifdef __ppc__ + char *dout=(char *)data; + if (dout) while (datasize >= unitsize) + { + WDL_Queue__bswap_buffer(dout,unitsize); + dout+=unitsize; + datasize-=unitsize; + } + #endif + return data; + } + + +private: + WDL_HeapBuf m_hb; + int m_pos; +public: + int __pad; // keep 8 byte aligned +}; + +template class WDL_TypedQueue +{ +public: + WDL_TypedQueue() : m_hb(4096 WDL_HEAPBUF_TRACEPARM("WDL_TypedQueue")), m_pos(0) { } + ~WDL_TypedQueue() { } + + T *Add(const T *buf, int len) + { + int olen=m_hb.GetSize(); + if (m_pos >= olen) olen=m_pos=0; + len *= (int)sizeof(T); + + char *newbuf=(char*)m_hb.ResizeOK(olen+len,false); + if (newbuf) + { + newbuf += olen; + if (buf) memcpy(newbuf,buf,len); + } + return (T*) newbuf; + } + + T *Get() const + { + if (m_pos >= 0 && m_pos < m_hb.GetSize()) return (T*)((char *)m_hb.Get()+m_pos); + return NULL; + } + + int GetSize() const + { + return m_pos < m_hb.GetSize() ? (m_hb.GetSize()-m_pos)/sizeof(T) : 0; + } + int Available() const { return GetSize(); } + + void Clear() + { + m_pos=0; + m_hb.Resize(0,false); + } + + void Advance(int cnt) + { + m_pos+=cnt*(int)sizeof(T); + if (m_pos<0)m_pos=0; + else if (m_pos > m_hb.GetSize()) m_pos=m_hb.GetSize(); + } + + void Compact(bool allocdown=false, bool force=false) + { + int olen=m_hb.GetSize(); + if (m_pos >= (force ? 0 : olen/2)) + { + olen -= m_pos; + if (olen > 0) + { + char *a=(char*)m_hb.Get(); + memmove(a,a+m_pos,olen); + } + else + { + olen = 0; + } + m_hb.Resize(olen,allocdown); + m_pos=0; + } + } + + void SetGranul(int granul) { m_hb.SetGranul(granul); } + +private: + WDL_HeapBuf m_hb; + int m_pos; +public: + int __pad; // keep 8 byte aligned +}; + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-appstub-generic.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-appstub-generic.cpp new file mode 100644 index 000000000..d6de07c1e --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-appstub-generic.cpp @@ -0,0 +1,83 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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 "swell.h" + + +#ifndef SWELL_PROVIDED_BY_APP + +// only add this file to your project if you are an application that wishes to publish the SWELL API to its modules/plugins +// the modules should be compiled using SWELL_PROVIDED_BY_APP and include swell-modstub-generic.cpp + +#undef _WDL_SWELL_H_API_DEFINED_ +#undef SWELL_API_DEFINE +#define SWELL_API_DEFINE(ret, func, parms) {#func, (void *)func }, +static struct api_ent +{ + const char *name; + void *func; +} +api_table[]= +{ +#include "swell.h" +}; + +static int compfunc(const void *a, const void *b) +{ + return strcmp(((struct api_ent*)a)->name,((struct api_ent*)b)->name); +} + +extern "C" { + +__attribute__ ((visibility ("default"))) void *SWELLAPI_GetFunc(const char *name) +{ + if (!name) return (void *)0x100; // version + static int a; + if (!a) + { + a=1; + qsort(api_table,sizeof(api_table)/sizeof(api_table[0]),sizeof(api_table[0]),compfunc); + } + struct api_ent find={name,NULL}; + struct api_ent *res=(struct api_ent *)bsearch(&find,api_table,sizeof(api_table)/sizeof(api_table[0]),sizeof(api_table[0]),compfunc); + if (res) return res->func; + return NULL; +} + +}; + +#ifdef SWELL_MAKING_DYLIB +static INT_PTR (*s_AppMain)(int msg, INT_PTR parm1, INT_PTR parm2); +INT_PTR SWELLAppMain(int msg, INT_PTR parm1, INT_PTR parm2) +{ + if (s_AppMain) return s_AppMain(msg,parm1,parm2); + return 0; +} + +extern "C" { +__attribute__ ((visibility ("default"))) void SWELL_set_app_main(INT_PTR (*AppMain)(int msg, INT_PTR parm1, INT_PTR parm2)) +{ + s_AppMain = AppMain; +} +}; + +#endif + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-appstub.mm b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-appstub.mm new file mode 100644 index 000000000..b08c11175 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-appstub.mm @@ -0,0 +1,61 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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 "swell.h" + + +#ifndef SWELL_PROVIDED_BY_APP + +// only add this file to your project if you are an application that wishes to publish the SWELL API to its modules/plugins +// the modules should be compiled using SWELL_PROVIDED_BY_APP and include swell-modstub.mm + +#undef _WDL_SWELL_H_API_DEFINED_ +#undef SWELL_API_DEFINE +#define SWELL_API_DEFINE(ret, func, parms) {#func, (void *)func }, +static struct api_ent +{ + const char *name; + void *func; +} +api_table[]= +{ +#include "swell-functions.h" +}; + +static int compfunc(const void *a, const void *b) +{ + return strcmp(((struct api_ent*)a)->name,((struct api_ent*)b)->name); +} + +void *SWELLAPI_GetFunc(const char *name) +{ + if (!name) return (void *)0x100; // version + static int a; + if (!a) + { + a=1; + qsort(api_table,sizeof(api_table)/sizeof(api_table[0]),sizeof(api_table[0]),compfunc); + } + struct api_ent find={name,NULL}; + struct api_ent *res=(struct api_ent *)bsearch(&find,api_table,sizeof(api_table)/sizeof(api_table[0]),sizeof(api_table[0]),compfunc); + if (res) return res->func; + return NULL; +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-dlg-generic.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-dlg-generic.cpp new file mode 100644 index 000000000..b3d0e0958 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-dlg-generic.cpp @@ -0,0 +1,373 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. +*/ + +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" +#include "swell-dlggen.h" + +#include "../ptrlist.h" + +static HMENU g_swell_defaultmenu,g_swell_defaultmenumodal; + +void (*SWELL_DDrop_onDragLeave)(); +void (*SWELL_DDrop_onDragOver)(POINT pt); +void (*SWELL_DDrop_onDragEnter)(void *hGlobal, POINT pt); +const char* (*SWELL_DDrop_getDroppedFileTargetPath)(const char* extension); + +bool SWELL_owned_windows_levelincrease=false; + +#include "swell-internal.h" + +static SWELL_DialogResourceIndex *resById(SWELL_DialogResourceIndex *reshead, const char *resid) +{ + SWELL_DialogResourceIndex *p=reshead; + while (p) + { + if (p->resid == resid) return p; + p=p->_next; + } + return 0; +} + +// keep list of modal dialogs +struct modalDlgRet { + HWND hwnd; + bool has_ret; + int ret; +}; + + +static WDL_PtrList s_modalDialogs; + +bool IsModalDialogBox(HWND hwnd) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return false; + int a = s_modalDialogs.GetSize(); + while (a-- > 0) + { + modalDlgRet *r = s_modalDialogs.Get(a); + if (r && r->hwnd == hwnd) return true; + } + return false; +} + +HWND DialogBoxIsActive() +{ + int a = s_modalDialogs.GetSize(); + while (a-- > 0) + { + modalDlgRet *r = s_modalDialogs.Get(a); + if (r && !r->has_ret && r->hwnd) return r->hwnd; + } + return NULL; +} + +static SWELL_OSWINDOW s_spare; +static RECT s_spare_rect; +static UINT_PTR s_spare_timer; +static int s_spare_style; + +void swell_dlg_destroyspare() +{ + if (s_spare_timer) + { + KillTimer(NULL,s_spare_timer); + s_spare_timer=0; + } + if (s_spare) + { +#ifdef SWELL_TARGET_GDK + gdk_window_destroy(s_spare); +#endif + s_spare=NULL; + } +} + +static void spareTimer(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwtime) +{ + swell_dlg_destroyspare(); +} + +static int s_last_dlgret; + +void EndDialog(HWND wnd, int ret) +{ + if (WDL_NOT_NORMALLY(!wnd)) return; + + int a = s_modalDialogs.GetSize(); + while (a-->0) + { + modalDlgRet *r = s_modalDialogs.Get(a); + if (r && r->hwnd == wnd) + { + r->ret = ret; + if (r->has_ret) return; + + r->has_ret=true; + } + } + + if (!wnd->m_hashaddestroy) + { + void RecurseDestroyWindow(HWND); + SendMessage(wnd,WM_DESTROY,0,0); + #ifndef SWELL_NO_SPARE_MODALDLG + if (wnd->m_oswindow && wnd->m_visible) + { + swell_dlg_destroyspare(); + GetWindowRect(wnd,&s_spare_rect); + s_spare_style = wnd->m_style; + s_spare = wnd->m_oswindow; + wnd->m_oswindow = NULL; + s_spare_timer = SetTimer(NULL,0, + swell_is_app_inactive()>0 ? 500 : 100, + spareTimer); + } + #endif + RecurseDestroyWindow(wnd); + } + s_last_dlgret = ret; +} + +int SWELL_DialogBox(SWELL_DialogResourceIndex *reshead, const char *resid, HWND parent, DLGPROC dlgproc, LPARAM param) +{ + SWELL_DialogResourceIndex *p=resById(reshead,resid); + if (resid) // allow modal dialogs to be created without template + { + if (!p||(p->windowTypeFlags&SWELL_DLG_WS_CHILD)) return -1; + } + else if (parent) + { + resid = (const char *)(INT_PTR)(0x400002); // force non-child, force no minimize box + } + + + int ret=-1; + s_last_dlgret = -1; + HWND hwnd = SWELL_CreateDialog(reshead,resid,parent,dlgproc,param); + // create dialog + if (hwnd) + { + hwnd->Retain(); + ReleaseCapture(); // force end of any captures + + WDL_PtrKeyedArray restwnds; + extern HWND__ *SWELL_topwindows; + HWND a = SWELL_topwindows; + while (a) + { + if (a!=hwnd) + { + int f=0; + if (a->m_enabled) { EnableWindow(a,FALSE); f|=1; } + if (a->m_israised) { SWELL_SetWindowLevel(a,0); f|=2; } + if (f) restwnds.AddUnsorted((INT_PTR)a,f); + } + a = a->m_next; + } + restwnds.Resort(); + SWELL_SetWindowLevel(hwnd,1); + + modalDlgRet r = { hwnd,false, -1 }; + s_modalDialogs.Add(&r); + + if (s_spare && s_spare_style == hwnd->m_style) + { + if (s_spare_timer) + { + KillTimer(NULL,s_spare_timer); + s_spare_timer = 0; + } + SWELL_OSWINDOW w = s_spare; + s_spare = NULL; + + int flags = 0; + const int dw = (hwnd->m_position.right-hwnd->m_position.left) - + (s_spare_rect.right - s_spare_rect.left); + const int dh = (hwnd->m_position.bottom-hwnd->m_position.top) - + (s_spare_rect.bottom - s_spare_rect.top); + + if (hwnd->m_has_had_position) flags |= 1; + if (dw || dh) flags |= 2; + + if (flags == 2) + { + // center on the old window + hwnd->m_position.right -= hwnd->m_position.left; + hwnd->m_position.bottom -= hwnd->m_position.top; + hwnd->m_position.left = s_spare_rect.left - dw/2; + hwnd->m_position.top = s_spare_rect.top - dh/2; + hwnd->m_position.right += hwnd->m_position.left; + hwnd->m_position.bottom += hwnd->m_position.top; + flags = 3; + } + + if (flags) + { + if (flags&2) swell_oswindow_begin_resize(w); + swell_oswindow_resize(w, flags, hwnd->m_position); + } + hwnd->m_oswindow = w; + ShowWindow(hwnd,SW_SHOWNA); + } + else + { + swell_dlg_destroyspare(); + ShowWindow(hwnd,SW_SHOW); + } + + while (!r.has_ret && !hwnd->m_hashaddestroy) + { + void SWELL_RunMessageLoop(); + SWELL_RunMessageLoop(); + Sleep(10); + } + ret=r.ret; + s_modalDialogs.DeletePtr(&r); + + a = SWELL_topwindows; + while (a) + { + if (a != hwnd) + { + int f = restwnds.Get((INT_PTR)a); + if (!a->m_enabled && (f&1)) EnableWindow(a,TRUE); + if (!a->m_israised && (f&2)) SWELL_SetWindowLevel(a,1); + } + a = a->m_next; + } + hwnd->Release(); + } + else + { + ret = s_last_dlgret; // SWELL_CreateDialog() failed, implies WM_INITDIALOG could have called EndDialog() + } + // while in list, do something + return ret; +} + +HWND SWELL_CreateDialog(SWELL_DialogResourceIndex *reshead, const char *resid, HWND parent, DLGPROC dlgproc, LPARAM param) +{ + int forceStyles=0; // 1=resizable, 2=no minimize, 4=no close + bool forceNonChild=false; + if ((((INT_PTR)resid)&~0xf)==0x400000) + { + forceStyles = (int) (((INT_PTR)resid)&0xf); + if (forceStyles) forceNonChild=true; + resid=0; + } + SWELL_DialogResourceIndex *p=resById(reshead,resid); + if (!p&&resid) return 0; + + RECT r={0,0,SWELL_UI_SCALE(p ? p->width : 300), SWELL_UI_SCALE(p ? p->height : 200) }; + HWND owner=NULL; + + if (!forceNonChild && parent && (!p || (p->windowTypeFlags&SWELL_DLG_WS_CHILD))) + { + } + else + { + owner = parent; + parent = NULL; // top level window + } + + HWND__ *h = new HWND__(parent,0,&r,NULL,false,NULL,NULL, owner); + if (forceNonChild || (p && !(p->windowTypeFlags&SWELL_DLG_WS_CHILD))) + { + if ((forceStyles&1) || (p && (p->windowTypeFlags&SWELL_DLG_WS_RESIZABLE))) + h->m_style |= WS_THICKFRAME|WS_CAPTION; + else h->m_style |= WS_CAPTION; + } + else if (!p && !parent) h->m_style |= WS_CAPTION; + else if (parent && (!p || (p->windowTypeFlags&SWELL_DLG_WS_CHILD))) h->m_style |= WS_CHILD; + + if (p) + { + h->m_style |= p->windowTypeFlags & (WS_CLIPSIBLINGS); + if (p->windowTypeFlags&SWELL_DLG_WS_DROPTARGET) + h->m_exstyle|=WS_EX_ACCEPTFILES; + } + + h->Retain(); + + if (p) + { + p->createFunc(h,p->windowTypeFlags); + if (p->title) SetWindowText(h,p->title); + + h->m_dlgproc = dlgproc; + h->m_wndproc = SwellDialogDefaultWindowProc; + + HWND hFoc=h->m_children; + while (hFoc) + { + if (hFoc->m_wantfocus && hFoc->m_visible && hFoc->m_enabled) + { + h->m_focused_child = hFoc; // default focus to hFoc, but set focus more aggressively after WM_INITDIALOG if the dlgproc returns 1 + break; + } + hFoc=hFoc->m_next; + } + + if (hFoc) hFoc->Retain(); + + if (h->m_dlgproc(h,WM_INITDIALOG,(WPARAM)hFoc,param)) + { + if (hFoc && hFoc->m_wantfocus && hFoc->m_visible && hFoc->m_enabled) + { + if (!h->m_hashaddestroy && !hFoc->m_hashaddestroy) + SetFocus(hFoc); + } + } + + if (hFoc) hFoc->Release(); + } + else + { + h->m_wndproc = (WNDPROC)dlgproc; + h->m_wndproc(h,WM_CREATE,0,param); + } + + HWND rv = h->m_hashaddestroy ? NULL : h; + h->Release(); + return rv; +} + + +HMENU SWELL_GetDefaultWindowMenu() { return g_swell_defaultmenu; } +void SWELL_SetDefaultWindowMenu(HMENU menu) +{ + g_swell_defaultmenu=menu; +} +HMENU SWELL_GetDefaultModalWindowMenu() +{ + return g_swell_defaultmenumodal; +} +void SWELL_SetDefaultModalWindowMenu(HMENU menu) +{ + g_swell_defaultmenumodal=menu; +} + + + +SWELL_DialogResourceIndex *SWELL_curmodule_dialogresource_head; // this eventually will go into a per-module stub file + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-dlg.mm b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-dlg.mm new file mode 100644 index 000000000..638f776dc --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-dlg.mm @@ -0,0 +1,4424 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. +*/ + +#ifndef SWELL_PROVIDED_BY_APP + + +#include "swell.h" +#include "swell-dlggen.h" + +#import +#include +#include + +#define NSRECTSET_RECT NSRect + +#import + +#include "swell-internal.h" + +#ifndef SWELL_NO_METAL +#undef min +#undef max +#include +#import +#import +#include "../assocarray.h" + +#ifdef __SSE__ +// for SWELL_fastDoubleUpImage +#include +#endif + +#define LIMIT_METAL_BOUNDS_SIZE(sz) \ + if ((sz).width > 16384.0) (sz).width = 16384.0; \ + if ((sz).height > 16384.0) (sz).height = 16384.0; + +static id __class_CAMetalLayer, __class_MTLRenderPassDescriptor, __class_MTLTextureDescriptor, __class_MTLRenderPipelineDescriptor, __class_MTLCompileOptions; +static id (*__MTLCreateSystemDefaultDevice)(void); +static id (*__CGDirectDisplayCopyCurrentMetalDevice)(CGDirectDisplayID); + +struct mtl_dev_rec { + id vertexFunction,fragmentFunction; +}; + +static void get_dev_shaders(id dev, id *vertex, id *frag) +{ + static WDL_PtrKeyedArray s_mtl_device_recs; + mtl_dev_rec *p = s_mtl_device_recs.GetPtr((INT_PTR)dev); + if (p) + { + *vertex = p->vertexFunction; + *frag = p->fragmentFunction; + return; + } + + mtl_dev_rec rec={NULL, }; + // open device, compiler shaders + id opt = (id)[[__class_MTLCompileOptions alloc] init]; + NSError *err=NULL; + NSString *code = +@"#include \n" +@"#include \n" +@"using namespace metal;\n" +@"typedef struct { float4 position [[position]]; float2 textureCoordinate; } RasterizerData;\n" +@"vertex RasterizerData\n" +@"vertexShader(uint vertexID [[ vertex_id ]], constant vector_float2 *vertexArray [[ buffer(0) ]]) {\n" +@" RasterizerData out;\n" +@" out.position = vector_float4(0.0, 0.0, 0.0, 1.0);\n" +@" out.position.xy = vertexArray[vertexID*2].xy;\n" +@" out.textureCoordinate = vertexArray[vertexID*2+1].xy;\n" +@" return out;\n" +@"}\n" +@"fragment float4\n" +@"samplingShader(RasterizerData in [[stage_in]], texture2d colorTexture [[ texture(0) ]]) { \n" +@" constexpr sampler textureSampler (mag_filter::linear, min_filter::linear);\n" +@" const half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinate);\n" +@" return float4(colorSample);\n" +@"}\n"; + + id mtl_lib = [dev newLibraryWithSource:code options:opt error:&err]; + if (err) NSLog(@"swell-cocoa: error compiling metal shaders for device %p %@: %@\n",dev,dev.name,err); + [opt release]; + + if (mtl_lib && + (*vertex = rec.vertexFunction = [mtl_lib newFunctionWithName:@"vertexShader"]) && + (*frag = rec.fragmentFunction = [mtl_lib newFunctionWithName:@"samplingShader"])) + { + NSLog(@"swell-cocoa: mtl ok for device %p %@!\n", dev, dev.name); + } + else + { + NSLog(@"swell-cocoa: mtl failed functions for device %p %@!\n", dev, dev.name); + } + s_mtl_device_recs.Insert((INT_PTR)dev, rec); +} + +#endif + +@interface NSRectSet : NSObject +{ + struct CGRect _bounds; + struct CGRect *_rects; + unsigned long long _count; +} + ++ (id)emptyRectSet; ++ (void)initialize; +- (void)strokeExactInterior; +- (void)fillExactInterior; +- (void)stroke; +- (void)fill; +- (void)setClip; +- (void)addClip; +- (void)convertFromAncestor:(id)arg1 toView:(id)arg2 clipTo:(NSRECTSET_RECT)arg3; +- (void)intersectWithRect:(NSRECTSET_RECT)arg1; +- (void)subtractRect:(NSRECTSET_RECT)arg1; +- (void)setEmpty; +- (unsigned long long)count; +- (const NSRECTSET_RECT *)rects; +- (NSRECTSET_RECT)bounds; +- (BOOL)isEmpty; +- (id)description; +- (id)copyWithZone:(struct _NSZone *)arg1; +- (void)dealloc; +- (id)initWithCopyOfRects:(const NSRECTSET_RECT *)arg1 count:(unsigned long long)arg2 bounds:(NSRECTSET_RECT)arg3; +- (id)initWithRegion:(id)arg1; +- (id)initWithRect:(NSRECTSET_RECT)arg1; +- (id)init; + +@end + +@interface _NSDisplayOperationStack : NSObject +{ +} ++ (_NSDisplayOperationStack *) currentThreadDisplayOperationStack; +- (void) setRectSetBeingDrawn:(NSRectSet *)rs forView:(NSView *)v; +@end + +@interface NSView (swell_internals) +-(void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)vr rectIsVisibleRectForView:(NSView*)view topView:(NSView *)v2; +@end + +#undef NSRECTSET_RECT + + +#ifndef SWELL_CUT_OUT_COMPOSITING_MIDDLEMAN +#define SWELL_CUT_OUT_COMPOSITING_MIDDLEMAN 1 // 2 gives more performance, not correctly drawn window frames (try NSThemeFrame stuff? bleh) +#endif + +static HMENU g_swell_defaultmenu,g_swell_defaultmenumodal; + +void (*SWELL_DDrop_onDragLeave)(); +void (*SWELL_DDrop_onDragOver)(POINT pt); +void (*SWELL_DDrop_onDragEnter)(void *hGlobal, POINT pt); +const char* (*SWELL_DDrop_getDroppedFileTargetPath)(const char* extension); + +bool SWELL_owned_windows_levelincrease=false; + +#include "../wdlstring.h" +#include "../wdlcstring.h" + +#define NSColorFromCol(a) [NSColor colorWithCalibratedRed:GetRValue(a)/255.0f green:GetGValue(a)/255.0f blue:GetBValue(a)/255.0f alpha:1.0f] +extern int g_swell_terminating; + +static LRESULT sendSwellMessage(id obj, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (obj && [obj respondsToSelector:@selector(onSwellMessage:p1:p2:)]) + return [(SWELL_hwndChild *)obj onSwellMessage:uMsg p1:wParam p2:lParam]; + return 0; +} + + +char g_swell_nomiddleman_cocoa_override=0; // -1 to disable, 1 to force + +static BOOL useNoMiddleManCocoa() +{ +#ifdef __ppc__ + return false; +#else + const int v = SWELL_GetOSXVersion(); + return v >= 0x1050 && (g_swell_nomiddleman_cocoa_override ? (g_swell_nomiddleman_cocoa_override>0) : v < 0x10a0); +#endif +} + +void updateWindowCollection(NSWindow *w) +{ + if (SWELL_GetOSXVersion()>=0x1060) + { + const int NSWindowCollectionBehaviorParticipatesInCycle = 1 << 5; + const int NSWindowCollectionBehaviorManaged = 1 << 2; + [(SWELL_WindowExtensions*)w setCollectionBehavior:NSWindowCollectionBehaviorManaged|NSWindowCollectionBehaviorParticipatesInCycle]; + } +} + +static void DrawSwellViewRectImpl(SWELL_hwndChild *view, NSRect rect, HDC hdc, bool isMetal=false); +static void swellRenderOptimizely(int passflags, SWELL_hwndChild *view, HDC hdc, BOOL doforce, WDL_PtrList *needdraws, const NSRect *rlist, NSInteger rlistcnt, int draw_xlate_x, int draw_xlate_y, bool iscv, NSView *rlist_coordview); + +static LRESULT SWELL_SendMouseMessage(SWELL_hwndChild *slf, int msg, NSEvent *event); +static LRESULT SWELL_SendMouseMessageImpl(SWELL_hwndChild *slf, int msg, NSEvent *theEvent) +{ + + NSView *capv=(NSView *)GetCapture(); + if (capv && capv != slf && [capv window] == [slf window] && [capv isKindOfClass:[SWELL_hwndChild class]]) + return SWELL_SendMouseMessage((SWELL_hwndChild*)capv,msg,theEvent); + + if (slf->m_hashaddestroy||!slf->m_wndproc) return -1; + + NSPoint swellProcessMouseEvent(int msg, NSView *view, NSEvent *event); + + NSPoint pt = swellProcessMouseEvent(msg,slf,theEvent); + unsigned short xpos=(int)floor(pt.x + 0.5); + unsigned short ypos=(int)floor(pt.y + 0.5); + + LRESULT htc=HTCLIENT; + if (msg != WM_MOUSEWHEEL && msg != WM_MOUSEHWHEEL && !capv) + { + DWORD p=GetMessagePos(); + htc=slf->m_wndproc((HWND)slf,WM_NCHITTEST,0,p); + if (slf->m_hashaddestroy||!slf->m_wndproc) return -1; // if somehow WM_NCHITTEST destroyed us, bail + + if (htc!=HTCLIENT) + { + if (msg==WM_MOUSEMOVE) return slf->m_wndproc((HWND)slf,WM_NCMOUSEMOVE,htc,p); + if (msg==WM_LBUTTONUP) return slf->m_wndproc((HWND)slf,WM_NCLBUTTONUP,htc,p); + if (msg==WM_LBUTTONDOWN) return slf->m_wndproc((HWND)slf,WM_NCLBUTTONDOWN,htc,p); + if (msg==WM_LBUTTONDBLCLK) return slf->m_wndproc((HWND)slf,WM_NCLBUTTONDBLCLK,htc,p); + if (msg==WM_RBUTTONUP) return slf->m_wndproc((HWND)slf,WM_NCRBUTTONUP,htc,p); + if (msg==WM_RBUTTONDOWN) return slf->m_wndproc((HWND)slf,WM_NCRBUTTONDOWN,htc,p); + if (msg==WM_RBUTTONDBLCLK) return slf->m_wndproc((HWND)slf,WM_NCRBUTTONDBLCLK,htc,p); + if (msg==WM_MBUTTONUP) return slf->m_wndproc((HWND)slf,WM_NCMBUTTONUP,htc,p); + if (msg==WM_MBUTTONDOWN) return slf->m_wndproc((HWND)slf,WM_NCMBUTTONDOWN,htc,p); + if (msg==WM_MBUTTONDBLCLK) return slf->m_wndproc((HWND)slf,WM_NCMBUTTONDBLCLK,htc,p); + } + } + + int l=0; + if (msg == WM_MOUSEWHEEL || msg == WM_MOUSEHWHEEL) + { + float dw = (msg == WM_MOUSEWHEEL ? [theEvent deltaY] : [theEvent deltaX]); + //if (!dy) dy=[theEvent deltaX]; // shift+mousewheel sends deltaX instead of deltaY + l = (int)(dw*60.0); + l <<= 16; + + // put todo: modifiers into low word of l? + + POINT p; + GetCursorPos(&p); + return slf->m_wndproc((HWND)slf,msg,l,(p.x&0xffff) + (p.y<<16)); + } + + LRESULT ret=slf->m_wndproc((HWND)slf,msg,l,(xpos&0xffff) + (ypos<<16)); + + if (msg==WM_LBUTTONUP || msg==WM_RBUTTONUP || msg==WM_MOUSEMOVE || msg==WM_MBUTTONUP) { + if (!GetCapture() && (slf->m_hashaddestroy || !slf->m_wndproc || !slf->m_wndproc((HWND)slf,WM_SETCURSOR,(WPARAM)slf,htc | (msg<<16)))) { + NSCursor *arr= [NSCursor arrowCursor]; + if (GetCursor() != (HCURSOR)arr) SetCursor((HCURSOR)arr); + } + } + return ret; +} +static LRESULT SWELL_SendMouseMessage(SWELL_hwndChild *slf, int msg, NSEvent *event) +{ + if (!slf) return 0; + [slf retain]; + LRESULT res=SWELL_SendMouseMessageImpl(slf,msg,event); + [slf release]; + return res; +} + +void SWELL_DoDialogColorUpdates(HWND hwnd, DLGPROC d, bool isUpdate) +{ + extern HDC__ *SWELL_GDP_CTX_NEW(); + NSArray *children = [(NSView *)hwnd subviews]; + + if (!d || !children || ![children count]) return; + + int had_flags=0; + + NSColor *staticFg=NULL; // had_flags&1, WM_CTLCOLORSTATIC + NSColor *editFg=NULL, *editBg=NULL; // had_flags&2, WM_CTLCOLOREDIT + NSColor *buttonFg=NULL; // had_flags&4, WM_CTLCOLORBTN + + int x; + for (x = 0; x < [children count]; x ++) + { + NSView *ch = [children objectAtIndex:x]; + if (ch) + { + if ([ch isKindOfClass:[NSButton class]] && [(NSButton *)ch image]) + { + if (!buttonFg && !(had_flags&4)) + { + had_flags|=4; + HDC__ *c = SWELL_GDP_CTX_NEW(); + if (c) + { + d(hwnd,WM_CTLCOLORBTN,(WPARAM)c,(LPARAM)ch); + if (c->curtextcol) buttonFg=NSColorFromCol(c->cur_text_color_int); + else if (isUpdate) buttonFg = [NSColor textColor]; // todo some other col? + if (buttonFg) [buttonFg retain]; + + SWELL_DeleteGfxContext((HDC)c); + } + } + if (buttonFg) + { + NSMutableAttributedString *attrTitle = [[NSMutableAttributedString alloc] initWithAttributedString:[(NSButton *)ch attributedTitle]]; + NSRange range = NSMakeRange(0, [attrTitle length]); + [attrTitle addAttribute:NSForegroundColorAttributeName value:buttonFg range:range]; + [attrTitle fixAttributesInRange:range]; + [(NSButton *)ch setAttributedTitle:attrTitle]; + [attrTitle release]; + } + } + else if ([ch isKindOfClass:[NSTextField class]] || [ch isKindOfClass:[NSBox class]]) + { + bool isbox = ([ch isKindOfClass:[NSBox class]]); + if (!isbox && [(NSTextField *)ch isEditable]) + { +#if 0 // no color overrides for editable text fields + if (!editFg && !editBg && !(had_flags&2)) + { + had_flags|=2; + HDC__ *c = SWELL_GDP_CTX_NEW(); + if (c) + { + d(hwnd,WM_CTLCOLOREDIT,(WPARAM)c,(LPARAM)ch); + if (c->curtextcol) + { + editFg=NSColorFromCol(c->cur_text_color_int); + editBg=[NSColor colorWithCalibratedRed:GetRValue(c->curbkcol)/255.0f green:GetGValue(c->curbkcol)/255.0f blue:GetBValue(c->curbkcol)/255.0f alpha:1.0f]; + } + else if (isUpdate) + { + editFg = [NSColor textColor]; + editBg = [NSColor textBackgroundColor]; + } + if (editFg) [editFg retain]; + if (editBg) [editBg retain]; + SWELL_DeleteGfxContext((HDC)c); + } + } + if (editFg) [(NSTextField*)ch setTextColor:editFg]; + if (editBg) [(NSTextField*)ch setBackgroundColor:editBg]; +#endif + } + else // isbox or noneditable + { + if (!staticFg && !(had_flags&1)) + { + had_flags|=1; + HDC__ *c = SWELL_GDP_CTX_NEW(); + if (c) + { + d(hwnd,WM_CTLCOLORSTATIC,(WPARAM)c,(LPARAM)ch); + if (c->curtextcol) staticFg=NSColorFromCol(c->cur_text_color_int); + else if (isUpdate) + { + staticFg = [NSColor textColor]; + } + if (staticFg) [staticFg retain]; + SWELL_DeleteGfxContext((HDC)c); + } + } + if (staticFg) + { + if (isbox) + { + [[(NSBox*)ch titleCell] setTextColor:staticFg]; + //[(NSBox*)ch setBorderColor:staticFg]; // see comment at SWELL_MakeGroupBox + } + else + { + if ([ch isKindOfClass:[SWELL_TextField class]]) + ((SWELL_TextField *)ch)->m_ctlcolor_set = true; + [(NSTextField*)ch setTextColor:staticFg]; + } + } + } // noneditable + } //nstextfield + } // child + } // children + if (buttonFg) [buttonFg release]; + if (staticFg) [staticFg release]; + if (editFg) [editFg release]; + if (editBg) [editBg release]; +} + +static LRESULT SwellDialogDefaultWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + DLGPROC d=(DLGPROC)GetWindowLong(hwnd,DWL_DLGPROC); + if (d) + { + if (uMsg == WM_PAINT) + { + if (!d(hwnd,WM_ERASEBKGND,0,0)) + { + const bool nommc=useNoMiddleManCocoa(); + NSView *cv = [[(NSView *)hwnd window] contentView]; + const bool hwndIsOpaque = [(NSView *)hwnd isOpaque]; + const bool isop = hwndIsOpaque || (nommc && [cv isOpaque]); + const bool hwndIsCV = cv == (NSView *)hwnd; + if (isop || hwndIsCV) + { + PAINTSTRUCT ps; + if (!nommc && !hwndIsOpaque && !hwndIsCV && !(((SWELL_hwndChild*)hwnd)->m_isdirty&1)) + { + // if not no-middleman, not opaque, not content view, and not directly invalidated + // then don't bother background drawing + } + else if (BeginPaint(hwnd,&ps)) + { + RECT r=ps.rcPaint; + if (r.right > r.left && r.bottom > r.top) + { + HBRUSH hbrush = (HBRUSH) d(hwnd,WM_CTLCOLORDLG,(WPARAM)ps.hdc,(LPARAM)hwnd); + if (hbrush && hbrush != (HBRUSH)1) + { + // char bf[512]; + // GetWindowText(hwnd,bf,sizeof(bf)); +// static int a; + // printf("%d filled custom bg, (%p %s) %d %d %d %d\n",a++,hwnd,bf,r.left,r.top,r.right-r.left,r.bottom-r.top); + FillRect(ps.hdc,&r,hbrush); + } + else if (isop) // no need to do this fill if it is a content view and is not opaque + { + // char bf[512]; + // GetWindowText(hwnd,bf,sizeof(bf)); + // static int a; + // printf("%d: filled stock bg, (%p %s) %d %d %d %d\n",a++,hwnd,bf,r.left,r.top,r.right-r.left,r.bottom-r.top); + SWELL_FillDialogBackground(ps.hdc,&r,3); + } + } + EndPaint(hwnd,&ps); + } + } + } + } + + LRESULT r=(LRESULT) d(hwnd,uMsg,wParam,lParam); + + if (r) return r; + } + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + +static SWELL_DialogResourceIndex *resById(SWELL_DialogResourceIndex *reshead, const char *resid) +{ + SWELL_DialogResourceIndex *p=reshead; + while (p) + { + if (p->resid == resid) return p; + p=p->_next; + } + return 0; +} + +static void DoPaintStuff(WNDPROC wndproc, HWND hwnd, HDC hdc, NSRect *modrect, bool isMetal) +{ +#ifdef _DEBUG + extern int g_swell_in_paint; + g_swell_in_paint++; +#endif + RECT r; + GetWindowRect(hwnd,&r); + if (r.top>r.bottom) { int tmp=r.top; r.top=r.bottom; r.bottom=tmp; } + NCCALCSIZE_PARAMS p={{r,},}; + wndproc(hwnd,WM_NCCALCSIZE,FALSE,(LPARAM)&p); + RECT r2=r; + r=p.rgrc[0]; + + if (!isMetal) wndproc(hwnd,WM_NCPAINT,(WPARAM)1,0); + modrect->origin.x += r.left-r2.left; + modrect->origin.y += r.top-r2.top; + + if (modrect->size.width >= 1 && modrect->size.height >= 1) + { + int a=0; + if (!isMetal && memcmp(&r,&r2,sizeof(r))) + { + RECT tr; + SWELL_PushClipRegion(hdc); + GetClientRect(hwnd,&tr); + SWELL_SetClipRegion(hdc,&tr); + a++; + } + wndproc(hwnd,WM_PAINT,(WPARAM)hdc,0); + if (a) SWELL_PopClipRegion(hdc); + } + if (isMetal) wndproc(hwnd,WM_NCPAINT,(WPARAM)1,0); +#ifdef _DEBUG + g_swell_in_paint--; +#endif +} + + +static int DelegateMouseMove(NSView *view, NSEvent *theEvent) +{ + static int __nofwd; + if (__nofwd) return 0; + + NSWindow *w=[theEvent window]; + if (!w) return 0; + + NSPoint p=[theEvent locationInWindow]; + NSPoint screen_p=[w convertBaseToScreen:p]; + + NSWindow *bestwnd = w; + HWND cap = GetCapture(); + if (!cap) + { + // if not captured, find the window that should receive this event + + NSArray *windows=[NSApp orderedWindows]; + const NSInteger cnt=windows ? [windows count] : 0; + NSWindow *kw = [NSApp keyWindow]; + if (kw && windows && [windows containsObject:kw]) kw=NULL; + // make sure the keywindow, if any, is checked, but not twice + + for (NSInteger x = kw ? -1 : 0; x < cnt; x ++) + { + NSWindow *wnd = x < 0 ? kw : [windows objectAtIndex:x]; + if (wnd && [wnd isVisible]) + { + NSRect fr=[wnd frame]; + if (screen_p.x >= fr.origin.x && screen_p.x < fr.origin.x + fr.size.width && + screen_p.y >= fr.origin.y && screen_p.y < fr.origin.y + fr.size.height) + { + bestwnd=wnd; + break; + } + } + } + } + + if (bestwnd == w || [NSApp modalWindow]) + { + NSView *v=[[w contentView] hitTest:p]; + if (!v || v == view) return 0; // default processing if in view, or if in nonclient area + + __nofwd=1; + [v mouseMoved:theEvent]; + __nofwd=0; + return 1; + } + + // bestwnd != w + NSView *cv = [bestwnd contentView]; + if (cv && [cv isKindOfClass:[SWELL_hwndChild class]]) + { + p = [bestwnd convertScreenToBase:screen_p]; + NSView *v=[cv hitTest:p]; + if (v) + { + theEvent = [NSEvent mouseEventWithType:[theEvent type] + location:p + modifierFlags:[theEvent modifierFlags] + timestamp:[theEvent timestamp] + windowNumber:[bestwnd windowNumber] + context:[bestwnd graphicsContext] + eventNumber:[theEvent eventNumber] + clickCount:[theEvent clickCount] + pressure:[theEvent pressure]]; + __nofwd=1; + [v mouseMoved:theEvent]; + __nofwd=0; + return 1; + } + } + if (!cap) + { + // set default cursor, and eat message + NSCursor *arr= [NSCursor arrowCursor]; + if (GetCursor() != (HCURSOR)arr) SetCursor((HCURSOR)arr); + return 1; + } + return 0; +} + + + +static void SendTreeViewExpandNotification(SWELL_hwndChild *par, NSNotification *notification, int action) +{ + NSOutlineView *sender=[notification object]; + NMTREEVIEW nmhdr={{(HWND)sender,(UINT_PTR)[sender tag],TVN_ITEMEXPANDING},0,}; + SWELL_DataHold *t=[[notification userInfo] valueForKey:@"NSObject"]; + HTREEITEM hi = t ? (HTREEITEM)[t getValue] : NULL; + if (hi) + { + nmhdr.action=action; + nmhdr.itemNew.hItem=hi; + nmhdr.itemNew.lParam=hi->m_param; + } + if (par->m_wndproc && !par->m_hashaddestroy) + { + par->m_wndproc((HWND)par, WM_NOTIFY, (int)[sender tag], (LPARAM)&nmhdr); + } +} + + +@implementation SWELL_hwndChild : NSView + +- (NSMenu *)textView:(NSTextView *)view + menu:(NSMenu *)menu + forEvent:(NSEvent *)event + atIndex:(NSUInteger)charIndex +{ + return [view respondsToSelector:@selector(swellWantsContextMenu)] && ![(SWELL_TextView *)view swellWantsContextMenu] ? nil : menu; +} + +-(void)viewDidHide +{ + SendMessage((HWND)self, WM_SHOWWINDOW, FALSE, 0); +#ifndef SWELL_NO_METAL + if (m_use_metal>0) swell_removeMetalDirty(self); +#endif +} +-(void) viewDidUnhide +{ + SendMessage((HWND)self, WM_SHOWWINDOW, TRUE, 0); +#ifndef SWELL_NO_METAL + if (m_use_metal>0) + { + [self swellDrawMetal:NULL]; + swell_removeMetalDirty(self); + } +#endif +} + +- (void)SWELL_Timer:(id)sender +{ + extern HWND g_swell_only_timerhwnd; + if (g_swell_only_timerhwnd && (HWND)self != g_swell_only_timerhwnd) return; + + id uinfo=[sender userInfo]; + if ([uinfo respondsToSelector:@selector(getValue)]) + { + WPARAM idx=(WPARAM)[(SWELL_DataHold*)uinfo getValue]; + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_TIMER,idx,0); + } +} + +- (int)swellCapChangeNotify { return YES; } + +- (LRESULT)onSwellMessage:(UINT)msg p1:(WPARAM)wParam p2:(LPARAM)lParam +{ + if (m_hashaddestroy) + { + if (m_hashaddestroy==2 || msg == WM_DESTROY || msg == WM_CAPTURECHANGED) return 0; + } + + if (msg==WM_DESTROY) // only ever called once per window + { + m_hashaddestroy=1; + if (GetCapture()==(HWND)self) ReleaseCapture(); + SWELL_MessageQueue_Clear((HWND)self); + + LRESULT ret=m_wndproc ? m_wndproc((HWND)self,msg,wParam,lParam) : 0; + + if ([[self window] contentView] == self && [[self window] respondsToSelector:@selector(swellDestroyAllOwnedWindows)]) + [(SWELL_ModelessWindow*)[self window] swellDestroyAllOwnedWindows]; + + if (GetCapture()==(HWND)self) ReleaseCapture(); + SWELL_MessageQueue_Clear((HWND)self); + + if (m_menu) + { + if ((HMENU)[NSApp mainMenu] == m_menu && !g_swell_terminating) [NSApp setMainMenu:nil]; + SWELL_SetMenuDestination(m_menu,NULL); + [(NSMenu *)m_menu release]; + m_menu=0; + } + NSView *v=self; + NSArray *ar; + if (v && [v isKindOfClass:[NSView class]] && (ar=[v subviews]) && [ar count]>0) + { + int x; + for (x = 0; x < [ar count]; x ++) + { + NSView *sv=[ar objectAtIndex:x]; + sendSwellMessage(sv,WM_DESTROY,0,0); + } + } + KillTimer((HWND)self,~(UINT_PTR)0); + m_hashaddestroy=2; + + return ret; + } + + return m_wndproc ? m_wndproc((HWND)self,msg,wParam,lParam) : 0; +} + +- (void) setEnabled:(BOOL)en +{ + m_enabled=en?1:0; +} + +- (void) setEnabledSwellNoFocus +{ + m_enabled = -1; +} + +- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + if ([aTableView isKindOfClass:[SWELL_ListView class]]) + { + SWELL_ListView *f = (SWELL_ListView *)aTableView; + + const unsigned short tag = (unsigned short)[aTableView tag]; + NMLVCUSTOMDRAW nmlv={ + { + {(HWND)aTableView,(UINT_PTR)tag, NM_CUSTOMDRAW}, + CDDS_ITEMPREPAINT,NULL, {0,0,0,0}, (DWORD)rowIndex, 0,0 + }, + (COLORREF)-1, + (COLORREF)-1, + f->m_cols ? f->m_cols->Find(aTableColumn) : 0 + }; + if (m_wndproc) m_wndproc((HWND)self,WM_NOTIFY,tag,(LPARAM)&nmlv); + // todo clrTextBk too + if (nmlv.clrText != (COLORREF)-1) + { + if ([aCell respondsToSelector:@selector(setTextColor:)]) + { + [aCell setTextColor:NSColorFromCol(nmlv.clrText)]; + } + return; + } + + if (f->m_selColors&&[aTableView isRowSelected:rowIndex]) + { + const NSInteger cnt = [f->m_selColors count]; + const NSInteger offs = GetFocus() == (HWND)aTableView ? 0 : 2; + if (cnt>=offs+2) + { + if ([aCell respondsToSelector:@selector(setTextColor:)]) [aCell setTextColor:[f->m_selColors objectAtIndex:(offs+1)]]; + return; + } + } + + if (f->m_fgColor && [aCell respondsToSelector:@selector(setTextColor:)]) [aCell setTextColor:f->m_fgColor]; + } +} +- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + if ([outlineView isKindOfClass:[SWELL_TreeView class]]) + { + SWELL_TreeView *f = (SWELL_TreeView *)outlineView; + if (f->m_selColors) + { + HTREEITEM sel = TreeView_GetSelection((HWND)outlineView); + if (sel && sel->m_dh == item) + { + const NSInteger cnt = [f->m_selColors count]; + const NSInteger offs = GetFocus() == (HWND)outlineView ? 0 : 2; + if (cnt>=offs+2) + { + if ([cell respondsToSelector:@selector(setTextColor:)]) [cell setTextColor:[f->m_selColors objectAtIndex:(offs+1)]]; + return; + } + } + } + if (f->m_fgColor && [cell respondsToSelector:@selector(setTextColor:)]) [cell setTextColor:f->m_fgColor]; + } +} + + +//- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item + +- (void)comboBoxWillPopUp:(NSNotification*)notification +{ + id sender=[notification object]; + int code=CBN_DROPDOWN; + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_COMMAND,([(NSControl*)sender tag])|(code<<16),(LPARAM)sender); +} + +- (void)comboBoxSelectionDidChange:(NSNotification *)notification +{ + id sender=[notification object]; + int code=CBN_SELCHANGE; + if (m_wndproc&&!m_hashaddestroy) + { + if ([sender isKindOfClass:[SWELL_ComboBox class]]) + { + SWELL_ComboBox *p = (SWELL_ComboBox *)sender; + const int sel = [p indexOfSelectedItem]; + if (sel == p->m_ignore_selchg) return; + p->m_ignore_selchg = sel; + } + m_wndproc((HWND)self,WM_COMMAND,([(NSControl*)sender tag])|(code<<16),(LPARAM)sender); + } +} + +- (void)comboBoxWillDismiss:(NSNotification *)notification +{ + id sender=[notification object]; + int code=CBN_CLOSEUP; + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_COMMAND,([(NSControl*)sender tag])|(code<<16),(LPARAM)sender); +} + +- (void)textDidEndEditing:(NSNotification *)aNotification +{ + id sender=[aNotification object]; + int code=EN_CHANGE; + if ([sender isKindOfClass:[NSComboBox class]]) return; + if (m_wndproc&&!m_hashaddestroy) + { + m_wndproc((HWND)self,WM_COMMAND,([(NSControl*)sender tag])|(code<<16),(LPARAM)sender); + code=EN_KILLFOCUS; + m_wndproc((HWND)self,WM_COMMAND,([(NSControl*)sender tag])|(code<<16),(LPARAM)sender); + } +} + +- (void)controlTextDidChange:(NSNotification *)aNotification +{ + id sender=[aNotification object]; + int code=EN_CHANGE; + if ([sender isKindOfClass:[NSComboBox class]]) code=CBN_EDITCHANGE; + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_COMMAND,([(NSControl*)sender tag])|(code<<16),(LPARAM)sender); +} + +- (void)controlTextDidEndEditing:(NSNotification *)aNotification +{ + id sender=[aNotification object]; + int code=EN_KILLFOCUS; + if (m_wndproc && !m_hashaddestroy) m_wndproc((HWND)self,WM_COMMAND,([(NSControl*)sender tag])|(code<<16),(LPARAM)sender); +} + +- (void)menuNeedsUpdate:(NSMenu *)menu +{ + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_INITMENUPOPUP,(WPARAM)menu,0); +} + +-(void) swellOnControlDoubleClick:(id)sender +{ + if (!m_wndproc||m_hashaddestroy) return; + + if ([sender isKindOfClass:[NSTableView class]] && + [sender respondsToSelector:@selector(getSwellNotificationMode)]) + { + if ([(SWELL_ListView*)sender getSwellNotificationMode]) + m_wndproc((HWND)self,WM_COMMAND,(LBN_DBLCLK<<16)|[(NSControl*)sender tag],(LPARAM)sender); + else + { + SWELL_ListView *lv = (SWELL_ListView*)sender; + NMLISTVIEW nmlv={{(HWND)sender,(UINT_PTR)[(NSControl*)sender tag], NM_DBLCLK}, (int) [lv clickedRow], (int) [sender clickedColumn], }; + + if (nmlv.iItem == -1) + { + // ignore doubleclicks in column headers + NSTableHeaderView *v = [sender headerView]; + if (v) + { + NSPoint pt=[NSEvent mouseLocation]; + NSWindow *w = [self window]; + pt = [w convertScreenToBase:pt]; + pt = [v convertPoint:pt fromView:nil]; + if (NSPointInRect(pt,[v bounds])) return; + } + } + + SWELL_ListView_Row *row=lv->m_items->Get(nmlv.iItem); + if (row) + nmlv.lParam = row->m_param; + m_wndproc((HWND)self,WM_NOTIFY,[(NSControl*)sender tag],(LPARAM)&nmlv); + } + } + else + { + NMCLICK nm={{(HWND)sender,(UINT_PTR)[(NSControl*)sender tag],NM_DBLCLK}, }; + m_wndproc((HWND)self,WM_NOTIFY,[(NSControl*)sender tag],(LPARAM)&nm); + } +} + +- (void)outlineViewItemWillExpand:(NSNotification*)notification +{ + SendTreeViewExpandNotification(self, notification, TVE_EXPAND); +} +- (void)outlineViewItemWillCollapse:(NSNotification*)notification +{ + SendTreeViewExpandNotification(self, notification, TVE_COLLAPSE); +} + +- (void)outlineViewSelectionDidChange:(NSNotification *)notification +{ + NSOutlineView *sender=[notification object]; + NMTREEVIEW nmhdr={{(HWND)sender,(UINT_PTR)[sender tag],TVN_SELCHANGED},0,}; // todo: better treeview notifications + HTREEITEM item = TreeView_GetSelection((HWND)sender); + nmhdr.itemNew.hItem = item; + nmhdr.itemNew.lParam = item ? item->m_param : 0; + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_NOTIFY,(int)[sender tag],(LPARAM)&nmhdr); +} +- (void)tableViewSelectionDidChange:(NSNotification *)aNotification +{ + extern int swell_ignore_listview_changes; + if (!swell_ignore_listview_changes) + { + swell_ignore_listview_changes++; + NSTableView *sender=[aNotification object]; + if ([sender respondsToSelector:@selector(getSwellNotificationMode)] && [(SWELL_ListView*)sender getSwellNotificationMode]) + { + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_COMMAND,(int)[sender tag] | (LBN_SELCHANGE<<16),(LPARAM)sender); + } + else + { + NMLISTVIEW nmhdr={{(HWND)sender,(UINT_PTR)[sender tag],LVN_ITEMCHANGED},(int)[sender selectedRow],0}; + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_NOTIFY,(int)[sender tag],(LPARAM)&nmhdr); + } + swell_ignore_listview_changes--; + } +} + +- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn +{ + if ([tableView isKindOfClass:[SWELL_ListView class]] && + ((SWELL_ListView *)tableView)->m_cols && + !((SWELL_ListView *)tableView)->m_lbMode && + !(((SWELL_ListView *)tableView)->style & LVS_NOSORTHEADER) + ) + { + int col=((SWELL_ListView *)tableView)->m_cols->Find(tableColumn); + + NMLISTVIEW hdr={{(HWND)tableView,(UINT_PTR)[tableView tag],LVN_COLUMNCLICK},-1,col}; + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_NOTIFY,[tableView tag], (LPARAM) &hdr); + } +} + +#ifdef MAC_OS_X_VERSION_10_8 +// for radio button with the OSX 10.8+ SDK, see comment in SWELL_MakeControl +-(void) onSwellCommand0:(id)sender { [self onSwellCommand:sender]; } +-(void) onSwellCommand2:(id)sender { [self onSwellCommand:sender]; } +-(void) onSwellCommand3:(id)sender { [self onSwellCommand:sender]; } +-(void) onSwellCommand4:(id)sender { [self onSwellCommand:sender]; } +-(void) onSwellCommand5:(id)sender { [self onSwellCommand:sender]; } +-(void) onSwellCommand6:(id)sender { [self onSwellCommand:sender]; } +-(void) onSwellCommand7:(id)sender { [self onSwellCommand:sender]; } +#endif +-(void) onSwellCommand:(id)sender +{ + if (!m_wndproc || m_hashaddestroy) return; + + if ([sender isKindOfClass:[NSSlider class]]) + { + m_wndproc((HWND)self,WM_HSCROLL,0,(LPARAM)sender); + // WM_HSCROLL, WM_VSCROLL + } + else if ([sender isKindOfClass:[NSTableView class]]) + { + #if 0 + if ([sender isKindOfClass:[NSOutlineView class]]) + { +// NMTREEVIEW nmhdr={{(HWND)sender,(int)[sender tag],TVN_SELCHANGED},0,}; // todo: better treeview notifications + // m_wndproc((HWND)self,WM_NOTIFY,(int)[sender tag],(LPARAM)&nmhdr); + } + else + { + + if ([sender respondsToSelector:@selector(getSwellNotificationMode)] && [(SWELL_ListView*)sender getSwellNotificationMode]) + { + m_wndproc((HWND)self,WM_COMMAND,(int)[sender tag] | (LBN_SELCHANGE<<16),(LPARAM)sender); + } + else + { + NMLISTVIEW nmhdr={{(HWND)sender,(int)[sender tag],LVN_ITEMCHANGED},(int)[sender clickedRow],0}; + m_wndproc((HWND)self,WM_NOTIFY,(int)[sender tag],(LPARAM)&nmhdr); + } + } + #endif + } + else + { + int cw=0; + if ([sender isKindOfClass:[NSComboBox class]]) return; // combo boxes will use delegate messages + else if ([sender isKindOfClass:[NSPopUpButton class]]) + { + cw=CBN_SELCHANGE; + } + else if ([sender isKindOfClass:[SWELL_Button class]]) + { + int rf; + if ((rf=(int)[(SWELL_Button*)sender swellGetRadioFlags])) + { + NSView *par=(NSView *)GetParent((HWND)sender); + if (par && [par isKindOfClass:[NSWindow class]]) par=[(NSWindow *)par contentView]; + if (par && [par isKindOfClass:[NSView class]]) + { + NSArray *ar=[par subviews]; + if (ar) + { + NSInteger x=[ar indexOfObject:sender]; + if (x != NSNotFound) + { + const NSInteger n = [ar count]; + NSInteger a=x; + if (!(rf&2)) while (--a >= 0) + { + NSView *item=[ar objectAtIndex:a]; + if (!item || ![item isKindOfClass:[SWELL_Button class]]) break; // we may want to allow other controls in there, but for now if it's non-button we're done + int bla=(int)[(SWELL_Button*)item swellGetRadioFlags]; + if (bla&1) if ([(NSButton *)item state]!=NSOffState) [(NSButton *)item setState:NSOffState]; + if (bla&2) break; + } + a=x; + while (++a < n) + { + NSView *item=[ar objectAtIndex:a]; + if (!item || ![item isKindOfClass:[SWELL_Button class]]) break; // we may want to allow other controls in there, but for now if it's non-button we're done + int bla=(int)[(SWELL_Button*)item swellGetRadioFlags]; + if (bla&2) break; + if (bla&1) if ([(NSButton *)item state]!=NSOffState) [(NSButton *)item setState:NSOffState]; + } + } + } + } + } + } + else if ([sender isKindOfClass:[NSControl class]]) + { + NSEvent *evt=[NSApp currentEvent]; + NSUInteger ty=evt?[evt type]:0; + if (evt && (ty==NSLeftMouseDown || ty==NSLeftMouseUp) && [evt clickCount] > 1) cw=STN_DBLCLK; + } + else if ([sender isKindOfClass:[NSMenuItem class]]) + { +// [[sender menu] update]; + // wish we could force the top level menu to update here, meh + } + m_wndproc((HWND)self,WM_COMMAND,[sender tag]|(cw<<16),(LPARAM)sender); + } + +} +-(void) dealloc +{ + int x; + for (x=0;x0) swell_removeMetalDirty(self); +#endif + SWELL_MessageQueue_Clear((HWND)self); + + [super dealloc]; +} + +-(NSInteger)tag { return m_tag; } +-(void)setTag:(NSInteger)t { m_tag=t; } +-(LONG_PTR)getSwellUserData { return m_userdata; } +-(void)setSwellUserData:(LONG_PTR)val { m_userdata=val; } +-(LPARAM)getSwellExtraData:(int)idx { idx/=sizeof(INT_PTR); if (idx>=0&&idx=0&&idxhdc = m_paintctx_hdc; + ps->fErase=false; + NSRECT_TO_RECT(&ps->rcPaint,m_paintctx_rect); + + // should NC_CALCSIZE to convert, but this will be good enough to fix this small scrollbar overdraw bug + RECT r; + GetClientRect((HWND)self,&r); + if (ps->rcPaint.right > r.right) ps->rcPaint.right = r.right; + if (ps->rcPaint.bottom > r.bottom) ps->rcPaint.bottom = r.bottom; + + } +} + +-(bool)swellCanPostMessage { return !m_hashaddestroy; } +-(int)swellEnumProps:(PROPENUMPROCEX)proc lp:(LPARAM)lParam +{ + WindowPropRec *p=m_props; + if (!p) return -1; + while (p) + { + WindowPropRec *ps=p; + p=p->_next; + if (!proc((HWND)self, ps->name, ps->data, lParam)) return 0; + } + return 1; +} + +-(void *)swellGetProp:(const char *)name wantRemove:(BOOL)rem +{ + WindowPropRec *p=m_props, *lp=NULL; + while (p) + { + if (p->name < (void *)65536) + { + if (name==p->name) break; + } + else if (name >= (void *)65536) + { + if (!strcmp(name,p->name)) break; + } + lp=p; p=p->_next; + } + if (!p) return NULL; + void *ret=p->data; + if (rem) + { + if (lp) lp->_next=p->_next; else m_props=p->_next; + free(p); + } + return ret; +} + +-(int)swellSetProp:(const char *)name value:(void *)val +{ + WindowPropRec *p=m_props; + while (p) + { + if (p->name < (void *)65536) + { + if (name==p->name) { p->data=val; return TRUE; }; + } + else if (name >= (void *)65536) + { + if (!strcmp(name,p->name)) { p->data=val; return TRUE; }; + } + p=p->_next; + } + p=(WindowPropRec*)malloc(sizeof(WindowPropRec)); + p->name = (name<(void*)65536) ? (char *)name : strdup(name); + p->data = val; p->_next=m_props; m_props=p; + return TRUE; +} + +-(NSOpenGLContext *)swellGetGLContext +{ + return m_glctx; +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent +{ + if (m_enabled) + { + SendMessage((HWND)self, WM_MOUSEACTIVATE, 0, 0); + NSView* par=[self superview]; + if (par) SendMessage((HWND)par, WM_MOUSEACTIVATE, 0, 0); + return YES; + } + return NO; +} + +-(HMENU)swellGetMenu { return m_menu; } +-(BOOL)swellHasBeenDestroyed { return !!m_hashaddestroy; } +-(void)swellSetMenu:(HMENU)menu { + if (m_menu) SWELL_SetMenuDestination(m_menu,NULL); // don't free m_menu, but at least make it not point to us anymore + m_menu=menu; + if (m_menu) SWELL_SetMenuDestination(m_menu,(HWND)self); +} + +#ifndef SWELL_NO_METAL + +-(CALayer *)makeBackingLayer +{ + if (m_use_metal>0 && __class_CAMetalLayer) + { + CALayer *layer = [__class_CAMetalLayer layer]; + if (layer) return layer; + } + return [super makeBackingLayer]; +} +#endif + +- (id)initChild:(SWELL_DialogResourceIndex *)resstate Parent:(NSView *)parent dlgProc:(DLGPROC)dlgproc Param:(LPARAM)par +{ + NSRect contentRect=NSMakeRect(0,0,resstate ? resstate->width : 300,resstate ? resstate->height : 200); + if (!(self = [super initWithFrame:contentRect])) return self; + + m_classname=NULL; + memset(m_access_cacheptrs,0,sizeof(m_access_cacheptrs)); + m_allow_nomiddleman=1; + m_isdirty=3; + m_glctx=NULL; + m_enabled=TRUE; + m_lastTopLevelOwner=NULL; + m_dlgproc=NULL; + m_wndproc=NULL; + m_userdata=0; + memset(&m_extradata,0,sizeof(m_extradata)); + m_tag=0; + m_isfakerightmouse=0; + m_hashaddestroy=false; + m_menu=0; + m_flip=0; + m_supports_ddrop=0; + m_paintctx_used=0; + m_paintctx_hdc=0; + m_props=0; + + m_titlestr[0]=0; + +#ifndef SWELL_NO_METAL + m_use_metal=0; + m_metal_dc_dirty=0; + m_metal_retina=false; + m_metal_device=NULL; + m_metal_device_lastchkt=0; + m_metal_texture=NULL; + m_metal_drawable=NULL; + m_metal_pipelineState=NULL; + m_metal_commandQueue=NULL; + m_metal_in_needref_list=false; + m_metal_gravity=0; + memset(&m_metal_lastframe,0,sizeof(m_metal_lastframe)); + memset(&m_metal_in_needref_rect,0,sizeof(m_metal_in_needref_rect)); +#endif + + m_wndproc=SwellDialogDefaultWindowProc; + + m_isopaque = !resstate || (resstate->windowTypeFlags&SWELL_DLG_WS_OPAQUE); + m_flip = !resstate || (resstate->windowTypeFlags&SWELL_DLG_WS_FLIPPED); + m_supports_ddrop = resstate && (resstate->windowTypeFlags&SWELL_DLG_WS_DROPTARGET); + + [self setHidden:YES]; + + if ([parent isKindOfClass:[NSWindow class]]) + { + if ([parent isKindOfClass:[NSPanel class]] && + [parent respondsToSelector:@selector(setAccessoryView:)]) + { + [(NSOpenPanel *)parent setAccessoryView:self]; + if ([parent isKindOfClass:[NSOpenPanel class]] || + [[parent className] isEqualToString:@"NSLocalOpenPanel"]) + { + if ([parent respondsToSelector:@selector(setAccessoryViewDisclosed:)]) + [(NSOpenPanel *)parent setAccessoryViewDisclosed:YES]; + } + [self setHidden:NO]; + } + else + { + [(NSWindow *)parent setContentView:self]; + } + } + else + { + [parent addSubview:self]; + } + if (resstate) resstate->createFunc((HWND)self,resstate->windowTypeFlags); + + if (resstate) m_dlgproc=dlgproc; + else if (dlgproc) m_wndproc=(WNDPROC)dlgproc; + + if (resstate && (resstate->windowTypeFlags&SWELL_DLG_WS_DROPTARGET)) + { + [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]]; + } + + if (!resstate) + m_wndproc((HWND)self,WM_CREATE,0,par); + + if (m_dlgproc) + { + HWND hFoc=0; + NSArray *ar=[self subviews]; + if (ar && [ar count]>0) + { + int x; + for (x = 0; x < [ar count] && !hFoc; x ++) + { + NSView *v=[ar objectAtIndex:x]; + if (v && [v isKindOfClass:[NSScrollView class]]) v=[(NSScrollView *)v documentView]; + if (v && [v acceptsFirstResponder]) hFoc=(HWND)v; + } + } + + INT_PTR a; + if ((a=m_dlgproc((HWND)self,WM_INITDIALOG,(WPARAM)hFoc,par))) + { + // set first responder to first item in window + if (a == 0xbeef) hFoc = (HWND)self; // ret 0xbeef overrides to make the window itself focused (argh, need a cleaner way) + if (hFoc) + { + id wnd = [self window]; + if (wnd && [wnd firstResponder] != (id)hFoc) [wnd makeFirstResponder:(id)hFoc]; + } + + + if (parent && [self window] == (NSWindow *)parent && [(id)parent isKindOfClass:[SWELL_ModelessWindow class]] && ![(NSWindow *)parent isVisible]) + { + // on win32, if you do CreateDialog(), WM_INITDIALOG(ret=1), then ShowWindow(SW_SHOWNA), you get the + // window brought to front. this simulates that, hackishly. + ((SWELL_ModelessWindow *)parent)->m_wantInitialKeyWindowOnShow = true; + } + } + else + { + // if top level dialog,always set default focus if it wasn't set + // if this causes problems, change NSWindow to be SWELL_ModalDialog, as that would + // only affect DialogBox() and not CreateDialog(), which might be preferable. + if (hFoc && parent && [self window] == (NSWindow *)parent && [(id)parent isKindOfClass:[NSWindow class]]) + { + id fr = [(NSWindow *)parent firstResponder]; + if (!fr || fr == self || fr == (id)parent) [(NSWindow *)parent makeFirstResponder:(id)hFoc]; + + } + } + + SWELL_DoDialogColorUpdates((HWND)self,m_dlgproc,false); + } + +// if (!wasHid) + // [self setHidden:NO]; + + return self; +} + +-(void)setOpaque:(bool)isOpaque +{ + m_isopaque = isOpaque; +} + +-(BOOL)isOpaque +{ + return m_isopaque; +} + +- (void)setFrame:(NSRect)frameRect +{ + [super setFrame:frameRect]; + if (m_wndproc&&!m_hashaddestroy) m_wndproc((HWND)self,WM_SIZE,0,0); + HWND par = GetParent((HWND)self); + if (par) InvalidateRect(par,NULL,FALSE); +} + +- (void)keyDown:(NSEvent *)theEvent +{ + int flag,code=SWELL_MacKeyToWindowsKey(theEvent,&flag); + if (!m_wndproc || m_hashaddestroy || m_wndproc((HWND)self,WM_KEYDOWN,code,flag)==69) + { + [super keyDown:theEvent]; + } +} + +- (void)keyUp:(NSEvent *)theEvent +{ + int flag,code=SWELL_MacKeyToWindowsKey(theEvent,&flag); + if (!m_wndproc || m_hashaddestroy || m_wndproc((HWND)self,WM_KEYUP,code,flag)==69) + { + [super keyUp:theEvent]; + } +} + +#if SWELL_CUT_OUT_COMPOSITING_MIDDLEMAN > 0 // not done yet + +- (void)didAddSubview:(NSView *)subview +{ + m_isdirty|=2; + NSView *view = [self superview]; + while (view) + { + if ([view isKindOfClass:[SWELL_hwndChild class]]) + { + if (((SWELL_hwndChild *)view)->m_isdirty&2) break; + ((SWELL_hwndChild *)view)->m_isdirty|=2; + } + view = [view superview]; + } +} +- (void)willRemoveSubview:(NSView *)subview +{ + m_isdirty|=3; + [self setNeedsDisplay:YES]; + NSView *view = [self superview]; + while (view) + { + if ([view isKindOfClass:[SWELL_hwndChild class]]) + { + if ((((SWELL_hwndChild *)view)->m_isdirty&3)==3) break; + ((SWELL_hwndChild *)view)->m_isdirty|=3; + } + [view setNeedsDisplay:YES]; + view = [view superview]; + } +} + +-(void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)vr rectIsVisibleRectForView:(NSView*)view topView:(NSView *)v2 +{ + + + + // once we figure out how to get other controls to notify their parents that the view is dirty, we can enable this for 10.4 + // 10.5+ has some nice property where it goes up the hierarchy + +// NSLog(@"r:%@ vr:%d v=%p tv=%p self=%p %p\n",NSStringFromRect(rect),vr,v,v2,self, [[self window] contentView]); + if (!useNoMiddleManCocoa() || ![self isOpaque] || [[self window] contentView] != self || [self isHiddenOrHasHiddenAncestor] || !m_allow_nomiddleman) + { + [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:vr rectIsVisibleRectForView:view topView:v2]; + return; + } + + if (!m_isdirty && ![self needsDisplay]) return; + + const NSRect *rlist=NULL; + NSInteger rlistcnt=0; + [self getRectsBeingDrawn:&rlist count:&rlistcnt]; + + + [self lockFocus]; + HDC hdc=SWELL_CreateGfxContext([NSGraphicsContext currentContext]); + + + const bool twoPassMode = false; // true makes it draw non-opaque items over all window backgrounds, but opaque children going last (so native controls over groups, etc) + // this is probably slower + + static WDL_PtrList ndlist; + int ndlist_oldsz=ndlist.GetSize(); + swellRenderOptimizely(twoPassMode?1:3,self,hdc,false,&ndlist,rlist,rlistcnt,0,0,true,self); + + while (ndlist.GetSize()>ndlist_oldsz+1) + { + NSView *v = (NSView *)ndlist.Get(ndlist.GetSize()-1); + ndlist.Delete(ndlist.GetSize()-1); + + int flag = (int)(INT_PTR) ndlist.Get(ndlist.GetSize()-1); + ndlist.Delete(ndlist.GetSize()-1); + + NSRect b = [v bounds]; + + NSRectSet *rs = nil; + + if (rlistcnt && !(flag&1)) + { + rs = [[NSRectSet alloc] initWithCopyOfRects:rlist count:rlistcnt bounds:[self bounds]]; + [rs convertFromAncestor:self toView:v clipTo:b]; + } + else + { + rs = [[NSRectSet alloc] initWithRect:b]; + } + + if (![rs isEmpty]) + { + [[_NSDisplayOperationStack currentThreadDisplayOperationStack] setRectSetBeingDrawn:rs forView:v]; + NSRect a=[rs bounds]; +// [v displayRectIgnoringOpacity:a]; + [v _recursiveDisplayRectIfNeededIgnoringOpacity:a isVisibleRect:TRUE rectIsVisibleRectForView:v topView:v2]; + } + + [rs release]; + [v setNeedsDisplay:NO]; + [v release]; + } + + + if (twoPassMode) swellRenderOptimizely(2,self,hdc,false,&ndlist,rlist,rlistcnt,0,0,true,self); + SWELL_DeleteGfxContext(hdc); + [self unlockFocus]; + [self setNeedsDisplay:NO]; + +} +#endif + +-(BOOL) swellWantsMetal +{ +#ifndef SWELL_NO_METAL + return m_use_metal > 0; +#else + return NO; +#endif +} +-(void) swellDrawMetal:(const RECT *)forRect +{ +#ifndef SWELL_NO_METAL + SWELL_AutoReleaseHelper arparp; + +#define swell_metal_set_layer_gravity(layer, g) do { \ + const int grav = (g); \ + (layer).contentsGravity = (grav&1) ? (grav&2) ? @"bottomRight" : @"topRight" : \ + (grav&2) ? @"bottomLeft" : @"topLeft"; } while(0) + + if (m_use_metal != 1 && m_use_metal != 2) return; + const bool direct_mode = m_use_metal == 1; + + CAMetalLayer *layer = (CAMetalLayer *)[self layer]; + + id device = m_metal_device; + +#if 1 + // support multiple devices. only check every second for device changes (it will use the old device and be slower in that duration) + // (checking the device takes about 20uS, which isn't a lot but also isn't nothing) + + // this seems to work correclty, *except* - if you're using the high-performance card, the system will never go back to integrated, + // presumably because our metal devices are open. Maybe we can flag them as "non-essential" ? + const DWORD now = GetTickCount(); + if (__CGDirectDisplayCopyCurrentMetalDevice && (!device || (now-m_metal_device_lastchkt)>1000)) + { + m_metal_device_lastchkt = now; + CGDirectDisplayID viewDisplayID = (CGDirectDisplayID) [self.window.screen.deviceDescription[@"NSScreenNumber"] unsignedIntegerValue]; + device = __CGDirectDisplayCopyCurrentMetalDevice(viewDisplayID); + } +#endif + if (!device) + { + static id def; + if (!def) def = __MTLCreateSystemDefaultDevice(); + device = def; + } + + if (device != m_metal_device) + { + id olddev = (id)m_metal_device; + if (olddev) NSLog(@"swell-cocoa: switching metal devices from %p %@ to %p %@\n",olddev,olddev.name,device,device.name); + m_metal_device = device; + [layer setDevice:device]; + swell_metal_set_layer_gravity(layer,m_metal_gravity ^ ([self isFlipped] ? 2 : 0)); + if (m_use_metal==1) + layer.framebufferOnly = NO; + [layer setPixelFormat:MTLPixelFormatBGRA8Unorm]; + + [m_metal_pipelineState release]; + [m_metal_commandQueue release]; + if (!direct_mode) [m_metal_texture release]; + + m_metal_commandQueue = NULL; + m_metal_pipelineState = NULL; + m_metal_texture = NULL; + m_metal_drawable = NULL; + } + + if (!device) return; + + if (!direct_mode) + { + if (!m_metal_pipelineState) + { + id vertex = NULL, frag = NULL; + get_dev_shaders(device, &vertex, &frag); + if (!vertex || !frag) return; // fail + + MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[__class_MTLRenderPipelineDescriptor alloc] init]; + + pipelineStateDescriptor.vertexFunction = vertex; + pipelineStateDescriptor.fragmentFunction = frag; + pipelineStateDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; + + NSError *error = NULL; + m_metal_pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; + [pipelineStateDescriptor release]; + } + } + + RECT cr; + GetClientRect((HWND)self,&cr); + if (m_use_metal == 2 && forRect) + { + WinIntersectRect(&cr,&cr,forRect); + } + + if (direct_mode) + { + m_metal_drawable = NULL; + m_metal_texture = NULL; + } + if (cr.right > cr.left && cr.bottom > cr.top) + { + // this might be good to enable for all metal windows? shrug + if (m_use_metal == 2) + { + NSRect frame = [self frame]; + NSView *cv = [[self window] contentView]; + if (cv != self) frame = [self convertRect:frame toView:cv]; + + const int last_grav = m_metal_gravity; + if (frame.size.width != m_metal_lastframe.size.width) + { + if (frame.origin.x != m_metal_lastframe.origin.x) m_metal_gravity|=1; + else m_metal_gravity&=~1; + } + if (frame.size.height != m_metal_lastframe.size.height) + { + if (frame.origin.y != m_metal_lastframe.origin.y) m_metal_gravity|=2; + else m_metal_gravity&=~2; + } + m_metal_lastframe = frame; + if (last_grav != m_metal_gravity) + swell_metal_set_layer_gravity(layer,m_metal_gravity ^ ([self isFlipped] ? 2 : 0)); + } + + HDC hdc = SWELL_CreateMetalDC(self); + + NSRect rect; + rect.origin.x = cr.left; + rect.origin.y = cr.top; + rect.size.width = cr.right-cr.left; + rect.size.height = cr.bottom-cr.top; + + m_metal_dc_dirty=0; + DrawSwellViewRectImpl(self,rect,hdc,true); + + SWELL_DeleteGfxContext(hdc); + } + m_metal_dc_dirty=0; + + if (direct_mode) + { + if (m_metal_drawable) + [m_metal_drawable present]; + m_metal_drawable = NULL; + m_metal_texture = NULL; + return; + } + + + id tex = (id) m_metal_texture; + if (!tex) return; // this can happen if GetDC()/ReleaseDC() are called before the first WM_PAINT + + NSRect bounds = [self bounds]; + if (bounds.size.width < 1 || bounds.size.height < 1) return; + + if (m_metal_retina) + { + bounds.size.width *= 2; + bounds.size.height *= 2; + } + + LIMIT_METAL_BOUNDS_SIZE(bounds.size) + + CGSize oldsc = layer.drawableSize; + if (oldsc.width != bounds.size.width || oldsc.height != bounds.size.height) + { + CGSize ns; + ns.width = bounds.size.width; + ns.height = bounds.size.height; + layer.drawableSize = ns; + layer.contentsScale = m_metal_retina ? 2.0 : 1.0; + } + id drawable = [layer nextDrawable]; + if (!drawable) + { + NSLog(@"swell-cocoa: metal surface got nul drawable\n"); + return; + } + + + RECT r = {0,0, (int)bounds.size.width, (int)bounds.size.height }; + + const float x_sc = (float) (r.right / (double)tex.width); + const float y_sc = (float) (r.bottom / (double)tex.height); + + vector_float2 quads[] = + { + { 1, -1 }, {x_sc, y_sc}, + { -1, -1 }, {0, y_sc}, + { -1, 1 }, {0, 0}, + + { 1, -1 }, {x_sc, y_sc}, + { -1, 1 }, {0, 0}, + { 1, 1 }, {x_sc, 0}, + }; + + if (!m_metal_commandQueue) + m_metal_commandQueue = [device newCommandQueue]; + + id commandBuffer = [m_metal_commandQueue commandBuffer]; + + MTLRenderPassDescriptor *renderPassDescriptor = [__class_MTLRenderPassDescriptor renderPassDescriptor]; + renderPassDescriptor.colorAttachments[0].texture = drawable.texture; + + id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + + // Set the region of the drawable to draw into. + [renderEncoder setViewport:(MTLViewport){0.0, 0.0, (double)r.right,(double)r.bottom, -1.0, 1.0 }]; + + [renderEncoder setRenderPipelineState:m_metal_pipelineState]; + [renderEncoder setVertexBytes:quads length:sizeof(quads) atIndex:0]; + [renderEncoder setFragmentTexture:m_metal_texture atIndex:0]; + + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6]; + + [renderEncoder endEncoding]; + + [commandBuffer presentDrawable:drawable]; + [commandBuffer commit]; +#endif +} + +-(void) drawRect:(NSRect)rect +{ + HDC hdc=SWELL_CreateGfxContext([NSGraphicsContext currentContext]); + DrawSwellViewRectImpl(self,rect,hdc); + SWELL_DeleteGfxContext(hdc); + m_isdirty=0; + +} + +- (void)rightMouseDragged:(NSEvent *)theEvent +{ + if (!m_enabled) return; + [self mouseDragged:theEvent]; +} +- (void)otherMouseDragged:(NSEvent *)theEvent +{ + if (!m_enabled) return; + [self mouseDragged:theEvent]; +} + +- (void)mouseDragged:(NSEvent *)theEvent +{ + if (!m_enabled) return; + + SWELL_SendMouseMessage(self,WM_MOUSEMOVE,theEvent); + if (SWELL_GetLastSetCursor()!=GetCursor()) SetCursor(SWELL_GetLastSetCursor()); +} +- (void)mouseMoved:(NSEvent *)theEvent +{ + if (DelegateMouseMove(self,theEvent)) return; + + if (m_enabled) if (!GetCapture() || GetCapture()==(HWND)self) { + SWELL_SendMouseMessage(self,WM_MOUSEMOVE, theEvent); + } +// [super mouseMoved:theEvent]; +} +- (void)mouseUp:(NSEvent *)theEvent +{ + if (!m_enabled) return; + if (m_isfakerightmouse) [self rightMouseUp:theEvent]; + else SWELL_SendMouseMessage(self,WM_LBUTTONUP,theEvent); +} +- (void)scrollWheel:(NSEvent *)theEvent +{ + if (!m_enabled) return; + // todo: use scrollingDeltaX/scrollingDeltaY etc on 10.7+ ? + if ([theEvent deltaY] != 0.0f) + { + SWELL_SendMouseMessage(self,WM_MOUSEWHEEL,theEvent); + } + if ([theEvent deltaX] != 0.0f) + { + SWELL_SendMouseMessage(self,WM_MOUSEHWHEEL,theEvent); + } +} +- (void)mouseDown:(NSEvent *)theEvent +{ + SWELL_FinishDragDrop(); + if (!m_enabled) return; + + m_isfakerightmouse=0; + if (([theEvent modifierFlags] & NSControlKeyMask) && IsRightClickEmulateEnabled()) + { + [self rightMouseDown:theEvent]; + if ([theEvent clickCount]<2) m_isfakerightmouse=1; + return; + } + + SWELL_SendMouseMessage(self,([theEvent clickCount]>1 ? WM_LBUTTONDBLCLK : WM_LBUTTONDOWN) ,theEvent); +} +- (void)rightMouseUp:(NSEvent *)theEvent +{ + if (!m_enabled) return; + m_isfakerightmouse=0; + SWELL_SendMouseMessage(self,WM_RBUTTONUP,theEvent); +} +- (void)rightMouseDown:(NSEvent *)theEvent +{ + m_isfakerightmouse=0; + if ([NSApp keyWindow] != [self window]) + { + SetFocus((HWND)[self window]); + } + SWELL_SendMouseMessage(self,([theEvent clickCount]>1 ? WM_RBUTTONDBLCLK : WM_RBUTTONDOWN),theEvent); +} +- (void)otherMouseUp:(NSEvent *)theEvent +{ + if (!m_enabled) return; + SWELL_SendMouseMessage(self,WM_MBUTTONUP,theEvent); +} +- (void)otherMouseDown:(NSEvent *)theEvent +{ + if ([NSApp keyWindow] != [self window]) + { + SetFocus((HWND)[self window]); + } + SWELL_SendMouseMessage(self,([theEvent clickCount]>1 ? WM_MBUTTONDBLCLK : WM_MBUTTONDOWN),theEvent); +} + +// multitouch support + +static void MakeGestureInfo(NSEvent* evt, GESTUREINFO* gi, HWND hwnd, int type) +{ + memset(gi, 0, sizeof(GESTUREINFO)); + gi->cbSize = sizeof(GESTUREINFO); + + gi->hwndTarget = hwnd; + gi->dwID = type; + + NSWindow* wnd = [evt window]; + NSPoint pt = [evt locationInWindow]; + pt = [wnd convertBaseToScreen:pt]; + gi->ptsLocation.x = pt.x; + gi->ptsLocation.y = pt.y; +} + +- (void)magnifyWithEvent:(NSEvent*)evt +{ + GESTUREINFO gi; + MakeGestureInfo(evt, &gi, (HWND) self, GID_ZOOM); + + gi.dwFlags = GF_BEGIN; + gi.ullArguments = 1024; // arbitrary + SendMessage((HWND)self, WM_GESTURE, 0, (LPARAM)&gi); + + gi.dwFlags = GF_END; + float z = [evt deltaZ]; // should be the same as 10.6 [evt magnification] + int a = (int)(1024.0f*z+0.5); + if (!a) a = (z >= 0.0f ? 1 : -1); + a += 1024; + if (a < 512) a=512; + else if (a > 2048) a=2048; + gi.ullArguments = a; + SendMessage((HWND)self, WM_GESTURE, gi.ullArguments, (LPARAM)&gi); +} + +- (void)swipeWithEvent:(NSEvent*)evt +{ + GESTUREINFO gi; + MakeGestureInfo(evt, &gi, (HWND) self, GID_PAN); + + gi.dwFlags = GF_BEGIN; + gi.ullArguments = 0; // for this gesture we only care about ptsLocation + SendMessage((HWND)self, WM_GESTURE, 0, (LPARAM)&gi); + + gi.dwFlags = GF_END; + NSRect r = [self bounds]; + int dx=0; + int dy=0; + + // for swipe events, deltaX/Y is either -1 or +1, convert to "one page" + if ([evt deltaX] < 0.0f) dx = -r.size.width; + else if ([evt deltaX] > 0.0f) dx = r.size.width; + else if ([evt deltaY] < 0.0f) dy = r.size.height; + else if ([evt deltaY] > 0.0f) dy = -r.size.height; + + gi.ptsLocation.x += dx; + gi.ptsLocation.y += dy; + + SendMessage((HWND)self, WM_GESTURE, gi.ullArguments, (LPARAM)&gi); +} + +-(void) rotateWithEvent:(NSEvent*)evt +{ + GESTUREINFO gi; + MakeGestureInfo(evt, &gi, (HWND) self, GID_ROTATE); + + gi.dwFlags = GF_BEGIN; + gi.ullArguments = 0; // Windows sends the absolute starting rotation as the first message, Mac doesn't + SendMessage((HWND)self, WM_GESTURE, 0, (LPARAM)&gi); + + gi.dwFlags = GF_END; + float z = [evt rotation]; + int i = (int)32767.0f*z/60.0f; + if (!i) i = (z >= 0.0f ? 1 : -1); + i += 32767; + if (i < 0) i=0; + else if (i > 65535) i=65535; + gi.ullArguments = i; + SendMessage((HWND)self, WM_GESTURE, i, (LPARAM)&gi); +} + + +- (const char *)onSwellGetText { return m_titlestr; } +-(void)onSwellSetText:(const char *)buf { lstrcpyn_safe(m_titlestr,buf,sizeof(m_titlestr)); } + + +// source-side drag/drop, only does something if source called SWELL_InitiateDragDrop while handling mouseDown +- (NSArray*) namesOfPromisedFilesDroppedAtDestination:(NSURL*) dropdestination +{ + NSArray* SWELL_DoDragDrop(NSURL*); + return SWELL_DoDragDrop(dropdestination); +} + + +- (BOOL)becomeFirstResponder +{ + int en = m_enabled; + if (en < 0) + { + if ([[self window] contentView]==self) en = 1; // accept focus if we're enabled-without-focus and the contentview + } + if (en <= 0 || ![super becomeFirstResponder]) return NO; + SendMessage((HWND)self, WM_MOUSEACTIVATE, 0, 0); + return YES; +} + +/* +- (BOOL)resignFirstResponder +{ + HWND foc=GetFocus(); + if (![super resignFirstResponder]) return NO; + [self onSwellMessage:WM_ACTIVATE p1:WA_INACTIVE p2:(LPARAM)foc]; + return YES; +} +*/ + +- (BOOL)acceptsFirstResponder +{ + if (m_enabled < 0) + { + // accept focus if we're enabled-without-focus and the contentview + if ([[self window] contentView]==self) return YES; + } + return m_enabled > 0?YES:NO; +} + +-(void)swellSetExtendedStyle:(LONG)st +{ + if (st&WS_EX_ACCEPTFILES) + { + if (!m_supports_ddrop) + { + [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFilesPromisePboardType, nil]]; + m_supports_ddrop=true; + } + } + else + { + if (m_supports_ddrop) + { + [self unregisterDraggedTypes]; + m_supports_ddrop=false; + } + } +} +-(LONG)swellGetExtendedStyle +{ + LONG ret=0; + if (m_supports_ddrop) ret|=WS_EX_ACCEPTFILES; + return ret; +} + +- (NSDragOperation)draggingEntered:(id )sender +{ + if (!m_supports_ddrop) return NSDragOperationNone; + + if (SWELL_DDrop_onDragEnter) + { + HANDLE h = (HANDLE)[self swellExtendedDragOp:sender retGlob:YES]; + if (h) + { + POINT pt; + NSPOINT_TO_POINT(&pt,[[self window] convertBaseToScreen:[sender draggingLocation]]); + SWELL_DDrop_onDragEnter(h,pt); + GlobalFree(h); + } + } + + return NSDragOperationGeneric; +} +- (BOOL) wantsPeriodicDraggingUpdates +{ + return NO; +} +- (NSDragOperation)draggingUpdated:(id )sender +{ + if (!m_supports_ddrop) return NSDragOperationNone; + + if (SWELL_DDrop_onDragOver) + { + POINT pt; + NSPOINT_TO_POINT(&pt,[[self window] convertBaseToScreen:[sender draggingLocation]]); + SWELL_DDrop_onDragOver(pt); + } + + return NSDragOperationGeneric; + +} +- (void)draggingExited:(id )sender +{ + if (m_supports_ddrop && SWELL_DDrop_onDragLeave) SWELL_DDrop_onDragLeave(); +} + +-(HANDLE)swellExtendedDragOp:(id )sender retGlob:(BOOL)retG +{ + if (!m_supports_ddrop) return 0; + + NSPasteboard *pboard; + NSDragOperation sourceDragMask; + sourceDragMask = [sender draggingSourceOperationMask]; + pboard = [sender draggingPasteboard]; + + enum { PB_FILEREF=1, PB_FILEPROMISE }; + int pbtype = 0; + if ([[pboard types] containsObject:NSFilenamesPboardType]) pbtype |= PB_FILEREF; + if ([[pboard types] containsObject:NSFilesPromisePboardType]) pbtype |= PB_FILEPROMISE; + if (!pbtype) return 0; + + int sz=sizeof(DROPFILES); + + bool maketmpfn = false; + NSArray *files = 0; + if (pbtype&PB_FILEREF) + { + files = [pboard propertyListForType:NSFilenamesPboardType]; + } + else if (pbtype&PB_FILEPROMISE) + { + NSArray* exts = [pboard propertyListForType:NSFilesPromisePboardType]; // just the file extensions + if (retG) + { + files = exts; + maketmpfn = true; + } + else if (SWELL_DDrop_getDroppedFileTargetPath) + { + char ext[256]; + ext[0] = 0; + if ([exts objectAtIndex:0]) SWELL_CFStringToCString([exts objectAtIndex:0], ext, sizeof(ext)); + + const char* droppath = SWELL_DDrop_getDroppedFileTargetPath(ext); + if (!droppath || !droppath[0]) droppath = "/tmp/"; + NSString* pathstr = (NSString*)SWELL_CStringToCFString(droppath); + NSURL* dest = [NSURL fileURLWithPath:pathstr]; + + files = [sender namesOfPromisedFilesDroppedAtDestination:dest]; // tells the drag source to create the files + + if ([files count]) + { + NSMutableArray* paths=[NSMutableArray arrayWithCapacity:[files count]]; + int i; + for (i=0; i < [files count]; ++i) + { + NSString* fn=[files objectAtIndex:i]; + if (fn) + { + [paths addObject:[pathstr stringByAppendingPathComponent:fn]]; + } + } + files=paths; + } + + [pathstr release]; + } + } + if (!files) return 0; + + int x; + for (x = 0; x < [files count]; x ++) + { + NSString *sv=[files objectAtIndex:x]; + if (sv) + { + char text[4096]; + text[0]=0; + SWELL_CFStringToCString(sv,text,sizeof(text)); + sz+=strlen(text)+1; + if (maketmpfn) sz += strlen("tmp."); + } + } + + NSPoint tpt = [self convertPoint:[sender draggingLocation] fromView:nil]; + + HANDLE gobj=GlobalAlloc(0,sz+1); + DROPFILES *df=(DROPFILES*)gobj; + df->pFiles=sizeof(DROPFILES); + NSPOINT_TO_POINT(&df->pt, tpt); + df->fNC = FALSE; + df->fWide = FALSE; + char *pout = (char *)(df+1); + for (x = 0; x < [files count]; x ++) + { + NSString *sv=[files objectAtIndex:x]; + if (sv) + { + char text[4096]; + text[0]=0; + SWELL_CFStringToCString(sv,text,sizeof(text)); + if (maketmpfn) + { + strcpy(pout, "tmp."); + pout += strlen("tmp."); + } + strcpy(pout,text); + pout+=strlen(pout)+1; + } + } + *pout=0; + + if (!retG) + { + [self onSwellMessage:WM_DROPFILES p1:(WPARAM)gobj p2:0]; + GlobalFree(gobj); + } + + return gobj; +} + +- (BOOL)performDragOperation:(id )sender +{ + if (m_supports_ddrop && SWELL_DDrop_onDragLeave) SWELL_DDrop_onDragLeave(); + + HWND cv = NULL; // view to disable "setwindowrepre()" for + + id dragsrc = [sender draggingSource]; + if ([dragsrc isKindOfClass:[NSView class]]) + { + if ([(NSView *)dragsrc window] == [self window]) // this means we're likely dragging from the titlebar, so we gotta disable setwindowrepre cause cocoa sucks + { + cv = (HWND) [[self window] contentView]; + } + } + + if (cv) SetProp(cv,"SWELL_DisableWindowRepre",(HANDLE)TRUE); + + NSView *v=[self hitTest:[[self superview] convertPoint:[sender draggingLocation] fromView:nil]]; + if (v && [v isDescendantOf:self]) + { + while (v && v!=self) + { + if ([v respondsToSelector:@selector(swellExtendedDragOp:retGlob:)]) + if ([(SWELL_hwndChild *)v swellExtendedDragOp:sender retGlob:NO]) + { + if (cv) RemoveProp(cv,"SWELL_DisableWindowRepre"); + return YES; + } + v=[v superview]; + } + } + + BOOL ret=!![self swellExtendedDragOp:sender retGlob:NO]; + if (cv) RemoveProp(cv,"SWELL_DisableWindowRepre"); + return ret; +} + +-(unsigned int)swellCreateWindowFlags +{ + return m_create_windowflags; +} + + + + +// NSAccessibility + + +- (id)accessibilityHitTest:(NSPoint)point +{ + id ret = NULL; + id use_obj = NULL; + SendMessage((HWND)self,WM_GETOBJECT,0x1001,(LPARAM)&use_obj); + if (use_obj) + { + ret = [use_obj accessibilityHitTest:point]; + if (ret == use_obj && [ret accessibilityIsIgnored]) ret = NULL; + } + + if (!ret) ret = [super accessibilityHitTest:point]; + return ret; +} +- (id)accessibilityFocusedUIElement +{ + id use_obj = NULL, ret = NULL; + SendMessage((HWND)self,WM_GETOBJECT,0x1001,(LPARAM)&use_obj); + if (use_obj) + { + ret = [use_obj accessibilityFocusedUIElement]; + if (ret == use_obj) ret= NULL; + } + if (!ret) ret = [super accessibilityFocusedUIElement]; + return ret; +} + +- (id)accessibilityAttributeValue:(NSString *)attribute +{ + id ret = [super accessibilityAttributeValue:attribute]; + int wo=0; + if ([attribute isEqual:NSAccessibilityChildrenAttribute] || (wo = !![attribute isEqual:NSAccessibilityVisibleChildrenAttribute])) + { + id *cp = wo ? m_access_cacheptrs+3 : m_access_cacheptrs; + id use_obj = NULL; + SendMessage((HWND)self,WM_GETOBJECT,0x1001,(LPARAM)&use_obj); + if (use_obj) + { + if (cp[0] && cp[1] && use_obj == cp[2] && (ret==cp[1] || [ret isEqualToArray:cp[1]])) return cp[0]; + + NSArray *ar=NULL; + if (ret && [ret count]) + { + ar = [NSMutableArray arrayWithArray:ret]; + [(NSMutableArray *)ar addObject:use_obj]; + } + else ar = [NSArray arrayWithObject:use_obj]; + + int x; + for (x=0;x<3;x++) if (cp[x]) { [cp[x] release]; cp[x]=0; } + + //cp[1]=ret; + //cp[2]=use_obj; + + ret = NSAccessibilityUnignoredChildren(ar); + //cp[0]=ret; + + for (x=0;x<3;x++) if (cp[x]) [cp[x] retain]; + + return ret; + } + int x; + for (x=0;x<3;x++) if (cp[x]) { [cp[x] release]; cp[x]=0; } + } + + return ret; +} +// Return YES if the UIElement doesn't show up to the outside world - i.e. its parent should return the UIElement's children as its own - cutting the UIElement out. E.g. NSControls are ignored when they are single-celled. +- (BOOL)accessibilityIsIgnored +{ + if (![[self subviews] count]) + { + id use_obj = NULL; + SendMessage((HWND)self,WM_GETOBJECT,0x1001,(LPARAM)&use_obj); + + if (use_obj) + { + return YES; + } + } + return [super accessibilityIsIgnored]; +} + +- (const char *)getSwellClass +{ + return m_classname; +} + +@end + + + + + +static HWND last_key_window; + +static HMENU swell_getEffectiveMenuForWindow(NSView *cv, NSWindow *window, bool isModal) // cv and window must be non-NULL +{ + for (;;) + { + if ([cv respondsToSelector:@selector(swellGetMenu)]) + { + HMENU menu = [(SWELL_hwndChild*)cv swellGetMenu]; + if (menu) return menu; + } + + if (isModal) return NULL; + + if (![window respondsToSelector:@selector(swellGetOwner)]) return NULL; + + id own = [(SWELL_ModelessWindow*)window swellGetOwner]; + if (!own || own == cv || own == window) return NULL; + + if ([own isKindOfClass:[NSWindow class]]) window = (NSWindow *)own; + else if ([own isKindOfClass:[NSView class]]) window = [(NSView *)own window]; + else return NULL; + + if (!window) return NULL; + + cv = [window contentView]; + if (!cv) return NULL; + } +} + +#define SWELLDIALOGCOMMONIMPLEMENTS_WND(ISMODAL) \ +-(BOOL)acceptsFirstResponder { return m_enabled?YES:NO; } \ +- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { return m_enabled?YES:NO; } \ +- (void)setFrame:(NSRect)frameRect display:(BOOL)displayFlag \ +{ \ + [super setFrame:frameRect display:displayFlag]; \ + if((int)frameRect.size.width != (int)lastFrameSize.width || (int)frameRect.size.height != (int)lastFrameSize.height) { \ + SWELL_hwndChild *hc = (SWELL_hwndChild*)[self contentView]; \ + sendSwellMessage(hc,WM_SIZE,0,0); \ + if ([hc isOpaque]) InvalidateRect((HWND)hc,NULL,FALSE); \ + lastFrameSize=frameRect.size; \ + } \ +} \ +- (void)windowDidMove:(NSNotification *)aNotification { \ + NSRect f=[self frame]; \ + sendSwellMessage([self contentView], WM_MOVE,0, MAKELPARAM((int)floor(f.origin.x+0.5),(int)floor(f.origin.y+0.5))); \ +} \ +- (BOOL)accessibilityIsIgnored \ +{ \ + if (!([self styleMask] & NSTitledWindowMask) && ![[[self contentView] subviews] count]) return YES; \ + return [super accessibilityIsIgnored]; \ +} \ +-(void)swellDoDestroyStuff \ +{ \ + if (last_key_window==(HWND)self) last_key_window=0; \ + OwnedWindowListRec *p=m_ownedwnds; m_ownedwnds=0; \ + while (p) \ + { \ + OwnedWindowListRec *next=p->_next; \ + DestroyWindow((HWND)p->hwnd); \ + free(p); p=next; \ + } \ + if (last_key_window==(HWND)self) last_key_window=0; \ + if (m_owner) { \ + [(SWELL_ModelessWindow*)m_owner swellRemoveOwnedWindow:self]; \ + if ([NSApp keyWindow] == self) [(SWELL_ModelessWindow*)m_owner makeKeyWindow]; \ + m_owner=0; \ + } \ +} \ +-(void)dealloc \ +{ \ + [self swellDoDestroyStuff]; \ + [super dealloc]; \ +} \ +- (void)swellDestroyAllOwnedWindows \ +{ \ + OwnedWindowListRec *p=m_ownedwnds; m_ownedwnds=0; \ + while (p) \ + { \ + OwnedWindowListRec *next=p->_next; \ + DestroyWindow((HWND)p->hwnd); \ + free(p); p=next; \ + } \ +} \ +- (void)resignKeyWindow { \ + [super resignKeyWindow]; \ + if (g_swell_terminating) return; \ + sendSwellMessage([self contentView],WM_ACTIVATE,WA_INACTIVE,0); \ + last_key_window=(HWND)self; \ +} \ +-(void)becomeKeyWindow \ +{ \ + [super becomeKeyWindow]; \ + if (g_swell_terminating) return; \ + NSView *foc=last_key_window && IsWindow(last_key_window) ? [(NSWindow *)last_key_window contentView] : 0; \ + HMENU menu=0; \ + if (foc && [foc respondsToSelector:@selector(swellHasBeenDestroyed)] && [(SWELL_hwndChild*)foc swellHasBeenDestroyed]) foc=NULL; \ + NSView *cv = [self contentView]; \ + if (!cv || ![cv respondsToSelector:@selector(swellHasBeenDestroyed)] || ![(SWELL_hwndChild*)cv swellHasBeenDestroyed]) { \ + menu = swell_getEffectiveMenuForWindow(cv,self,ISMODAL); \ + if (!menu) menu=ISMODAL && g_swell_defaultmenumodal ? g_swell_defaultmenumodal : g_swell_defaultmenu; \ + if (menu && menu != (HMENU)[NSApp mainMenu] && !g_swell_terminating) [NSApp setMainMenu:(NSMenu *)menu]; \ + sendSwellMessage(cv,WM_ACTIVATE,WA_ACTIVE,(LPARAM)foc); \ + sendSwellMessage(cv,WM_MOUSEACTIVATE,0,0); \ + } \ +} \ +-(BOOL)windowShouldClose:(id)sender \ +{ \ + NSView *v=[self contentView]; \ + if ([v respondsToSelector:@selector(onSwellMessage:p1:p2:)]) \ + if (![(SWELL_hwndChild*)v onSwellMessage:WM_CLOSE p1:0 p2:0]) \ + [(SWELL_hwndChild*)v onSwellMessage:WM_COMMAND p1:IDCANCEL p2:0]; \ + return NO; \ +} \ +- (BOOL)canBecomeKeyWindow { return !!m_enabled && !g_swell_terminating; } \ +- (void **)swellGetOwnerWindowHead { return (void **)&m_ownedwnds; } \ +- (void)swellAddOwnedWindow:(NSWindow*)wnd \ +{ \ + OwnedWindowListRec *p=m_ownedwnds; \ + while (p) { \ + if (p->hwnd == wnd) return; \ + p=p->_next; \ + } \ + p=(OwnedWindowListRec*)malloc(sizeof(OwnedWindowListRec)); \ + p->hwnd=wnd; p->_next=m_ownedwnds; m_ownedwnds=p; \ + if ([wnd respondsToSelector:@selector(swellSetOwner:)]) [(SWELL_ModelessWindow*)wnd swellSetOwner:self]; \ + if (SWELL_owned_windows_levelincrease) if ([wnd isKindOfClass:[NSWindow class]]) \ + { \ + int extra = [wnd isKindOfClass:[SWELL_ModelessWindow class]] ? ((SWELL_ModelessWindow *)wnd)->m_wantraiseamt : 0; \ + if ([NSApp isActive]) [wnd setLevel:[self level]+1+extra]; \ + } \ +} \ +- (void)swellRemoveOwnedWindow:(NSWindow *)wnd \ +{ \ + OwnedWindowListRec *p=m_ownedwnds, *lp=NULL; \ + while (p) { \ + if (p->hwnd == wnd) { \ + if (lp) lp->_next=p->_next; \ + else m_ownedwnds=p->_next; \ + free(p); \ + return; \ + } \ + lp=p; \ + p=p->_next; \ + } \ +} \ +- (void)swellResetOwnedWindowLevels { \ + if (SWELL_owned_windows_levelincrease) { OwnedWindowListRec *p=m_ownedwnds; \ + bool active = [NSApp isActive]; \ + int l=(int)[self level]+!!active; \ + while (p) { \ + if (p->hwnd) { \ + int extra = active && [(id)p->hwnd isKindOfClass:[SWELL_ModelessWindow class]] ? ((SWELL_ModelessWindow *)p->hwnd)->m_wantraiseamt : 0; \ + [(NSWindow *)p->hwnd setLevel:l+extra]; \ + if ([(id)p->hwnd respondsToSelector:@selector(swellResetOwnedWindowLevels)]) \ + [(id)p->hwnd swellResetOwnedWindowLevels]; \ + } \ + p=p->_next; \ + } \ + } \ +} \ +- (void)swellSetOwner:(id)owner { m_owner=owner; } \ +- (id)swellGetOwner { return m_owner; } \ +- (NSSize)minSize \ +{ \ + MINMAXINFO mmi; \ + memset(&mmi,0,sizeof(mmi)); \ + NSSize minsz=(NSSize)[super minSize]; \ + mmi.ptMinTrackSize.x=(int)minsz.width; mmi.ptMinTrackSize.y=(int)minsz.height; \ + sendSwellMessage([self contentView],WM_GETMINMAXINFO,0,(LPARAM)&mmi); \ + minsz.width=mmi.ptMinTrackSize.x; minsz.height=mmi.ptMinTrackSize.y; \ + return minsz; \ +} \ +- (NSSize)maxSize \ +{ \ + MINMAXINFO mmi; \ + memset(&mmi,0,sizeof(mmi)); \ + NSSize maxsz=(NSSize)[super maxSize]; NSSize tmp=maxsz;\ + if (tmp.width<1)tmp.width=1; else if (tmp.width > 1000000.0) tmp.width=1000000.0; \ + if (tmp.height<1)tmp.height=1; else if (tmp.height > 1000000.0) tmp.height=1000000.0; \ + mmi.ptMaxTrackSize.x=(int)tmp.width; mmi.ptMaxTrackSize.y=(int)tmp.height; \ + sendSwellMessage([self contentView], WM_GETMINMAXINFO, 0, (LPARAM)&mmi); \ + if (mmi.ptMaxTrackSize.x < 1000000) maxsz.width=mmi.ptMaxTrackSize.x; \ + if (mmi.ptMaxTrackSize.y < 1000000) maxsz.height=mmi.ptMaxTrackSize.y; \ + return maxsz; \ +} \ + + + +#define INIT_COMMON_VARS \ + m_enabled=TRUE; \ + m_owner=0; \ + m_ownedwnds=0; + + +#if 0 +#define DOWINDOWMINMAXSIZES(ch) \ +{ \ + MINMAXINFO mmi={0}; \ + NSSize minsz=(NSSize)[super contentMinSize]; \ + mmi.ptMinTrackSize.x=(int)minsz.width; mmi.ptMinTrackSize.y=(int)minsz.height; \ + sendSwellMessage(ch,WM_GETMINMAXINFO,0,(LPARAM)&mmi); \ + minsz.width=mmi.ptMinTrackSize.x; minsz.height=mmi.ptMinTrackSize.y; \ + [super setContentMinSize:minsz]; \ +} + +#endif + +static void GetInitialWndPos(HWND owner, int h, int* x, int* y) +{ + RECT r; + if (owner) GetWindowRect(owner, &r); + else SWELL_GetViewPort(&r, 0, false); + *x = r.left+50; + *y = r.bottom-h-100; +} + +NSView **g_swell_mac_foreign_key_event_sink; + + +@implementation SWELL_ModelessWindow : NSWindow + +SWELLDIALOGCOMMONIMPLEMENTS_WND(0) + + +- (id)initModelessForChild:(HWND)child owner:(HWND)owner styleMask:(unsigned int)smask +{ + INIT_COMMON_VARS + m_wantInitialKeyWindowOnShow=0; + m_wantraiseamt=0; + lastFrameSize.width=lastFrameSize.height=0.0f; + + NSRect cr=[(NSView *)child bounds]; + + int wx, wy; + GetInitialWndPos(owner, cr.size.height, &wx, &wy); + NSRect contentRect=NSMakeRect(wx,wy,cr.size.width,cr.size.height); + if (!(self = [super initWithContentRect:contentRect styleMask:smask backing:NSBackingStoreBuffered defer:NO])) return self; + + [self setDelegate:(id)self]; + [self disableCursorRects]; + [self setAcceptsMouseMovedEvents:YES]; + [self setContentView:(NSView *)child]; + [self useOptimizedDrawing:YES]; + if (SWELL_GetOSXVersion()>=0x10c0) [self setValue:[NSNumber numberWithInt:2] forKey:@"tabbingMode"]; + + updateWindowCollection(self); + + if (owner && [(id)owner respondsToSelector:@selector(swellAddOwnedWindow:)]) + { + [(id)owner swellAddOwnedWindow:self]; + } + else if (owner && [(id)owner isKindOfClass:[NSView class]]) + { + NSWindow *w=[(id)owner window]; + if (w && [w respondsToSelector:@selector(swellAddOwnedWindow:)]) + { + [(SWELL_ModelessWindow*)w swellAddOwnedWindow:self]; + } + } + + [self setAutorecalculatesKeyViewLoop:YES]; + [self display]; + return self; +} + +- (id)initModeless:(SWELL_DialogResourceIndex *)resstate Parent:(HWND)parent dlgProc:(DLGPROC)dlgproc Param:(LPARAM)par outputHwnd:(HWND *)hwndOut forceStyles:(unsigned int)smask +{ + INIT_COMMON_VARS + m_wantInitialKeyWindowOnShow=0; + m_wantraiseamt=0; + + lastFrameSize.width=lastFrameSize.height=0.0f; + + int w = (resstate ? resstate->width : 10); + int h = (resstate ? resstate->height : 10); + + int wx, wy; + GetInitialWndPos(parent, h, &wx, &wy); + NSRect contentRect=NSMakeRect(wx,wy,w,h); + int sf=smask; + + if (resstate) + { + sf |= NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask; + if (resstate->windowTypeFlags&SWELL_DLG_WS_RESIZABLE) sf |= NSResizableWindowMask; + } + + if (!(self = [super initWithContentRect:contentRect styleMask:sf backing:NSBackingStoreBuffered defer:NO])) return self; + + [self disableCursorRects]; + [self setAcceptsMouseMovedEvents:YES]; + [self useOptimizedDrawing:YES]; + [self setDelegate:(id)self]; + if (SWELL_GetOSXVersion()>=0x10c0) [self setValue:[NSNumber numberWithInt:2] forKey:@"tabbingMode"]; + + updateWindowCollection(self); + + if (resstate&&resstate->title) SetWindowText((HWND)self, resstate->title); + + + if (parent && [(id)parent respondsToSelector:@selector(swellAddOwnedWindow:)]) + { + [(id)parent swellAddOwnedWindow:self]; + } + else if (parent && [(id)parent isKindOfClass:[NSView class]]) + { + NSWindow *ww=[(id)parent window]; + if (ww && [ww respondsToSelector:@selector(swellAddOwnedWindow:)]) + { + [(SWELL_ModelessWindow*)ww swellAddOwnedWindow:self]; + } + } + + [self retain]; // in case WM_INITDIALOG goes and releases us + + SWELL_hwndChild *ch=[[SWELL_hwndChild alloc] initChild:resstate Parent:(NSView *)self dlgProc:dlgproc Param:par]; // create a new child view class + ch->m_create_windowflags=sf; + *hwndOut = (HWND)ch; + + [ch release]; + + [self setAutorecalculatesKeyViewLoop:YES]; + [self display]; + [self release]; // matching retain above + + return self; +} +-(NSInteger)level +{ + //if (SWELL_owned_windows_levelincrease) return NSNormalWindowLevel; + return [super level]; +} + +#if SWELL_CUT_OUT_COMPOSITING_MIDDLEMAN > 1 +-(void) displayIfNeeded +{ + if (![[self contentView] isOpaque]) + { + [super displayIfNeeded]; + } + else + { + // NSThemeFrame + if ([self viewsNeedDisplay]) + { + [[self contentView] _recursiveDisplayRectIfNeededIgnoringOpacity:NSMakeRect(0,0,0,0) isVisibleRect:YES rectIsVisibleRectForView:[self contentView] topView:[self contentView]]; + [self setViewsNeedDisplay:NO]; + [self flushWindow]; + } + + } +} +#endif + +-(void)keyDown:(NSEvent *)event +{ + if (g_swell_mac_foreign_key_event_sink && [event window] != self) + { + *g_swell_mac_foreign_key_event_sink = [self contentView]; + } + else + { + [super keyDown:event]; + } +} + +-(void)toggleFullScreen:(id)sender +{ + if (!SendMessage((HWND)[self contentView],WM_SWELL_EXTENDED,(WPARAM)"toggleFullScreen",(LPARAM)sender)) + [super toggleFullScreen:sender]; +} + +@end + + + + +@implementation SWELL_ModalDialog : NSPanel + +SWELLDIALOGCOMMONIMPLEMENTS_WND(1) + + + +- (id)initDialogBox:(SWELL_DialogResourceIndex *)resstate Parent:(HWND)parent dlgProc:(DLGPROC)dlgproc Param:(LPARAM)par +{ + m_rv=0; + m_hasrv=false; + INIT_COMMON_VARS + + NSRect contentRect=NSMakeRect(0,0,resstate->width,resstate->height); + unsigned int sf=(NSTitledWindowMask|NSClosableWindowMask|((resstate->windowTypeFlags&SWELL_DLG_WS_RESIZABLE)? NSResizableWindowMask : 0)); + if (!(self = [super initWithContentRect:contentRect styleMask:sf backing:NSBackingStoreBuffered defer:NO])) return self; + + [self setAcceptsMouseMovedEvents:YES]; + [self disableCursorRects]; + [self useOptimizedDrawing:YES]; + [self setDelegate:(id)self]; + updateWindowCollection(self); + + if (parent && [(id)parent respondsToSelector:@selector(swellAddOwnedWindow:)]) + { + [(id)parent swellAddOwnedWindow:self]; + } + else if (parent && [(id)parent isKindOfClass:[NSView class]]) + { + NSWindow *w=[(id)parent window]; + if (w && [w respondsToSelector:@selector(swellAddOwnedWindow:)]) + { + [(SWELL_ModelessWindow*)w swellAddOwnedWindow:self]; + } + } + if (resstate&&resstate->title) SetWindowText((HWND)self, resstate->title); + + SWELL_hwndChild *ch=[[SWELL_hwndChild alloc] initChild:resstate Parent:(NSView *)self dlgProc:dlgproc Param:par]; // create a new child view class + ch->m_create_windowflags=sf; + [ch setHidden:NO]; +// DOWINDOWMINMAXSIZES(ch) + [ch release]; + + [self setAutorecalculatesKeyViewLoop:YES]; + [self setHidesOnDeactivate:NO]; + [self display]; + + return self; +} + + +-(void)swellSetModalRetVal:(int)r +{ + m_hasrv=true; + m_rv=r; +} +-(int)swellGetModalRetVal +{ + return m_rv; +} +-(bool)swellHasModalRetVal +{ + return m_hasrv; +} + +@end + +void EndDialog(HWND wnd, int ret) +{ + if (WDL_NOT_NORMALLY(!wnd)) return; + + NSWindow *nswnd=NULL; + NSView *nsview = NULL; + if ([(id)wnd isKindOfClass:[NSView class]]) + { + nsview = (NSView *)wnd; + nswnd = [nsview window]; + } + else if ([(id)wnd isKindOfClass:[NSWindow class]]) + { + nswnd = (NSWindow *)wnd; + nsview = [nswnd contentView]; + } + if (!nswnd) return; + + if ([nswnd respondsToSelector:@selector(swellSetModalRetVal:)]) + [(SWELL_ModalDialog*)nswnd swellSetModalRetVal:ret]; + + if ([NSApp modalWindow] == nswnd) + { + sendSwellMessage(nsview,WM_DESTROY,0,0); + + NSEvent *evt=[NSApp currentEvent]; + if (evt && [evt window] == nswnd) + { + [NSApp stopModal]; + } + + [NSApp abortModal]; // always call this, otherwise if running in runModalForWindow: it can often require another even tto come through before things continue + + [nswnd close]; + } +} + + +int SWELL_DialogBox(SWELL_DialogResourceIndex *reshead, const char *resid, HWND parent, DLGPROC dlgproc, LPARAM param) +{ + SWELL_DialogResourceIndex *p=resById(reshead,resid); + if (!p||(p->windowTypeFlags&SWELL_DLG_WS_CHILD)) return -1; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + SWELL_ModalDialog *box = [[SWELL_ModalDialog alloc] initDialogBox:p Parent:parent dlgProc:dlgproc Param:param]; + + if (!box) + { + [pool release]; + return -1; + } + + if ([box swellHasModalRetVal]) // detect EndDialog() in WM_INITDIALOG + { + int ret=[box swellGetModalRetVal]; + sendSwellMessage([box contentView],WM_DESTROY,0,0); + [box close]; + [box release]; + [pool release]; + return ret; + } + + if (0 && ![NSApp isActive]) // using this enables better background processing (i.e. if the app isnt active it still runs) + { + [NSApp activateIgnoringOtherApps:YES]; + NSModalSession session = [NSApp beginModalSessionForWindow:box]; + for (;;) + { + if ([NSApp runModalSession:session] != NSRunContinuesResponse) break; + Sleep(1); + } + [NSApp endModalSession:session]; + } + else + { + [NSApp runModalForWindow:box]; + } + int ret=[box swellGetModalRetVal]; + [box release]; + [pool release]; + return ret; +} + +HWND SWELL_CreateModelessFrameForWindow(HWND childW, HWND ownerW, unsigned int windowFlags) +{ + SWELL_ModelessWindow *ch=[[SWELL_ModelessWindow alloc] initModelessForChild:childW owner:ownerW styleMask:windowFlags]; + return (HWND)ch; +} + + +HWND SWELL_CreateDialog(SWELL_DialogResourceIndex *reshead, const char *resid, HWND parent, DLGPROC dlgproc, LPARAM param) +{ + unsigned int forceStyles=0; + bool forceNonChild=false; + if ((((INT_PTR)resid)&~0xf)==0x400000) + { + const int a = ((int)(INT_PTR)resid)&0xf; + forceStyles = NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask; + if (a&1) forceStyles|=NSResizableWindowMask; + if (a&2) forceStyles&=~NSMiniaturizableWindowMask; + if (a&4) forceStyles&=~NSClosableWindowMask; + if (a) forceNonChild=true; + resid=NULL; + } + SWELL_DialogResourceIndex *p=resById(reshead,resid); + if (!p&&resid) return 0; + + NSView *parview=NULL; + if (parent) + { + if ([(id)parent isKindOfClass:[NSView class]]) + { + parview = (NSView *)parent; + } + else if ([(id)parent isKindOfClass:[NSWindow class]]) + { + if ([(id)parent isKindOfClass:[NSPanel class]] && + [(id)parent respondsToSelector:@selector(setAccessoryView:)]) + { + parview=(NSView *)parent; + } + else + { + parview=(NSView *)[(NSWindow *)parent contentView]; + } + } + } + + if ((!p || (p->windowTypeFlags&SWELL_DLG_WS_CHILD)) && parview && (p || !forceNonChild)) + { + SWELL_hwndChild *ch=[[SWELL_hwndChild alloc] initChild:p Parent:parview dlgProc:dlgproc Param:param]; // create a new child view class + ch->m_create_windowflags=(NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask|NSResizableWindowMask); + [ch release]; + return (HWND)ch; + } + else + { + HWND h=NULL; + [[SWELL_ModelessWindow alloc] initModeless:p Parent:parent dlgProc:dlgproc Param:param outputHwnd:&h forceStyles:forceStyles]; + return h; + } + + return 0; +} + + +HMENU SWELL_GetDefaultWindowMenu() { return g_swell_defaultmenu; } +void SWELL_SetDefaultWindowMenu(HMENU menu) +{ + g_swell_defaultmenu=menu; +} +HMENU SWELL_GetDefaultModalWindowMenu() +{ + return g_swell_defaultmenumodal; +} +void SWELL_SetDefaultModalWindowMenu(HMENU menu) +{ + g_swell_defaultmenumodal=menu; +} + + + +SWELL_DialogResourceIndex *SWELL_curmodule_dialogresource_head; // this eventually will go into a per-module stub file + + +#import + + +#if 0 +static void PrintAllHIViews(HIViewRef f, const char *bla) +{ + char tmp[4096]; + sprintf(tmp,"%s:%08x",bla,f); + + HIRect r; + HIViewGetFrame(f,&r); + printf("%s beg %f %f %f %f\n",tmp,r.origin.x,r.origin.y,r.size.width, r.size.height); + HIViewRef a=HIViewGetFirstSubview(f); + while (a) + { + PrintAllHIViews(a,tmp); + a=HIViewGetNextView(a); + } + printf("%s end\n",tmp); +} +#endif + +#ifndef __LP64__ +// carbon event handler for carbon-in-cocoa +OSStatus CarbonEvtHandler(EventHandlerCallRef nextHandlerRef, EventRef event, void* userdata) +{ + SWELL_hwndCarbonHost* _this = (SWELL_hwndCarbonHost*)userdata; + UInt32 evtkind = GetEventKind(event); + + switch (evtkind) + { + case kEventWindowActivated: + if (!g_swell_terminating) [NSApp setMainMenu:nil]; + break; + + case kEventWindowGetClickActivation: + { + ClickActivationResult car = kActivateAndHandleClick; + SetEventParameter(event, kEventParamClickActivation, typeClickActivationResult, sizeof(ClickActivationResult), &car); + } + break; + + case kEventWindowHandleDeactivate: + { + if (_this) + { + WindowRef wndref = (WindowRef)[_this->m_cwnd windowRef]; + if (wndref) ActivateWindow(wndref, true); + } + } + break; + + case kEventControlBoundsChanged: + { + if (_this && !_this->m_whileresizing) + { + Rect prevr, curr; + GetEventParameter(event, kEventParamPreviousBounds, typeQDRectangle, 0, sizeof(Rect), 0, &prevr); + GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, 0, sizeof(Rect), 0, &curr); + + RECT parr; + GetWindowRect((HWND)_this, &parr); + parr.left += curr.left-prevr.left; + parr.top += curr.top-prevr.top; + parr.right += curr.right-prevr.right; + parr.bottom += curr.bottom-prevr.bottom; + _this->m_whileresizing = true; + SetWindowPos((HWND)_this, 0, parr.left, parr.right, parr.right-parr.left, parr.bottom-parr.top, SWP_NOZORDER|SWP_NOACTIVATE); + _this->m_whileresizing = false; + } + } + break; + + case kEventRawKeyDown: + case kEventRawKeyUp: + case kEventRawKeyModifiersChanged: + { + if (_this->m_wantallkeys) return eventNotHandledErr; + + WindowRef wndref = (WindowRef)[_this->m_cwnd windowRef]; + if (wndref) + { + ControlRef ctlref=0; + GetKeyboardFocus(wndref, &ctlref); + if (ctlref) + { + ControlKind ctlkind = { 0, 0 }; + GetControlKind(ctlref, &ctlkind); + if (ctlkind.kind == kControlKindEditText || + ctlkind.kind == kControlKindEditUnicodeText || + ctlkind.kind == kControlKindHITextView) + { + // ControlDefinitions.h, HITextViews.h, etc list control types, + // we may want to pass on some other types too + return eventNotHandledErr; + } + } + } + + UInt32 keycode; + UInt32 modifiers; + char c[2] = { 0, 0 }; + GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(UInt32), 0, &keycode); + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0, sizeof(UInt32), 0, &modifiers); + GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, 0, sizeof(char), 0, &c[0]); + + NSEventType type; + if (evtkind == kEventRawKeyDown) type = NSKeyDown; + else if (evtkind == kEventRawKeyUp) type = NSKeyUp; + else /*if (evtkind == kEventRawKeyModifiersChanged) */ + type = NSFlagsChanged; + + NSString* str = (NSString*)SWELL_CStringToCFString(c); + NSTimeInterval ts = 0; // [[NSApp currentevent] timestamp]; + NSEvent* evt = [NSEvent keyEventWithType:type location:NSMakePoint(0,0) + modifierFlags:modifiers + timestamp:ts windowNumber:0 + context:[NSGraphicsContext currentContext] + characters:str charactersIgnoringModifiers:str + isARepeat:NO keyCode:keycode]; + [str release]; + if (evt) [NSApp sendEvent:evt]; + return noErr; + } + } + return noErr; +} + +void SWELL_CarbonWndHost_SetWantAllKeys(void* carbonhost, bool want) +{ + SWELL_hwndCarbonHost* h = (SWELL_hwndCarbonHost*)carbonhost; + if (h && [h isKindOfClass:[SWELL_hwndCarbonHost class]]) h->m_wantallkeys = want; +} + +#endif // __LP + +@implementation SWELL_hwndCarbonHost + +- (id)initCarbonChild:(NSView *)parent rect:(Rect*)r composit:(bool)wantComp +{ + if (!(self = [super initChild:nil Parent:parent dlgProc:nil Param:0])) return self; + + m_wantallkeys=false; + +#ifndef __LP64__ + WindowRef wndref=0; + CreateNewWindow (kPlainWindowClass, (wantComp ? kWindowCompositingAttribute : 0) | kWindowStandardHandlerAttribute|kWindowNoShadowAttribute, r, &wndref); + if (wndref) + { + // eventually we should set this and have the real NSWindow parent call ActivateWindow when activated/deactivated + // SetWindowActivationScope( m_wndref, kWindowActivationScopeNone); + + // adding a Carbon event handler to catch special stuff that NSWindow::initWithWindowRef + // doesn't automatically redirect to a standard Cocoa window method + + ControlRef ctl=0; + if (!wantComp) CreateRootControl(wndref, &ctl); // creating root control here so callers must use GetRootControl + + EventTypeSpec winevts[] = + { + { kEventClassWindow, kEventWindowActivated }, + { kEventClassWindow, kEventWindowGetClickActivation }, + { kEventClassWindow, kEventWindowHandleDeactivate }, + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyUp }, + { kEventClassKeyboard, kEventRawKeyModifiersChanged }, + }; + int nwinevts = sizeof(winevts)/sizeof(EventTypeSpec); + + EventTypeSpec ctlevts[] = + { + //{ kEventClassControl, kEventControlInitialize }, + //{ kEventClassControl, kEventControlDraw }, + { kEventClassControl, kEventControlBoundsChanged }, + }; + int nctlevts = sizeof(ctlevts)/sizeof(EventTypeSpec); + + EventHandlerRef wndhandler=0, ctlhandler=0; + InstallWindowEventHandler(wndref, CarbonEvtHandler, nwinevts, winevts, self, &wndhandler); + if (!wantComp) InstallControlEventHandler(ctl, CarbonEvtHandler, nctlevts, ctlevts, self, &ctlhandler); + m_wndhandler = wndhandler; + m_ctlhandler = ctlhandler; + + // initWithWindowRef does not retain // MAKE SURE THIS IS NOT BAD TO DO + //CFRetain(wndref); + + m_cwnd = [[NSWindow alloc] initWithWindowRef:wndref]; + [m_cwnd setDelegate:(id)self]; + + ShowWindow(wndref); + + //[[parent window] addChildWindow:m_cwnd ordered:NSWindowAbove]; + //[self swellDoRepos]; + SetTimer((HWND)self,1,10,NULL); + } +#endif + return self; +} + +-(BOOL)swellIsCarbonHostingView { return YES; } + + +-(void)close +{ + KillTimer((HWND)self,1); + +#ifndef __LP64__ + if (m_wndhandler) + { + EventHandlerRef wndhandler = (EventHandlerRef)m_wndhandler; + RemoveEventHandler(wndhandler); + m_wndhandler = 0; + } + if (m_ctlhandler) + { + EventHandlerRef ctlhandler = (EventHandlerRef)m_ctlhandler; + RemoveEventHandler(ctlhandler); + m_ctlhandler = 0; + } + + if (m_cwnd) + { + if ([m_cwnd parentWindow]) [[m_cwnd parentWindow] removeChildWindow:m_cwnd]; + [m_cwnd orderOut:self]; + [m_cwnd close]; // this disposes the owned wndref + m_cwnd=0; + } +#endif +} + +-(void)dealloc +{ + [self close]; + [super dealloc]; // ?! +} + +- (void)SWELL_Timer:(id)sender +{ +#ifndef __LP64__ + id uinfo=[sender userInfo]; + if ([uinfo respondsToSelector:@selector(getValue)]) + { + int idx=(int)(INT_PTR)[(SWELL_DataHold*)uinfo getValue]; + if (idx==1) + { + if (![self superview] || [[self superview] isHiddenOrHasHiddenAncestor]) + { + NSWindow *oldw=[m_cwnd parentWindow]; + if (oldw) + { + [oldw removeChildWindow:(NSWindow *)m_cwnd]; + [m_cwnd orderOut:self]; + } + } + else + { + if (![m_cwnd parentWindow]) + { + NSWindow *par = [self window]; + if (par) + { + [par addChildWindow:m_cwnd ordered:NSWindowAbove]; + [self swellDoRepos]; + } + } + else + { + if (GetCurrentEventButtonState()&7) + { + if ([NSApp keyWindow] == [self window]) + { + POINT p; + GetCursorPos(&p); + RECT r; + GetWindowRect((HWND)self,&r); + if (r.top>r.bottom) + { + int a=r.top; + r.top=r.bottom; + r.bottom=a; + } + if (m_cwnd && p.x >=r.left &&p.x < r.right && p.y >= r.top && p.y < r.bottom) + { + [(NSWindow *)m_cwnd makeKeyWindow]; + } + } + } + } + } + return; + } + KillTimer((HWND)self,idx); + return; + } +#endif +} +- (LRESULT)onSwellMessage:(UINT)msg p1:(WPARAM)wParam p2:(LPARAM)lParam +{ + if (msg == WM_DESTROY) + { + if (m_cwnd) + { + if ([NSApp keyWindow] == m_cwnd) // restore focus to highest window that is not us! + { + NSArray *ar = [NSApp orderedWindows]; + int x; + for (x = 0; x < (ar ? [ar count] : 0); x ++) + { + NSWindow *w=[ar objectAtIndex:x]; + if (w && w != m_cwnd && [w isVisible]) { [w makeKeyWindow]; break; } + } + } + + [self close]; + } + } + return [super onSwellMessage:msg p1:wParam p2:lParam]; +} +- (void)windowDidResignKey:(NSNotification *)aNotification +{ +} +- (void)windowDidBecomeKey:(NSNotification *)aNotification +{ +} + + +- (void)viewDidMoveToWindow +{ + [super viewDidMoveToWindow]; + if (m_cwnd) + { + // reparent m_cwnd to new owner + NSWindow *neww=[self window]; + NSWindow *oldw=[m_cwnd parentWindow]; + if (neww != oldw) + { + if (oldw) [oldw removeChildWindow:m_cwnd]; + } + } +} +-(void)swellDoRepos +{ +#ifndef __LP64__ + if (m_cwnd) + { + RECT r; + GetWindowRect((HWND)self,&r); + if (r.top>r.bottom) + { + int a=r.top; + r.top=r.bottom; + r.bottom=a; + } + + // [m_cwnd setFrameOrigin:NSMakePoint(r.left,r.top)]; + + { + Rect bounds; + bounds.left = r.left; + bounds.top = CGRectGetHeight(CGDisplayBounds(kCGDirectMainDisplay))-r.bottom; + // GetWindowBounds (m_wndref, kWindowContentRgn, &bounds); + bounds.right = bounds.left + (r.right-r.left); + bounds.bottom = bounds.top + (r.bottom-r.top); + + WindowRef wndref = (WindowRef)[m_cwnd windowRef]; + SetWindowBounds (wndref, kWindowContentRgn, &bounds); + + // might make sense to only do this on initial show, but doesnt seem to hurt to do it often + WindowAttributes wa=0; + GetWindowAttributes(wndref,&wa); + + if (wa&kWindowCompositingAttribute) + { +// [[m_cwnd contentView] setNeedsDisplay:YES]; + HIViewRef ref = HIViewGetRoot(wndref); + if (ref) + { + // PrintAllHIViews(ref,""); + + HIViewRef ref2=HIViewGetFirstSubview(ref); + while (ref2) + { + /* + HIRect r3=CGRectMake(0,0,bounds.right-bounds.left,bounds.bottom-bounds.top); + HIViewRef ref3=HIViewGetFirstSubview(ref2); + while (ref3) + { + HIViewSetVisible(ref3,true); + HIViewSetNeedsDisplay(ref3,true); + HIViewSetFrame(ref3,&r3); + ref3=HIViewGetNextView(ref3); + } + */ + + // HIViewSetVisible(ref2,true); + HIViewSetNeedsDisplay(ref2,true); + ref2=HIViewGetNextView(ref2); + } + //HIViewSetVisible(ref,true); + HIViewSetNeedsDisplay(ref,true); + HIViewRender(ref); + } + } + else + { + +#if 0 + ControlRef rc=NULL; + GetRootControl(m_wndref,&rc); + if (rc) + { + RgnHandle rgn=NewRgn(); + GetControlRegion(rc,kControlEntireControl,rgn); + UpdateControls(m_wndref,rgn); + CloseRgn(rgn); + } +#endif + // Rect r={0,0,bounds.bottom-bounds.top,bounds.right-bounds.left}; + // InvalWindowRect(m_wndref,&r); + + // or we could just do: + DrawControls(wndref); + } + } + } +#endif +} + +- (void)viewDidMoveToSuperview +{ + [super viewDidMoveToSuperview]; + [self swellDoRepos]; +} +- (void)setFrameSize:(NSSize)newSize +{ + [super setFrameSize:newSize]; + [self swellDoRepos]; +} +- (void)setFrame:(NSRect)frameRect +{ + [super setFrame:frameRect]; + [self swellDoRepos]; +} +- (void)setFrameOrigin:(NSPoint)newOrigin +{ + [super setFrameOrigin:newOrigin]; + [self swellDoRepos]; +} + + +-(BOOL)isOpaque +{ + return NO; +} + +@end + +HWND SWELL_GetAudioUnitCocoaView(HWND parent, AudioUnit aunit, AudioUnitCocoaViewInfo* viewinfo, RECT* r) +{ + NSString* classname = (NSString*)(viewinfo->mCocoaAUViewClass[0]); + if (!classname) return 0; + + NSBundle* bundle=0; + if ([NSBundle respondsToSelector:@selector(bundleWithURL:)]) + { + bundle=[NSBundle bundleWithURL:(NSURL*)viewinfo->mCocoaAUViewBundleLocation]; + } + + if (!bundle) + { + NSString* path = (NSString*)(CFURLCopyFileSystemPath(viewinfo->mCocoaAUViewBundleLocation,kCFURLPOSIXPathStyle)); + if (path) + { + bundle = [NSBundle bundleWithPath:path]; + [path release]; + } + } + + if (!bundle) return 0; + + Class factoryclass = [bundle classNamed:classname]; + if (![factoryclass conformsToProtocol: @protocol(AUCocoaUIBase)]) return 0; + if (![factoryclass instancesRespondToSelector: @selector(uiViewForAudioUnit:withSize:)]) return 0; + id viewfactory = [[factoryclass alloc] init]; + if (!viewfactory) return 0; + NSView* view = [viewfactory uiViewForAudioUnit:aunit withSize:NSMakeSize(r->right-r->left, r->bottom-r->top)]; + if (!view) + { + [viewfactory release]; + return 0; + } + + [view retain]; + + NSRect bounds = [view bounds]; + r->left = r->top = 0; + r->right = bounds.size.width; + r->bottom = bounds.size.height; + + [((NSView*)parent) setAutoresizesSubviews:NO]; + SetWindowPos((HWND)parent,NULL, 0,0, r->right,r->bottom, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE); + + [(NSView*)parent addSubview:view]; + + [view release]; + [viewfactory release]; + + return (HWND)view; +} + + +HWND SWELL_CreateCarbonWindowView(HWND viewpar, void **wref, const RECT* r, bool wantcomp) // window is created with a root control +{ + RECT wndr = *r; + ClientToScreen(viewpar, (POINT*)&wndr); + ClientToScreen(viewpar, (POINT*)&wndr+1); + //Rect r2 = { wndr.top, wndr.left, wndr.bottom, wndr.right }; + Rect r2 = { (short)wndr.bottom, (short)wndr.left, (short)wndr.top, (short)wndr.right }; + SWELL_hwndCarbonHost *w = [[SWELL_hwndCarbonHost alloc] initCarbonChild:(NSView*)viewpar rect:&r2 composit:wantcomp]; + if (w) *wref = [w->m_cwnd windowRef]; + return (HWND)w; +} + +void* SWELL_GetWindowFromCarbonWindowView(HWND cwv) +{ + SWELL_hwndCarbonHost* w = (SWELL_hwndCarbonHost*)cwv; + if (WDL_NORMALLY(w)) return [w->m_cwnd windowRef]; + return 0; +} + +void SWELL_AddCarbonPaneToView(HWND cwv, void* pane) // not currently used +{ +#ifndef __LP64__ + SWELL_hwndCarbonHost* w = (SWELL_hwndCarbonHost*)cwv; + if (WDL_NORMALLY(w)) + { + WindowRef wndref = (WindowRef)[w->m_cwnd windowRef]; + if (wndref) + { + EventTypeSpec ctlevts[] = + { + //{ kEventClassControl, kEventControlInitialize }, + //{ kEventClassControl, kEventControlDraw }, + { kEventClassControl, kEventControlBoundsChanged }, + }; + int nctlevts = sizeof(ctlevts)/sizeof(EventTypeSpec); + + EventHandlerRef ctlhandler = (EventHandlerRef)w->m_ctlhandler; + InstallControlEventHandler((ControlRef)pane, CarbonEvtHandler, nctlevts, ctlevts, w, &ctlhandler); + } + } +#endif +} + +void SWELL_SetWindowFlip(HWND hwnd, bool flip) +{ + SWELL_hwndChild * hc = (SWELL_hwndChild*)hwnd; + if (WDL_NORMALLY(hc && [hc isKindOfClass:[SWELL_hwndChild class]])) + { + hc->m_flip = flip; + } +} + + +static char* s_dragdropsrcfn = 0; +static void (*s_dragdropsrccallback)(const char*) = 0; + +void SWELL_InitiateDragDrop(HWND hwnd, RECT* srcrect, const char* srcfn, void (*callback)(const char* dropfn)) +{ + SWELL_FinishDragDrop(); + + if (![(id)hwnd isKindOfClass:[SWELL_hwndChild class]]) return; + + s_dragdropsrcfn = strdup(srcfn); + s_dragdropsrccallback = callback; + + char* p = s_dragdropsrcfn+strlen(s_dragdropsrcfn)-1; + while (p >= s_dragdropsrcfn && *p != '.') --p; + ++p; + + NSString* str = (NSString*)SWELL_CStringToCFString(p); + NSRect r = NSMakeRect(srcrect->left, srcrect->top, srcrect->right-srcrect->left, srcrect->bottom-srcrect->top); + NSEvent* evt = [NSApp currentEvent]; + [(NSView*)hwnd dragPromisedFilesOfTypes:[NSArray arrayWithObject:str] fromRect:r source:(NSView*)hwnd slideBack:YES event:evt]; + [str release]; +} + +// owner owns srclist, make copies here etc +void SWELL_InitiateDragDropOfFileList(HWND hwnd, RECT *srcrect, const char **srclist, int srccount, HICON icon) +{ + SWELL_FinishDragDrop(); + + if (![(id)hwnd isKindOfClass:[SWELL_hwndChild class]]) return; + + NSMutableArray *ar = [[NSMutableArray alloc] initWithCapacity:srccount]; + int x; + + for(x=0;xleft,srcrect->top) offset:NSMakeSize(0,0) event:evt pasteboard:pb source:(id)hwnd slideBack:YES]; + + [ar release]; +} + + +static bool _file_exists(const char* fn) +{ + struct stat sb= { 0 }; + return !stat(fn, &sb); +} + +NSArray* SWELL_DoDragDrop(NSURL* droplocation) +{ + NSArray* fnarr=0; + if (s_dragdropsrcfn && s_dragdropsrccallback && droplocation) + { + const char* srcpath=s_dragdropsrcfn; + + const char* fn = srcpath+strlen(srcpath)-1; + while (fn >= srcpath && *fn != '/') --fn; + ++fn; + + WDL_String destpath; + destpath.SetFormatted(4096, "%s/%s", [[droplocation path] UTF8String], fn); + + bool ok=!_file_exists(destpath.Get()); + if (!ok) + { + NSInteger ret=NSRunAlertPanel(@"Copy", + @"An item named \"%s\" already exists in this location. Do you want to replace it with the one you're moving?", + @"Keep Both Files", @"Stop", @"Replace", fn); + + if (ret == -1) // replace + { + ok=true; + } + else if (ret == 1) // keep both + { + WDL_String base(destpath.Get()); + char* p=base.Get(); + size_t len=strlen(p); + const char* ext=""; + int incr=0; + + const char* q=fn+strlen(fn)-1; + while (q > fn && *q != '.') --q; + if (*q == '.') + { + ext=q; + len -= strlen(ext); + p[len]=0; + } + + int digits=0; + int i; + for (i=0; i < 3 && len > i+1 && isdigit(p[len-i-1]); ++i) ++digits; + if (len > digits+1 && (p[len-digits-1] == ' ' || p[len-digits-1] == '-' || p[len-digits-1] == '_')) + { + incr=atoi(p+len-digits); + p[len-digits]=0; + } + else + { + base.Append(" "); + } + + WDL_String trypath; + while (!ok && ++incr < 1000) + { + trypath.SetFormatted(4096, "%s%03d%s", base.Get(), incr, ext); + ok=!_file_exists(trypath.Get()); + } + + if (ok) destpath.Set(trypath.Get()); + } + } + + if (ok) + { + s_dragdropsrccallback(destpath.Get()); + ok=_file_exists(destpath.Get()); + } + + if (ok) + { + fn=destpath.Get(); + fn += strlen(fn)-1; + while (fn >= destpath.Get() && *fn != '/') --fn; + ++fn; + + NSString* nfn=(NSString*)SWELL_CStringToCFString(fn); + fnarr=[NSArray arrayWithObject:nfn]; + [nfn release]; + } + } + + SWELL_FinishDragDrop(); + return fnarr; +} + +void SWELL_FinishDragDrop() +{ + free(s_dragdropsrcfn); + s_dragdropsrcfn = 0; + s_dragdropsrccallback = 0; +} + +bool SWELL_SetGLContextToView(HWND h) +{ + if (!h) [NSOpenGLContext clearCurrentContext]; + else if (WDL_NORMALLY([(id)h isKindOfClass:[SWELL_hwndChild class]])) + { + SWELL_hwndChild *hc = (SWELL_hwndChild*)h; + if (hc->m_glctx) + { + [hc->m_glctx makeCurrentContext]; + return true; + } + } + return false; +} + +void SWELL_SetViewGL(HWND h, char wantGL) +{ + if (WDL_NORMALLY(h && [(id)h isKindOfClass:[SWELL_hwndChild class]])) + { + SWELL_hwndChild *hc = (SWELL_hwndChild*)h; + if (!!wantGL != !!hc->m_glctx) + { + if (wantGL) + { + if (wantGL == 2 && SWELL_GetOSXVersion()>=0x1070) [(NSView *)h setWantsBestResolutionOpenGLSurface:YES]; + + NSOpenGLPixelFormatAttribute atr[] = { + (NSOpenGLPixelFormatAttribute)96/*NSOpenGLPFAAllowOfflineRenderers*/, // allows use of NSSupportsAutomaticGraphicsSwitching and no gpu-forcing + (NSOpenGLPixelFormatAttribute)0 + }; // todo: optionally add any attributes before the 0 + if (SWELL_GetOSXVersion() < 0x1050) atr[0]=(NSOpenGLPixelFormatAttribute)0; // 10.4 can't use offline renderers and will fail trying + + NSOpenGLPixelFormat *fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:atr]; + + hc->m_glctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:nil]; + [fmt release]; + } + else + { + if ([NSOpenGLContext currentContext] == hc->m_glctx) [NSOpenGLContext clearCurrentContext]; + [hc->m_glctx release]; + hc->m_glctx=0; + } + } + + } +} + +bool SWELL_GetViewGL(HWND h) +{ + return WDL_NORMALLY(h && [(id)h isKindOfClass:[SWELL_hwndChild class]]) && ((SWELL_hwndChild*)h)->m_glctx; +} +void DrawSwellViewRectImpl(SWELL_hwndChild *view, NSRect rect, HDC hdc, bool isMetal) +{ + if (view->m_hashaddestroy) + { + return; + } + view->m_paintctx_hdc=hdc; + if (view->m_paintctx_hdc) + { + view->m_paintctx_hdc->GLgfxctx = view->m_glctx; + if (view->m_glctx) + { + [view->m_glctx setView:view]; + [view->m_glctx makeCurrentContext]; + [view->m_glctx update]; + } + } + view->m_paintctx_rect=rect; + view->m_paintctx_used=false; + DoPaintStuff(view->m_wndproc,(HWND)view,view->m_paintctx_hdc,&view->m_paintctx_rect,isMetal); + + if (view->m_paintctx_hdc) + { + if (view->m_glctx && [NSOpenGLContext currentContext] == view->m_glctx) + { + [NSOpenGLContext clearCurrentContext]; + } + view->m_paintctx_hdc->GLgfxctx = NULL; + } + view->m_paintctx_hdc=0; + if (!view->m_paintctx_used) { + /*[super drawRect:rect];*/ + } + +#if 0 + // debug: show everything + static CGColorSpaceRef cspace; + if (!cspace) cspace=CGColorSpaceCreateDeviceRGB(); + float cols[4]={0.0f,1.0f,0.0f,0.8f}; + CGColorRef color=CGColorCreate(cspace,cols); + + CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + CGContextSetStrokeColorWithColor(ctx,color); + CGContextStrokeRectWithWidth(ctx, CGRectMake(rect.origin.x,rect.origin.y,rect.size.width,rect.size.height), 1); + + CGColorRelease(color); + + cols[0]=1.0f; + cols[1]=0.0f; + cols[2]=0.0f; + cols[3]=1.0f; + color=CGColorCreate(cspace,cols); + + NSRect rect2=[view bounds]; + CGContextSetStrokeColorWithColor(ctx,color); + CGContextStrokeRectWithWidth(ctx, CGRectMake(rect2.origin.x,rect2.origin.y,rect2.size.width,rect2.size.height), 1); + + + CGColorRelease(color); + + cols[0]=0.0f; + cols[1]=0.0f; + cols[2]=1.0f; + cols[3]=0.7f; + color=CGColorCreate(cspace,cols); + cols[3]=0.25; + cols[2]=0.5; + CGColorRef color2=CGColorCreate(cspace,cols); + + NSArray *ar = [view subviews]; + if (ar) + { + int x; + for(x=0;x<[ar count];x++) + { + NSView *v = [ar objectAtIndex:x]; + if (v && ![v isHidden]) + { + NSRect rect = [v frame]; + CGContextSetStrokeColorWithColor(ctx,color); + CGContextStrokeRectWithWidth(ctx, CGRectMake(rect.origin.x,rect.origin.y,rect.size.width,rect.size.height), 1); + CGContextSetFillColorWithColor(ctx,color2); + CGContextFillRect(ctx, CGRectMake(rect.origin.x,rect.origin.y,rect.size.width,rect.size.height)); + } + } + + // draw children + } + CGColorRelease(color); + CGColorRelease(color2); + +#endif + + + +} + +void swellRenderOptimizely(int passflags, SWELL_hwndChild *view, HDC hdc, BOOL doforce, WDL_PtrList *needdraws, const NSRect *rlist, NSInteger rlistcnt, int draw_xlate_x, int draw_xlate_y, bool iscv, NSView *rlist_coordview) +{ + if (view->m_isdirty&1) doforce=true; + NSArray *sv = [view subviews]; + if (doforce&&(passflags & ([sv count]?1:2))) + { + NSRect drawr = [view bounds]; + if (rlistcnt > 0) + { + if (view != rlist_coordview) drawr = [rlist_coordview convertRect:drawr fromView:view]; + + int x; + NSRect update_rect = rlist[0]; + for(x=1;x 0.0 && + drawr.size.height > 0.0 && + view != rlist_coordview) drawr = [rlist_coordview convertRect:drawr toView:view]; + + // if drawr is empty, might be good to update it back to bounds? if something stops painting right that would be a good thing to check + } + if (drawr.size.width > 0.0 && drawr.size.height > 0.0) + DrawSwellViewRectImpl(view,drawr, hdc); + } + + if (sv) + { + [sv retain]; + const NSInteger n=[sv count]; + HBRUSH bgbr=0; + bool bgbr_valid=false; + for (NSInteger x=0;xm_isdirty)|| [v needsDisplay]) + { + if (isSwellChild && ((SWELL_hwndChild *)v)->m_allow_nomiddleman) + { + NSRect fr = [v frame]; + CGContextSaveGState(hdc->ctx); + CGContextClipToRect(hdc->ctx,CGRectMake(fr.origin.x,fr.origin.y,fr.size.width,fr.size.height)); + CGContextTranslateCTM(hdc->ctx, fr.origin.x,fr.origin.y); + swellRenderOptimizely(passflags,(SWELL_hwndChild*)v,hdc,doforce,needdraws,rlist,rlistcnt,draw_xlate_x-(int)fr.origin.x,draw_xlate_y-(int)fr.origin.y,false,rlist_coordview); + CGContextRestoreGState(hdc->ctx); + if (passflags&2) [v setNeedsDisplay:NO]; + bgbr_valid=false; // code in swellRenderOptimizely() may trigger WM_CTLCOLORDLG which may invalidate our brush, so clear the cached value here + } + else if (passflags&1) + { + if ([v isKindOfClass:[NSScrollView class]]) + { + NSView *ccv = [(NSScrollView *)v contentView]; + if (ccv) + { + [v retain]; + needdraws->Add((void*)(INT_PTR)(doforce?1:0)); + needdraws->Add(v); + v=ccv; + } + } + [v retain]; + if (!doforce && ![v isOpaque]) + { + + NSRect fr= [v frame]; + + // we could recursively go up looking for WM_CTLCOLORDLG, but actually we just need to use the current window + if (!bgbr_valid) // note that any code in this loop that does anything that could trigger messages might invalidate bgbr, so it should clear bgbr_checked here + { + bgbr=(HGDIOBJ)SendMessage((HWND)view,WM_CTLCOLORDLG,(WPARAM)hdc,(LPARAM)view); + bgbr_valid=true; + } + + if (!iscv) fr = [view convertRect:fr toView:[[view window] contentView]]; + + int ri; + for(ri=0;ri0 && ff.size.height>0) + { + RECT r; + NSRECT_TO_RECT(&r,ff); + r.left+=draw_xlate_x; + r.right+=draw_xlate_x; + r.top+=draw_xlate_y; + r.bottom+=draw_xlate_y; + if (bgbr_valid && bgbr && bgbr != (HBRUSH)1) FillRect(hdc,&r,bgbr); + else SWELL_FillDialogBackground(hdc,&r,3); + } + } + } + needdraws->Add((void*)(INT_PTR)(doforce?1:0)); + needdraws->Add(v); + } + } + } + } + [sv release]; + } + if (passflags&2) + view->m_isdirty=0; +} + +#ifndef SWELL_NO_METAL + + +static void metalUpdateProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + swell_updateAllMetalDirty(); +} + +static bool mtl_init() +{ + static int state; + if (!state) + { + state=-1; + + if (SWELL_GetOSXVersion() < 0x10b0) return false; + + dlopen("/System/Library/Frameworks/QuartzCore.framework/Versions/Current/QuartzCore",RTLD_LAZY); + void *lib = dlopen("/System/Library/Frameworks/Metal.framework/Versions/Current/Metal",RTLD_LAZY); + void *cgf = dlopen("/System/Library/Frameworks/CoreGraphics.framework/Versions/Current/CoreGraphics",RTLD_LAZY); + if (!lib || !cgf) return false; + NSArray *(*__MTLCopyAllDevices)(void); + *(void **)&__MTLCopyAllDevices = dlsym(lib,"MTLCopyAllDevices"); + *(void **)&__MTLCreateSystemDefaultDevice = dlsym(lib,"MTLCreateSystemDefaultDevice"); + *(void **)&__CGDirectDisplayCopyCurrentMetalDevice = dlsym(cgf,"CGDirectDisplayCopyCurrentMetalDevice"); + + if (__MTLCreateSystemDefaultDevice && + __MTLCopyAllDevices && + (__class_CAMetalLayer = objc_getClass("CAMetalLayer")) && + (__class_MTLRenderPassDescriptor = objc_getClass("MTLRenderPassDescriptor")) && + (__class_MTLRenderPipelineDescriptor = objc_getClass("MTLRenderPipelineDescriptor")) && + (__class_MTLTextureDescriptor = objc_getClass("MTLTextureDescriptor")) && + (__class_MTLCompileOptions = objc_getClass("MTLCompileOptions")) + ) + { + NSArray *ar = __MTLCopyAllDevices(); + NSUInteger cnt = [ar count]; + [ar release]; + if (cnt>0) + { + state=1; + SetTimer(NULL,1,1,metalUpdateProc); + } + } + } + + return state>0; +} + + +#ifndef __ppc__ +static void SWELL_fastDoubleUpImage(unsigned int *op, const unsigned int *ip, int w, int h, int sw, int newspan) +{ + int y = h; + while (y-->0) + { + const unsigned int *rd = ip; + unsigned int *wr = op; + int remaining = w; + +#if 0 // def __AVX__ + // this isn't really any faster than SSE anyway + if (remaining >= 8) + { + if (!((INT_PTR)rd & 31)) + { + int x = remaining/8; + while (x-->0) + { + const __m256 m = _mm256_load_ps((const float *)rd); + rd+=8; + + const __m256 p1 = _mm256_permutevar_ps(_mm256_permute2f128_ps(m,m,0),_mm256_set_epi32(3,3,2,2,1,1,0,0)); + const __m256 p2 = _mm256_permutevar_ps(_mm256_permute2f128_ps(m,m,1|(1<<4)),_mm256_set_epi32(3,3,2,2,1,1,0,0)); + + unsigned int *wr2 = wr+newspan; + _mm256_store_ps((float*)wr,p1); + _mm256_store_ps((float*)wr2,p1); + + _mm256_store_ps((float*)wr + 8,p2); + _mm256_store_ps((float*)wr2 + 8,p2); + + wr += 16; + } + remaining &= 7; + + } + } +#endif + +#ifdef __SSE__ + if (remaining >= 4) + { + // with SSE is about 2x faster than without + if (((INT_PTR)rd & 7)) + { + // input isn't 8 byte aligned, must use unaligned reads + int x = remaining/4; + while (x-->0) + { + __m128 m = _mm_loadu_ps((const float *)rd); + __m128 p1 = _mm_shuffle_ps(m,m,_MM_SHUFFLE(1,1,0,0)); + __m128 p2 = _mm_shuffle_ps(m,m,_MM_SHUFFLE(3,3,2,2)); + + unsigned int *wr2 = wr+newspan; + rd+=4; + + _mm_store_ps((float*)wr,p1); + _mm_store_ps((float*)wr2,p1); + + _mm_store_ps((float*)wr + 4,p2); + _mm_store_ps((float*)wr2 + 4,p2); + + wr += 8; + } + } + else + { + // if rd is 8 byte aligned, we can do SSE without unaligned reads + + // but if it is not 16 byte aligned, we need to preprocess a pair of pixels + // (advancing rd by 8 bytes, and wr by 16) + + if ((INT_PTR)rd & 15) + { + unsigned int *nwr = wr+newspan; + wr[0] = wr[1] = nwr[0] = nwr[1] = rd[0]; + wr[2] = wr[3] = nwr[2] = nwr[3] = rd[1]; + wr+=4; + rd+=2; + remaining-=2; + } + + int x = remaining/4; + while (x-->0) + { + __m128 m = _mm_load_ps((const float *)rd); + __m128 p1 = _mm_shuffle_ps(m,m,_MM_SHUFFLE(1,1,0,0)); + __m128 p2 = _mm_shuffle_ps(m,m,_MM_SHUFFLE(3,3,2,2)); + + unsigned int *wr2 = wr+newspan; + rd+=4; + + _mm_store_ps((float*)wr,p1); + _mm_store_ps((float*)wr2,p1); + + _mm_store_ps((float*)wr + 4,p2); + _mm_store_ps((float*)wr2 + 4,p2); + + wr += 8; + } + } + remaining &= 3; + } +#endif //__SSE__ + + int x = remaining/2; + while (x-->0) + { + unsigned int *nwr = wr+newspan; + wr[0] = wr[1] = nwr[0] = nwr[1] = rd[0]; + wr[2] = wr[3] = nwr[2] = nwr[3] = rd[1]; + rd+=2; + wr+=4; + } + if (remaining&1) + { + wr[0] = wr[1] = wr[newspan] = wr[newspan+1] = *rd; + } + ip += sw; + op += newspan*2; + } +} +#endif + +static bool SWELL_Metal_ReadTex(SWELL_hwndChild *wnd, unsigned int *destbuf, int x, int y, int w, int h, int span) +{ + id tex = (id) wnd->m_metal_texture; + const int texw = (int)tex.width, texh = (int)tex.height; + if (WDL_NOT_NORMALLY(x<0) || + WDL_NOT_NORMALLY(y<0) || + WDL_NOT_NORMALLY(x+w > texw) || + WDL_NOT_NORMALLY(y+h > texh) || + WDL_NOT_NORMALLY(span>24); + if (alpha) + { + if (alpha<255) + { + const unsigned int dp = *dest; + const unsigned int ra=256-alpha; + *dest++ = (((dp&0xff) * ra + (v&0xff)*alpha) >> 8) + + ((((dp&0xff00) * ra + (v&0xff00)*alpha) >> 8) & 0xff00) + + ((((dp&0xff0000) * ra + (v&0xff0000)*alpha) >> 8) & 0xff0000); + } + else + *dest++ = v; + } + else + dest++; + } +} + +static void SWELL_Metal_WriteTex(SWELL_hwndChild *wnd, const unsigned int *srcbuf, int x, int y, int w, int h, int span, bool retina_hint) +{ + id tex = (id) wnd->m_metal_texture; + int texw = 0, texh = 0; + if (tex) + { + texw = (int)tex.width; + texh = (int)tex.height; + } + if (!wnd->m_metal_dc_dirty) + { + NSRect bounds = [wnd bounds]; + if (bounds.size.width < 1 || bounds.size.height < 1) return; + + wnd->m_metal_retina = retina_hint; + if (retina_hint) + { + bounds.size.width *= 2.0; + bounds.size.height *= 2.0; + } + + LIMIT_METAL_BOUNDS_SIZE(bounds.size) + + if (wnd->m_use_metal == 1) // direct + { + CAMetalLayer *layer = (CAMetalLayer *)[wnd layer]; + if (layer) + { + CGSize oldsc = layer.drawableSize; + if (oldsc.width != bounds.size.width || oldsc.height != bounds.size.height) + { + CGSize ns; + ns.width = bounds.size.width; + ns.height = bounds.size.height; + layer.drawableSize = ns; + layer.contentsScale = retina_hint ? 2.0 : 1.0; + } + tex = [(wnd->m_metal_drawable = [layer nextDrawable]) texture]; + wnd->m_metal_texture = tex; + } + } + else + { + const int want_w = ((int)bounds.size.width + 3) & ~3, want_h = ((int)bounds.size.height + 3)&~3; + // + if (!tex || texw < want_w || texh < want_h || texw > want_w * 2 || texh > want_h * 2) + { + [wnd->m_metal_texture release]; + MTLTextureDescriptor *textureDescriptor = [[__class_MTLTextureDescriptor alloc] init]; + textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; + textureDescriptor.width = want_w; + textureDescriptor.height = want_h; + tex = [wnd->m_metal_device newTextureWithDescriptor:textureDescriptor]; + wnd->m_metal_texture = tex; + + [textureDescriptor release]; + } + } + if (tex) + { + texw = (int)tex.width; + texh = (int)tex.height; + } + } + wnd->m_metal_dc_dirty=1; + + if (!tex) return; + + if (x<0) { w += x; srcbuf -= x; x=0; } + if (y<0) { h += y; srcbuf -= y*span; y=0; } + if (x+w > texw) w = texw-x; + if (y+h > texh) h = texh-y; + + if (w<1 || h<1) return; + + MTLRegion region = { { (NSUInteger)x, (NSUInteger)y, 0 }, {(NSUInteger)w,(NSUInteger)h, 1} }; + [tex replaceRegion:region mipmapLevel:0 withBytes:srcbuf bytesPerRow:span*4]; +} + +static WDL_TypedBuf s_metal_tmp; +void SWELL_Metal_Blit(void *_tex, const unsigned int *srcbuf, int x, int y, int w, int h, int span, bool retina_hint, bool use_alpha) +{ + if (!_tex) return; + SWELL_hwndChild *wnd = (SWELL_hwndChild *)_tex; + if (wnd->m_metal_dc_dirty) + { + if (retina_hint) + { + // source is retina + if (!wnd->m_metal_retina) + { + if (wnd->m_use_metal > 1) + { + NSLog(@"swell-cocoa: PERFORMANCE WARNING: metal non-retina drawing followed by retina drawing, calling code should be fixed.\n"); + if (WDL_NORMALLY(wnd->m_metal_texture)) + { + id tex = (id) wnd->m_metal_texture; + const int texw = (int)tex.width, texh = (int)tex.height; + const int oldspan = (texw+3)&~3, newspan = (texw*2+3)&~3; + unsigned int *dblframe = s_metal_tmp.ResizeOK(oldspan*texh + newspan*texh*2,false); + if (WDL_NOT_NORMALLY(!dblframe)) return; + unsigned int *frame = dblframe + newspan*texh*2; + if (SWELL_Metal_ReadTex(wnd,frame,0,0,texw,texh,oldspan)) + SWELL_fastDoubleUpImage(dblframe,frame,texw,texh,oldspan,newspan); + else + memset(dblframe,0,newspan*sizeof(int)*texh*2); + + if (x<0) { w += x; srcbuf -= x; x=0; } + if (y<0) { h += y; srcbuf -= y*span; y=0; } + if (x+w > texw*2) w=texw*2-x; + if (y+h > texh*2) h=texh*2-y; + + // blit our image into the full image + if (w>0 && h>0) + { + unsigned int *wr = dblframe + x + y*newspan; + const unsigned int *rd = srcbuf; + for (int i = 0; i < h; i ++) + { + if (use_alpha) + srcalphablend_line(wr, rd, w); + else + memcpy(wr, rd, w*sizeof(int)); + wr += newspan; + rd += span; + } + } + + // draw full image back + x=y=0; + w = texw*2; + h = texh*2; + span = newspan; + srcbuf = dblframe; + wnd->m_metal_dc_dirty = false; + use_alpha = false; + } + } + else + { + NSLog(@"swell-cocoa: DRAWING UNSUPPORTED: metal direct-mode non-retina drawing followed by retina drawing, will cause incorrect display.\n"); + } + } + } + else + { + // source is not retina + if (wnd->m_metal_retina) + { + // upsample to retina + const int newspan = (w*2+3)&~3; + unsigned int *p = s_metal_tmp.ResizeOK(newspan*h*2 + 32/4,false); + if (WDL_NOT_NORMALLY(!p)) return; + const UINT_PTR align = (UINT_PTR)p & 31; + if (align) p += 32-align; + SWELL_fastDoubleUpImage(p,srcbuf,w,h,span,newspan); + srcbuf = p; + w*=2; + h*=2; + x*=2; + y*=2; + span = newspan; + retina_hint = true; + } + } + + if (use_alpha && WDL_NORMALLY(wnd->m_metal_texture)) + { + id tex = (id) wnd->m_metal_texture; + const int texw = (int)tex.width, texh = (int)tex.height; + if (x<0) { w += x; srcbuf -= x; x=0; } + if (y<0) { h += y; srcbuf -= y*span; y=0; } + if (x+w > texw) w=texw-x; + if (y+h > texh) h=texh-y; + if (w<0 || h<0) return; + + const int lspan = (w+3)&~3; + static WDL_TypedBuf s_metal_tmp2; // if we are in one of the corner case modes (e.g. upsampled to retina), use this + unsigned int *p; + if (srcbuf >= s_metal_tmp.Get() && srcbuf < s_metal_tmp.Get()+s_metal_tmp.GetSize()) p=s_metal_tmp2.ResizeOK(lspan*h,false); + else p=s_metal_tmp.ResizeOK(lspan*h,false); + + if (WDL_NORMALLY(p) && SWELL_Metal_ReadTex(wnd,p,x,y,w,h,lspan)) + { + unsigned int *wr = p; + const unsigned int *rd = srcbuf; + for (int i = 0; i < h; i ++) + { + srcalphablend_line(wr, rd, w); + wr += lspan; + rd += span; + } + srcbuf = p; + span = lspan; + } + } + } + SWELL_Metal_WriteTex(wnd, srcbuf, x, y, w, h, span, retina_hint); +} + +void SWELL_Metal_FillRect(void *_tex, int x, int y, int w, int h, int colori) +{ + if (!_tex || w<1 || h<1) return; + + SWELL_hwndChild *wnd = (SWELL_hwndChild *)_tex; + const bool retina_hint = wnd->m_metal_dc_dirty && wnd->m_metal_retina; + if (retina_hint) + { + x*=2; y*=2; w*=2; h*=2; + } + + const int npix = w*h; + if (WDL_NOT_NORMALLY(npix < 0)) return; // overflow + + const unsigned int color = (unsigned int)colori; + unsigned int tmp[4096], *buf=tmp; + if (((unsigned int)npix*sizeof(int)) > sizeof(tmp)) + { + buf = s_metal_tmp.ResizeOK(npix); + if (WDL_NOT_NORMALLY(!buf)) return; + } + for (int i = 0; i < npix; i++) buf[i] = color; + SWELL_Metal_WriteTex(wnd,buf,x,y,w,h,w, retina_hint); +} + +WDL_PtrList s_mtl_dirty_list; + +#endif + +int SWELL_EnableMetal(HWND hwnd, int mode) +{ +#ifndef SWELL_NO_METAL + if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_hwndChild class]])) return 0; + + SWELL_hwndChild *ch = (SWELL_hwndChild *)hwnd; + if (g_swell_nomiddleman_cocoa_override==0 && !ch->m_use_metal) + { + if (mode < 0) + { + ch->m_use_metal = mode; + [ch setWantsLayer:YES]; + if (mode == -1 && SWELL_GetOSXVersion() >= 0x1080) + [[ch layer] setDrawsAsynchronously:YES]; + } + else if (mode>0 && mtl_init()) + { + ch->m_use_metal = mode>1 ? 2 : mode; + [ch setWantsLayer:YES]; + InvalidateRect(hwnd,NULL,FALSE); + } + } + return ch->m_use_metal; +#else + return 0; +#endif +} + + +void swell_updateAllMetalDirty() // run from a timer, or called by UpdateWindow() +{ +#ifndef SWELL_NO_METAL + static bool r; + if (r) return; + r=true; + + int x = s_mtl_dirty_list.GetSize(); + while (--x>=0) + { + SWELL_hwndChild *slf = s_mtl_dirty_list.Get(x); + if (!slf) break; // deleted out from under us?! + + const RECT tr = slf->m_metal_in_needref_rect; + s_mtl_dirty_list.Delete(x); + slf->m_metal_in_needref_list=false; + memset(&slf->m_metal_in_needref_rect,0,sizeof(slf->m_metal_in_needref_rect)); + + [slf swellDrawMetal:&tr]; + } + + r=false; +#endif +} + + +#ifndef SWELL_NO_METAL + +void swell_addMetalDirty(SWELL_hwndChild *slf, const RECT *r, bool isReleaseDC) +{ + if (!slf) return; + if (isReleaseDC) + { + // just tag it dirty + } + else if (!r) + { + slf->m_metal_in_needref_rect.left = slf->m_metal_in_needref_rect.top = 0; + slf->m_metal_in_needref_rect.right = slf->m_metal_in_needref_rect.bottom = (1<<28); + } + else + { + if (r->right <= r->left || r->bottom <= r->top) return; // no rect + + WinUnionRect(&slf->m_metal_in_needref_rect,&slf->m_metal_in_needref_rect,r); + } + if (!slf->m_metal_in_needref_list) + { + slf->m_metal_in_needref_list=true; + + WDL_ASSERT(s_mtl_dirty_list.Find(slf)<0); + s_mtl_dirty_list.Add(slf); + } +} + +void swell_removeMetalDirty(SWELL_hwndChild *slf) +{ + WDL_ASSERT(!slf || (slf->m_metal_in_needref_list == (s_mtl_dirty_list.Find(slf)>=0))); + if (slf && slf->m_metal_in_needref_list) + { + slf->m_metal_in_needref_list=false; + memset(&slf->m_metal_in_needref_rect,0,sizeof(slf->m_metal_in_needref_rect)); + s_mtl_dirty_list.DeletePtr(slf); + } +} + +#endif + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-dlggen.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-dlggen.h new file mode 100644 index 000000000..37e19e25d --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-dlggen.h @@ -0,0 +1,274 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + DialogBox emulation is here. To declare the resource at a global level, use (in any source file that includes this file and resource.h): + + + #ifdef MAC + + + SWELL_DEFINE_DIALOG_RESOURCE_BEGIN(IDD_SOMEDIALOG,0,"Dialog Box Title",222,55,1.8) // width, height, scale (1.8 is usually good) + + BEGIN + DEFPUSHBUTTON "OK",IDOK,117,33,47,14 + CONTROL "Expand MIDI tracks to new REAPER tracks ",IDC_CHECK1, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,7,214,10 + CONTROL "Merge MIDI tempo map to project tempo map at ", + IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,19, + 214,10 + PUSHBUTTON "Cancel",IDCANCEL,168,33,50,14 + END + + SWELL_DEFINE_DIALOG_RESOURCE_END(IDD_SOMEDIALOG) + + + #endif + + + This file also provides functions to dynamically create controls in a view from a win32 resource script. + + + +*/ + + + + +#ifndef _SWELL_DLGGEN_H_ +#define _SWELL_DLGGEN_H_ + +#ifdef BEGIN +#undef BEGIN +#endif + +#ifdef END +#undef END +#endif + + +struct SWELL_DlgResourceEntry +{ + const char *str1; + int flag1; + const char *str2; + + int p1; // often used for ID + + // todo: see at runtime if some of these can be short instead of int (p2-p6 probably can, but not completely sure) -- i.e. do we use any + // big values in flags. note: it can't be unsigned short, because we want negative values. + int p2, p3, p4, p5, p6; // meaning is dependent on class +}; + + +#define BEGIN {NULL, +#define END }, + +#define PUSHBUTTON }, { "__SWELL_BUTTON", 0, +#define DEFPUSHBUTTON }, { "__SWELL_BUTTON", 1, +#define EDITTEXT }, { "__SWELL_EDIT", 0, "", +#define CTEXT }, { "__SWELL_LABEL", 0, +#define LTEXT }, { "__SWELL_LABEL", -1, +#define RTEXT }, { "__SWELL_LABEL", 1, +#define CONTROL }, { +#define COMBOBOX }, { "__SWELL_COMBO", 0, "", +#define GROUPBOX }, { "__SWELL_GROUP", 0, +#define CHECKBOX }, { "__SWELL_CHECKBOX", 0, +#define LISTBOX }, { "__SWELL_LISTBOX", 0, "", +#define ICON }, { "__SWELL_ICON", 0, (const char*)(INT_PTR) + +#define NOT + +// flags we may use +#define CBS_DROPDOWNLIST 0x0003L +#define CBS_DROPDOWN 0x0002L +#define CBS_SORT 0x0100L +#define ES_PASSWORD 0x0020L +#define ES_READONLY 0x0800L +#define ES_WANTRETURN 0x1000L +#define ES_NUMBER 0x2000L + +#define SS_LEFT 0 +#define SS_CENTER 0x1L +#define SS_RIGHT 0x2L +#define SS_BLACKRECT 0x4L +#define SS_BLACKFRAME (SS_BLACKRECT) +#define SS_LEFTNOWORDWRAP 0xCL +#define SS_ETCHEDHORZ 0x10L +#define SS_ETCHEDVERT 0x11L +#define SS_ETCHEDFRAME 0x12L +#define SS_TYPEMASK 0x1FL +#define SS_NOTIFY 0x0100L + +#define BS_LEFTTEXT 0x0020L + +#define BS_LEFT 0x100L +#define BS_CENTER 0x300L +#define BS_XPOSITION_MASK BS_CENTER + +#define BS_GROUPBOX 0x20000000 +#define BS_DEFPUSHBUTTON 0x10000000 +#define BS_PUSHBUTTON 0x8000000 + +#define LVS_LIST 0 /* 0x0003 */ +#define LVS_NOCOLUMNHEADER 0x4000 +#define LVS_NOSORTHEADER 0x8000 +#define LVS_REPORT 0x0001 +#define LVS_TYPEMASK 0x0003 +#define LVS_SINGLESEL 0x0004 +#define LVS_OWNERDATA 0x1000 +#define LVS_SORTASCENDING 0x0010 +#define LVS_SORTDESCENDING 0x0020 + +#define LBS_SORT 0x0002L +#define LBS_OWNERDRAWFIXED 0x0010L +#define LBS_EXTENDEDSEL 0x0800L + +#define ES_LEFT 0 +#define ES_CENTER 1 +#define ES_RIGHT 2 +#define ES_MULTILINE 4 +#define ES_AUTOHSCROLL 0x80 + +// flags we ignore +#define LVS_SHOWSELALWAYS 0 +#define LVS_SHAREIMAGELISTS 0 +#define ES_AUTOVSCROLL 0 +#define GROUP 0 +#define PBS_SMOOTH 0 +#define CBS_AUTOHSCROLL 0 +#define TBS_NOTICKS 0 +#define TBS_TOP 0 +#define TBS_BOTH 0 +#define LBS_NOINTEGRALHEIGHT 0 +#define TVS_HASLINES 0 +#define TVS_LINESATROOT 0 +#define TVS_SHOWSELALWAYS 0 +#define TVS_HASBUTTONS 0 +#define TVS_TRACKSELECT 0 +#define TVS_NONEVENHEIGHT 0 +#define TVS_NOTOOLTIPS 0 +#define BS_FLAT 0 +#define SS_SUNKEN 0 +#define BS_RIGHT 0 +#define WS_EX_STATICEDGE 0 +#define WS_EX_RIGHT 0 +#define SS_CENTERIMAGE 0 +#define SS_NOPREFIX 0 + +// more ignore flags for vc11+ +#define LVS_ALIGNLEFT 0 /* 0x0800 */ + +#ifndef IDC_STATIC +#define IDC_STATIC 0 +#endif + + + + +#define SWELL_DLG_WS_CHILD 1 +#define SWELL_DLG_WS_RESIZABLE 2 +#define SWELL_DLG_WS_FLIPPED 4 +#define SWELL_DLG_WS_NOAUTOSIZE 8 +#define SWELL_DLG_WS_OPAQUE 16 +#define SWELL_DLG_WS_DROPTARGET 32 + +typedef struct SWELL_DialogResourceIndex +{ + const char *resid; + const char *title; + int windowTypeFlags; + void (*createFunc)(HWND, int); + int width,height; + struct SWELL_DialogResourceIndex *_next; +} SWELL_DialogResourceIndex; + +typedef struct SWELL_CursorResourceIndex +{ + const char *resid; + const char *resname; + POINT hotspot; + HCURSOR cachedCursor; + struct SWELL_CursorResourceIndex *_next; +} SWELL_CursorResourceIndex; + + +class SWELL_DialogRegHelper { + public: + SWELL_DialogResourceIndex m_rec; + SWELL_DialogRegHelper(SWELL_DialogResourceIndex **h, void (*cf)(HWND,int), int recid, int flags, const char *titlestr, int wid, int hei, double scale) + { + if (recid) + { + m_rec.resid=MAKEINTRESOURCE(recid); + m_rec.title=titlestr; + m_rec.windowTypeFlags=flags; + m_rec.createFunc=cf; + m_rec.width=(int)((wid)*(scale)); + m_rec.height=(int)((hei)*(scale)); + m_rec._next=*h; + *h = &m_rec; + } + } +}; + +#ifdef _DEBUG + #include "../assocarray.h" + class SWELL_DialogRegValidator + { + public: + SWELL_DialogRegValidator(const SWELL_DlgResourceEntry *recs, size_t recs_sz) + { + if (recs_sz>1) + { + // check for duplicate IDs + WDL_IntKeyedArray tmp; + for (size_t x = 0; x < recs_sz; x ++) + { + const SWELL_DlgResourceEntry *list = recs + x; + const int idx = strncmp(list->str1,"__SWELL_",8) ? list->flag1 : list->p1; + if (idx != 0 && idx != -1) + { + WDL_ASSERT(!tmp.Get(idx)); + tmp.Insert(idx,true); + } + } + } + } + }; + #define SWELL_VALIDATE_DIALOG_RESOURCE(v,r) static SWELL_DialogRegValidator v(r+1, sizeof(r)/sizeof(r[0])-1); +#else + #define SWELL_VALIDATE_DIALOG_RESOURCE(v,r) +#endif + + +#define SWELL_DEFINE_DIALOG_RESOURCE_BEGIN(recid, flags, titlestr, wid, hei, scale) \ + static void SWELL__dlg_cf__##recid(HWND view, int wflags); \ + const float __swell_dlg_scale__##recid = (float) (scale); \ + static SWELL_DialogRegHelper __swell_dlg_helper_##recid(&SWELL_curmodule_dialogresource_head, SWELL__dlg_cf__##recid, recid,flags,titlestr,wid,hei,scale); \ + static const SWELL_DlgResourceEntry __swell_dlg_list__##recid[]={ + + +#define SWELL_DEFINE_DIALOG_RESOURCE_END(recid ) }; \ + SWELL_VALIDATE_DIALOG_RESOURCE( __swell_dlg_validator__##recid, __swell_dlg_list__##recid) \ + static void SWELL__dlg_cf__##recid(HWND view, int wflags) { \ + SWELL_MakeSetCurParms(__swell_dlg_scale__##recid,__swell_dlg_scale__##recid,0,0,view,false,!(wflags&SWELL_DLG_WS_NOAUTOSIZE)); \ + SWELL_GenerateDialogFromList(__swell_dlg_list__##recid+1,sizeof(__swell_dlg_list__##recid)/sizeof(__swell_dlg_list__##recid[0])-1); \ + } + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-functions.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-functions.h new file mode 100644 index 000000000..c19e674b2 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-functions.h @@ -0,0 +1,1096 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + SWELL provides _EXTREMELY BASIC_ win32 wrapping for OS X and maybe other platforms. + + */ + +#ifndef _WDL_SWELL_H_API_DEFINED_ +#define _WDL_SWELL_H_API_DEFINED_ + +//////////////////////////////////////////// +/////////// FUNCTIONS +//////////////////////////////////////////// + +#ifndef SWELL_API_DEFINE + + +#if defined(SWELL_PROVIDED_BY_APP) || defined(SWELL_LOAD_SWELL_DYLIB) + #ifdef __cplusplus + #define SWELL_API_DEFINE(ret,func,parms) extern "C" ret (*func)parms; + #else + #define SWELL_API_DEFINE(ret,func,parms) extern ret (*func)parms; + #endif +#else +#define SWELL_API_DEFINE(ret,func,parms) ret func parms ; +#endif +#endif + +// when adding APIs, add it using: +// SWELL_API_DEFINE(void, function_name, (int parm, int parm2)) +// rather than: +// void function_name(int parm, int parm2); + +/* +** lstrcpyn: this is provided because strncpy is braindead (filling with zeroes, and not +** NULL terminating if the destination buffer is too short? ASKING for trouble..) +** lstrpcyn always null terminates the string and doesnt fill anything extra. +** +** note: wdlcstring.h defines lstrcpyn_safe(), which is preferred on win32 because of +** exception handling behavior. +*/ +SWELL_API_DEFINE(char *, lstrcpyn, (char *dest, const char *src, int l)) + + +/* +** MulDiv(): (parm1*parm2)/parm3 +** Implemented using long longs. +*/ +SWELL_API_DEFINE(int, MulDiv, (int, int, int)) + + +/* +** Sleep() sleeps for specified milliseconds. This maps to usleep, with a ms value of 0 +** usleeping for 100 microseconds. +*/ +SWELL_API_DEFINE(void, Sleep,(int ms)) + +/* +** GetTickCount() and timeGetTime() give you ms level timings via gettimeofday() or mach_getabsolutetime() or clock_gettime() +** +** NOTE: This doesn't map to time since system start (like in win32), so a wrap around +** is slightly more likely (i.e. even if you booted your system an hour ago it could happen). +*/ +SWELL_API_DEFINE(DWORD, GetTickCount,()) +#ifndef timeGetTime +#define timeGetTime() GetTickCount() +#endif + +/* +** GetFileTime() gets the file time of a file (FILE *), and converts it to the Windows time. +** +** NOTE: while it returns a 64 bit time value, it is only accurate to the second since thats +** what fstat() returns. Takes an int filedes rather than a HANDLE. +*/ +SWELL_API_DEFINE(BOOL, GetFileTime,(int filedes, FILETIME *lpCreationTime, FILETIME *lpLastAccessTime, FILETIME *lpLastWriteTime)) + +/* +** *PrivateProfileString/Int(): +** These are mostly thread-safe, mostly inter-process safe, and mostly module safe +** (i.e. writes from other modules) should be synchronized). +** +** NOTES: +** the filename used MUST be the full filename, unlike on Windows where files without paths go to +** C:/Windows, here they will be opened in the current directory. +** +** You can pass an empty string for filename to use ~/.libSwell.ini (that can be overridden by the app) +** +** It's probably not a good idea to push your luck with simultaneous writes from multiple +** modules in different threads/processes, but in theory it should work. +*/ +SWELL_API_DEFINE(BOOL, WritePrivateProfileString, (const char *appname, const char *keyname, const char *val, const char *fn)) +SWELL_API_DEFINE(DWORD, GetPrivateProfileString, (const char *appname, const char *keyname, const char *def, char *ret, int retsize, const char *fn)) +SWELL_API_DEFINE(int, GetPrivateProfileInt,(const char *appname, const char *keyname, int def, const char *fn)) +SWELL_API_DEFINE(BOOL, GetPrivateProfileStruct,(const char *appname, const char *keyname, void *buf, int bufsz, const char *fn)) +SWELL_API_DEFINE(BOOL, WritePrivateProfileStruct,(const char *appname, const char *keyname, const void *buf, int bufsz, const char *fn)) +SWELL_API_DEFINE(BOOL, WritePrivateProfileSection, (const char *appname, const char *strings, const char *fn)) +SWELL_API_DEFINE(DWORD, GetPrivateProfileSection, (const char *appname, char *strout, DWORD strout_len, const char *fn)) + +/* +** GetModuleFileName() +** Can pass NULL (exe filename) or a hInstance from DllMain or LoadLibrary +*/ +SWELL_API_DEFINE(DWORD, GetModuleFileName,(HINSTANCE hInst, char *fn, DWORD nSize)) + +#ifdef SWELL_TARGET_OSX +/* +** SWELL_CStringToCFString(): Creates a CFString/NSString * from a C string. This is mostly +** used internally but you may wish to use it as well (though none of the SWELL APIs take +** CFString/NSString. +*/ +SWELL_API_DEFINE(void *,SWELL_CStringToCFString,(const char *str)) +SWELL_API_DEFINE(void, SWELL_CFStringToCString, (const void *str, char *buf, int buflen)) +#endif + + +#ifdef PtInRect +#undef PtInRect +// #define funkiness because some Mac system headers define PtInRect as well. +#endif +#define PtInRect(r,p) SWELL_PtInRect(r,p) +SWELL_API_DEFINE(BOOL, SWELL_PtInRect,(const RECT *r, POINT p)) + +/* +** ShellExecute(): +** notes: +** action is ignored +** content1 can be a http:// or https:// URL +** content1 can be notepad/notepad.exe (maps to xdg-open or TextEdit.app) w/ content2 as a document +** content1 can be explorer.exe (optionally with /select) (maps to open finder or xdg-open) +** otherwise content1 can be an app w/ parameters in content2 +*/ +SWELL_API_DEFINE(BOOL, ShellExecute,(HWND hwndDlg, const char *action, const char *content1, const char *content2, const char *content3, int blah)) + +SWELL_API_DEFINE(int, MessageBox,(HWND hwndParent, const char *text, const char *caption, int type)) + + +/* +** GetOpenFileName() / GetSaveFileName() +** These are a different API because we didnt feel like reeimplimenting the full API. +** Extlist is something similar you'd pass getopenfilename, +** initialdir and initialfile are optional (and NULL means not set). +*/ + +// free() the result of this, if non-NULL. +// if allowmul is set, the multiple files are specified the same way GetOpenFileName() returns. +SWELL_API_DEFINE(char *,BrowseForFiles,(const char *text, const char *initialdir, + const char *initialfile, bool allowmul, const char *extlist)) + +// returns TRUE if file was chosen. +SWELL_API_DEFINE(bool, BrowseForSaveFile,(const char *text, const char *initialdir, const char *initialfile, const char *extlist, + char *fn, int fnsize)) + +// returns TRUE if path was chosen. +SWELL_API_DEFINE(bool, BrowseForDirectory,(const char *text, const char *initialdir, char *fn, int fnsize)) + +// can use this before calling BrowseForFiles or BrowseForSaveFile to use a template dialog +SWELL_API_DEFINE(void,BrowseFile_SetTemplate,(const char *dlgid, DLGPROC dlgProc, struct SWELL_DialogResourceIndex *reshead)) + + +// Note that window functions are generally NOT threadsafe. +// on macOS: all of these treat HWND as NSView and/or NSWindow (usually smartish about it) + +/* + * GetDlgItem() notes: +** macOS: GetDlgItem(hwnd,0) returns hwnd if hwnd is a NSView, or the contentview if hwnd is a NSWindow. +** macOS: note that this GetDlgItem will actually search a view hierarchy for the tagged view (win32 or -generic will only search immediate children) +*/ +SWELL_API_DEFINE(HWND, GetDlgItem,(HWND, int)) + + +SWELL_API_DEFINE(void, ShowWindow,(HWND, int)) + +SWELL_API_DEFINE(void, DestroyWindow,(HWND hwnd)) + +SWELL_API_DEFINE(BOOL, SWELL_GetGestureInfo, (LPARAM lParam, GESTUREINFO* gi)) + +SWELL_API_DEFINE(void, SWELL_HideApp,()) + +/* +** These should all work like their Win32 versions, though if idx=0 it gets/sets the +** value for the window. +** +** macOS: SetDlgItemText() for an edit control does NOT send a WM_COMMAND notification (win32 and -generic do) +*/ +SWELL_API_DEFINE(BOOL, SetDlgItemText,(HWND, int idx, const char *text)) +SWELL_API_DEFINE(BOOL, SetDlgItemInt,(HWND, int idx, int val, int issigned)) +SWELL_API_DEFINE(int, GetDlgItemInt,(HWND, int idx, BOOL *translated, int issigned)) +SWELL_API_DEFINE(BOOL, GetDlgItemText,(HWND, int idx, char *text, int textlen)) +SWELL_API_DEFINE(int, GetWindowTextLength,(HWND)) + +#ifndef GetWindowText +#define GetWindowText(hwnd,text,textlen) GetDlgItemText(hwnd,0,text,textlen) +#define SetWindowText(hwnd,text) SetDlgItemText(hwnd,0,text) +#endif + + +SWELL_API_DEFINE(void, CheckDlgButton,(HWND hwnd, int idx, int check)) +SWELL_API_DEFINE(int, IsDlgButtonChecked,(HWND hwnd, int idx)) +SWELL_API_DEFINE(void, EnableWindow,(HWND hwnd, int enable)) +SWELL_API_DEFINE(void, SetFocus,(HWND hwnd)) +SWELL_API_DEFINE(HWND, GetFocus,()) +SWELL_API_DEFINE(void, SetForegroundWindow,(HWND hwnd)) +SWELL_API_DEFINE(HWND, GetForegroundWindow,()) +#ifndef GetActiveWindow +#define GetActiveWindow() GetForegroundWindow() +#endif +#ifndef SetActiveWindow +#define SetActiveWindow(x) SetForegroundWindow(x) +#endif + +/* +** macOS: note that any HWND that returns YES to swellCapChangeNotify should do the following on +** destroy or dealloc: if (GetCapture()==(HWND)self) ReleaseCapture(); Failure to do so +** can cause a dealloc'd window to get messages sent to it. +*/ +SWELL_API_DEFINE(HWND, SetCapture,(HWND hwnd)) +SWELL_API_DEFINE(HWND, GetCapture,()) +SWELL_API_DEFINE(void, ReleaseCapture,()) + +/* +** IsChild() +** macOS: hwndChild must be a NSView, hwndParent can be a NSWindow or NSView. NSWindow level ownership/children are not detected. +*/ +SWELL_API_DEFINE(int, IsChild,(HWND hwndParent, HWND hwndChild)) + + +SWELL_API_DEFINE(HWND, GetParent,(HWND hwnd)) + +/* +** SetParent() +** macOS: hwnd must be a NSView, newPar can be either NSView or NSWindow. +*/ +SWELL_API_DEFINE(HWND, SetParent,(HWND hwnd, HWND newPar)) + +SWELL_API_DEFINE(HWND, GetWindow,(HWND hwnd, int what)) + +SWELL_API_DEFINE(BOOL, EnumWindows, (BOOL (*proc)(HWND, LPARAM), LPARAM lp)) + +SWELL_API_DEFINE(HWND,FindWindowEx,(HWND par, HWND lastw, const char *classname, const char *title)) + + +/* +** macOS note: common win32 code like this: +** RECT r; +** GetWindowRect(hwnd,&r); +** ScreenToClient(otherhwnd,(LPPOINT)&r); +** ScreenToClient(otherhwnd,((LPPOINT)&r)+1); +** does work, however be aware that in certain instances r.bottom may be less +** than r.top, due to flipped coordinates. SetWindowPos and other functions +** handle negative heights gracefully, and you should too. +** +** GetWindowContentViewRect gets the rectangle of the content view (pre-NCCALCSIZE etc) +*/ +SWELL_API_DEFINE(void, ClientToScreen,(HWND hwnd, POINT *p)) +SWELL_API_DEFINE(void, ScreenToClient,(HWND hwnd, POINT *p)) +SWELL_API_DEFINE(bool, GetWindowRect,(HWND hwnd, RECT *r)) +SWELL_API_DEFINE(void, GetWindowContentViewRect, (HWND hwnd, RECT *r)) +SWELL_API_DEFINE(void, GetClientRect,(HWND hwnd, RECT *r)) +SWELL_API_DEFINE(HWND, WindowFromPoint,(POINT p)) +SWELL_API_DEFINE(BOOL, WinOffsetRect, (LPRECT lprc, int dx, int dy)) +SWELL_API_DEFINE(BOOL, WinSetRect, (LPRECT lprc, int xLeft, int yTop, int xRight, int yBottom)) +SWELL_API_DEFINE(void,WinUnionRect,(RECT *out, const RECT *in1, const RECT *in2)) +SWELL_API_DEFINE(int,WinIntersectRect,(RECT *out, const RECT *in1, const RECT *in2)) + +SWELL_API_DEFINE(void, SetWindowPos,(HWND hwnd, HWND unused, int x, int y, int cx, int cy, int flags)) + +SWELL_API_DEFINE(int, SWELL_SetWindowLevel, (HWND hwnd, int newlevel)) + +SWELL_API_DEFINE(BOOL,InvalidateRect,(HWND hwnd, const RECT *r, int eraseBk)) + +SWELL_API_DEFINE(void,UpdateWindow,(HWND hwnd)) + + +/* +** GetWindowLong()/SetWindowLong() +** +** macOS: +** GWL_ID is supported for all objects that support the 'tag'/'setTag' methods, +** which would be controls and SWELL created windows/dialogs/controls. +** +** GWL_USERDATA is supported by SWELL created windows/dialogs/controls, using +** (int)getSwellUserData and setSwellUserData:(int). +** +** GWL_WNDPROC is supported by SWELL created windows/dialogs/controls, using +** (int)getSwellWindowProc and setSwellWindowProc:(int). +** +** DWL_DLGPROC is supported by SWELL-created dialogs now (it might work in windows/controls but isnt recommended) +** +** GWL_STYLE is only supported for NSButton. Currently the only flags supported are +** BS_AUTO3STATE (BS_AUTOCHECKBOX is returned but also ignored). +** +** indices of >= 0 and < 128 (32 integers) are supported for SWELL created +** windows/dialogs/controls, via (int)getSwellExtraData:(int)idx and +** setSwellExtraData:(int)idx value:(int)val . +** +** generic: indices of >= 0 && < 64*sizeof(INT_PTR) are supported +*/ +SWELL_API_DEFINE(LONG_PTR, GetWindowLong,(HWND hwnd, int idx)) +SWELL_API_DEFINE(LONG_PTR, SetWindowLong,(HWND hwnd, int idx, LONG_PTR val)) + + +SWELL_API_DEFINE(BOOL, ScrollWindow, (HWND hwnd, int xamt, int yamt, const RECT *lpRect, const RECT *lpClipRect)) + +/* +** GetProp() SetProp() RemoveProp() EnumPropsEx() +** Free your props otherwise they will leak. +** Restriction on what you can do in the PROPENUMPROCEX is similar to win32 +** (you can remove only the called prop, and can't add props within it). +** if the prop name is < (void *)65536 then it is treated as a short identifier. +*/ +SWELL_API_DEFINE(int, EnumPropsEx,(HWND, PROPENUMPROCEX, LPARAM)) +SWELL_API_DEFINE(HANDLE, GetProp, (HWND, const char *)) +SWELL_API_DEFINE(BOOL, SetProp, (HWND, const char *, HANDLE)) +SWELL_API_DEFINE(HANDLE, RemoveProp, (HWND, const char *)) + + +/* +** IsWindowVisible() +** macOS: +** if hwnd is a NSView, returns !isHiddenOrHasHiddenAncestor +** if hwnd is a NSWindow returns isVisible +** otherwise returns TRUE if non-null hwnd +*/ +SWELL_API_DEFINE(bool, IsWindowVisible,(HWND hwnd)) + +// IsWindow() +// probably best avoided. +// macOS: very costly (compared to win32) -- enumerates all windows, searches for hwnd +// generic: may not be implemented +SWELL_API_DEFINE(bool, IsWindow, (HWND hwnd)) + + +/* +** SetTimer/KillTimer(): +** Notes: +** The timer API may be threadsafe though it is highly untested. It is safest to only set +** timers from the main thread. +** +** Kill all timers for a window using KillTimer(hwnd,-1); +** +** macOS: Note also that the mechanism for sending timers is SWELL_Timer:(id). +** You MUST kill all timers for a window before destroying it. Note that SWELL created +** windows/dialogs/controls automatically do this, but if you use SetTimer() on a NSView * +** or NSWindow * directly, then you should kill all timers in -dealloc. +*/ +SWELL_API_DEFINE(UINT_PTR, SetTimer,(HWND hwnd, UINT_PTR timerid, UINT rate, TIMERPROC tProc)) +SWELL_API_DEFINE(BOOL, KillTimer,(HWND hwnd, UINT_PTR timerid)) + +#ifdef SWELL_TARGET_OSX +/* +** SendMessage can/should now be used with CB_* etc. +** macOS: Combo boxes may be implemented using a NSComboBox or NSPopUpButton depending on the style. +*/ +SWELL_API_DEFINE(int, SWELL_CB_AddString,(HWND hwnd, int idx, const char *str)) +SWELL_API_DEFINE(void, SWELL_CB_SetCurSel,(HWND hwnd, int idx, int sel)) +SWELL_API_DEFINE(int, SWELL_CB_GetCurSel,(HWND hwnd, int idx)) +SWELL_API_DEFINE(int, SWELL_CB_GetNumItems,(HWND hwnd, int idx)) +SWELL_API_DEFINE(void, SWELL_CB_SetItemData,(HWND hwnd, int idx, int item, LONG_PTR data)) +SWELL_API_DEFINE(LONG_PTR, SWELL_CB_GetItemData,(HWND hwnd, int idx, int item)) +SWELL_API_DEFINE(void, SWELL_CB_Empty,(HWND hwnd, int idx)) +SWELL_API_DEFINE(int, SWELL_CB_InsertString,(HWND hwnd, int idx, int pos, const char *str)) +SWELL_API_DEFINE(int, SWELL_CB_GetItemText,(HWND hwnd, int idx, int item, char *buf, int bufsz)) +SWELL_API_DEFINE(void, SWELL_CB_DeleteString,(HWND hwnd, int idx, int wh)) +SWELL_API_DEFINE(int, SWELL_CB_FindString,(HWND hwnd, int idx, int startAfter, const char *str, bool exact)) + + +/* +** Trackbar API +** You can/should now use SendMessage with TBM_* instead. +*/ +SWELL_API_DEFINE(void, SWELL_TB_SetPos,(HWND hwnd, int idx, int pos)) +SWELL_API_DEFINE(void, SWELL_TB_SetRange,(HWND hwnd, int idx, int low, int hi)) +SWELL_API_DEFINE(int, SWELL_TB_GetPos,(HWND hwnd, int idx)) +SWELL_API_DEFINE(void, SWELL_TB_SetTic,(HWND hwnd, int idx, int pos)) + + +#endif + +/* +** ListViews -- in owner data mode only LVN_GETDISPINFO is required (LVN_ODFINDITEM is never sent) +*/ +SWELL_API_DEFINE(void, ListView_SetExtendedListViewStyleEx,(HWND h, int mask, int style)) +SWELL_API_DEFINE(void, ListView_InsertColumn,(HWND h, int pos, const LVCOLUMN *lvc)) +SWELL_API_DEFINE(bool, ListView_DeleteColumn,(HWND h, int pos)) +SWELL_API_DEFINE(void, ListView_SetColumn,(HWND h, int pos, const LVCOLUMN *lvc)) +SWELL_API_DEFINE(int, ListView_GetColumnWidth,(HWND h, int pos)) +SWELL_API_DEFINE(int, ListView_InsertItem,(HWND h, const LVITEM *item)) +SWELL_API_DEFINE(void, ListView_SetItemText,(HWND h, int ipos, int cpos, const char *txt)) +SWELL_API_DEFINE(bool, ListView_SetItem,(HWND h, LVITEM *item)) +SWELL_API_DEFINE(int, ListView_GetNextItem,(HWND h, int istart, int flags)) +SWELL_API_DEFINE(bool, ListView_GetItem,(HWND h, LVITEM *item)) +SWELL_API_DEFINE(int, ListView_GetItemState,(HWND h, int ipos, UINT mask)) +SWELL_API_DEFINE(void, ListView_DeleteItem,(HWND h, int ipos)) +SWELL_API_DEFINE(void, ListView_DeleteAllItems,(HWND h)) +SWELL_API_DEFINE(int, ListView_GetSelectedCount,(HWND h)) +SWELL_API_DEFINE(int, ListView_GetItemCount,(HWND h)) +SWELL_API_DEFINE(int, ListView_GetSelectionMark,(HWND h)) +SWELL_API_DEFINE(void, ListView_SetColumnWidth,(HWND h, int colpos, int wid)) +SWELL_API_DEFINE(bool, ListView_SetItemState,(HWND h, int item, UINT state, UINT statemask)) +SWELL_API_DEFINE(void, ListView_RedrawItems,(HWND h, int startitem, int enditem)) +SWELL_API_DEFINE(void, ListView_SetItemCount,(HWND h, int cnt)) +#ifdef ListView_SetItemCountEx +#undef ListView_SetItemCountEx +#endif +#define ListView_SetItemCountEx(list,cnt,flags) ListView_SetItemCount(list,cnt) + +SWELL_API_DEFINE(void, ListView_EnsureVisible,(HWND h, int i, BOOL pok)) +SWELL_API_DEFINE(void, ListView_SetImageList,(HWND h, HIMAGELIST imagelist, int which)) +SWELL_API_DEFINE(int, ListView_SubItemHitTest,(HWND h, LVHITTESTINFO *pinf)) +SWELL_API_DEFINE(void, ListView_GetItemText,(HWND hwnd, int item, int subitem, char *text, int textmax)) +SWELL_API_DEFINE(void, ListView_SortItems,(HWND hwnd, PFNLVCOMPARE compf, LPARAM parm)) +SWELL_API_DEFINE(bool, ListView_Scroll,(HWND h, int xscroll, int yscroll)) +SWELL_API_DEFINE(int, ListView_GetTopIndex,(HWND h)) +SWELL_API_DEFINE(int, ListView_GetCountPerPage,(HWND h)) +SWELL_API_DEFINE(BOOL, ListView_SetColumnOrderArray,(HWND h, int cnt, int* arr)) +SWELL_API_DEFINE(BOOL, ListView_GetColumnOrderArray,(HWND h, int cnt, int* arr)) +SWELL_API_DEFINE(HWND, ListView_GetHeader,(HWND h)) +SWELL_API_DEFINE(int, Header_GetItemCount,(HWND h)) +SWELL_API_DEFINE(BOOL, Header_GetItem,(HWND h, int col, HDITEM* hi)) +SWELL_API_DEFINE(BOOL, Header_SetItem,(HWND h, int col, HDITEM* hi)) + + // NOTE: the Cocoa versions of these functions behave differently than swell-generic and Windows: + // they return the absolute (unscrolled) coordinates. In order to behave properly, the caller should + // use ClientToScreen in order to get to screen coordinates, and then convert those as desired. +SWELL_API_DEFINE(bool, ListView_GetItemRect,(HWND h, int item, RECT *r, int code)) +SWELL_API_DEFINE(bool, ListView_GetSubItemRect,(HWND h, int item, int subitem, int code, RECT *r)) + // NOTE: Cocoa version takes absolute (unscrolled) coordinates. ScreenToClient(listview) will convert screen + // coordinates to the correct coorindates +SWELL_API_DEFINE(int, ListView_HitTest,(HWND h, LVHITTESTINFO *pinf)) + +SWELL_API_DEFINE(int, SWELL_GetListViewHeaderHeight, (HWND h)) + +#ifndef ImageList_Create +#define ImageList_Create(x,y,a,b,c) ImageList_CreateEx(); +#endif +SWELL_API_DEFINE(HIMAGELIST, ImageList_CreateEx,()) +SWELL_API_DEFINE(BOOL, ImageList_Remove, (HIMAGELIST list, int idx)) +SWELL_API_DEFINE(int, ImageList_ReplaceIcon,(HIMAGELIST list, int offset, HICON image)) +SWELL_API_DEFINE(int, ImageList_Add,(HIMAGELIST list, HBITMAP image, HBITMAP mask)) +SWELL_API_DEFINE(void, ImageList_Destroy, (HIMAGELIST)) +/* +** TabCtrl api. +*/ +SWELL_API_DEFINE(int, TabCtrl_GetItemCount,(HWND hwnd)) +SWELL_API_DEFINE(BOOL, TabCtrl_DeleteItem,(HWND hwnd, int idx)) +SWELL_API_DEFINE(int, TabCtrl_InsertItem,(HWND hwnd, int idx, TCITEM *item)) +SWELL_API_DEFINE(int, TabCtrl_SetCurSel,(HWND hwnd, int idx)) +SWELL_API_DEFINE(int, TabCtrl_GetCurSel,(HWND hwnd)) +SWELL_API_DEFINE(BOOL, TabCtrl_AdjustRect, (HWND hwnd, BOOL fLarger, RECT *r)) + +/* +** TreeView +*/ + +SWELL_API_DEFINE(HTREEITEM, TreeView_InsertItem, (HWND hwnd, TV_INSERTSTRUCT *ins)) +SWELL_API_DEFINE(BOOL, TreeView_Expand,(HWND hwnd, HTREEITEM item, UINT flag)) +SWELL_API_DEFINE(HTREEITEM, TreeView_GetSelection,(HWND hwnd)) +SWELL_API_DEFINE(void, TreeView_DeleteItem,(HWND hwnd, HTREEITEM item)) +SWELL_API_DEFINE(void, TreeView_DeleteAllItems,(HWND hwnd)) +SWELL_API_DEFINE(void, TreeView_SelectItem,(HWND hwnd, HTREEITEM item)) +SWELL_API_DEFINE(void, TreeView_EnsureVisible,(HWND hwnd, HTREEITEM item)) +SWELL_API_DEFINE(BOOL, TreeView_GetItem,(HWND hwnd, LPTVITEM pitem)) +SWELL_API_DEFINE(BOOL, TreeView_SetItem,(HWND hwnd, LPTVITEM pitem)) +SWELL_API_DEFINE(HTREEITEM, TreeView_HitTest, (HWND hwnd, TVHITTESTINFO *hti)) +SWELL_API_DEFINE(BOOL, TreeView_SetIndent,(HWND hwnd, int indent)) + +SWELL_API_DEFINE(HTREEITEM, TreeView_GetParent, (HWND hwnd, HTREEITEM item)) +SWELL_API_DEFINE(HTREEITEM, TreeView_GetChild, (HWND hwnd, HTREEITEM item)) +SWELL_API_DEFINE(HTREEITEM, TreeView_GetNextSibling, (HWND hwnd, HTREEITEM item)) +SWELL_API_DEFINE(HTREEITEM, TreeView_GetRoot, (HWND hwnd)) + +SWELL_API_DEFINE(void,TreeView_SetBkColor,(HWND hwnd, int color)) +SWELL_API_DEFINE(void,TreeView_SetTextColor,(HWND hwnd, int color)) +SWELL_API_DEFINE(void,ListView_SetBkColor,(HWND hwnd, int color)) +SWELL_API_DEFINE(void,ListView_SetTextBkColor,(HWND hwnd, int color)) +SWELL_API_DEFINE(void,ListView_SetTextColor,(HWND hwnd, int color)) +SWELL_API_DEFINE(void,ListView_SetGridColor,(HWND hwnd, int color)) +SWELL_API_DEFINE(void,ListView_SetSelColors,(HWND hwnd, int *colors, int ncolors)) + +/* +** These are deprecated macOS-only functions for launching a modal window but still running +** your own code. In general use DialogBox with a timer if needed instead. +*/ +SWELL_API_DEFINE(void *, SWELL_ModalWindowStart,(HWND hwnd)) +SWELL_API_DEFINE(bool, SWELL_ModalWindowRun,(void *ctx, int *ret)) // returns false and puts retval in *ret when done +SWELL_API_DEFINE(void, SWELL_ModalWindowEnd,(void *ctx)) +SWELL_API_DEFINE(void, SWELL_CloseWindow,(HWND hwnd)) + +/* +** Menu functions +** macOS: HMENU is a NSMenu *. +*/ +SWELL_API_DEFINE(HMENU, CreatePopupMenu,()) +SWELL_API_DEFINE(HMENU, CreatePopupMenuEx,(const char *title)) +SWELL_API_DEFINE(void, DestroyMenu,(HMENU hMenu)) +SWELL_API_DEFINE(int, AddMenuItem,(HMENU hMenu, int pos, const char *name, int tagid)) +SWELL_API_DEFINE(HMENU, GetSubMenu,(HMENU hMenu, int pos)) +SWELL_API_DEFINE(int, GetMenuItemCount,(HMENU hMenu)) +SWELL_API_DEFINE(int, GetMenuItemID,(HMENU hMenu, int pos)) +SWELL_API_DEFINE(bool, SetMenuItemModifier,(HMENU hMenu, int idx, int flag, int code, unsigned int mask)) +SWELL_API_DEFINE(bool, SetMenuItemText,(HMENU hMenu, int idx, int flag, const char *text)) +SWELL_API_DEFINE(bool, EnableMenuItem,(HMENU hMenu, int idx, int en)) +SWELL_API_DEFINE(bool, DeleteMenu,(HMENU hMenu, int idx, int flag)) +SWELL_API_DEFINE(bool, CheckMenuItem,(HMENU hMenu, int idx, int chk)) +SWELL_API_DEFINE(void, InsertMenuItem,(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi)) +SWELL_API_DEFINE(void,SWELL_InsertMenu,(HMENU menu, int pos, unsigned int flag, UINT_PTR idx, const char *str)) +#ifdef InsertMenu +#undef InsertMenu +#endif +#define InsertMenu SWELL_InsertMenu + +SWELL_API_DEFINE(BOOL, GetMenuItemInfo,(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi)) +SWELL_API_DEFINE(BOOL, SetMenuItemInfo,(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi)) +SWELL_API_DEFINE(void, DrawMenuBar,(HWND)) + + + +/* +** LoadMenu() +** Loads a menu created with SWELL_DEFINE_MENU_RESOURCE_BEGIN(), see swell-menugen.h +** Notes: the hinst parameter is ignored, the menu must have been defined in the same +** module (executable or shared library) as the LoadMenu call. If you wish to load a +** menu from another module, get its SWELL_curmodule_menuresource_head and pass it to +** SWELL_LoadMenu directly. +*/ +#ifndef LoadMenu +#define LoadMenu(hinst,resid) SWELL_LoadMenu(SWELL_curmodule_menuresource_head,(resid)) +#endif +SWELL_API_DEFINE(HMENU, SWELL_LoadMenu,(struct SWELL_MenuResourceIndex *head, const char *resid)) + +/* +** TrackPopupMenu +** Notes: the rectangle is ignored, and resvd should always be 0. +*/ +SWELL_API_DEFINE(int, TrackPopupMenu,(HMENU hMenu, int flags, int xpos, int ypos, int resvd, HWND hwnd, const RECT *r)) + +/* +** SWELL_SetMenuDestination: set the action destination for all items and subitems in a menu +** macOS only, TrackPopupMenu and SetMenu use this internally, but it may be useful. +*/ +SWELL_API_DEFINE(void, SWELL_SetMenuDestination,(HMENU menu, HWND hwnd)) + +/* +** SWELL_DuplicateMenu: +** Copies an entire menu. +*/ +SWELL_API_DEFINE(HMENU, SWELL_DuplicateMenu,(HMENU menu)) + +/* +** SetMenu()/GetMenu() +** macOS: These work on SWELL created NSWindows, or objective C objects that respond to +** swellSetMenu:(HMENU) and swellGetMenu. SWELL windows will automatically set the +** application menu via NSApp setMainMenu: when activated. +*/ +SWELL_API_DEFINE(BOOL, SetMenu,(HWND hwnd, HMENU menu)) +SWELL_API_DEFINE(HMENU, GetMenu,(HWND hwnd)) + +/* +** SWELL_SetDefaultWindowMenu()/SWELL_GetDefaultWindowMenu() +** macOS: these set an internal flag for the default window menu, which will be set +** when switching to a window that has no menu set. Set this to your application's +** main menu. +** +** generic: these set the internal state, which is currently unused +*/ +SWELL_API_DEFINE(HMENU, SWELL_GetDefaultWindowMenu,()) +SWELL_API_DEFINE(void, SWELL_SetDefaultWindowMenu,(HMENU)) +SWELL_API_DEFINE(HMENU, SWELL_GetDefaultModalWindowMenu,()) +SWELL_API_DEFINE(void, SWELL_SetDefaultModalWindowMenu,(HMENU)) +SWELL_API_DEFINE(HMENU, SWELL_GetCurrentMenu,()) +SWELL_API_DEFINE(void, SWELL_SetCurrentMenu,(HMENU)) + + + +/* +** SWELL dialog box/control/window/child dialog/etc creation +** DialogBox(), DialogBoxParam(), CreateDialog(), and CreateDialogParam() +** +** Notes: +** hInstance parameters are ignored. If you wish to load a dialog resource from another +** module (shared library/executable), you should get its SWELL_curmodule_dialogresource_head +** via your own mechanism and pass it as the first parameter of SWELL_DialogBox or whichever API. +** +** If you are using CreateDialog() and creating a child window, you can use a resource ID of +** 0, which creates an opaque child window. Instead of passing a DLGPROC, you should pass a +** (WNDPROC) routine that retuns LRESULT (and cast it to DLGPROC). +** +*/ + +#ifndef DialogBox +#define DialogBox(hinst, resid, par, dlgproc) SWELL_DialogBox(SWELL_curmodule_dialogresource_head,(resid),par,dlgproc,0) +#define DialogBoxParam(hinst, resid, par, dlgproc, param) SWELL_DialogBox(SWELL_curmodule_dialogresource_head,(resid),par,dlgproc,param) +#define CreateDialog(hinst,resid,par,dlgproc) SWELL_CreateDialog(SWELL_curmodule_dialogresource_head,(resid),par,dlgproc,0) +#define CreateDialogParam(hinst,resid,par,dlgproc,param) SWELL_CreateDialog(SWELL_curmodule_dialogresource_head,(resid),par,dlgproc,param) +#endif +SWELL_API_DEFINE(int, SWELL_DialogBox,(struct SWELL_DialogResourceIndex *reshead, const char *resid, HWND parent, DLGPROC dlgproc, LPARAM param)) +SWELL_API_DEFINE(HWND, SWELL_CreateDialog,(struct SWELL_DialogResourceIndex *reshead, const char *resid, HWND parent, DLGPROC dlgproc, LPARAM param)) + + +/* +** SWELL_RegisterCustomControlCreator(), SWELL_UnregisterCustomControlCreator() +** Notes: +** Pass these a callback function that can create custom controls based on classname. +*/ + +SWELL_API_DEFINE(void, SWELL_RegisterCustomControlCreator,(SWELL_ControlCreatorProc proc)) +SWELL_API_DEFINE(void, SWELL_UnregisterCustomControlCreator,(SWELL_ControlCreatorProc proc)) + +SWELL_API_DEFINE(LRESULT, DefWindowProc,(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)) + +SWELL_API_DEFINE(void, EndDialog,(HWND, int)) + + +SWELL_API_DEFINE(int,SWELL_GetDefaultButtonID,(HWND hwndDlg, bool onlyIfEnabled)) + + +/* +** SendMessage() +** Notes: +** LIMITATION - SendMessage should only be used from the same thread that the window/view +** was created in. Cross-thread use SHOULD BE AVOIDED. It may work, but it may blow up. +** PostMessage (below) can be used in certain instances for asynchronous notifications. +*/ +SWELL_API_DEFINE(LRESULT, SendMessage,(HWND, UINT, WPARAM, LPARAM)) + +#ifndef SendDlgItemMessage +#define SendDlgItemMessage(hwnd,idx,msg,wparam,lparam) SendMessage(GetDlgItem(hwnd,idx),msg,wparam,lparam) +#endif + +/* +** SWELL_BroadcastMessage() +** sends a message to all top-level windows +*/ +SWELL_API_DEFINE(void,SWELL_BroadcastMessage,(UINT, WPARAM, LPARAM)) + +/* +** PostMessage() +** Notes: +** Queues a message into the application message queue. Note that you should only ever +** send messages to destinations that were created from the main thread. They will be +** processed later from a timer (in the main thread). +** When a window is destroyed any outstanding messages will be discarded for it. +*/ +SWELL_API_DEFINE(BOOL, PostMessage,(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)) + +/* +** SWELL_MessageQueue_Flush(): +** Notes: +** Processes all messages in the message queue. ONLY call from the main thread. +*/ +SWELL_API_DEFINE(void, SWELL_MessageQueue_Flush,()) + +/* +** SWELL_MessageQueue_Clear(): +** Notes: +** Discards all messages from the message queue if h is NULL, otherwise discards all messages +** to h. +*/ +SWELL_API_DEFINE(void, SWELL_MessageQueue_Clear,(HWND h)) + + + +/* +** keyboard/mouse support +*/ + +/* +** SWELL_MacKeyToWindowsKey() +** Pass a keyboard NSEvent *, and it will return a windows VK_ keycode (or ascii), and set flags, +** including (possibly) FSHIFT, FCONTROL (apple key), FALT, and FVIRTKEY. The ctrl key is not checked, +** as SWELL generally encourages this to be used soley for a right mouse button (as modifier). +** flags may also include 0x1000000 for arrow keys and home/end (matching the lParam behavior of win32), +** or for numeric keypad enter +*/ +#ifdef SWELL_TARGET_OSX +SWELL_API_DEFINE(int, SWELL_MacKeyToWindowsKey,(void *nsevent, int *flags)) + + // ex is the same as normal, except if mode=1 it does more processing of raw keys w/ modifiers + // and also if nsevent==NULL current event is used +SWELL_API_DEFINE(int, SWELL_MacKeyToWindowsKeyEx,(void *nsevent, int *flags, int mode)) +#endif +SWELL_API_DEFINE(int,SWELL_KeyToASCII,(int wParam, int lParam, int *newflags)) + + +/* +** GetAsyncKeyState() +** macOS: only supports VK_LBUTTON, VK_RBUTTON, VK_MBUTTON, VK_SHIFT, VK_MENU, VK_CONTROL (apple/command key), VK_LWIN (control key) +** GDK: only supports VK_LBUTTON, VK_RBUTTON, VK_MBUTTON, VK_SHIFT, VK_MENU, VK_CONTROL, VK_LWIN +*/ +SWELL_API_DEFINE(WORD, GetAsyncKeyState,(int key)) + +/* +** GetCursorPos(), GetMessagePos() +** GetMessagePos() currently returns the same coordinates as GetCursorPos() +*/ +SWELL_API_DEFINE(void, GetCursorPos,(POINT *pt)) +SWELL_API_DEFINE(DWORD, GetMessagePos,()) + +/* +** LoadCursor(). +** Notes: hinstance parameter ignored, supports loading some of the predefined values (e.g. IDC_SIZEALL) +** and cursors registered into the main module +** macOS: HCURSOR = NSCursor * +** see also: SWELL_LoadCursorFromFile +*/ +SWELL_API_DEFINE(HCURSOR, SWELL_LoadCursor,(const char *idx)) +#ifndef LoadCursor +#define LoadCursor(a,x) SWELL_LoadCursor(x) +#endif + +/* +** SetCursor() +** macOS: can cast a NSCursor* to HCURSOR if desired +*/ +#ifdef SetCursor +#undef SetCursor +#endif +#define SetCursor(x) SWELL_SetCursor(x) +SWELL_API_DEFINE(void, SWELL_SetCursor,(HCURSOR curs)) + + +#ifdef GetCursor +#undef GetCursor +#endif +#define GetCursor SWELL_GetCursor + +#ifdef ShowCursor +#undef ShowCursor +#endif +#define ShowCursor SWELL_ShowCursor + +#ifdef SetCursorPos +#undef SetCursorPos +#endif +#define SetCursorPos SWELL_SetCursorPos + +#ifdef ScrollWindowEx +#undef ScrollWindowEx +#endif +#define ScrollWindowEx(a,b,c,d,e,f,g,h) ScrollWindow(a,b,c,d,e) + + +/* +** SWELL_EnableRightClickEmulate() +** macOS only +** Globally enable or disable emulating mouse right-click using control+left-click +*/ +SWELL_API_DEFINE(void, SWELL_EnableRightClickEmulate, (BOOL enable)) + + +/* +** GetCursor() gets the actual system cursor, +** SWELL_GetLastSetCursor() gets the last cursor set via SWELL (if they differ than some other window must have changed the cursor) +*/ +SWELL_API_DEFINE(HCURSOR, SWELL_GetCursor,()) +SWELL_API_DEFINE(HCURSOR, SWELL_GetLastSetCursor,()) + +SWELL_API_DEFINE(bool, SWELL_IsCursorVisible, ()) +SWELL_API_DEFINE(int, SWELL_ShowCursor, (BOOL bShow)) +SWELL_API_DEFINE(BOOL, SWELL_SetCursorPos, (int X, int Y)) + +/* +** SWELL_GetViewPort +** Gets screen information, for the screen that contains sourcerect. if wantWork is set +** it excluses the menu bar etc. +*/ +SWELL_API_DEFINE(void, SWELL_GetViewPort,(RECT *r, const RECT *sourcerect, bool wantWork)) + +/* +** Clipboard API +** macOS: setting multiple types may not be supported +** GDK: only CF_TEXT is shared with system, other types are stored internally +*/ +SWELL_API_DEFINE(bool, OpenClipboard,(HWND hwndDlg)) +SWELL_API_DEFINE(void, CloseClipboard,()) +SWELL_API_DEFINE(HANDLE, GetClipboardData,(UINT type)) + +SWELL_API_DEFINE(void, EmptyClipboard,()) +SWELL_API_DEFINE(void, SetClipboardData,(UINT type, HANDLE h)) +SWELL_API_DEFINE(UINT, RegisterClipboardFormat,(const char *desc)) +SWELL_API_DEFINE(UINT, EnumClipboardFormats,(UINT lastfmt)) + +SWELL_API_DEFINE(HANDLE, GlobalAlloc,(int flags, int sz)) +SWELL_API_DEFINE(void *, GlobalLock,(HANDLE h)) +SWELL_API_DEFINE(int, GlobalSize,(HANDLE h)) +SWELL_API_DEFINE(void, GlobalUnlock,(HANDLE h)) +SWELL_API_DEFINE(void, GlobalFree,(HANDLE h)) + + +SWELL_API_DEFINE(HANDLE,CreateThread,(void *TA, DWORD stackSize, DWORD (*ThreadProc)(LPVOID), LPVOID parm, DWORD cf, DWORD *tidOut)) +SWELL_API_DEFINE(HANDLE,CreateEvent,(void *SA, BOOL manualReset, BOOL initialSig, const char *ignored)) +SWELL_API_DEFINE(HANDLE,CreateEventAsSocket,(void *SA, BOOL manualReset, BOOL initialSig, const char *ignored)) + + +#ifdef _beginthreadex +#undef _beginthreadex +#endif +#define _beginthreadex(a,b,c,d,e,f) ((UINT_PTR)CreateThread(a,b,(unsigned (*)(LPVOID))(c),d,e,(DWORD*)(f))) + +SWELL_API_DEFINE(DWORD,GetCurrentThreadId,()) +SWELL_API_DEFINE(DWORD,WaitForSingleObject,(HANDLE hand, DWORD msTO)) +SWELL_API_DEFINE(DWORD,WaitForAnySocketObject,(int numObjs, HANDLE *objs, DWORD msTO)) +SWELL_API_DEFINE(BOOL,CloseHandle,(HANDLE hand)) +SWELL_API_DEFINE(BOOL,SetThreadPriority,(HANDLE evt, int prio)) +SWELL_API_DEFINE(BOOL,SetEvent,(HANDLE evt)) +SWELL_API_DEFINE(BOOL,ResetEvent,(HANDLE evt)) + +#ifdef SWELL_TARGET_OSX +SWELL_API_DEFINE(void,SWELL_EnsureMultithreadedCocoa,()) +SWELL_API_DEFINE(void *, SWELL_InitAutoRelease,()) +SWELL_API_DEFINE(void, SWELL_QuitAutoRelease,(void *p)) +SWELL_API_DEFINE(int,SWELL_TerminateProcess,(HANDLE hand)) +SWELL_API_DEFINE(HANDLE,SWELL_CreateProcessIO,(const char *exe, int nparams, const char **params, bool redirectIO)) +SWELL_API_DEFINE(int,SWELL_ReadWriteProcessIO,(HANDLE, int w/*stdin,stdout,stderr*/, char *buf, int bufsz)) +#else +SWELL_API_DEFINE(HANDLE,SWELL_CreateProcessFromPID,(int pid)) +#endif + +SWELL_API_DEFINE(HANDLE,SWELL_CreateProcess,(const char *exe, int nparams, const char **params)) +SWELL_API_DEFINE(int,SWELL_GetProcessExitCode,(HANDLE hand)) + + +SWELL_API_DEFINE(HINSTANCE,LoadLibraryGlobals,(const char *fileName, bool symbolsAsGlobals)) +SWELL_API_DEFINE(HINSTANCE,LoadLibrary,(const char *fileName)) +SWELL_API_DEFINE(void *,GetProcAddress,(HINSTANCE hInst, const char *procName)) +SWELL_API_DEFINE(BOOL,FreeLibrary,(HINSTANCE hInst)) + +SWELL_API_DEFINE(void*,SWELL_GetBundle,(HINSTANCE hInst)) + +/* +** SWELL_CreateMemContext() +** Creates a memory context (that you can get the bits for, below) +** hdc is ignored +*/ +SWELL_API_DEFINE(HDC, SWELL_CreateMemContext,(HDC hdc, int w, int h)) + +/* +** SWELL_DeleteGfxContext() +** Deletes a context created with SWELL_CreateMemContext() (or the internal SWELL_CreateGfxContext) +*/ +SWELL_API_DEFINE(void, SWELL_DeleteGfxContext,(HDC)) + +/* +** SWELL_GetCtxGC() +** macOS: Returns the CGContextRef of a HDC +** GDK: returns NULL +*/ +SWELL_API_DEFINE(void *, SWELL_GetCtxGC,(HDC ctx)) + + +/* +** SWELL_GetCtxFrameBuffer() +** Gets the framebuffer of a memory context. NULL if none available. +*/ +SWELL_API_DEFINE(void *, SWELL_GetCtxFrameBuffer,(HDC ctx)) + + + +/* +** Some utility functions for pushing, setting, and popping the clip region. +** macOS-only +*/ +SWELL_API_DEFINE(void, SWELL_PushClipRegion,(HDC ctx)) +SWELL_API_DEFINE(void, SWELL_SetClipRegion,(HDC ctx, const RECT *r)) +SWELL_API_DEFINE(void, SWELL_PopClipRegion,(HDC ctx)) + + + +SWELL_API_DEFINE(HFONT, CreateFontIndirect,(LOGFONT *)) +SWELL_API_DEFINE(HFONT, CreateFont,(int lfHeight, int lfWidth, int lfEscapement, int lfOrientation, int lfWeight, char lfItalic, + char lfUnderline, char lfStrikeOut, char lfCharSet, char lfOutPrecision, char lfClipPrecision, + char lfQuality, char lfPitchAndFamily, const char *lfFaceName)) + +SWELL_API_DEFINE(HPEN, CreatePen,(int attr, int wid, int col)) +SWELL_API_DEFINE(HBRUSH, CreateSolidBrush,(int col)) +SWELL_API_DEFINE(HPEN, CreatePenAlpha,(int attr, int wid, int col, float alpha)) +SWELL_API_DEFINE(HBRUSH, CreateSolidBrushAlpha,(int col, float alpha)) +SWELL_API_DEFINE(HGDIOBJ, SelectObject,(HDC ctx, HGDIOBJ pen)) +SWELL_API_DEFINE(HGDIOBJ, GetStockObject,(int wh)) +SWELL_API_DEFINE(void, DeleteObject,(HGDIOBJ)) +#ifndef DestroyIcon +#define DestroyIcon(x) DeleteObject(x) +#endif + +#ifdef LineTo +#undef LineTo +#endif +#ifdef SetPixel +#undef SetPixel +#endif +#ifdef FillRect +#undef FillRect +#endif +#ifdef DrawText +#undef DrawText +#endif +#ifdef Polygon +#undef Polygon +#endif + +#define DrawText SWELL_DrawText +#define FillRect SWELL_FillRect +#define LineTo SWELL_LineTo +#define SetPixel SWELL_SetPixel +#define Polygon(a,b,c) SWELL_Polygon(a,b,c) + +SWELL_API_DEFINE(void, SWELL_FillRect,(HDC ctx, const RECT *r, HBRUSH br)) +SWELL_API_DEFINE(void, Rectangle,(HDC ctx, int l, int t, int r, int b)) +SWELL_API_DEFINE(void, Ellipse,(HDC ctx, int l, int t, int r, int b)) +SWELL_API_DEFINE(void, SWELL_Polygon,(HDC ctx, POINT *pts, int npts)) +SWELL_API_DEFINE(void, MoveToEx,(HDC ctx, int x, int y, POINT *op)) +SWELL_API_DEFINE(void, LineTo,(HDC ctx, int x, int y)) +SWELL_API_DEFINE(void, SetPixel,(HDC ctx, int x, int y, int c)) +SWELL_API_DEFINE(void, PolyBezierTo,(HDC ctx, POINT *pts, int np)) +SWELL_API_DEFINE(int, SWELL_DrawText,(HDC ctx, const char *buf, int len, RECT *r, int align)) +SWELL_API_DEFINE(void, SetTextColor,(HDC ctx, int col)) +SWELL_API_DEFINE(int, GetTextColor,(HDC ctx)) +SWELL_API_DEFINE(void, SetBkColor,(HDC ctx, int col)) +SWELL_API_DEFINE(void, SetBkMode,(HDC ctx, int col)) +SWELL_API_DEFINE(int, GetGlyphIndicesW, (HDC ctx, wchar_t *buf, int len, unsigned short *indices, int flags)) + +SWELL_API_DEFINE(void, RoundRect,(HDC ctx, int x, int y, int x2, int y2, int xrnd, int yrnd)) +SWELL_API_DEFINE(void, PolyPolyline,(HDC ctx, POINT *pts, DWORD *cnts, int nseg)) +SWELL_API_DEFINE(BOOL, GetTextMetrics,(HDC ctx, TEXTMETRIC *tm)) +SWELL_API_DEFINE(int, GetTextFace,(HDC ctx, int nCount, LPTSTR lpFaceName)) +#ifdef SWELL_TARGET_OSX +SWELL_API_DEFINE(void *, GetNSImageFromHICON,(HICON)) +#endif +SWELL_API_DEFINE(BOOL, GetObject, (HICON icon, int bmsz, void *_bm)) +SWELL_API_DEFINE(HICON, CreateIconIndirect, (ICONINFO* iconinfo)) +SWELL_API_DEFINE(HICON, LoadNamedImage,(const char *name, bool alphaFromMask)) +SWELL_API_DEFINE(void, DrawImageInRect,(HDC ctx, HICON img, const RECT *r)) +SWELL_API_DEFINE(void, BitBlt,(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int mode)) +SWELL_API_DEFINE(void, StretchBlt,(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int srcw, int srch, int mode)) +#ifndef SWELL_TARGET_OSX +SWELL_API_DEFINE(void, StretchBltFromMem,(HDC hdcOut, int x, int y, int w, int h, const void *bits, int srcw, int srch, int srcspan)) +SWELL_API_DEFINE(int, SWELL_GetScaling256, (void)) +#endif + +SWELL_API_DEFINE(void*, SWELL_ExtendedAPI, (const char *key, void *v)) + +SWELL_API_DEFINE(int, GetSysColor,(int idx)) +SWELL_API_DEFINE(HBITMAP, CreateBitmap,(int width, int height, int numplanes, int bitsperpixel, unsigned char* bits)) + +SWELL_API_DEFINE(void, SetOpaque, (HWND h, bool isopaque)) +SWELL_API_DEFINE(void, SetAllowNoMiddleManRendering, (HWND h, bool allow)) // defaults to allow, use this to disable +#ifdef SWELL_TARGET_OSX +SWELL_API_DEFINE(int, SWELL_IsRetinaDC, (HDC hdc)) // returns 1 if DC is a retina DC (2x res possible) +SWELL_API_DEFINE(int, SWELL_IsRetinaHWND, (HWND h)) // returns 1 if HWND is a retina HWND +#endif + +SWELL_API_DEFINE(void, SWELL_SetViewGL, (HWND h, char wantGL)) // wantGL=2 to enable wantsBestResolutionOpenGLSurface +SWELL_API_DEFINE(bool, SWELL_GetViewGL, (HWND h)) +SWELL_API_DEFINE(bool, SWELL_SetGLContextToView, (HWND h)) // sets GL context to that view, returns TRUE if successs (use NULL to clear GL context) + +#if defined(SWELL_TARGET_OSX) +SWELL_API_DEFINE(int, SWELL_EnableMetal,(HWND h, int mode)) // can only call once per window. calling with 0 does nothing. 1=metal enabled, 2=metal enabled and support GetDC()/ReleaseDC() for drawing (more overhead). returns metal setting. mode=-1 for non-metal async layered mode. mode=-2 for non-metal non-async layered mode + // NOTE: if using SWELL_EnableMetal(-1), any BitBlt()/StretchBlt() __MUST__ have the source bitmap persist. If it is resized after Blit it could cause crashes, too. So really this method is unsafe for practical use. +#else + #ifndef SWELL_EnableMetal + #define SWELL_EnableMetal(hwnd,x) (void)(x) + #endif +#endif + +SWELL_API_DEFINE(HDC, BeginPaint,(HWND, PAINTSTRUCT *)) +SWELL_API_DEFINE(BOOL, EndPaint,(HWND, PAINTSTRUCT *)) + +SWELL_API_DEFINE(HDC, GetDC,(HWND)) // use these sparingly! they kinda work but shouldnt be overused!! +SWELL_API_DEFINE(HDC, GetWindowDC,(HWND)) +SWELL_API_DEFINE(void, ReleaseDC,(HWND, HDC)) + +#ifdef SWELL_TARGET_OSX +SWELL_API_DEFINE(void, SWELL_FlushWindow,(HWND)) +#endif + +SWELL_API_DEFINE(void, SWELL_FillDialogBackground,(HDC hdc, const RECT *r, int level)) + +SWELL_API_DEFINE(HGDIOBJ,SWELL_CloneGDIObject,(HGDIOBJ a)) + +SWELL_API_DEFINE(int, GetSystemMetrics, (int)) + +SWELL_API_DEFINE(BOOL, DragQueryPoint,(HDROP,LPPOINT)) +SWELL_API_DEFINE(void, DragFinish,(HDROP)) +SWELL_API_DEFINE(UINT, DragQueryFile,(HDROP,UINT,char *,UINT)) + +// source drag/drop - callback is source implementing "create dropped files at droppath" +SWELL_API_DEFINE(void, SWELL_InitiateDragDrop, (HWND, RECT* srcrect, const char* srcfn, void (*callback)(const char* droppath))) +SWELL_API_DEFINE(void,SWELL_InitiateDragDropOfFileList,(HWND, RECT *srcrect, const char **srclist, int srccount, HICON icon)) +SWELL_API_DEFINE(void, SWELL_FinishDragDrop, ()) // cancels any outstanding InitiateDragDrop + + + +// focus rects aren't implemented as XOR as on win32, might be a straight blit or a separate window +// rct=NULL to "free" handle +// otherwise rct is in hwndPar coordinates +SWELL_API_DEFINE(void,SWELL_DrawFocusRect,(HWND hwndPar, RECT *rct, void **handle)) + + +#ifdef SWELL_TARGET_OSX +SWELL_API_DEFINE(void,SWELL_SetWindowRepre,(HWND hwnd, const char *fn, bool isDirty)) // sets the represented file and edited state +SWELL_API_DEFINE(void,SWELL_PostQuitMessage,(void *sender)) +SWELL_API_DEFINE(bool,SWELL_osx_is_dark_mode,(int mode)) // mode=0 for dark mode enabled enabled, 1=dark mode allowed (Breaks various things) +#endif + +/* +** Functions used by swell-dlggen.h and swell-menugen.h +** No need to really dig into these unless you're working on swell or debugging.. +*/ + +SWELL_API_DEFINE(void, SWELL_MakeSetCurParms,(float xscale, float yscale, float xtrans, float ytrans, HWND parent, bool doauto, bool dosizetofit)) + +SWELL_API_DEFINE(HWND, SWELL_MakeButton,(int def, const char *label, int idx, int x, int y, int w, int h, int flags)) +SWELL_API_DEFINE(HWND, SWELL_MakeEditField,(int idx, int x, int y, int w, int h, int flags)) +SWELL_API_DEFINE(HWND, SWELL_MakeLabel,(int align, const char *label, int idx, int x, int y, int w, int h, int flags)) +SWELL_API_DEFINE(HWND, SWELL_MakeControl,(const char *cname, int idx, const char *classname, int style, int x, int y, int w, int h, int exstyle)) +SWELL_API_DEFINE(HWND, SWELL_MakeCombo,(int idx, int x, int y, int w, int h, int flags)) +SWELL_API_DEFINE(HWND, SWELL_MakeGroupBox,(const char *name, int idx, int x, int y, int w, int h, int style)) +SWELL_API_DEFINE(HWND, SWELL_MakeCheckBox,(const char *name, int idx, int x, int y, int w, int h, int flags)) +SWELL_API_DEFINE(HWND, SWELL_MakeListBox,(int idx, int x, int y, int w, int h, int styles)) + +SWELL_API_DEFINE(void, SWELL_Menu_AddMenuItem,(HMENU hMenu, const char *name, int idx, unsigned int flags)) +SWELL_API_DEFINE(int, SWELL_GenerateMenuFromList,(HMENU hMenu, const void *list, int listsz)) // list is SWELL_MenuGen_Entry + +SWELL_API_DEFINE(void, SWELL_GenerateDialogFromList, (const void *list, int listsz)) + + +SWELL_API_DEFINE(unsigned int, _controlfp,(unsigned int flag, unsigned int mask)) + +SWELL_API_DEFINE(void,SWELL_Internal_PostMessage_Init,()) + + +SWELL_API_DEFINE(HCURSOR,SWELL_LoadCursorFromFile,(const char *fn)) +SWELL_API_DEFINE(void,SWELL_SetWindowWantRaiseAmt,(HWND h, int amt)) +SWELL_API_DEFINE(int,SWELL_GetWindowWantRaiseAmt,(HWND)) + +SWELL_API_DEFINE(void,SWELL_SetListViewFastClickMask,(HWND hList, int mask)) + + +SWELL_API_DEFINE(void,GetTempPath,(int sz, char *buf)) + +#ifndef SWELL_TARGET_OSX +SWELL_API_DEFINE(void,SWELL_initargs,(int *argc, char ***argv)) +SWELL_API_DEFINE(void,SWELL_RunMessageLoop,()) +SWELL_API_DEFINE(HWND,SWELL_CreateXBridgeWindow,(HWND viewpar, void **wref, const RECT*)) +#endif + +SWELL_API_DEFINE(bool,SWELL_GenerateGUID,(void *g)) + +SWELL_API_DEFINE(BOOL,EnumChildWindows,(HWND hwnd, BOOL (*cwEnumFunc)(HWND,LPARAM),LPARAM lParam)) + + +SWELL_API_DEFINE(BOOL,SWELL_IsGroupBox,(HWND)) +SWELL_API_DEFINE(BOOL,SWELL_IsButton,(HWND)) +SWELL_API_DEFINE(BOOL,SWELL_IsStaticText,(HWND)) +SWELL_API_DEFINE(void,SWELL_GetDesiredControlSize,(HWND hwnd, RECT *r)) + +SWELL_API_DEFINE(int,AddFontResourceEx,(LPCTSTR str, DWORD fl, void *pdv)) + +#ifdef SWELL_TARGET_OSX +SWELL_API_DEFINE(void,SWELL_DisableAppNap,(int disable)) +SWELL_API_DEFINE(int,SWELL_GetOSXVersion,()) +#endif + +SWELL_API_DEFINE(void,SWELL_Register_Cursor_Resource,(const char *idx, const char *name, int hotspot_x, int hotspot_y)) + +SWELL_API_DEFINE(bool, SWELL_ChooseColor, (HWND, COLORREF *, int ncustom, COLORREF *custom)) +SWELL_API_DEFINE(bool, SWELL_ChooseFont, (HWND, LOGFONT*)) + +SWELL_API_DEFINE(bool, IsWindowEnabled, (HWND)) + +SWELL_API_DEFINE(int, GetClassName, (HWND, char *, int)) // only partially implemented, if using custom control creators they should call SWELL_SetClassName() to set the class name (reading class name is desired) +SWELL_API_DEFINE(void, SWELL_SetClassName, (HWND, const char*)) // must pass a static string! + +SWELL_API_DEFINE(void, SWELL_DisableContextMenu, (HWND, bool)) + +SWELL_API_DEFINE(BOOL, EnumDisplayMonitors, (HDC,const LPRECT,MONITORENUMPROC,LPARAM)) +SWELL_API_DEFINE(BOOL, GetMonitorInfo, (HMONITOR, void *)) + +#endif // _WDL_SWELL_H_API_DEFINED_ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi-generic.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi-generic.cpp new file mode 100644 index 000000000..e2af1704a --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi-generic.cpp @@ -0,0 +1,796 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic win32 GDI--> null translation. + +*/ + +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" +#include "swell-internal.h" +#include "../wdlcstring.h" + +const char *g_swell_deffont_face = "Arial"; +const char *swell_last_font_filename; + +swell_colortheme g_swell_ctheme = { +#define __def_theme_ent(x,c) (c), +#define __def_theme_ent_fb(x,c,fb) (c), +SWELL_GENERIC_THEMEDEFS(__def_theme_ent,__def_theme_ent_fb) +#undef __def_theme_ent +#undef __def_theme_ent_fb +}; + +int GetSysColor(int idx) +{ + switch (idx) + { + case COLOR_WINDOW: + case COLOR_3DFACE: + case COLOR_BTNFACE: return g_swell_ctheme._3dface; + case COLOR_3DSHADOW: return g_swell_ctheme._3dshadow; + case COLOR_3DHILIGHT: return g_swell_ctheme._3dhilight; + case COLOR_3DDKSHADOW: return g_swell_ctheme._3ddkshadow; + case COLOR_BTNTEXT: return g_swell_ctheme.button_text; + case COLOR_INFOBK: return g_swell_ctheme.info_bk; + case COLOR_INFOTEXT: return g_swell_ctheme.info_text; + case COLOR_SCROLLBAR: return g_swell_ctheme.scrollbar; + } + return 0; +} + +int g_swell_ui_scale = 256; + +int SWELL_GetScaling256(void) +{ + return g_swell_ui_scale; +} + +#ifndef SWELL_LICE_GDI + +#include "../mutex.h" +#include "../ptrlist.h" + +#include "swell-gdi-internalpool.h" + +HDC SWELL_CreateGfxContext(void *c) +{ + HDC__ *ctx=SWELL_GDP_CTX_NEW(); + + + return ctx; +} + +HDC SWELL_CreateMemContext(HDC hdc, int w, int h) +{ + // we could use CGLayer here, but it's 10.4+ and seems to be slower than this +// if (w&1) w++; + void *buf=calloc(w*4,h); + if (!buf) return 0; + + HDC__ *ctx=SWELL_GDP_CTX_NEW(); + ctx->ownedData=buf; + + SetTextColor(ctx,0); + return ctx; +} + + +void SWELL_DeleteGfxContext(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (HDC_VALID(ct)) + { + if (ct->ownedData) + { + free(ct->ownedData); + } + SWELL_GDP_CTX_DELETE(ct); + } +} +HPEN CreatePen(int attr, int wid, int col) +{ + return CreatePenAlpha(attr,wid,col,1.0f); +} + +HBRUSH CreateSolidBrush(int col) +{ + return CreateSolidBrushAlpha(col,1.0f); +} + +HPEN CreatePenAlpha(int attr, int wid, int col, float alpha) +{ + HGDIOBJ__ *pen=GDP_OBJECT_NEW(); + pen->type=TYPE_PEN; + pen->wid=wid<0?0:wid; +// pen->color=CreateColor(col,alpha); + return pen; +} +HBRUSH CreateSolidBrushAlpha(int col, float alpha) +{ + HGDIOBJ__ *brush=GDP_OBJECT_NEW(); + brush->type=TYPE_BRUSH; +// brush->color=CreateColor(col,alpha); + brush->wid=0; + return brush; +} + +#define FONTSCALE 0.9 +HFONT CreateFont(int lfHeight, int lfWidth, int lfEscapement, int lfOrientation, int lfWeight, char lfItalic, + char lfUnderline, char lfStrikeOut, char lfCharSet, char lfOutPrecision, char lfClipPrecision, + char lfQuality, char lfPitchAndFamily, const char *lfFaceName) +{ + HGDIOBJ__ *font=GDP_OBJECT_NEW(); + font->type=TYPE_FONT; + float fontwid=lfHeight; + + + if (!fontwid) fontwid=lfWidth; + if (fontwid<0)fontwid=-fontwid; + + if (fontwid < 2 || fontwid > 8192) fontwid=10; + + return font; +} + +HFONT SWELL_GetDefaultFont() +{ + return NULL; +} + + +HFONT CreateFontIndirect(LOGFONT *lf) +{ + return CreateFont(lf->lfHeight, lf->lfWidth,lf->lfEscapement, lf->lfOrientation, lf->lfWeight, lf->lfItalic, + lf->lfUnderline, lf->lfStrikeOut, lf->lfCharSet, lf->lfOutPrecision,lf->lfClipPrecision, + lf->lfQuality, lf->lfPitchAndFamily, lf->lfFaceName); +} + +int GetTextFace(HDC ctx, int nCount, LPTSTR lpFaceName) +{ + if (lpFaceName) lpFaceName[0]=0; + return 0; +} + +void DeleteObject(HGDIOBJ pen) +{ + if (HGDIOBJ_VALID(pen)) + { + HGDIOBJ__ *p=(HGDIOBJ__ *)pen; + if (--p->additional_refcnt < 0) + { + if (p->type == TYPE_PEN || p->type == TYPE_BRUSH || p->type == TYPE_FONT || p->type == TYPE_BITMAP) + { + if (p->type == TYPE_PEN || p->type == TYPE_BRUSH) + if (p->wid<0) return; + + GDP_OBJECT_DELETE(p); + } + // JF> don't free unknown objects, this should never happen anyway: else free(p); + } + } +} + + +HGDIOBJ SelectObject(HDC ctx, HGDIOBJ pen) +{ + HDC__ *c=(HDC__ *)ctx; + HGDIOBJ__ *p=(HGDIOBJ__ *) pen; + HGDIOBJ__ **mod=0; + if (!HDC_VALID(c)||!p) return 0; + + if (p == (HGDIOBJ__ *)TYPE_PEN) mod=&c->curpen; + else if (p == (HGDIOBJ__ *)TYPE_BRUSH) mod=&c->curbrush; + else if (p == (HGDIOBJ__ *)TYPE_FONT) mod=&c->curfont; + + if (mod) + { + HGDIOBJ__ *np=*mod; + *mod=0; + return np?np:p; + } + + if (!HGDIOBJ_VALID(p)) return 0; + + if (p->type == TYPE_PEN) mod=&c->curpen; + else if (p->type == TYPE_BRUSH) mod=&c->curbrush; + else if (p->type == TYPE_FONT) mod=&c->curfont; + else return 0; + + HGDIOBJ__ *op=*mod; + if (!op) op=(HGDIOBJ__ *)(INT_PTR)p->type; + if (op != p) + { + *mod=p; + + if (p->type == TYPE_FONT) + { + } + } + return op; +} + + + +void SWELL_FillRect(HDC ctx, const RECT *r, HBRUSH br) +{ + HDC__ *c=(HDC__ *)ctx; + HGDIOBJ__ *b=(HGDIOBJ__ *) br; + if (!HDC_VALID(c) || !HGDIOBJ_VALID(b,TYPE_BRUSH)) return; + + if (b->wid<0) return; + + +} + +void RoundRect(HDC ctx, int x, int y, int x2, int y2, int xrnd, int yrnd) +{ + xrnd/=3; + yrnd/=3; + POINT pts[10]={ // todo: curves between edges + {x,y+yrnd}, + {x+xrnd,y}, + {x2-xrnd,y}, + {x2,y+yrnd}, + {x2,y2-yrnd}, + {x2-xrnd,y2}, + {x+xrnd,y2}, + {x,y2-yrnd}, + {x,y+yrnd}, + {x+xrnd,y}, +}; + + WDL_GDP_Polygon(ctx,pts,sizeof(pts)/sizeof(pts[0])); +} + +void Ellipse(HDC ctx, int l, int t, int r, int b) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)) return; + + //CGRect rect=CGRectMake(l,t,r-l,b-t); + + if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >=0) + { + } + if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0) + { + } +} + +void Rectangle(HDC ctx, int l, int t, int r, int b) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)) return; + + if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0) + { + } + if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0) + { + } +} + +HGDIOBJ GetStockObject(int wh) +{ + switch (wh) + { + case NULL_BRUSH: + { + static HGDIOBJ__ br={0,}; + br.type=TYPE_BRUSH; + br.wid=-1; + return &br; + } + case NULL_PEN: + { + static HGDIOBJ__ pen={0,}; + pen.type=TYPE_PEN; + pen.wid=-1; + return &pen; + } + } + return 0; +} + +void Polygon(HDC ctx, POINT *pts, int npts) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)) return; + if (((!HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH)||c->curbrush->wid<0) && + (!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0)) || npts<2) return; + +} + +void MoveToEx(HDC ctx, int x, int y, POINT *op) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)) return; + if (op) + { + op->x = (int) (c->lastpos_x); + op->y = (int) (c->lastpos_y); + } + c->lastpos_x=(float)x; + c->lastpos_y=(float)y; +} + +void PolyBezierTo(HDC ctx, POINT *pts, int np) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||np<3) return; + + int x; + float xp,yp; + for (x = 0; x < np-2; x += 3) + { + xp=(float)pts[x+2].x; + yp=(float)pts[x+2].y; + } + c->lastpos_x=(float)xp; + c->lastpos_y=(float)yp; +} + + +void SWELL_LineTo(HDC ctx, int x, int y) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0) return; + + float fx=(float)x,fy=(float)y; + + c->lastpos_x=fx; + c->lastpos_y=fy; +} + +void PolyPolyline(HDC ctx, POINT *pts, DWORD *cnts, int nseg) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||nseg<1) return; + + while (nseg-->0) + { + DWORD cnt=*cnts++; + if (!cnt) continue; + if (!--cnt) { pts++; continue; } + + pts++; + + while (cnt--) + { + pts++; + } + } +} +void *SWELL_GetCtxGC(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return 0; + return NULL; +} + + +void SWELL_SetPixel(HDC ctx, int x, int y, int c) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; +} + + +BOOL GetTextMetrics(HDC ctx, TEXTMETRIC *tm) +{ + HDC__ *ct=(HDC__ *)ctx; + if (tm) // give some sane defaults + { + tm->tmInternalLeading=3; + tm->tmAscent=12; + tm->tmDescent=4; + tm->tmHeight=16; + tm->tmAveCharWidth = 10; + } + if (!HDC_VALID(ct)||!tm) return 0; + + return 1; +} + + +int DrawText(HDC ctx, const char *buf, int buflen, RECT *r, int align) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return 0; + if (r && (align&DT_CALCRECT)) + { + r->top=r->left=0; + r->bottom=10; + r->right = ( buflen < 0 ? strlen(buf) : buflen ) *8; + } + else printf("DrawText: %s\n",buf); + return 10; +} + +void SetBkColor(HDC ctx, int col) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; + ct->curbkcol=col; +} + +void SetBkMode(HDC ctx, int col) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; + ct->curbkmode=col; +} +int GetTextColor(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return -1; + return ct->cur_text_color_int; +} + +void SetTextColor(HDC ctx, int col) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; + ct->cur_text_color_int = col; + +} + +HICON LoadNamedImage(const char *name, bool alphaFromMask) +{ + return 0; // todo +} + +void DrawImageInRect(HDC ctx, HICON img, const RECT *r) +{ + // todo +} + +void SWELL_SetViewGL(HWND h, char wantGL) +{ +} +bool SWELL_GetViewGL(HWND h) +{ + return false; +} +bool SWELL_SetGLContextToView(HWND h) +{ + return false; +} + +BOOL GetObject(HICON icon, int bmsz, void *_bm) +{ + memset(_bm,0,bmsz); + if (bmsz != sizeof(BITMAP)) return false; + HGDIOBJ__ *i = (HGDIOBJ__ *)icon; + if (!HGDIOBJ_VALID(i,TYPE_BITMAP)) return false; + //BITMAP *bm=(BITMAP *)_bm; + + return false; +} + +void BitBltAlphaFromMem(HDC hdcOut, int x, int y, int w, int h, void *inbufptr, int inbuf_span, int inbuf_h, int xin, int yin, int mode, bool useAlphaChannel, float opacity) +{ +} + +void BitBltAlpha(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int mode, bool useAlphaChannel, float opacity) +{ +} + +void BitBlt(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int mode) +{ +} + +void StretchBlt(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int srcw, int srch, int mode) +{ +} + +void StretchBltFromMem(HDC hdcOut, int x, int y, int w, int h, const void *bits, int srcw, int srch, int srcspan) +{ +} + +void SWELL_PushClipRegion(HDC ctx) +{ +// HDC__ *ct=(HDC__ *)ctx; +} + +void SWELL_SetClipRegion(HDC ctx, const RECT *r) +{ +// HDC__ *ct=(HDC__ *)ctx; + +} + +void SWELL_PopClipRegion(HDC ctx) +{ +// HDC__ *ct=(HDC__ *)ctx; +} + +void *SWELL_GetCtxFrameBuffer(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (HDC_VALID(ct)) return ct->ownedData; + return 0; +} + + +HDC GetDC(HWND h) +{ + return NULL; +} + +HDC GetWindowDC(HWND h) +{ + return NULL; +} + +void ReleaseDC(HWND h, HDC hdc) +{ +} + +void SWELL_FillDialogBackground(HDC hdc, const RECT *r, int level) +{ +} + +HGDIOBJ SWELL_CloneGDIObject(HGDIOBJ a) +{ + if (HGDIOBJ_VALID(a)) + { + a->additional_refcnt++; + return a; + } + return NULL; +} + +HDC BeginPaint(HWND hwnd, PAINTSTRUCT *ps) +{ + if (!ps) return 0; + memset(ps,0,sizeof(PAINTSTRUCT)); + if (!hwnd) return 0; + + return NULL; +} + + +HBITMAP CreateBitmap(int width, int height, int numplanes, int bitsperpixel, unsigned char* bits) +{ + return NULL; +} + +HICON CreateIconIndirect(ICONINFO* iconinfo) +{ + return NULL; +} +HIMAGELIST ImageList_CreateEx() +{ + return (HIMAGELIST)new WDL_PtrList; +} +BOOL ImageList_Remove(HIMAGELIST list, int idx) +{ + WDL_PtrList* imglist=(WDL_PtrList*)list; + if (imglist && idx < imglist->GetSize()) + { + if (idx < 0) + { + int x,n=imglist->GetSize(); + for (x=0;xGet(x); + if (a) DeleteObject(a); + } + imglist->Empty(); + } + else + { + HGDIOBJ__ *a = imglist->Get(idx); + imglist->Set(idx, NULL); + if (a) DeleteObject(a); + } + return TRUE; + } + + return FALSE; +} + +void ImageList_Destroy(HIMAGELIST list) +{ + if (!list) return; + WDL_PtrList *p=(WDL_PtrList*)list; + ImageList_Remove(list,-1); + delete p; +} + +int ImageList_ReplaceIcon(HIMAGELIST list, int offset, HICON image) +{ + if (!image || !list) return -1; + WDL_PtrList *l=(WDL_PtrList *)list; + + HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image; + if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1; + + HGDIOBJ__* icon=GDP_OBJECT_NEW(); + icon->type=TYPE_BITMAP; + icon->wid=1; + // todo: copy underlying image + + image = (HICON) icon; + + if (offset<0||offset>=l->GetSize()) + { + l->Add(image); + offset=l->GetSize()-1; + } + else + { + HICON old=l->Get(offset); + l->Set(offset,image); + if (old) DeleteObject(old); + } + return offset; +} + +int ImageList_Add(HIMAGELIST list, HBITMAP image, HBITMAP mask) +{ + if (!image || !list) return -1; + WDL_PtrList *l=(WDL_PtrList *)list; + + HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image; + if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1; + + HGDIOBJ__* icon=GDP_OBJECT_NEW(); + icon->type=TYPE_BITMAP; + icon->wid=1; + // todo: copy underlying image + + image = (HICON) icon; + + l->Add(image); + return l->GetSize(); +} + + +int AddFontResourceEx(LPCTSTR str, DWORD fl, void *pdv) +{ + return 0; +} + +int GetGlyphIndicesW(HDC ctx, wchar_t *buf, int len, unsigned short *indices, int flags) +{ + int i; + for (i=0; i < len; ++i) indices[i]=(flags == GGI_MARK_NONEXISTING_GLYPHS ? 0xFFFF : 0); + return 0; +} + +#endif // !SWELL_LICE_GDI + +#ifdef SWELL__MAKE_THEME +void print_ent(const char *x, int c, const char *def) +{ + if (def) + printf("; %s #%02x%02x%02x ; defaults to %s\n",x,GetRValue(c),GetGValue(c),GetBValue(c),def); + else + { + if (strstr(x,"_size") || + strstr(x,"_height") || + strstr(x,"_width")) + printf("%s %d\n",x,c); + else printf("%s #%02x%02x%02x\n",x,GetRValue(c),GetGValue(c),GetBValue(c)); + } +} + +int main() +{ +#define __def_theme_ent(x,c) print_ent(#x,c,NULL); +#define __def_theme_ent_fb(x,c,fb) print_ent(#x,c,#fb); + +printf("default_font_face %s\n",g_swell_deffont_face); +SWELL_GENERIC_THEMEDEFS(__def_theme_ent,__def_theme_ent_fb) +return 0; +} +#else + +void swell_load_color_theme(const char *fn) +{ + FILE *fp = WDL_fopenA(fn,"r"); + if (fp) + { + swell_colortheme load; + memset(&load,-1,sizeof(load)); + char buf[1024]; + + for (;;) + { + if (!fgets(buf,sizeof(buf),fp)) break; + char *p = buf; + while (*p == ' ' || *p == '\t') p++; + char *np = p; + while (*np > 0 && (*np == '_' || isalnum(*np))) np++; + if (!*np || np == p) continue; + *np++ = 0; + while (*np == ' ' || *np == '\t') np++; + + if(!stricmp(p,"default_font_face")) + { + if (*np > 0 && !isspace(*np)) + { + char *b = strdup(np); + g_swell_deffont_face = b; + while (*b && *b != ';' && *b != '#') b++; + while (b>g_swell_deffont_face && b[-1] > 0 && isspace(b[-1])) b--; + *b=0; + } + continue; + } + + int col; + if (*np == '#') + { + np++; + char *next; + col = strtol(np,&next,16); + if (next != np+6) + { + if (next != np+3) continue; + col = ((col&0xf)<<4) | ((col&0xf0)<<8) | ((col&0xf00)<<12); + } + } + else if (*np >= '0' && *np <= '9') + { + col = atoi(np); + } + else continue; + + if(0){} +#define __def_theme_ent(x,c) else if (!stricmp(p,#x)) load.x = col; +#define __def_theme_ent_fb(x,c,fb) else if (!stricmp(p,#x)) load.x = col; +SWELL_GENERIC_THEMEDEFS(__def_theme_ent,__def_theme_ent_fb) +#undef __def_theme_ent +#undef __def_theme_ent_fb + } +#define __def_theme_ent(x,c) g_swell_ctheme.x = load.x == -1 ? c : load.x; +#define __def_theme_ent_fb(x,c,fb) g_swell_ctheme.x = load.x == -1 ? g_swell_ctheme.fb : load.x; +SWELL_GENERIC_THEMEDEFS(__def_theme_ent,__def_theme_ent_fb) +#undef __def_theme_ent +#undef __def_theme_ent_fb + + fclose(fp); + } +} + +// load color theme +class swellColorThemeLoader +{ +public: + swellColorThemeLoader() + { + char buf[1024]; + GetModuleFileName(NULL,buf,sizeof(buf)); + WDL_remove_filepart(buf); + lstrcatn(buf,"/libSwell.colortheme",sizeof(buf)); + swell_load_color_theme(buf); + } +}; +swellColorThemeLoader g_swell_themeloader; + + + +#endif + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi-internalpool.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi-internalpool.h new file mode 100644 index 000000000..41ac60957 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi-internalpool.h @@ -0,0 +1,222 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + // used for HDC/HGDIOBJ pooling (to avoid excess heap use), used by swell-gdi.mm and swell-gdi-generic.cpp +*/ + +#if defined(_DEBUG) + #define SWELL_GDI_DEBUG +#endif + +static WDL_Mutex *m_ctxpool_mutex; +#ifdef SWELL_GDI_DEBUG + #include "../ptrlist.h" + static WDL_PtrList *m_ctxpool_debug; + static WDL_PtrList *m_objpool_debug; +#else + static HDC__ *m_ctxpool; + static int m_ctxpool_size; + static HGDIOBJ__ *m_objpool; + static int m_objpool_size; +#endif + + + +HDC__ *SWELL_GDP_CTX_NEW() +{ + if (!m_ctxpool_mutex) m_ctxpool_mutex=new WDL_Mutex; + + HDC__ *p=NULL; +#ifdef SWELL_GDI_DEBUG + m_ctxpool_mutex->Enter(); + if (!m_ctxpool_debug) m_ctxpool_debug = new WDL_PtrList; + if (m_ctxpool_debug->GetSize() > 8192) + { + p = m_ctxpool_debug->Get(0); + m_ctxpool_debug->Delete(0); + memset(p,0,sizeof(*p)); + } + m_ctxpool_mutex->Leave(); +#else + if (m_ctxpool) + { + m_ctxpool_mutex->Enter(); + if ((p=m_ctxpool)) + { + m_ctxpool=p->_next; + m_ctxpool_size--; + memset(p,0,sizeof(*p)); + } + m_ctxpool_mutex->Leave(); + } +#endif + if (!p) + { +// printf("alloc ctx\n"); + p=(HDC__ *)calloc(sizeof(HDC__)+128,1); // extra space in case things want to use it (i.e. swell-gdi-lice does) + } + return p; +} +static void SWELL_GDP_CTX_DELETE(HDC__ *p) +{ + if (!m_ctxpool_mutex) m_ctxpool_mutex=new WDL_Mutex; + + if (WDL_NOT_NORMALLY(!p || p->_infreelist)) return; + + memset(p,0,sizeof(*p)); + +#ifdef SWELL_GDI_DEBUG + m_ctxpool_mutex->Enter(); + p->_infreelist=true; + if (!m_ctxpool_debug) m_ctxpool_debug = new WDL_PtrList; + m_ctxpool_debug->Add(p); + m_ctxpool_mutex->Leave(); +#else + if (m_ctxpool_size<100) + { + m_ctxpool_mutex->Enter(); + p->_infreelist=true; + p->_next = m_ctxpool; + m_ctxpool = p; + m_ctxpool_size++; + m_ctxpool_mutex->Leave(); + } + else + { + // printf("free ctx\n"); + free(p); + } +#endif +} +static HGDIOBJ__ *GDP_OBJECT_NEW() +{ + if (!m_ctxpool_mutex) m_ctxpool_mutex=new WDL_Mutex; + HGDIOBJ__ *p=NULL; +#ifdef SWELL_GDI_DEBUG + m_ctxpool_mutex->Enter(); + if (!m_objpool_debug) m_objpool_debug = new WDL_PtrList; + if (m_objpool_debug->GetSize()>8192) + { + p = m_objpool_debug->Get(0); + m_objpool_debug->Delete(0); + memset(p,0,sizeof(*p)); + } + m_ctxpool_mutex->Leave(); +#else + if (m_objpool) + { + m_ctxpool_mutex->Enter(); + if ((p=m_objpool)) + { + m_objpool = p->_next; + m_objpool_size--; + memset(p,0,sizeof(*p)); + } + m_ctxpool_mutex->Leave(); + } +#endif + if (!p) + { + // printf("alloc obj\n"); + p=(HGDIOBJ__ *)calloc(sizeof(HGDIOBJ__),1); + } + return p; +} + +static bool HGDIOBJ_VALID(HGDIOBJ__ *p, int reqType=0) +{ + return p && + WDL_NORMALLY( p != (HGDIOBJ__*)TYPE_PEN && p != (HGDIOBJ__*)TYPE_BRUSH && + p != (HGDIOBJ__*)TYPE_FONT && p != (HGDIOBJ__*)TYPE_BITMAP) && + WDL_NORMALLY(!p->_infreelist) && + WDL_NORMALLY(!reqType || reqType == p->type); +} + +static void GDP_OBJECT_DELETE(HGDIOBJ__ *p) +{ + if (!m_ctxpool_mutex) m_ctxpool_mutex=new WDL_Mutex; + if (WDL_NOT_NORMALLY(!p) || !HGDIOBJ_VALID(p)) return; + + memset(p,0,sizeof(*p)); +#ifdef SWELL_GDI_DEBUG + m_ctxpool_mutex->Enter(); + p->_infreelist = true; + if (!m_objpool_debug) m_objpool_debug = new WDL_PtrList; + m_objpool_debug->Add(p); + m_ctxpool_mutex->Leave(); +#else + if (m_objpool_size<200) + { + m_ctxpool_mutex->Enter(); + p->_infreelist = true; + p->_next = m_objpool; + m_objpool = p; + m_objpool_size++; + m_ctxpool_mutex->Leave(); + } + else + { + // printf("free obj\n"); + free(p); + } +#endif +} + +static bool HDC_VALID(HDC__ *ct) +{ + return ct && WDL_NORMALLY(!ct->_infreelist); +} + + +#if !defined(SWELL_GDI_DEBUG) && defined(SWELL_CLEANUP_ON_UNLOAD) + +class _swellGdiUnloader +{ + public: + _swellGdiUnloader() { } + ~_swellGdiUnloader() + { + { + HDC__ *p = m_ctxpool; + m_ctxpool = NULL; + while (p) + { + HDC__ *t = p; + p = p->_next; + free(t); + } + } + { + HGDIOBJ__ *p = m_objpool; + m_objpool = NULL; + while (p) + { + HGDIOBJ__ *t = p; + p = p->_next; + free(t); + } + } + + delete m_ctxpool_mutex; + m_ctxpool_mutex=NULL; + } +}; + +_swellGdiUnloader __swell__swellGdiUnloader; +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi-lice.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi-lice.cpp new file mode 100644 index 000000000..9adb87056 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi-lice.cpp @@ -0,0 +1,1878 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic win32 GDI-->lice translation. + +*/ + +#ifdef SWELL_LICE_GDI +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" +#include "swell-internal.h" + +#include "../mutex.h" +#include "../ptrlist.h" +#include "../wdlcstring.h" +#include "../wdlutf8.h" + +#include "swell-gdi-internalpool.h" + + +#ifdef SWELL_FREETYPE +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#ifdef SWELL_FONTCONFIG +#include +#else +#include "../dirscan.h" +#endif + +static bool s_freetype_failed; +static FT_Library s_freetype; // todo: TLS for multithread support? -- none of the text drawing is thread safe! + +extern const char *swell_last_font_filename; + +#ifndef SWELL_FREETYPE_CACHE_SIZE +#define SWELL_FREETYPE_CACHE_SIZE 80 +#endif + +class fontConfigCacheEnt +{ + public: + fontConfigCacheEnt(const char *name, int flags, int w, int h, FT_Face face, const char *fndesc) + { + m_name = strdup(name); + m_flags = flags; + m_face = face; + m_w=w; + m_h=h; + m_fndesc = strdup(fndesc); + FT_Reference_Face(m_face); + } + ~fontConfigCacheEnt() + { + free(m_name); + free(m_fndesc); + FT_Done_Face(m_face); + } + + char *m_name; + char *m_fndesc; + int m_flags, m_w, m_h; + FT_Face m_face; +}; + + +#ifdef SWELL_FONTCONFIG + +static FcConfig *s_fontconfig; +const char *swell_enumFontFiles(int x) +{ + if (!s_fontconfig) return NULL; + + static FcPattern *pat; + static FcObjectSet *prop; + static FcFontSet *fonts; + if (x<0) + { + if (fonts) FcFontSetDestroy(fonts); + if (prop) FcObjectSetDestroy(prop); + if (pat) FcPatternDestroy(pat); + pat=NULL; + prop=NULL; + fonts=NULL; + return NULL; + } + if (!pat) + { + pat = FcPatternCreate(); + prop = FcObjectSetBuild(FC_FAMILY, NULL); + fonts = FcFontList(s_fontconfig,pat,prop); + } + if (fonts && x < fonts->nfont) + { + FcPattern *f = fonts->fonts[x]; + FcChar8 *fn = NULL; + if (FcPatternGetString(f,FC_FAMILY,0,&fn) == FcResultMatch && fn && *fn) + return (const char *)fn; + } + + return NULL; +} + +#else + +static WDL_PtrList s_freetype_fontlist; +static WDL_PtrList s_freetype_regfonts; + +const char *swell_enumFontFiles(int x) +{ + const int n1 = s_freetype_regfonts.GetSize(); + if (x < n1) return s_freetype_regfonts.Get(x); + return s_freetype_fontlist.Get(x-n1); +} + + +static void ScanFontDirectory(const char *path, int maxrec=3) +{ + WDL_DirScan ds; + WDL_FastString fs; + if (!ds.First(path)) do + { + if (ds.GetCurrentFN()[0] != '.') + { + if (ds.GetCurrentIsDirectory()) + { + if (maxrec>0 && + stricmp(ds.GetCurrentFN(),"type1") && + stricmp(ds.GetCurrentFN(),"cmap") && + stricmp(ds.GetCurrentFN(),"ghostscript") && + strcmp(ds.GetCurrentFN(),"X11") + ) + { + ds.GetCurrentFullFN(&fs); + ScanFontDirectory(fs.Get(),maxrec-1); + } + } + else + { + const char *ext = WDL_get_fileext(ds.GetCurrentFN()); + if (!stricmp(ext,".ttf") || !stricmp(ext,".otf")) + { + ds.GetCurrentFullFN(&fs); + s_freetype_fontlist.Add(strdup(fs.Get())); + } + } + } + } while (!ds.Next()); +} + +static int sortByFilePart(const char **a, const char **b) +{ + return stricmp(WDL_get_filepart(*a),WDL_get_filepart(*b)); +} + +struct fontScoreMatched { + int score1,score2; + const char *fn; + + static int sortfunc(const void *a, const void *b) + { + const int v = ((const fontScoreMatched *)a)->score1 - ((const fontScoreMatched *)b)->score1; + return v?v:((const fontScoreMatched *)a)->score2 - ((const fontScoreMatched *)b)->score2; + } + +}; + +static FT_Face MatchFont(const char *lfFaceName, int weight, int italic, int exact, char *matchFnOut, size_t matchFnOutSize) +{ + const int fn_len = strlen(lfFaceName), ntab=2; + WDL_PtrList *tab[ntab]= { &s_freetype_regfonts, &s_freetype_fontlist }; + int x; + bool match; + static WDL_TypedBuf matchlist; + matchlist.Resize(0,false); + + for (x=0;x *t = tab[x]; + int px = t->LowerBound(lfFaceName,&match,sortByFilePart); + for (;;) + { + const char *fn = t->Get(px++); + if (!fn) break; + const char *fnp = WDL_get_filepart(fn); + if (strnicmp(fnp,lfFaceName,fn_len)) break; + + fontScoreMatched s; + const char *residual = fnp + fn_len; + const char *dash = strstr(residual,"-"); + const char *ext = WDL_get_fileext(residual); + + if (!dash) + { + if (!strnicmp(residual,"Bold",4) || + !strnicmp(residual,"Italic",6) || + !strnicmp(residual,"Light",5) || + !strnicmp(residual,"Oblique",7)) + dash = residual; + else if (ext > residual && ext <= residual+2) + { + char c1 = residual[0],c2=residual[1]; + if (c1>0) c1=toupper(c1); + if (c2>0) c2=toupper(c2); + if ((c1 == 'B' || c1 == 'I' || c1 == 'L') && + (c2 == 'B' || c2 == 'I' || c2 == 'L' || c2 == '.')) + dash=residual; + } + } + + s.fn = fn; + s.score1 = (int)((dash?dash:ext)-residual); // characters between font and either "-" or "." + + if (exact > 0) + { + if (s.score1) continue; + } + else if (exact < 0 && !s.score1) + { + continue; + } + + s.score2 = 0; + + if (dash) { if (*dash == '-') dash++; } + else dash=residual; + + while (*dash && *dash != '.') + { + if (*dash > 0 && isalnum(*dash)) s.score2++; + dash++; + } + + if (WDL_stristr(residual,"Regular")) s.score2 -= 7; // ignore "Regular" + if (italic && WDL_stristr(residual,"Italic")) s.score2 -= 6+7; + else if (italic && WDL_stristr(residual,"Oblique")) s.score2 -= 7+3; // if Italic isnt available, use Oblique + if (weight >= FW_BOLD && WDL_stristr(residual,"Bold")) s.score2 -= 4+7; + else if (weight <= FW_LIGHT && WDL_stristr(residual,"Light")) s.score2 -= 5+7; + + if (ext > residual && ext <= residual+2) + { + char c1 = residual[0],c2=residual[1]; + if (c1>0) c1=toupper(c1); + if (c2>0) c2=toupper(c2); + + if (weight >= FW_BOLD && (c1 == 'B' || c2 == 'B')) s.score2 -= 2; + else if (weight <= FW_LIGHT && (c1 == 'L' || c2 == 'L')) s.score2 -= 2; + if (italic && (c1 == 'I' || c2 == 'I')) s.score2 -= 2; + } + + s.score2 = s.score2*ntab + x; + + matchlist.Add(s); + } + } + if (matchlist.GetSize()>1) + qsort(matchlist.Get(),matchlist.GetSize(),sizeof(fontScoreMatched),fontScoreMatched::sortfunc); + + for (x=0; x < matchlist.GetSize(); x ++) + { + const fontScoreMatched *s = matchlist.Get()+x; + + FT_Face face=NULL; + //printf("trying '%s' for '%s' score %d,%d w %d i %d\n",s->fn,lfFaceName,s->score1,s->score2,weight,italic); + FT_New_Face(s_freetype,s->fn,0,&face); + if (face) + { + lstrcpyn_safe(matchFnOut,s->fn,matchFnOutSize); + return face; + } + } + return NULL; +} +#endif // !SWELL_FONTCONFIG + +#endif // SWELL_FREETYPE + +HDC SWELL_CreateMemContext(HDC hdc, int w, int h) +{ + LICE_MemBitmap * bm = new LICE_MemBitmap(w,h); + if (WDL_NOT_NORMALLY(!bm)) return 0; + LICE_Clear(bm,LICE_RGBA(0,0,0,0)); + + HDC__ *ctx=SWELL_GDP_CTX_NEW(); + ctx->surface = bm; + ctx->surface_offs.x=0; + ctx->surface_offs.y=0; + ctx->dirty_rect_valid=false; + + SetTextColor(ctx,0); + return ctx; +} + + +void SWELL_DeleteGfxContext(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (HDC_VALID(ct)) + { + delete ct->surface; + ct->surface=0; + SWELL_GDP_CTX_DELETE(ct); + } +} + +HPEN CreatePen(int attr, int wid, int col) +{ + return CreatePenAlpha(attr,wid,col,1.0f); +} + +HBRUSH CreateSolidBrush(int col) +{ + return CreateSolidBrushAlpha(col,1.0f); +} + +HPEN CreatePenAlpha(int attr, int wid, int col, float alpha) +{ + HGDIOBJ__ *pen=GDP_OBJECT_NEW(); + pen->type=TYPE_PEN; + pen->wid=wid<0?0:wid; + pen->alpha = alpha; + pen->color=LICE_RGBA_FROMNATIVE(col); + return pen; +} +HBRUSH CreateSolidBrushAlpha(int col, float alpha) +{ + HGDIOBJ__ *brush=GDP_OBJECT_NEW(); + brush->type=TYPE_BRUSH; + brush->color=LICE_RGBA_FROMNATIVE(col); + brush->alpha = alpha; + brush->wid=0; + return brush; +} + +void SetBkColor(HDC ctx, int col) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; + ct->curbkcol=LICE_RGBA_FROMNATIVE(col,255); +} + +void SetBkMode(HDC ctx, int col) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; + ct->curbkmode=col; +} + +HGDIOBJ GetStockObject(int wh) +{ + switch (wh) + { + case NULL_BRUSH: + { + static HGDIOBJ__ br={0,}; + br.type=TYPE_BRUSH; + br.wid=-1; + return &br; + } + case NULL_PEN: + { + static HGDIOBJ__ pen={0,}; + pen.type=TYPE_PEN; + pen.wid=-1; + return &pen; + } + } + WDL_ASSERT(false); + return 0; +} + +HFONT CreateFont(int lfHeight, int lfWidth, int lfEscapement, int lfOrientation, int lfWeight, char lfItalic, + char lfUnderline, char lfStrikeOut, char lfCharSet, char lfOutPrecision, char lfClipPrecision, + char lfQuality, char lfPitchAndFamily, const char *lfFaceName) +{ + HGDIOBJ__ *font = GDP_OBJECT_NEW(); + font->typedata = NULL; + font->type=TYPE_FONT; + font->alpha = 1.0f; +#ifdef SWELL_FREETYPE + if (!s_freetype_failed && !s_freetype) + { + s_freetype_failed = !!FT_Init_FreeType(&s_freetype); + if (s_freetype) + { +#ifdef SWELL_FONTCONFIG + if (!s_fontconfig) s_fontconfig = FcInitLoadConfigAndFonts(); +#else + ScanFontDirectory("/usr/share/fonts"); + + qsort(s_freetype_fontlist.GetList(),s_freetype_fontlist.GetSize(),sizeof(const char *),(int (*)(const void *,const void*))sortByFilePart); +#endif + } + } + if (lfWidth<0) lfWidth=-lfWidth; + if (lfHeight<0) lfHeight=-lfHeight; + + static WDL_PtrList cache; + const int cache_flag = wdl_max(lfWeight,0) | (lfItalic ? (1<<30) : 0); + FT_Face face=NULL; + for (int x=0;xm_flags==cache_flag && + ent->m_w == lfWidth && + ent->m_h == lfHeight && + !strcmp(ent->m_name,lfFaceName?lfFaceName:"")) + { + face = ent->m_face; + swell_last_font_filename = ent->m_fndesc; + FT_Reference_Face(face); + if (x < cache.GetSize()-1) + { + cache.Delete(x); + cache.Add(ent); // make this cache entry most recent + } + break; + } + } + + if (!face && s_freetype) + { + int face_idx=0; + char face_fn[1024]; + face_fn[0]=0; +#ifdef SWELL_FONTCONFIG + if (!face && s_fontconfig) + { + FcPattern *pat = FcPatternCreate(); + if (pat) + { + if (lfFaceName && *lfFaceName) FcPatternAddString(pat,FC_FAMILY,(FcChar8*)lfFaceName); + if (lfWeight > 0) + { + int wt; + if (lfWeight >= FW_HEAVY) wt=FC_WEIGHT_HEAVY; + else if (lfWeight >= FW_EXTRABOLD) wt=FC_WEIGHT_EXTRABOLD; + else if (lfWeight >= FW_BOLD) wt=FC_WEIGHT_BOLD; + else if (lfWeight >= FW_SEMIBOLD) wt=FC_WEIGHT_SEMIBOLD; + else if (lfWeight >= FW_MEDIUM) wt=FC_WEIGHT_MEDIUM; + else if (lfWeight >= FW_NORMAL) wt=FC_WEIGHT_NORMAL; + else if (lfWeight >= FW_LIGHT) wt=FC_WEIGHT_LIGHT; + else if (lfWeight >= FW_EXTRALIGHT) wt=FC_WEIGHT_EXTRALIGHT; + else wt=FC_WEIGHT_THIN; + FcPatternAddInteger(pat,FC_WEIGHT,wt); + } + if (lfItalic) + FcPatternAddInteger(pat,FC_SLANT,FC_SLANT_ITALIC); + FcConfigSubstitute(s_fontconfig,pat,FcMatchPattern); + FcDefaultSubstitute(pat); + FcResult result; + FcPattern *hit = FcFontMatch(s_fontconfig,pat,&result); + if (hit) + { + FcChar8 *fn = NULL; + if (FcPatternGetString(hit,FC_FILE,0,&fn) == FcResultMatch && fn && *fn) + { + if (FcPatternGetInteger(hit,FC_INDEX,0,&face_idx) != FcResultMatch) face_idx=0; + FT_New_Face(s_freetype,(const char *)fn,face_idx,&face); + if (face) lstrcpyn_safe(face_fn,(const char *)fn,sizeof(face_fn)); + } + FcPatternDestroy(hit); + } + FcPatternDestroy(pat); + } + } +#else + + if (!face && lfFaceName && *lfFaceName) face = MatchFont(lfFaceName,lfWeight,lfItalic,0, face_fn, sizeof(face_fn)); + + if (!face) + { + static const char *fallbacklist[2]; + const int wl = (lfFaceName && ( + !strnicmp(lfFaceName,"Courier",7) || + !strnicmp(lfFaceName,"Fixed",5) + )) ? 1 : 0; + if (!fallbacklist[wl]) + { + static const char *ent[2] = { "ft_font_fallback", "ft_font_fallback_fixedwidth" }; + static const char *def[2] = { + "// LiberationSans Cantarell FreeSans DejaVuSans NotoSans Oxygen Arial Verdana", + "// LiberationMono FreeMono DejaVuSansMono NotoMono OxygenMono Courier" + }; + char tmp[1024]; + GetPrivateProfileString(".swell",ent[wl],"",tmp,sizeof(tmp),""); + if (!tmp[0]) + WritePrivateProfileString(".swell",ent[wl],def[wl],""); + const char *rp = tmp; + while (*rp == ' ' || *rp == '\t') rp++; + if (!*rp || *rp == '/') rp = def[wl] + 3; + + char *b = (char*) malloc(strlen(rp) + 2); + fallbacklist[wl] = b ? b : "FreeSans\0"; + if (b) + { + for(;;) + { + while (*rp == ' ' || *rp == '\t') rp++; + while (*rp && *rp != ' ' && *rp != '\t') *b++ = *rp++; + *b++=0; + if (!*rp) break; + } + *b++=0; + } + } + for (int exact=0;exact<2 && !face;exact++) + { + const char *l = fallbacklist[wl]; + while (*l && !face) + { + face = MatchFont(l,lfWeight,lfItalic,exact?-1:1, face_fn, sizeof(face_fn)); + l += strlen(l)+1; + } + } + } +#endif + + if (face) + { + if (face_idx) snprintf_append(face_fn,sizeof(face_fn)," <%d>",face_idx); + fontConfigCacheEnt *ce = new fontConfigCacheEnt(lfFaceName?lfFaceName:"",cache_flag,lfWidth,lfHeight,face, face_fn); + cache.Add(ce); + if (cache.GetSize()>SWELL_FREETYPE_CACHE_SIZE) cache.Delete(0,true); + swell_last_font_filename = ce->m_fndesc; + + FT_Set_Char_Size(face,lfWidth*64, lfHeight*64,0,0); // 72dpi + } + } + font->typedata = face; +#endif + + return font; +} + + +HFONT CreateFontIndirect(LOGFONT *lf) +{ + return CreateFont(lf->lfHeight, lf->lfWidth,lf->lfEscapement, lf->lfOrientation, lf->lfWeight, lf->lfItalic, + lf->lfUnderline, lf->lfStrikeOut, lf->lfCharSet, lf->lfOutPrecision,lf->lfClipPrecision, + lf->lfQuality, lf->lfPitchAndFamily, lf->lfFaceName); +} + +int GetTextFace(HDC ctx, int nCount, LPTSTR lpFaceName) +{ + if (lpFaceName && nCount>0) lpFaceName[0]=0; +#ifdef SWELL_FREETYPE + HDC__ *ct=(HDC__*)ctx; + if (!HDC_VALID(ct) || WDL_NOT_NORMALLY(nCount<1 || !lpFaceName) || !ct->curfont) return 0; + + const FT_FaceRec *p = (const FT_FaceRec *)ct->curfont->typedata; + if (p) + { + lstrcpyn_safe(lpFaceName, p->family_name, nCount); + return 1; + } +#endif + return 0; +} + +void DeleteObject(HGDIOBJ pen) +{ + if (HGDIOBJ_VALID(pen)) + { + HGDIOBJ__ *p=(HGDIOBJ__ *)pen; + if (--p->additional_refcnt < 0) + { + if (WDL_NORMALLY(p->type == TYPE_PEN || p->type == TYPE_BRUSH || p->type == TYPE_FONT || p->type == TYPE_BITMAP)) + { + if (p->type == TYPE_FONT) + { +#ifdef SWELL_FREETYPE + if (p->typedata) + { + FT_Done_Face((FT_Face)p->typedata); + p->typedata = 0; + } +#endif + } + else if (p->type == TYPE_PEN || p->type == TYPE_BRUSH) + { + if (p->wid<0) return; + } + else if (p->type == TYPE_BITMAP) + { + if (p->wid>0) delete (LICE_IBitmap *)p->typedata; + p->typedata = NULL; + } + + GDP_OBJECT_DELETE(p); + } + } + } +} + + +HGDIOBJ SelectObject(HDC ctx, HGDIOBJ pen) +{ + HDC__ *c=(HDC__ *)ctx; + HGDIOBJ__ *p= pen; + HGDIOBJ__ **mod=0; + if (!HDC_VALID(c)||WDL_NOT_NORMALLY(!p)) return 0; + + if (p == (HGDIOBJ__ *)TYPE_PEN) mod=&c->curpen; + else if (p == (HGDIOBJ__ *)TYPE_BRUSH) mod=&c->curbrush; + else if (p == (HGDIOBJ__ *)TYPE_FONT) mod=&c->curfont; + + if (mod) + { + HGDIOBJ__ *np=*mod; + *mod=0; + return np?np:p; + } + + if (!HGDIOBJ_VALID(p)) return 0; + + if (p->type == TYPE_PEN) mod=&c->curpen; + else if (p->type == TYPE_BRUSH) mod=&c->curbrush; + else if (p->type == TYPE_FONT) mod=&c->curfont; + else return 0; + + HGDIOBJ__ *op=*mod; + if (!op) op=(HGDIOBJ__ *)(INT_PTR)p->type; + if (op != p) + { + *mod=p; + } + return op; +} + + +static void swell_DirtyContext(HDC__ *out, int x1, int y1, int x2, int y2) +{ + if (x2surface_offs.x; + x2+=out->surface_offs.x; + y1+=out->surface_offs.y; + y2+=out->surface_offs.y; + + if (!out->dirty_rect_valid) + { + out->dirty_rect_valid=true; + out->dirty_rect.left = x1; + out->dirty_rect.top = y1; + out->dirty_rect.right = x2; + out->dirty_rect.bottom = y2; + } + else + { + if (out->dirty_rect.left > x1) out->dirty_rect.left=x1; + if (out->dirty_rect.top > y1) out->dirty_rect.top = y1; + if (out->dirty_rect.right < x2) out->dirty_rect.right = x2; + if (out->dirty_rect.bottom < y2) out->dirty_rect.bottom = y2; + } +} + + +void SWELL_FillRect(HDC ctx, const RECT *r, HBRUSH br) +{ + HDC__ *c=(HDC__ *)ctx; + HGDIOBJ__ *b=(HGDIOBJ__ *) br; + if (!HDC_VALID(c) || !HGDIOBJ_VALID(b,TYPE_BRUSH)) return; + if (!c->surface) return; + + if (b->wid<0) return; + LICE_FillRect(c->surface, + r->left+c->surface_offs.x, + r->top+c->surface_offs.y, + r->right-r->left,r->bottom-r->top,b->color,b->alpha,LICE_BLIT_MODE_COPY); + swell_DirtyContext(ctx,r->left,r->top,r->right,r->bottom); +} + +void RoundRect(HDC ctx, int x, int y, int x2, int y2, int xrnd, int yrnd) +{ + xrnd/=3; + yrnd/=3; + POINT pts[10]={ // todo: curves between edges + {x,y+yrnd}, + {x+xrnd,y}, + {x2-xrnd,y}, + {x2,y+yrnd}, + {x2,y2-yrnd}, + {x2-xrnd,y2}, + {x+xrnd,y2}, + {x,y2-yrnd}, + {x,y+yrnd}, + {x+xrnd,y}, +}; + + WDL_GDP_Polygon(ctx,pts,sizeof(pts)/sizeof(pts[0])); +} + +void Ellipse(HDC ctx, int l, int t, int r, int b) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c) || !c->surface) return; + + swell_DirtyContext(ctx,l,t,r,b); + + l += c->surface_offs.x; + t += c->surface_offs.y; + r += c->surface_offs.x; + b += c->surface_offs.y; + + int rad = wdl_min(r-l, b-t)/2; // todo: actual ellipse, for now just circles + + bool wantPen = HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0; + if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0) + { + int use_rad = rad; + if (use_rad > 0) LICE_FillCircle(c->surface,l+use_rad,t+use_rad,use_rad,c->curbrush->color,c->curbrush->alpha,LICE_BLIT_MODE_COPY,!wantPen); + } + if (wantPen) + { + LICE_Circle(c->surface,l+rad,t+rad,rad,c->curpen->color,c->curpen->alpha,LICE_BLIT_MODE_COPY,true); + } +} + +void Rectangle(HDC ctx, int l, int t, int r, int b) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c) || !c->surface) return; + + //CGRect rect=CGRectMake(l,t,r-l,b-t); + + swell_DirtyContext(ctx,l,t,r,b); + + l += c->surface_offs.x; + t += c->surface_offs.y; + r += c->surface_offs.x; + b += c->surface_offs.y; + + if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0) + { + LICE_FillRect(c->surface,l,t,r-l,b-t,c->curbrush->color,c->curbrush->alpha,LICE_BLIT_MODE_COPY); + } + if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0) + { + if (r>l+1 && b>t+1) + LICE_DrawRect(c->surface,l,t,r-l-1,b-t-1,c->curpen->color,c->curpen->alpha,LICE_BLIT_MODE_COPY); + } +} + +void Polygon(HDC ctx, POINT *pts, int npts) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c) || !c->surface) return; + const bool fill=HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH)&&c->curbrush->wid>=0; + const bool outline = HGDIOBJ_VALID(c->curpen,TYPE_PEN)&&c->curpen->wid>=0; + if ((!fill && !outline) || npts<2 || !pts) return; + + const int dx=c->surface_offs.x; + const int dy=c->surface_offs.y; + + int minx=c->surface->getWidth()+1, maxx=0; + int miny=c->surface->getHeight()+1, maxy=0; + + if (fill) + { + int _tmp[256], *tmp=_tmp; + if (npts>128) + { + tmp = (int *)malloc(npts*2*sizeof(int)); + } + if (tmp) + { + for (int x = 0; x < npts; x ++) + { + const int xp = pts[x].x, yp = pts[x].y; + if (xp < minx) minx=xp; + if (xp > maxx) maxx=xp; + if (yp < miny) miny=yp; + if (yp > maxy) maxy=yp; + tmp[x] = xp + dx; + tmp[x+npts] = yp + dy; + } + LICE_FillConvexPolygon(c->surface,tmp, tmp+npts,npts, + c->curbrush->color,c->curbrush->alpha,LICE_BLIT_MODE_COPY); + } + if (tmp != _tmp) free(tmp); + } + if (outline) + { + int lx=0,ly=0,sx=0,sy=0; + for (int x = 0; x < npts; x ++) + { + const int xp = pts[x].x, yp = pts[x].y; + if (xp < minx) minx=xp; + if (xp > maxx) maxx=xp; + if (yp < miny) miny=yp; + if (yp > maxy) maxy=yp; + if (x) + LICE_Line(c->surface,xp+dx,yp+dy,lx+dx,ly+dy,c->curpen->color,c->curpen->alpha,LICE_BLIT_MODE_COPY,true); + else { sx=xp; sy=yp; } + lx=xp; + ly=yp; + } + LICE_Line(c->surface,sx+dx,sy+dy,lx+dx,ly+dy,c->curpen->color,c->curpen->alpha,LICE_BLIT_MODE_COPY,true); + } + + if (maxx>minx && maxy>miny) + swell_DirtyContext(ctx, minx,miny,maxx,maxy); +} + +void MoveToEx(HDC ctx, int x, int y, POINT *op) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)) return; + if (op) + { + op->x = (int) (c->lastpos_x); + op->y = (int) (c->lastpos_y); + } + c->lastpos_x=(float)x; + c->lastpos_y=(float)y; +} + +void PolyBezierTo(HDC ctx, POINT *pts, int np) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||np<3) return; + + int x; + float xp=c->lastpos_x,yp=c->lastpos_y; + for (x = 0; x < np-2; x += 3) + { + // todo + xp=(float)pts[x+2].x; + yp=(float)pts[x+2].y; + } + c->lastpos_x=(float)xp; + c->lastpos_y=(float)yp; +} + + +void SWELL_LineTo(HDC ctx, int x, int y) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0) return; + + float fx=(float)x,fy=(float)y; + + int dx=c->surface_offs.x; + int dy=c->surface_offs.y; + int lx = (int)c->lastpos_x, ly = (int) c->lastpos_y; + if (c->surface) + LICE_Line(c->surface,x+dx,y+dy,lx+dx,ly+dy,c->curpen->color,c->curpen->alpha,LICE_BLIT_MODE_COPY,false); + + c->lastpos_x=fx; + c->lastpos_y=fy; + + if (lxcurpen,TYPE_PEN)||c->curpen->wid<0||nseg<1) return; + + while (nseg-->0) + { + DWORD cnt=*cnts++; + if (!cnt) continue; + if (!--cnt) { pts++; continue; } + + // CGContextMoveToPoint(c->ctx,(float)pts->x,(float)pts->y); + pts++; + + while (cnt--) + { +// CGContextAddLineToPoint(c->ctx,(float)pts->x,(float)pts->y); + pts++; + } + } +// CGContextStrokePath(c->ctx); +} +void *SWELL_GetCtxGC(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return 0; + return NULL; +} + + +void SWELL_SetPixel(HDC ctx, int x, int y, int c) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct) || !ct->surface) return; + LICE_PutPixel(ct->surface,x+ct->surface_offs.x, y+ct->surface_offs.y, LICE_RGBA_FROMNATIVE(c,255),1.0f,LICE_BLIT_MODE_COPY); + swell_DirtyContext(ct,x,y,x+1,y+1); +} + +HFONT SWELL_GetDefaultFont() +{ + static HFONT def; + if (!def) + { + def = CreateFont(g_swell_ctheme.default_font_size,0,0,0,FW_NORMAL,0,0,0,0,0,0,0,0,g_swell_deffont_face); + } + return def; +} + +BOOL GetTextMetrics(HDC ctx, TEXTMETRIC *tm) +{ + HDC__ *ct=(HDC__ *)ctx; + if (tm) // give some sane defaults + { + tm->tmInternalLeading=0; + tm->tmAscent=8; + tm->tmDescent=0; + tm->tmHeight=8; + tm->tmAveCharWidth = 8; + } + if (!HDC_VALID(ct)||WDL_NOT_NORMALLY(!tm)) return 0; + +#ifdef SWELL_FREETYPE + HGDIOBJ__ *font = HGDIOBJ_VALID(ct->curfont,TYPE_FONT) ? ct->curfont : SWELL_GetDefaultFont(); + if (font && font->typedata) + { + FT_Face face=(FT_Face) font->typedata; + tm->tmAscent = face->size->metrics.ascender/64; + tm->tmDescent = -face->size->metrics.descender/64; + tm->tmHeight = (face->size->metrics.ascender - face->size->metrics.descender)/64; + tm->tmAveCharWidth = face->size->metrics.height / 112; + + tm->tmInternalLeading = (face->size->metrics.ascender + face->size->metrics.descender - face->size->metrics.height)/64; + if (tm->tmInternalLeading<0) tm->tmInternalLeading=0; + } +#endif + + return 1; +} + + +int DrawText(HDC ctx, const char *buf, int buflen, RECT *r, int align) +{ + HDC__ *ct=(HDC__ *)ctx; + if (WDL_NOT_NORMALLY(!r)) return 0; + + int lineh = 8; + int charw = 8; + + HGDIOBJ__ *font = NULL; + int ascent=8, descent=0; +#ifdef SWELL_FREETYPE + font = HDC_VALID(ct) && HGDIOBJ_VALID(ct->curfont,TYPE_FONT) ? ct->curfont : SWELL_GetDefaultFont(); + FT_Face face = NULL; + if (font && font->typedata) + { + face=(FT_Face)font->typedata; + lineh = face->size->metrics.height/64; + ascent = face->size->metrics.ascender/64; + descent = face->size->metrics.descender/64; + charw = face->size->metrics.height / 112; + } +#endif + + if (align&DT_CALCRECT) + { + int xpos=0; + int ypos=0; + + r->bottom=r->top; + bool in_prefix=false; + while (buflen && *buf) // if buflen<0, go forever + { + int c=0, charlen = wdl_utf8_parsechar(buf,&c); + buf+=charlen; + if (buflen > 0) + { + buflen -= charlen; + if (buflen < 0) buflen=0; + } + if (!c) break; + + if (c=='&' && !in_prefix && !(align&DT_NOPREFIX)) + { + in_prefix = true; + continue; + } + in_prefix=false; + + if (c == '\n' && (align & DT_SINGLELINE)) c=' '; + + if (c == '\n') { ypos += lineh; xpos=0; } + else if (c != '\r') + { + if (font) + { +#ifdef SWELL_FREETYPE + if (c != '\t' && !FT_Load_Char(face, c, FT_LOAD_DEFAULT) && face->glyph) + { + // measure character + FT_GlyphSlot g = face->glyph; + int rext = xpos; + if ((align&(DT_BOTTOM|DT_VCENTER|DT_CENTER|DT_RIGHT))!=DT_RIGHT) + rext += (g->metrics.width + g->metrics.horiBearingX)/64; + + xpos += g->metrics.horiAdvance/64; + if (rextleft+rext > r->right) r->right = r->left+rext; + + int bext = r->top + ypos + ascent - descent; + if (bext > r->bottom) r->bottom = bext; + continue; + } +#endif + } + xpos += c=='\t' ? charw*5 : charw; + int bext = r->top + ypos + ascent - descent; + if (bext > r->bottom) r->bottom = bext; + if (r->left+xpos>r->right) r->right=r->left+xpos; + } + } + return r->bottom-r->top; + } + if (!HDC_VALID(ct)) return 0; + + RECT use_r = *r; + if ((align & DT_VCENTER) && use_r.top > use_r.bottom) + { + use_r.top = r->bottom; + use_r.bottom = r->top; + } + + use_r.left += ct->surface_offs.x; + use_r.right += ct->surface_offs.x; + use_r.top += ct->surface_offs.y; + use_r.bottom += ct->surface_offs.y; + + int xpos = use_r.left; + int ypos = use_r.top; + if (align&(DT_CENTER|DT_VCENTER|DT_RIGHT|DT_BOTTOM)) + { + RECT tr={0,}; + DrawText(ctx,buf,buflen,&tr,align|DT_CALCRECT); + + if (align&DT_CENTER) + xpos -= ((tr.right-tr.left) - (use_r.right-use_r.left))/2; + else if (align&DT_RIGHT) + xpos = use_r.right - (tr.right-tr.left); + + if (align&DT_VCENTER) + ypos -= ((tr.bottom-tr.top) - (use_r.bottom-use_r.top))/2; + else if (align&DT_BOTTOM) + ypos = use_r.bottom - (tr.bottom-tr.top); + } + LICE_IBitmap *surface = ct->surface; + int fgcol = ct->cur_text_color_int; + int bgcol = ct->curbkcol; + int bgmode = ct->curbkmode; + + + int clip_x1=wdl_max(use_r.left,0), clip_y1 = wdl_max(use_r.top,0); + int clip_w=0, clip_h=0; + if (surface) + { + clip_w = wdl_min(use_r.right,surface->getWidth())-clip_x1; + clip_h = wdl_min(use_r.bottom,surface->getHeight())-clip_y1; + if (clip_w<0)clip_w=0; + if (clip_h<0)clip_h=0; + } + + LICE_SubBitmap clipbm(surface,clip_x1,clip_y1,clip_w,clip_h); + if (surface && !(align&DT_NOCLIP)) { surface = &clipbm; xpos-=clip_x1; ypos-=clip_y1; } + + int left_xpos = xpos,start_ypos = ypos, max_ypos=ypos,max_xpos=0; + bool in_prefix=false; + + while (buflen && *buf) + { + int c=0, charlen = wdl_utf8_parsechar(buf,&c); + if (buflen>0) + { + buflen -= charlen; + if (buflen<0) buflen=0; + } + buf+=charlen; + + bool doUl=in_prefix; + + if (c=='&' && !in_prefix && !(align&DT_NOPREFIX)) + { + in_prefix = true; + continue; + } + in_prefix=false; + + if (c == '\n' && (align & DT_SINGLELINE)) c=' '; + + if (c=='\n' && !(align&DT_SINGLELINE)) { xpos=left_xpos; ypos+=lineh; } + else if (c=='\r') {} + else + { + bool needr=true; + if (font) + { +#ifdef SWELL_FREETYPE + if (c != '\t' && !FT_Load_Char(face, c, FT_LOAD_RENDER) && face->glyph) + { + FT_GlyphSlot g = face->glyph; + const int ha = g->metrics.horiAdvance/64; + if (bgmode==OPAQUE) LICE_FillRect(surface,xpos,ypos,ha,(align & DT_SINGLELINE) ? (ascent-descent) : lineh,bgcol,1.0f,LICE_BLIT_MODE_COPY); + + if (g->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) + { + LICE_DrawMonoGlyph(surface,xpos+g->bitmap_left,ypos+ascent-g->bitmap_top,fgcol,(const unsigned char*)g->bitmap.buffer,g->bitmap.width,g->bitmap.pitch,g->bitmap.rows,1.0f,LICE_BLIT_MODE_COPY); + } + else // FT_PIXEL_MODE_GRAY (hopefully!) + { + LICE_DrawGlyphEx(surface,xpos+g->bitmap_left,ypos+ascent-g->bitmap_top,fgcol,(LICE_pixel_chan *)g->bitmap.buffer,g->bitmap.width,g->bitmap.pitch,g->bitmap.rows,1.0f,LICE_BLIT_MODE_COPY); + } + if (doUl) + { + int xw = g->metrics.width/64; + if (xw > 1) xw--; + LICE_Line(surface,xpos + g->metrics.horiBearingX/64,ypos+ascent+1, + xpos + xw,ypos+ascent+1,fgcol,1.0f,LICE_BLIT_MODE_COPY,false); + } + + int rext = xpos + (g->metrics.width + g->metrics.horiBearingX)/64; + if (rext<=xpos) rext=xpos + ha; + if (rext > max_xpos) max_xpos=rext; + xpos += ha; + const int bext = ypos + ascent-descent; + if (max_ypos < bext) max_ypos=bext; + needr=false; + } +#endif + } + + if (needr) + { + if (c=='\t') + { + if (bgmode==OPAQUE) LICE_FillRect(surface,xpos,ypos,charw*5,(align & DT_SINGLELINE) ? (ascent-descent) : lineh,bgcol,1.0f,LICE_BLIT_MODE_COPY); + xpos+=charw*5; + + const int bext = ypos+ascent-descent; + if (max_ypos < bext) max_ypos=bext; + } + else + { + if (bgmode==OPAQUE) LICE_FillRect(surface,xpos,ypos,charw,(align & DT_SINGLELINE) ? (ascent-descent) : lineh,bgcol,1.0f,LICE_BLIT_MODE_COPY); + LICE_DrawChar(surface,xpos,ypos,c,fgcol,1.0f,LICE_BLIT_MODE_COPY); + if (doUl) LICE_Line(surface,xpos,ypos+(ascent-descent)+1,xpos+charw,ypos+(ascent-descent)+1,fgcol,1.0f,LICE_BLIT_MODE_COPY,false); + + const int bext=ypos+ascent-descent+(doUl ? 2:1); + if (max_ypos < bext) max_ypos=bext; + xpos+=charw; + } + } + } + if(xpos>max_xpos)max_xpos=xpos; + } + if (surface==&clipbm) + swell_DirtyContext(ct,clip_x1+left_xpos,clip_y1+start_ypos,clip_x1+max_xpos,clip_y1+max_ypos); + else + swell_DirtyContext(ct,left_xpos,start_ypos,max_xpos,max_ypos); + return max_ypos - start_ypos; +} + + +int GetTextColor(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return -1; + return ct->cur_text_color_int; +} +void SetTextColor(HDC ctx, int col) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; + ct->cur_text_color_int = LICE_RGBA_FROMNATIVE(col,255); + +} + + +////////// todo: some sort of HICON emul + +HICON LoadNamedImage(const char *name, bool alphaFromMask) +{ +#ifdef SWELL_TARGET_GDK + char buf[1024]; + GdkPixbuf *pb = NULL; + if (strstr(name,"/")) + { + lstrcpyn_safe(buf,name,sizeof(buf)); + pb = gdk_pixbuf_new_from_file(buf,NULL); + } + else + { + GetModuleFileName(NULL,buf,sizeof(buf)); + WDL_remove_filepart(buf); + snprintf_append(buf,sizeof(buf),"/Resources/%s.png",name); + pb = gdk_pixbuf_new_from_file(buf,NULL); + if (!pb) + { + WDL_remove_fileext(buf); + lstrcatn(buf,".ico",sizeof(buf)); + pb = gdk_pixbuf_new_from_file(buf,NULL); + } + if (!pb) + { + WDL_remove_fileext(buf); + lstrcatn(buf,".bmp",sizeof(buf)); + pb = gdk_pixbuf_new_from_file(buf,NULL); + } + } + if (pb) + { + HGDIOBJ__ *ret=NULL; + const int w = gdk_pixbuf_get_width(pb), h = gdk_pixbuf_get_height(pb); + const int bpc = gdk_pixbuf_get_bits_per_sample(pb), chan = gdk_pixbuf_get_n_channels(pb); + const int alpha = gdk_pixbuf_get_has_alpha(pb); + const guchar *rd = gdk_pixbuf_get_pixels(pb); + if (bpc == 8 && (chan == 4 || chan == 3) && w > 0 && h>0 && rd) + { + LICE_MemBitmap *bm = new LICE_MemBitmap(w,h); + LICE_pixel_chan *wr = (LICE_pixel_chan*)bm->getBits(); + if (wr) + { + const int rdadv = gdk_pixbuf_get_rowstride(pb); + const int wradv = bm->getRowSpan()*4; + const unsigned char alphamod = !alpha ? 255 : 0; + int y; + for (y=0;y type=TYPE_BITMAP; + ret->alpha = 1.0f; + ret->wid=1; + ret->typedata = bm; + } + else delete wr; + } + g_object_unref(pb); + return ret; + } + + +#endif + return 0; // todo +} + +void DrawImageInRect(HDC hdcOut, HICON in, const RECT *r) +{ + HDC__ *out = (HDC__ *)hdcOut; + if (!HDC_VALID(out) || !HGDIOBJ_VALID(in,TYPE_BITMAP) || !out->surface || !in->typedata) return; + + const int x = r->left, y=r->top, w=r->right-x, h=r->bottom-y; + LICE_IBitmap *src=(LICE_IBitmap *)in->typedata; + LICE_ScaledBlit(out->surface,src, + x+out->surface_offs.x,y+out->surface_offs.y,w,h, + 0,0, src->getWidth(),src->getHeight(), + 1.0f,LICE_BLIT_MODE_COPY|LICE_BLIT_USE_ALPHA|LICE_BLIT_FILTER_BILINEAR); + swell_DirtyContext(out,x,y,x+w,y+h); +} + + +BOOL GetObject(HICON icon, int bmsz, void *_bm) +{ + memset(_bm,0,bmsz); + if (WDL_NOT_NORMALLY(bmsz < 2*(int)sizeof(LONG))) return false; + BITMAP *bm=(BITMAP *)_bm; + HGDIOBJ__ *i = (HGDIOBJ__ *)icon; + if (!HGDIOBJ_VALID(i,TYPE_BITMAP) || !i->typedata) return false; + + bm->bmWidth = ((LICE_IBitmap *)i->typedata)->getWidth(); + bm->bmHeight = ((LICE_IBitmap *)i->typedata)->getHeight(); + + if (bmsz >= (int)sizeof(BITMAP)) + { + bm->bmWidthBytes = ((LICE_IBitmap *)i->typedata)->getRowSpan()*4; + bm->bmPlanes = 1; + bm->bmBitsPixel = 32; + bm->bmBits = ((LICE_IBitmap *)i->typedata)->getBits(); + } + + return true; +} + + +//////////////////////////////////// + + +void BitBltAlphaFromMem(HDC hdcOut, int x, int y, int w, int h, void *inbufptr, int inbuf_span, int inbuf_h, int xin, int yin, int mode, bool useAlphaChannel, float opacity) +{ + // todo: use LICE_WrapperBitmap? +} + +void BitBltAlpha(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int mode, bool useAlphaChannel, float opacity) +{ + HDC__ *in = (HDC__ *)hdcIn; + HDC__ *out = (HDC__ *)hdcOut; + if (!HDC_VALID(out) || !HDC_VALID(in)) return; + if (!in->surface || !out->surface) return; + LICE_Blit(out->surface,in->surface, + x+out->surface_offs.x,y+out->surface_offs.y, + xin+in->surface_offs.x,yin+in->surface_offs.y,w,h, + opacity,LICE_BLIT_MODE_COPY|(useAlphaChannel?LICE_BLIT_USE_ALPHA:0)); + swell_DirtyContext(out,x,y,x+w,y+h); +} + +void BitBlt(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int mode) +{ + HDC__ *in = (HDC__ *)hdcIn; + HDC__ *out = (HDC__ *)hdcOut; + if (!HDC_VALID(out) || !HDC_VALID(in)) return; + if (!in->surface || !out->surface) return; + LICE_Blit(out->surface,in->surface, + x+out->surface_offs.x,y+out->surface_offs.y, + xin+in->surface_offs.x,yin+in->surface_offs.y,w,h, + 1.0f,LICE_BLIT_MODE_COPY | (mode == (int)SRCCOPY_USEALPHACHAN ? LICE_BLIT_USE_ALPHA : 0)); + swell_DirtyContext(out,x,y,x+w,y+h); +} + +void StretchBltFromMem(HDC hdcOut, int x, int y, int w, int h, const void *bits, int srcw, int srch, int srcspan) +{ + HDC__ *out = (HDC__ *)hdcOut; + if (!HDC_VALID(out) || !bits) return; + if (!out->surface) return; + + LICE_WrapperBitmap srcbm((LICE_pixel*)bits,srcw,srch,srcspan,false); + LICE_ScaledBlit(out->surface,&srcbm, + x+out->surface_offs.x,y+out->surface_offs.y,w,h, + 0,0,srcw,srch, + 1.0f,LICE_BLIT_MODE_COPY); + swell_DirtyContext(out,x,y,x+w,y+h); +} + +void StretchBlt(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int srcw, int srch, int mode) +{ + HDC__ *in = (HDC__ *)hdcIn; + HDC__ *out = (HDC__ *)hdcOut; + if (!HDC_VALID(out) || !HDC_VALID(in)) return; + if (!in->surface || !out->surface) return; + LICE_ScaledBlit(out->surface,in->surface, + x+out->surface_offs.x,y+out->surface_offs.y,w,h, + xin+in->surface_offs.x,yin+in->surface_offs.y,srcw,srch, + 1.0f,LICE_BLIT_MODE_COPY | (mode == (int)SRCCOPY_USEALPHACHAN ? LICE_BLIT_USE_ALPHA : 0)); + swell_DirtyContext(out,x,y,x+w,y+h); +} + +void SWELL_FillDialogBackground(HDC hdc, const RECT *r, int level) +{ + HBRUSH br = CreateSolidBrush(g_swell_ctheme._3dface); + FillRect(hdc,r,br); + DeleteObject(br); +} + +HGDIOBJ SWELL_CloneGDIObject(HGDIOBJ a) +{ + if (HGDIOBJ_VALID(a)) + { + a->additional_refcnt++; + return a; + } + return NULL; +} + +void SWELL_PushClipRegion(HDC ctx) +{ +// HDC__ *ct=(HDC__ *)ctx; +// if (ct && ct->ctx) CGContextSaveGState(ct->ctx); +} + +void SWELL_SetClipRegion(HDC ctx, const RECT *r) +{ +// HDC__ *ct=(HDC__ *)ctx; +// if (ct && ct->ctx) CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top)); + +} + +void SWELL_PopClipRegion(HDC ctx) +{ +// HDC__ *ct=(HDC__ *)ctx; +// if (ct && ct->ctx) CGContextRestoreGState(ct->ctx); +} + +void *SWELL_GetCtxFrameBuffer(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (HDC_VALID(ct)&&ct->surface) return ct->surface->getBits(); + return 0; +} + + + +struct swell_gdpLocalContext +{ + HDC__ ctx; + RECT clipr; +}; + + + +HDC SWELL_internalGetWindowDC(HWND h, bool calcsize_on_first) +{ + if (WDL_NOT_NORMALLY(!h)) return NULL; + + int xoffs=0,yoffs=0; + int wndw = h->m_position.right-h->m_position.left; + int wndh = h->m_position.bottom-h->m_position.top; + + bool vis=true; + HWND starth = h; + int ltrim=0, ttrim=0, rtrim=0, btrim=0; + for (;;) + { + if ((calcsize_on_first || h!=starth) && h->m_wndproc) + { + RECT r; + GetWindowContentViewRect(h,&r); + NCCALCSIZE_PARAMS p={{r,},}; + h->m_wndproc(h,WM_NCCALCSIZE,FALSE,(LPARAM)&p); + RECT r2=r; + r=p.rgrc[0]; + + // todo: handle left edges and adjust postions/etc accordingly + if (h==starth) // if initial window, adjust rects to client area (this implies calcsize_on_first being false) + { + wndw=r.right-r.left; + wndh=r.bottom-r.top; + } + yoffs += r.top-r2.top; + xoffs += r.left-r2.left; + } + if (!h->m_visible) vis = false; + + if (h->m_backingstore || !h->m_parent) break; // found our target window + + xoffs += h->m_position.left; + yoffs += h->m_position.top; + + ltrim = wdl_max(ltrim, -xoffs); + ttrim = wdl_max(ttrim, -yoffs); + rtrim = wdl_max(rtrim, xoffs+wndw - h->m_position.right); + btrim = wdl_max(btrim, yoffs+wndh - h->m_position.bottom); + + h = h->m_parent; + } + + swell_gdpLocalContext *p = (swell_gdpLocalContext*)SWELL_GDP_CTX_NEW(); + + p->clipr.left=p->clipr.right=xoffs + ltrim; + p->clipr.top=p->clipr.bottom=yoffs + ttrim; + + if (h->m_backingstore && vis) + { + p->ctx.surface=new LICE_SubBitmap(h->m_backingstore, + xoffs+ltrim,yoffs+ttrim, + wndw-ltrim-rtrim,wndh-ttrim-btrim); + p->clipr.right += p->ctx.surface->getWidth(); + p->clipr.bottom += p->ctx.surface->getHeight(); + } + if (xoffs<0) p->ctx.surface_offs.x = xoffs; + if (yoffs<0) p->ctx.surface_offs.y = yoffs; + p->ctx.surface_offs.x -= ltrim; + p->ctx.surface_offs.y -= ttrim; + + p->ctx.curfont = starth->m_font; + // todo: other GDI defaults? + + return (HDC)p; +} +HDC GetWindowDC(HWND h) +{ + return SWELL_internalGetWindowDC(h,0); +} + +HDC GetDC(HWND h) +{ + return SWELL_internalGetWindowDC(h,1); +} + +void ReleaseDC(HWND h, HDC hdc) +{ + if (WDL_NOT_NORMALLY(!h) || !HDC_VALID(hdc)) return; + swell_gdpLocalContext *p = (swell_gdpLocalContext*)hdc; + + + if (!h->m_paintctx) + { + // handle blitting? + HWND par = h; + while (par && !par->m_backingstore) par=par->m_parent; + if (par) + { + if (p->ctx.dirty_rect_valid) + { + RECT r = p->clipr, dr=p->ctx.dirty_rect; + dr.left += r.left; + dr.top += r.top; + dr.right += r.left; + dr.bottom += r.top; +#if 1 + if (dr.left > r.left) r.left=dr.left; + if (dr.top > r.top) r.top=dr.top; + if (dr.right < r.right) r.right=dr.right; + if (dr.bottom < r.bottom) r.bottom=dr.bottom; +#endif + + if (r.topctx.surface; + SWELL_GDP_CTX_DELETE(hdc); +} + + + +HDC BeginPaint(HWND hwnd, PAINTSTRUCT *ps) +{ + if (WDL_NOT_NORMALLY(!ps)) return 0; + memset(ps,0,sizeof(PAINTSTRUCT)); + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + if (WDL_NOT_NORMALLY(!hwnd->m_paintctx)) return NULL; + + swell_gdpLocalContext *ctx = (swell_gdpLocalContext *)hwnd->m_paintctx; + ps->rcPaint = ctx->clipr; + ps->hdc = &ctx->ctx; + return &ctx->ctx; +} + + +// paint hwnd into bmout, where bmout points at bmout_xpos,bmout_ypos in window coordinates +void SWELL_internalLICEpaint(HWND hwnd, LICE_IBitmap *bmout, int bmout_xpos, int bmout_ypos, bool forceref) +{ +#ifdef _DEBUG + extern int g_swell_in_paint; + g_swell_in_paint++; +#endif + // todo: check to see if any children completely intersect clip region, if so then we don't need to render ourselves + // todo: opaque flag for windows? (to disable above behavior) + // todo: implement/use per-window backing store. + // we would want to only really use them for top level windows, and on those, only update windows (and children of windows) who had backingstore invalidated. + if (hwnd->m_invalidated) forceref=true; + + if (forceref || hwnd->m_child_invalidated) + { + swell_gdpLocalContext ctx; + memset(&ctx,0,sizeof(ctx)); + ctx.ctx.surface = bmout; + ctx.ctx.surface_offs.x = -bmout_xpos; + ctx.ctx.surface_offs.y = -bmout_ypos; + ctx.clipr.left = bmout_xpos; + ctx.clipr.top = bmout_ypos; + ctx.clipr.right = ctx.clipr.left + bmout->getWidth(); + ctx.clipr.bottom = ctx.clipr.top + bmout->getHeight(); + + void *oldpaintctx = hwnd->m_paintctx; + if (forceref) hwnd->m_paintctx = &ctx; + + LICE_SubBitmap tmpsub(NULL,0,0,0,0); + if (hwnd->m_wndproc) // this happens after m_paintctx is set -- that way GetWindowDC()/GetDC()/ReleaseDC() know to not actually update the screen + { + RECT r2; + GetWindowContentViewRect(hwnd,&r2); + WinOffsetRect(&r2,-r2.left,-r2.top); + + NCCALCSIZE_PARAMS p; + memset(&p,0,sizeof(p)); + p.rgrc[0]=r2; + hwnd->m_wndproc(hwnd,WM_NCCALCSIZE,FALSE,(LPARAM)&p); + RECT r = p.rgrc[0]; + if (forceref) hwnd->m_wndproc(hwnd,WM_NCPAINT,(WPARAM)1,0); + + // dx,dy is offset from the window's nonclient root + const int dx = r.left-r2.left,dy=r.top-r2.top; + + WinOffsetRect(&ctx.clipr,-dx,-dy); + ctx.ctx.surface_offs.x += dx; + ctx.ctx.surface_offs.y += dy; + bmout_xpos -= dx; + bmout_ypos -= dy; + + // make sure we can't overwrite the nonclient area + const int xo = wdl_max(-bmout_xpos,0), yo = wdl_max(-bmout_ypos,0); + if (xo || yo) + { + tmpsub.m_parent = ctx.ctx.surface; + tmpsub.m_x = xo; + tmpsub.m_y = yo; + tmpsub.m_w = ctx.ctx.surface->getWidth()-xo; + tmpsub.m_h = ctx.ctx.surface->getHeight()-yo; + if (tmpsub.m_w<0) tmpsub.m_w=0; + if (tmpsub.m_h<0) tmpsub.m_h=0; + ctx.ctx.surface = &tmpsub; + ctx.ctx.surface_offs.x -= xo; + ctx.ctx.surface_offs.y -= yo; + } + + // constrain clip rect to right/bottom extents + if (ctx.clipr.left < 0) ctx.clipr.left=0; + if (ctx.clipr.top < 0) ctx.clipr.top=0; + int newr = r.right-r.left; + if (ctx.clipr.right > newr) ctx.clipr.right=newr; + int newb = r.bottom-r.top; + if (ctx.clipr.bottom > newb) ctx.clipr.bottom=newb; + } + + // paint + if (forceref) + { + if (hwnd->m_wndproc && ctx.clipr.right > ctx.clipr.left && ctx.clipr.bottom > ctx.clipr.top) + { + ctx.ctx.curfont = hwnd->m_font; + hwnd->m_wndproc(hwnd,WM_PAINT,(WPARAM)&ctx,0); + } + + hwnd->m_paintctx = oldpaintctx; + + // it might be good to blit here on some OSes, rather than from the top level caller... + hwnd->m_invalidated=false; + } + } + + bool okToClearChild=true; + if (forceref || hwnd->m_child_invalidated) + { + HWND h = hwnd->m_children; + while (h) + { + if (h->m_visible && (forceref || h->m_invalidated||h->m_child_invalidated)) + { + int width = h->m_position.right - h->m_position.left, + height = h->m_position.bottom - h->m_position.top; // max width possible for this window + int xp = h->m_position.left - bmout_xpos, yp = h->m_position.top - bmout_ypos; + + if (okToClearChild && !forceref) + { + if (xp < 0 || xp+width > bmout->getWidth() || + yp < 0 || yp+height > bmout->getHeight()) okToClearChild = false; // extends outside of our region + } + // if xp/yp < 0, that means that the clip region starts inside the window, so we need to pass a positive render offset, and decrease the potential draw width + // if xp/yp > 0, then the clip region starts before the window, so we use the subbitmap and pass 0 for the offset parm + int subx = 0, suby=0; + if (xp<0) width+=xp; + else { subx=xp; xp=0; } + + if (yp<0) height+=yp; + else { suby=yp; yp=0; } + + LICE_SubBitmap subbm(bmout,subx,suby,width,height); // the right/bottom will automatically be clipped to the clip rect etc + if (subbm.getWidth()>0 && subbm.getHeight()>0) + SWELL_internalLICEpaint(h,&subbm,-xp,-yp,forceref); + } + h = h->m_next; + } + } + if (okToClearChild) hwnd->m_child_invalidated=false; +#ifdef _DEBUG + g_swell_in_paint--; +#endif +} + +HBITMAP CreateBitmap(int width, int height, int numplanes, int bitsperpixel, unsigned char* bits) +{ + if (WDL_NOT_NORMALLY(width < 1 || height < 1 || numplanes != 1 || bitsperpixel != 32 || !bits)) return NULL; + LICE_MemBitmap *bm = new LICE_MemBitmap(width,height); + if (WDL_NOT_NORMALLY(!bm->getBits())) { delete bm; return NULL; } + int y; + LICE_pixel *wr = bm->getBits(); + for (y=0;ygetRowSpan(); + } + + + + HGDIOBJ__* icon=GDP_OBJECT_NEW(); + icon->type=TYPE_BITMAP; + icon->wid=1; + icon->typedata = (void*)bm; + return icon; +} + +HICON CreateIconIndirect(ICONINFO* iconinfo) +{ + if (WDL_NOT_NORMALLY(!iconinfo || !iconinfo->fIcon)) return 0; + HGDIOBJ__* i=iconinfo->hbmColor; + if (!HGDIOBJ_VALID(i,TYPE_BITMAP) ) return 0; + + if (!i->typedata) return 0; + + LICE_MemBitmap *bm = new LICE_MemBitmap; + LICE_Copy(bm,(LICE_IBitmap*)i->typedata); + + HGDIOBJ__* icon=GDP_OBJECT_NEW(); + icon->type=TYPE_BITMAP; + icon->wid=1; + icon->typedata = (void*)bm; + + return icon; +} + +HIMAGELIST ImageList_CreateEx() +{ + return (HIMAGELIST)new WDL_PtrList; +} + +BOOL ImageList_Remove(HIMAGELIST list, int idx) +{ + WDL_PtrList* imglist=(WDL_PtrList*)list; + if (WDL_NORMALLY(imglist) && idx < imglist->GetSize()) + { + if (idx < 0) + { + int x,n=imglist->GetSize(); + for (x=0;xGet(x); + if (a) DeleteObject(a); + } + imglist->Empty(); + } + else + { + HGDIOBJ__ *a = imglist->Get(idx); + imglist->Set(idx, NULL); + if (a) DeleteObject(a); + } + return TRUE; + } + + return FALSE; +} + +void ImageList_Destroy(HIMAGELIST list) +{ + if (WDL_NOT_NORMALLY(!list)) return; + WDL_PtrList *p=(WDL_PtrList*)list; + ImageList_Remove(list,-1); + delete p; +} + +int ImageList_ReplaceIcon(HIMAGELIST list, int offset, HICON image) +{ + if (WDL_NOT_NORMALLY(!image || !list)) return -1; + WDL_PtrList *l=(WDL_PtrList *)list; + + HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image; + if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1; + + HGDIOBJ__* icon=GDP_OBJECT_NEW(); + LICE_MemBitmap *bm = new LICE_MemBitmap; + LICE_Copy(bm,(LICE_IBitmap*)imgsrc->typedata); + + icon->type=TYPE_BITMAP; + icon->alpha = 1.0f; + icon->wid=1; + icon->typedata = (void*)bm; + + image = (HICON) icon; + + if (offset<0||offset>=l->GetSize()) + { + l->Add(image); + offset=l->GetSize()-1; + } + else + { + HICON old=l->Get(offset); + l->Set(offset,image); + if (old) DeleteObject(old); + } + return offset; +} + +int ImageList_Add(HIMAGELIST list, HBITMAP image, HBITMAP mask) +{ + if (WDL_NOT_NORMALLY(!image || !list)) return -1; + WDL_PtrList *l=(WDL_PtrList *)list; + + HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image; + if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1; + + HGDIOBJ__* icon=GDP_OBJECT_NEW(); + LICE_MemBitmap *bm = new LICE_MemBitmap; + LICE_Copy(bm,(LICE_IBitmap*)imgsrc->typedata); + + icon->type=TYPE_BITMAP; + icon->wid=1; + icon->typedata = (void*)bm; + + image = (HICON) icon; + + l->Add(image); + return l->GetSize(); +} + +int AddFontResourceEx(LPCTSTR str, DWORD fl, void *pdv) +{ +#ifdef SWELL_FREETYPE + if (str && *str) + { +#ifdef SWELL_FONTCONFIG + if (!s_fontconfig) s_fontconfig = FcInitLoadConfigAndFonts(); + return s_fontconfig && FcConfigAppFontAddFile(s_fontconfig,(const FcChar8 *)str) != FcFalse; +#else + if (s_freetype_regfonts.FindSorted(str,sortByFilePart)>=0) return 0; + s_freetype_regfonts.InsertSorted(strdup(str), sortByFilePart); +#endif + return 1; + } +#endif + return 0; +} + +int GetGlyphIndicesW(HDC ctx, wchar_t *buf, int len, unsigned short *indices, int flags) +{ +#ifdef SWELL_FREETYPE + if (ctx) + { + HGDIOBJ font = HDC_VALID(ctx) && HGDIOBJ_VALID(ctx->curfont,TYPE_FONT) ? ctx->curfont : SWELL_GetDefaultFont(); + if (font) + { + FT_Face face=(FT_Face)font->typedata; + if (face) + { + for (int i=0; i < len; ++i) + { + int c = FT_Get_Char_Index(face,buf[i]); + indices[i] = c ? c : 0xFFFF; + } + return len; + } + } + } +#endif + + for (int i=0; i < len; ++i) indices[i]=0xFFFF; + return len; +} + +#endif + +#endif // SWELL_LICE_GDI diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi.mm b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi.mm new file mode 100644 index 000000000..d04c74399 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-gdi.mm @@ -0,0 +1,2036 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic win32 GDI-->Quartz translation. It uses features that require OS X 10.4+ + +*/ + +#ifndef SWELL_PROVIDED_BY_APP + +#import +#import +#import +#import +#include "swell.h" +#define SWELL_GetOSXVersion SWELL_GDI_GetOSXVersion +#define SWELL_IMPLEMENT_GETOSXVERSION static +#include "swell-internal.h" + +#include "../mutex.h" +#include "../assocarray.h" +#include "../wdlcstring.h" + +#ifdef __SSE__ +#include +#endif + +#ifdef SWELL_SUPPORT_OPENGL_BLIT +#include +#endif + +#ifndef SWELL_NO_METAL +void SWELL_Metal_FillRect(void *_tex, int x, int y, int w, int h, int color); +#endif + +#ifdef __AVX__ +#include +#endif + +#ifndef MAC_OS_X_VERSION_10_6 +// 10.5 SDK doesn't include CGContextSetAllowsFontSmoothing() in header (but apparently does in libs) +CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef c, bool) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER; +#endif + +#ifndef SWELL_NO_CORETEXT +static bool IsCoreTextSupported() +{ +#ifdef SWELL_ATSUI_TEXT_SUPPORT + return SWELL_GDI_GetOSXVersion() >= 0x1050 && CTFontCreateWithName && CTLineDraw && CTFramesetterCreateWithAttributedString && CTFramesetterCreateFrame && + CTFrameGetLines && CTLineGetTypographicBounds && CTLineCreateWithAttributedString && CTFontCopyPostScriptName + ; +#else + // no ATSUI, targetting 10.5+, CT is always valid + return true; +#endif +} + +static CTFontRef GetCoreTextDefaultFont() +{ + static CTFontRef deffr; + static bool ok; + if (!ok) + { + ok=true; + if (IsCoreTextSupported()) + { + deffr=(CTFontRef) [[NSFont labelFontOfSize:10.0] retain]; + } + } + return deffr; +} +#endif // !SWELL_NO_CORETEXT + + +static NSString *CStringToNSString(const char *str) +{ + if (!str) str=""; + NSString *ret; + + ret=(NSString *)CFStringCreateWithCString(NULL,str,kCFStringEncodingUTF8); + if (ret) return ret; + ret=(NSString *)CFStringCreateWithCString(NULL,str,kCFStringEncodingASCII); + return ret; +} +CGColorSpaceRef __GetBitmapColorSpace() +{ + static CGColorSpaceRef cs; + if (!cs) cs = CGColorSpaceCreateDeviceRGB(); + return cs; +} + +CGColorSpaceRef __GetDisplayColorSpace() +{ + static CGColorSpaceRef cs; + if (!cs) + { + // use monitor profile for 10.7+ + if (SWELL_GDI_GetOSXVersion() >= 0x1070) + { + +#ifdef MAC_OS_X_VERSION_10_11 + // OSX 10.11 SDK removes CMGetSystemProfile + // this may be preferable on older SDKs as well, need to test (though CGDisplayCopyColorSpace is only available on 10.5+) + cs = CGDisplayCopyColorSpace(CGMainDisplayID()); +#else + CMProfileRef systemMonitorProfile = NULL; + CMError getProfileErr = CMGetSystemProfile(&systemMonitorProfile); + if(noErr == getProfileErr) + { + cs = CGColorSpaceCreateWithPlatformColorSpace(systemMonitorProfile); + CMCloseProfile(systemMonitorProfile); + } +#endif + } + } + if (!cs) + cs = CGColorSpaceCreateDeviceRGB(); + return cs; +} + +static CGColorRef CreateColor(int col, float alpha=1.0f) +{ + CGFloat cols[4]={GetRValue(col)/255.0f,GetGValue(col)/255.0f,GetBValue(col)/255.0f,alpha}; + CGColorRef color=CGColorCreate(__GetBitmapColorSpace(),cols); + return color; +} + + +#include "swell-gdi-internalpool.h" + +char g_swell_disable_retina; + +int SWELL_IsRetinaHWND(HWND hwnd) +{ + if (!hwnd || SWELL_GDI_GetOSXVersion() < 0x1070) return 0; + + int retina_disabled = g_swell_disable_retina; + NSWindow *w=NULL; + if ([(id)hwnd isKindOfClass:[NSView class]]) + { + if (retina_disabled && + [(id)hwnd isKindOfClass:[SWELL_hwndChild class]] && + ((SWELL_hwndChild*)hwnd)->m_glctx != NULL) + retina_disabled = 0; + + w = [(NSView *)hwnd window]; + } + else if ([(id)hwnd isKindOfClass:[NSWindow class]]) w = (NSWindow *)hwnd; + + if (retina_disabled) return 0; + + if (w) + { + NSRect r=NSMakeRect(0,0,1,1); +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_6 + NSRect str = [w convertRectToBacking:r]; +#else + NSRect (*tmp)(id receiver, SEL operation, NSRect) = (NSRect (*)(id, SEL, NSRect))objc_msgSend_stret; + NSRect str = tmp(w,sel_getUid("convertRectToBacking:"),r); +#endif + + if (str.size.width > 1.9) return 1; + } + return 0; +} + +int SWELL_IsRetinaDC(HDC hdc) +{ + if (g_swell_disable_retina) return 0; + HDC__ *src=(HDC__*)hdc; + if (!src || !HDC_VALID(src)) return 0; + + if (!src->ctx) + { +#ifndef SWELL_NO_METAL + if (src->metal_ctx) + { + SWELL_hwndChild *ctx = (SWELL_hwndChild*)src->metal_ctx; + if (ctx->m_metal_dc_dirty) return ctx->m_metal_retina ? 1 : 0; + + return SWELL_IsRetinaHWND((HWND)src->metal_ctx); + } +#endif + return 0; + } + return CGContextConvertSizeToDeviceSpace((CGContextRef)src->ctx, CGSizeMake(1,1)).width > 1.9 ? 1 : 0; +} + + +HDC SWELL_CreateGfxContext(void *c) +{ + HDC__ *ctx=SWELL_GDP_CTX_NEW(); + NSGraphicsContext *nsc = (NSGraphicsContext *)c; +// if (![nsc isFlipped]) +// nsc = [NSGraphicsContext graphicsContextWithGraphicsPort:[nsc graphicsPort] flipped:YES]; + + ctx->ctx=(CGContextRef)[nsc graphicsPort]; +// CGAffineTransform f={1,0,0,-1,0,0}; + //CGContextSetTextMatrix(ctx->ctx,f); + //SetTextColor(ctx,0); + + // CGContextSelectFont(ctx->ctx,"Arial",12.0,kCGEncodingMacRoman); + return ctx; +} + +#ifndef SWELL_NO_METAL +HDC SWELL_CreateMetalDC(SWELL_hwndChild *tex) +{ + HDC__ *ctx=SWELL_GDP_CTX_NEW(); + ctx->metal_ctx = tex; + return ctx; +} +#endif + +#define ALIGN_EXTRA 63 +static void *ALIGN_FBUF(void *inbuf) +{ + const UINT_PTR extra = ALIGN_EXTRA; + return (void *) (((UINT_PTR)inbuf+extra)&~extra); +} + +HDC SWELL_CreateMemContext(HDC hdc, int w, int h) +{ + void *buf=calloc(w*4*h+ALIGN_EXTRA,1); + if (WDL_NOT_NORMALLY(!buf)) return 0; + CGContextRef c=CGBitmapContextCreate(ALIGN_FBUF(buf),w,h,8,w*4, __GetBitmapColorSpace(), kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host); + if (WDL_NOT_NORMALLY(!c)) + { + free(buf); + return 0; + } + + + CGContextTranslateCTM(c,0.0,h); + CGContextScaleCTM(c,1.0,-1.0); + CGContextSetAllowsFontSmoothing(c,0); // we may wish to enable this for some contexts eventually, but this is to match previous behavior + + HDC__ *ctx=SWELL_GDP_CTX_NEW(); + ctx->ctx=(CGContextRef)c; + ctx->ownedData=buf; + // CGContextSelectFont(ctx->ctx,"Arial",12.0,kCGEncodingMacRoman); + + SetTextColor(ctx,0); + return ctx; +} + +void SWELL_DeleteGfxContext(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (HDC_VALID(ct)) + { + if (ct->ownedData) + { + CGContextRelease(ct->ctx); + free(ct->ownedData); + } + if (ct->curtextcol) CFRelease(ct->curtextcol); + SWELL_GDP_CTX_DELETE(ct); + } +} +HPEN CreatePen(int attr, int wid, int col) +{ + return CreatePenAlpha(attr,wid,col,1.0f); +} + +HBRUSH CreateSolidBrush(int col) +{ + return CreateSolidBrushAlpha(col,1.0f); +} + + + +HPEN CreatePenAlpha(int attr, int wid, int col, float alpha) +{ + HGDIOBJ__ *pen=GDP_OBJECT_NEW(); + pen->type=TYPE_PEN; + pen->wid=wid<0?0:wid; + pen->color=CreateColor(col,alpha); + pen->color_int = col; + return pen; +} +HBRUSH CreateSolidBrushAlpha(int col, float alpha) +{ + HGDIOBJ__ *brush=GDP_OBJECT_NEW(); + brush->type=TYPE_BRUSH; + brush->color=CreateColor(col,alpha); + brush->color_int = col; + brush->wid=0; + return brush; +} + + +HFONT CreateFontIndirect(LOGFONT *lf) +{ + return CreateFont(lf->lfHeight, lf->lfWidth,lf->lfEscapement, lf->lfOrientation, lf->lfWeight, lf->lfItalic, + lf->lfUnderline, lf->lfStrikeOut, lf->lfCharSet, lf->lfOutPrecision,lf->lfClipPrecision, + lf->lfQuality, lf->lfPitchAndFamily, lf->lfFaceName); +} + +static HGDIOBJ__ global_objs[2]; + +void DeleteObject(HGDIOBJ pen) +{ + HGDIOBJ__ *p=(HGDIOBJ__ *)pen; + if (p >= global_objs && p < global_objs + sizeof(global_objs)/sizeof(global_objs[0])) return; + + if (HGDIOBJ_VALID(p)) + { + if (--p->additional_refcnt < 0) + { + if (p->type == TYPE_PEN || p->type == TYPE_BRUSH || p->type == TYPE_FONT || p->type == TYPE_BITMAP) + { + if (p->type == TYPE_PEN || p->type == TYPE_BRUSH) + if (p->wid<0) return; + if (p->color) CGColorRelease(p->color); + + if (p->ct_FontRef) CFRelease(p->ct_FontRef); + +#ifdef SWELL_ATSUI_TEXT_SUPPORT + if (p->atsui_font_style) ATSUDisposeStyle(p->atsui_font_style); +#endif + + if (p->wid && p->bitmapptr) [p->bitmapptr release]; + GDP_OBJECT_DELETE(p); + } + // JF> don't free unknown objects, this shouldn't ever happen anyway: else free(p); + } + } +} + + +HGDIOBJ SelectObject(HDC ctx, HGDIOBJ pen) +{ + HDC__ *c=(HDC__ *)ctx; + HGDIOBJ__ *p=(HGDIOBJ__*) pen; + HGDIOBJ__ **mod=0; + if (!HDC_VALID(c)) return 0; + + if (p == (HGDIOBJ__*)TYPE_PEN) mod=&c->curpen; + else if (p == (HGDIOBJ__*)TYPE_BRUSH) mod=&c->curbrush; + else if (p == (HGDIOBJ__*)TYPE_FONT) mod=&c->curfont; + + if (mod) // clearing a particular thing + { + HGDIOBJ__ *np=*mod; + *mod=0; + return HGDIOBJ_VALID(np,(int)(INT_PTR)p)?np:p; + } + + if (!HGDIOBJ_VALID(p)) return 0; + + if (p->type == TYPE_PEN) mod=&c->curpen; + else if (p->type == TYPE_BRUSH) mod=&c->curbrush; + else if (p->type == TYPE_FONT) mod=&c->curfont; + + if (!mod) return 0; + + HGDIOBJ__ *op=*mod; + if (!HGDIOBJ_VALID(op,p->type)) op=(HGDIOBJ__*)(INT_PTR)p->type; + if (op != p) *mod=p; + return op; +} + + + +void SWELL_FillRect(HDC ctx, const RECT *r, HBRUSH br) +{ + HDC__ *c=(HDC__ *)ctx; + HGDIOBJ__ *b=(HGDIOBJ__*) br; + if (!HDC_VALID(c) || !HGDIOBJ_VALID(b,TYPE_BRUSH) || b == (HGDIOBJ__*)TYPE_BRUSH || b->type != TYPE_BRUSH) return; + +#ifndef SWELL_NO_METAL + if (c->metal_ctx) + { + if (b->wid>=0) + SWELL_Metal_FillRect(c->metal_ctx, r->left, r->top, r->right-r->left,r->bottom-r->top, b->color_int); + return; + } +#endif + + if (!c->ctx) return; + + if (b->wid<0) return; + + CGRect rect=CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top); + CGContextSetFillColorWithColor(c->ctx,b->color); + CGContextFillRect(c->ctx,rect); + +} + +void RoundRect(HDC ctx, int x, int y, int x2, int y2, int xrnd, int yrnd) +{ + xrnd/=3; + yrnd/=3; + POINT pts[10]={ // todo: curves between edges + {x,y+yrnd}, + {x+xrnd,y}, + {x2-xrnd,y}, + {x2,y+yrnd}, + {x2,y2-yrnd}, + {x2-xrnd,y2}, + {x+xrnd,y2}, + {x,y2-yrnd}, + {x,y+yrnd}, + {x+xrnd,y}, +}; + + WDL_GDP_Polygon(ctx,pts,sizeof(pts)/sizeof(pts[0])); +} + +void Ellipse(HDC ctx, int l, int t, int r, int b) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)) return; + if (!c->ctx) return; + + CGRect rect=CGRectMake(l,t,r-l,b-t); + + if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >=0) + { + CGContextSetFillColorWithColor(c->ctx,c->curbrush->color); + CGContextFillEllipseInRect(c->ctx,rect); + } + if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0) + { + CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color); + CGContextStrokeEllipseInRect(c->ctx, rect); //, (float)wdl_max(1,c->curpen->wid)); + } +} + +void Rectangle(HDC ctx, int l, int t, int r, int b) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)) return; +#ifndef SWELL_NO_METAL + if (c->metal_ctx) + { + if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0) + { + SWELL_Metal_FillRect(c->metal_ctx, l,t,r-l,b-t, c->curbrush->color_int); + } + if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0) + { + const int wid = wdl_max(1,c->curpen->wid); + SWELL_Metal_FillRect(c->metal_ctx, l,t,r-l,wid, c->curpen->color_int); + SWELL_Metal_FillRect(c->metal_ctx, l,b-wid,r-l,wid, c->curpen->color_int); + SWELL_Metal_FillRect(c->metal_ctx, l,t+wid,wid,b-t-wid*2, c->curpen->color_int); + SWELL_Metal_FillRect(c->metal_ctx, r-wid,t+wid,wid,b-t-wid*2, c->curpen->color_int); + } + return; + } +#endif + if (!c->ctx) return; + + CGRect rect=CGRectMake(l,t,r-l,b-t); + + if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0) + { + CGContextSetFillColorWithColor(c->ctx,c->curbrush->color); + CGContextFillRect(c->ctx,rect); + } + if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid >= 0) + { + CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color); + CGContextStrokeRectWithWidth(c->ctx, rect, (float)wdl_max(1,c->curpen->wid)); + } +} + + +HGDIOBJ GetStockObject(int wh) +{ + switch (wh) + { + case NULL_BRUSH: + { + HGDIOBJ__ *p = &global_objs[0]; + p->type=TYPE_BRUSH; + p->wid=-1; + return p; + } + case NULL_PEN: + { + HGDIOBJ__ *p = &global_objs[1]; + p->type=TYPE_PEN; + p->wid=-1; + return p; + } + } + WDL_ASSERT(false); + return 0; +} + +void Polygon(HDC ctx, POINT *pts, int npts) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)) return; + if (!c->ctx) return; + if (((!HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH)||c->curbrush->wid<0) && (!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0)) || npts<2) return; + + CGContextBeginPath(c->ctx); + CGContextMoveToPoint(c->ctx,(float)pts[0].x,(float)pts[0].y); + int x; + for (x = 1; x < npts; x ++) + { + CGContextAddLineToPoint(c->ctx,(float)pts[x].x,(float)pts[x].y); + } + if (HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid >= 0) + { + CGContextSetFillColorWithColor(c->ctx,c->curbrush->color); + } + if (HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0) + { + CGContextSetLineWidth(c->ctx,(float)wdl_max(c->curpen->wid,1)); + CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color); + } + CGContextDrawPath(c->ctx,HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0 && HGDIOBJ_VALID(c->curbrush,TYPE_BRUSH) && c->curbrush->wid>=0 ? kCGPathFillStroke : HGDIOBJ_VALID(c->curpen,TYPE_PEN) && c->curpen->wid>=0 ? kCGPathStroke : kCGPathFill); +} + +void MoveToEx(HDC ctx, int x, int y, POINT *op) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)) return; + if (op) + { + op->x = (int) (c->lastpos_x); + op->y = (int) (c->lastpos_y); + } + c->lastpos_x=(float)x; + c->lastpos_y=(float)y; +} + +void PolyBezierTo(HDC ctx, POINT *pts, int np) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||np<3) return; + if (!c->ctx) return; + + CGContextSetLineWidth(c->ctx,(float)wdl_max(c->curpen->wid,1)); + CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color); + + CGContextBeginPath(c->ctx); + CGContextMoveToPoint(c->ctx,c->lastpos_x,c->lastpos_y); + int x; + float xp,yp; + for (x = 0; x < np-2; x += 3) + { + CGContextAddCurveToPoint(c->ctx, + (float)pts[x].x,(float)pts[x].y, + (float)pts[x+1].x,(float)pts[x+1].y, + xp=(float)pts[x+2].x,yp=(float)pts[x+2].y); + } + c->lastpos_x=(float)xp; + c->lastpos_y=(float)yp; + CGContextStrokePath(c->ctx); +} + + +void SWELL_LineTo(HDC ctx, int x, int y) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0) return; +#ifndef SWELL_NO_METAL + if (c->metal_ctx) + { + if (x == c->lastpos_x) + { + const int my=wdl_min(y,c->lastpos_y); + SWELL_Metal_FillRect(c->metal_ctx, x, my, 1, wdl_max(y,c->lastpos_y)-my+1, c->curpen->color_int); + } + else if (y == c->lastpos_y) + { + const int mx = wdl_min(x,c->lastpos_x); + SWELL_Metal_FillRect(c->metal_ctx, mx, y, wdl_max(x,c->lastpos_x)-mx+1,1, c->curpen->color_int); + } + c->lastpos_x = x; + c->lastpos_y = y; + return; + } +#endif + if (!c->ctx) return; + + float w = (float)wdl_max(c->curpen->wid,1); + CGContextSetLineWidth(c->ctx,w); + CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color); + + CGContextBeginPath(c->ctx); + CGContextMoveToPoint(c->ctx,c->lastpos_x + w * 0.5,c->lastpos_y + w*0.5); + float fx=(float)x,fy=(float)y; + + CGContextAddLineToPoint(c->ctx,fx+w*0.5,fy+w*0.5); + c->lastpos_x=fx; + c->lastpos_y=fy; + CGContextStrokePath(c->ctx); +} + +void PolyPolyline(HDC ctx, POINT *pts, DWORD *cnts, int nseg) +{ + HDC__ *c=(HDC__ *)ctx; + if (!HDC_VALID(c)||!HGDIOBJ_VALID(c->curpen,TYPE_PEN)||c->curpen->wid<0||nseg<1) return; + if (!c->ctx) return; + + float w = (float)wdl_max(c->curpen->wid,1); + CGContextSetLineWidth(c->ctx,w); + CGContextSetStrokeColorWithColor(c->ctx,c->curpen->color); + + CGContextBeginPath(c->ctx); + + while (nseg-->0) + { + DWORD cnt=*cnts++; + if (!cnt) continue; + if (!--cnt) { pts++; continue; } + + CGContextMoveToPoint(c->ctx,(float)pts->x+w*0.5,(float)pts->y+w*0.5); + pts++; + + while (cnt--) + { + CGContextAddLineToPoint(c->ctx,(float)pts->x+w*0.5,(float)pts->y+w*0.5); + pts++; + } + } + CGContextStrokePath(c->ctx); +} +void *SWELL_GetCtxGC(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return 0; + return ct->ctx; +} + + +void SWELL_SetPixel(HDC ctx, int x, int y, int c) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; +#ifndef SWELL_NO_METAL + if (ct->metal_ctx) + { + SWELL_Metal_FillRect(ct->metal_ctx, x, y, 1, 1,c); + return; + } +#endif + + if (!ct->ctx) return; + CGContextBeginPath(ct->ctx); + CGContextMoveToPoint(ct->ctx,(float)x-0.5,(float)y-0.5); + CGContextAddLineToPoint(ct->ctx,(float)x+0.5,(float)y+0.5); + CGContextSetLineWidth(ct->ctx,(float)1.0); + CGContextSetRGBStrokeColor(ct->ctx,GetRValue(c)/255.0,GetGValue(c)/255.0,GetBValue(c)/255.0,1.0); + CGContextStrokePath(ct->ctx); +} + + +static WDL_Mutex s_fontnamecache_mutex; + +#ifdef SWELL_CLEANUP_ON_UNLOAD +static void releaseString(NSString *s) { [s release]; } +#endif +static WDL_StringKeyedArray s_fontnamecache(true, +#ifdef SWELL_CLEANUP_ON_UNLOAD + releaseString +#else + NULL +#endif + ); + +static NSString *SWELL_GetCachedFontName(const char *nm) +{ + NSString *ret = NULL; + if (nm && *nm) + { + s_fontnamecache_mutex.Enter(); + ret = s_fontnamecache.Get(nm); + s_fontnamecache_mutex.Leave(); + if (!ret) + { + ret = CStringToNSString(nm); + if (ret) + { +#ifndef SWELL_NO_CORETEXT + // only do postscript name lookups on 10.9+ + if (floor(NSFoundationVersionNumber) > 945.00) // NSFoundationVersionNumber10_8 + { + NSFont *font = [NSFont fontWithName:ret size:10]; + NSString *nr = font ? (NSString *)CTFontCopyPostScriptName((CTFontRef)font) : NULL; + if (nr) + { + [ret release]; + ret = nr; + } + } +#endif + + s_fontnamecache_mutex.Enter(); + s_fontnamecache.Insert(nm,ret); + s_fontnamecache_mutex.Leave(); + } + } + } + return ret ? ret : @""; +} + +HFONT CreateFont(int lfHeight, int lfWidth, int lfEscapement, int lfOrientation, int lfWeight, char lfItalic, + char lfUnderline, char lfStrikeOut, char lfCharSet, char lfOutPrecision, char lfClipPrecision, + char lfQuality, char lfPitchAndFamily, const char *lfFaceName) +{ + HGDIOBJ__ *font=GDP_OBJECT_NEW(); + font->type=TYPE_FONT; + float fontwid=lfHeight; + + if (!fontwid) fontwid=lfWidth; + if (fontwid<0)fontwid=-fontwid; + + if (fontwid < 2 || fontwid > 8192) fontwid=10; + + font->font_rotation = lfOrientation/10.0; + +#ifndef SWELL_NO_CORETEXT + if (IsCoreTextSupported()) + { + char buf[1024]; + lstrcpyn_safe(buf,lfFaceName,900); + if (lfWeight >= FW_BOLD) strcat(buf," Bold"); + if (lfItalic) strcat(buf," Italic"); + + font->ct_FontRef = (void*)CTFontCreateWithName((CFStringRef)SWELL_GetCachedFontName(buf),fontwid,NULL); + if (!font->ct_FontRef) font->ct_FontRef = (void*)[[NSFont labelFontOfSize:fontwid] retain]; + + // might want to make this conditional (i.e. only return font if created successfully), but I think we'd rather fallback to a system font than use ATSUI + return font; + } +#endif + +#ifdef SWELL_ATSUI_TEXT_SUPPORT + ATSUFontID fontid=kATSUInvalidFontID; + if (lfFaceName && lfFaceName[0]) + { + ATSUFindFontFromName(lfFaceName,strlen(lfFaceName),kFontFullName /* kFontFamilyName? */ ,(FontPlatformCode)kFontNoPlatform,kFontNoScriptCode,kFontNoLanguageCode,&fontid); + // if (fontid==kATSUInvalidFontID) printf("looked up %s and got %d\n",lfFaceName,fontid); + } + + if (ATSUCreateStyle(&font->atsui_font_style) == noErr && font->atsui_font_style) + { + Fixed fsize=Long2Fix(fontwid); + + Boolean isBold=lfWeight >= FW_BOLD; + Boolean isItal=!!lfItalic; + Boolean isUnder=!!lfUnderline; + + ATSUAttributeTag theTags[] = { kATSUQDBoldfaceTag, kATSUQDItalicTag, kATSUQDUnderlineTag,kATSUSizeTag,kATSUFontTag }; + ByteCount theSizes[] = { sizeof(Boolean),sizeof(Boolean),sizeof(Boolean), sizeof(Fixed),sizeof(ATSUFontID) }; + ATSUAttributeValuePtr theValues[] = {&isBold, &isItal, &isUnder, &fsize, &fontid } ; + + int attrcnt=sizeof(theTags)/sizeof(theTags[0]); + if (fontid == kATSUInvalidFontID) attrcnt--; + + if (ATSUSetAttributes (font->atsui_font_style, + attrcnt, + theTags, + theSizes, + theValues)!=noErr) + { + ATSUDisposeStyle(font->atsui_font_style); + font->atsui_font_style=0; + } + } + else + font->atsui_font_style=0; + +#endif + + + return font; +} + +int GetTextFace(HDC ctx, int nCount, LPTSTR lpFaceName) +{ + HDC__ *ct=(HDC__*)ctx; + if (!HDC_VALID(ct) || WDL_NOT_NORMALLY(!nCount || !lpFaceName)) return 0; + +#ifndef SWELL_NO_CORETEXT + CTFontRef fr=NULL; + if (HGDIOBJ_VALID(ct->curfont,TYPE_FONT)) fr=(CTFontRef)ct->curfont->ct_FontRef; + if (!fr) fr=GetCoreTextDefaultFont(); + + if (fr) + { + CFStringRef name=CTFontCopyDisplayName(fr); + const char* p=[(NSString*)name UTF8String]; + if (p) + { + lstrcpyn_safe(lpFaceName, p, nCount); + return (int)strlen(lpFaceName); + } + } +#endif + + return 0; +} + +BOOL GetTextMetrics(HDC ctx, TEXTMETRIC *tm) +{ + HDC__ *ct=(HDC__ *)ctx; + if (tm) // give some sane defaults + { + tm->tmInternalLeading=3; + tm->tmAscent=12; + tm->tmDescent=4; + tm->tmHeight=16; + tm->tmAveCharWidth = 10; + } + if (!HDC_VALID(ct)||WDL_NOT_NORMALLY(!tm)) return 0; + + bool curfont_valid=HGDIOBJ_VALID(ct->curfont,TYPE_FONT); + +#ifdef SWELL_ATSUI_TEXT_SUPPORT + if (curfont_valid && ct->curfont->atsui_font_style) + { + ATSUTextMeasurement ascent=Long2Fix(10); + ATSUTextMeasurement descent=Long2Fix(3); + ATSUTextMeasurement sz=Long2Fix(0); + ATSUTextMeasurement width =Long2Fix(12); + ATSUGetAttribute(ct->curfont->atsui_font_style, kATSUAscentTag, sizeof(ATSUTextMeasurement), &ascent,NULL); + ATSUGetAttribute(ct->curfont->atsui_font_style, kATSUDescentTag, sizeof(ATSUTextMeasurement), &descent,NULL); + ATSUGetAttribute(ct->curfont->atsui_font_style, kATSUSizeTag, sizeof(ATSUTextMeasurement), &sz,NULL); + ATSUGetAttribute(ct->curfont->atsui_font_style, kATSULineWidthTag, sizeof(ATSUTextMeasurement),&width,NULL); + + float asc=Fix2X(ascent); + float desc=Fix2X(descent); + float size = Fix2X(sz); + + if (size < (asc+desc)*0.2) size=asc+desc; + + tm->tmAscent = (int)ceil(asc); + tm->tmDescent = (int)ceil(desc); + tm->tmInternalLeading=(int)ceil(asc+desc-size); + if (tm->tmInternalLeading<0)tm->tmInternalLeading=0; + tm->tmHeight=(int) ceil(asc+desc); + tm->tmAveCharWidth = (int) (ceil(asc+desc)*0.65); // (int)ceil(Fix2X(width)); + + return 1; + } +#endif + +#ifndef SWELL_NO_CORETEXT + CTFontRef fr = curfont_valid ? (CTFontRef)ct->curfont->ct_FontRef : NULL; + if (!fr) fr=GetCoreTextDefaultFont(); + + if (fr) + { + tm->tmInternalLeading = CTFontGetLeading(fr); + tm->tmAscent = CTFontGetAscent(fr); + tm->tmDescent = CTFontGetDescent(fr); + tm->tmHeight = (tm->tmInternalLeading + tm->tmAscent + tm->tmDescent); + tm->tmAveCharWidth = tm->tmHeight*2/3; // todo + + if (tm->tmHeight) tm->tmHeight++; + + return 1; + } +#endif + + + return 1; +} + + + +#ifdef SWELL_ATSUI_TEXT_SUPPORT + +static int DrawTextATSUI(HDC ctx, CFStringRef strin, RECT *r, int align, bool *err) +{ + HDC__ *ct=(HDC__ *)ctx; + HGDIOBJ__ *font=ct->curfont; // caller must specify a valid font + + UniChar strbuf[4096]; + int strbuf_len; + + { + strbuf[0]=0; + CFRange r = {0,CFStringGetLength(strin)}; + if (r.length > 4095) r.length=4095; + strbuf_len=CFStringGetBytes(strin,r,kCFStringEncodingUTF16,' ',false,(UInt8*)strbuf,sizeof(strbuf)-2,NULL); + if (strbuf_len<0)strbuf_len=0; + else if (strbuf_len>4095) strbuf_len=4095; + strbuf[strbuf_len]=0; + } + + { + ATSUAttributeTag theTags[] = { kATSUColorTag, }; + ByteCount theSizes[] = { sizeof(RGBColor), }; + + RGBColor tcolor; + ATSUAttributeValuePtr theValues[] = {&tcolor, } ; + + tcolor.red = GetRValue(ct->cur_text_color_int)*256; + tcolor.green = GetGValue(ct->cur_text_color_int)*256; + tcolor.blue = GetBValue(ct->cur_text_color_int)*256; + + // error check this? we can live with the wrong color maybe? + ATSUSetAttributes(font->atsui_font_style, sizeof(theTags)/sizeof(theTags[0]), theTags, theSizes, theValues); + } + + UniCharCount runLengths[1]={kATSUToTextEnd}; + ATSUTextLayout layout; + if (ATSUCreateTextLayoutWithTextPtr(strbuf, kATSUFromTextBeginning, kATSUToTextEnd, strbuf_len, 1, runLengths, &font->atsui_font_style, &layout)!=noErr) + { + *err=true; + return 0; + } + + { + Fixed frot = X2Fix(font->font_rotation); + + ATSULineTruncation tv = (align & DT_END_ELLIPSIS) ? kATSUTruncateEnd : kATSUTruncateNone; + ATSUAttributeTag theTags[] = { kATSUCGContextTag, kATSULineTruncationTag, kATSULineRotationTag }; + ByteCount theSizes[] = { sizeof (CGContextRef), sizeof(ATSULineTruncation), sizeof(Fixed)}; + ATSUAttributeValuePtr theValues[] = { &ct->ctx, &tv, &frot } ; + + + if (ATSUSetLayoutControls (layout, + + sizeof(theTags)/sizeof(theTags[0]), + + theTags, + + theSizes, + + theValues)!=noErr) + { + *err=true; + ATSUDisposeTextLayout(layout); + return 0; + } + } + + + ATSUTextMeasurement leftFixed, rightFixed, ascentFixed, descentFixed; + + if (ATSUGetUnjustifiedBounds(layout, kATSUFromTextBeginning, kATSUToTextEnd, &leftFixed, &rightFixed, &ascentFixed, &descentFixed)!=noErr) + { + *err=true; + ATSUDisposeTextLayout(layout); + return 0; + } + + int w=Fix2Long(rightFixed); + int descent=Fix2Long(descentFixed); + int h=descent + Fix2Long(ascentFixed); + if (align&DT_CALCRECT) + { + ATSUDisposeTextLayout(layout); + r->right=r->left+w; + r->bottom=r->top+h; + return h; + } + CGContextSaveGState(ct->ctx); + + if (!(align & DT_NOCLIP)) + CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top)); + + int l=r->left, t=r->top; + + if (fabs(font->font_rotation)<45.0) + { + if (align & DT_RIGHT) l = r->right-w; + else if (align & DT_CENTER) l = (r->right+r->left)/2 - w/2; + } + else l+=Fix2Long(ascentFixed); // 90 degree special case (we should generalize this to be correct throughout the rotation range, but oh well) + + if (align & DT_BOTTOM) t = r->bottom-h; + else if (align & DT_VCENTER) t = (r->bottom+r->top)/2 - h/2; + + CGContextTranslateCTM(ct->ctx,0,t); + CGContextScaleCTM(ct->ctx,1,-1); + CGContextTranslateCTM(ct->ctx,0,-t-h); + + if (ct->curbkmode == OPAQUE) + { + CGRect bgr = CGRectMake(l, t, w, h); + CGColorRef bgc = CreateColor(ct->curbkcol); + CGContextSetFillColorWithColor(ct->ctx, bgc); + CGContextFillRect(ct->ctx, bgr); + CGColorRelease(bgc); + } + + if (ATSUDrawText(layout,kATSUFromTextBeginning,kATSUToTextEnd,Long2Fix(l),Long2Fix(t+descent))!=noErr) + *err=true; + + CGContextRestoreGState(ct->ctx); + + ATSUDisposeTextLayout(layout); + + return h; +} + +#endif + +int DrawText(HDC ctx, const char *buf, int buflen, RECT *r, int align) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return 0; + if (!(align & DT_CALCRECT) && !ct->ctx) return 0; + + bool has_ml=false; + char tmp[4096]; + const char *p=buf; + char *op=tmp; + while (*p && (op-tmp)curfont,TYPE_FONT); +#ifdef SWELL_ATSUI_TEXT_SUPPORT + if (curfont_valid && ct->curfont->atsui_font_style) + { + bool err=false; + int ret = DrawTextATSUI(ctx,(CFStringRef)str,r,align,&err); + [str release]; + + if (!err) return ret; + return 0; + } +#endif + +#ifndef SWELL_NO_CORETEXT + CTFontRef fr = curfont_valid ? (CTFontRef)ct->curfont->ct_FontRef : NULL; + if (!fr) fr=GetCoreTextDefaultFont(); + if (fr) + { + // Initialize string, font, and context + CFStringRef keys[] = { kCTFontAttributeName,kCTForegroundColorAttributeName }; + CFTypeRef values[] = { fr,ct->curtextcol }; + + int nk= sizeof(keys) / sizeof(keys[0]); + if (!values[1]) nk--; + + CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, (const void**)&values, nk, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + CFAttributedStringRef attrString = + CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)str, attributes); + CFRelease(attributes); + [str release]; + + + CTFrameRef frame = NULL; + CFArrayRef lines = NULL; + CTLineRef line = NULL; + CGFloat asc=0; + int line_w=0,line_h=0; + if (has_ml) + { + CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString); + if (framesetter) + { + CGMutablePathRef path=CGPathCreateMutable(); + CGPathAddRect(path,NULL,CGRectMake(0,0,100000,100000)); + frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0),path,NULL); + CFRelease(framesetter); + CFRelease(path); + } + if (frame) + { + lines = CTFrameGetLines(frame); + const int n = (int)CFArrayGetCount(lines); + for (int x=0;xright = r->left+line_w; + r->bottom = r->top+line_h; + if (line) CFRelease(line); + if (frame) CFRelease(frame); + return line_h; + } + + float xo=r->left,yo=r->top; + if (align & DT_RIGHT) xo += (r->right-r->left) - line_w; + else if (align & DT_CENTER) xo += (r->right-r->left)/2 - line_w/2; + + if (align & DT_BOTTOM) yo += (r->bottom-r->top) - line_h; + else if (align & DT_VCENTER) yo += (r->bottom-r->top)/2 - line_h/2; + + + CGContextSaveGState(ct->ctx); + + CGAffineTransform f={1,0,0,-1,0,0}; + CGContextSetTextMatrix(ct->ctx, f); + + if (!(align & DT_NOCLIP)) + { + CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top)); + } + + CGColorRef bgc = NULL; + if (ct->curbkmode == OPAQUE) + { + bgc = CreateColor(ct->curbkcol); + } + + if (line) + { + if (bgc) + { + CGContextSetFillColorWithColor(ct->ctx, bgc); + CGContextFillRect(ct->ctx, CGRectMake(xo,yo,line_w,line_h)); + } + CGContextSetTextPosition(ct->ctx, xo, yo + asc); + CTLineDraw(line,ct->ctx); + + } + if (lines) + { + const int n = (int)CFArrayGetCount(lines); + for (int x=0;xctx, bgc); + CGContextFillRect(ct->ctx, CGRectMake(xo,yo,lw,asc+desc+lead)); + } + CGContextSetTextPosition(ct->ctx, xo, yo + asc); + CTLineDraw(l,ct->ctx); + + yo += floor(asc+desc+lead+0.5); + } + } + } + + CGContextRestoreGState(ct->ctx); + if (bgc) CGColorRelease(bgc); + if (line) CFRelease(line); + if (frame) CFRelease(frame); + + return line_h; + } +#endif + + + [str release]; + return 0; +} + + +int GetGlyphIndicesW(HDC ctx, wchar_t *buf, int len, unsigned short *indices, int flags) +{ + HDC__ *ct=(HDC__*)ctx; + if (HDC_VALID(ct) && HGDIOBJ_VALID(ct->curfont, TYPE_FONT)) + { +#ifndef SWELL_NO_CORETEXT + CTFontRef f=(CTFontRef)ct->curfont->ct_FontRef; + if (f && CTFontGetGlyphsForCharacters(f, (const UniChar*)buf, (CGGlyph*)indices, (CFIndex)len)) return len; +#endif + } + + int i; + for (i=0; i < len; ++i) indices[i]=(flags == GGI_MARK_NONEXISTING_GLYPHS ? 0xFFFF : 0); + return 0; +} + + +NSFont *SWELL_GetNSFont(HGDIOBJ__ *obj) +{ + if (HGDIOBJ_VALID(obj,TYPE_FONT)) + { + if (obj->ct_FontRef) return (NSFont *)obj->ct_FontRef; +#ifdef SWELL_ATSUI_TEXT_SUPPORT + else if (obj->atsui_font_style) + { + ATSUFontID fontid = kATSUInvalidFontID; + Fixed fsize = 0; + Boolean isbold = NO; + Boolean isital = NO; + Boolean isunder = NO; + if (ATSUGetAttribute(obj->atsui_font_style, kATSUFontTag, sizeof(ATSUFontID), &fontid, 0) == noErr && + ATSUGetAttribute(obj->atsui_font_style, kATSUSizeTag, sizeof(Fixed), &fsize, 0) == noErr && fsize && + ATSUGetAttribute(obj->atsui_font_style, kATSUQDBoldfaceTag, sizeof(Boolean), &isbold, 0) == noErr && + ATSUGetAttribute(obj->atsui_font_style, kATSUQDItalicTag, sizeof(Boolean), &isital, 0) == noErr && + ATSUGetAttribute(obj->atsui_font_style, kATSUQDUnderlineTag, sizeof(Boolean), &isunder, 0) == noErr) + { + char name[255]; + name[0]=0; + ByteCount namelen=0; + if (ATSUFindFontName(fontid, kFontFullName, (FontPlatformCode)kFontNoPlatform, kFontNoScriptCode, kFontNoLanguageCode, sizeof(name), name, &namelen, 0) == noErr && name[0] && namelen) + { + namelen /= 2; + int i; + for (i = 0; i < namelen; ++i) name[i] = name[2*i]; + name[namelen]=0; + + // todo bold/ital/underline + NSString* str = (NSString*)SWELL_CStringToCFString(name); + CGFloat sz = Fix2Long(fsize); + NSFont* font = [NSFont fontWithName:str size:sz]; + [str release]; + return font; + } + } + } +#endif + } + return NULL; +} + + +void SetBkColor(HDC ctx, int col) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; + ct->curbkcol=col; +} + +void SetBkMode(HDC ctx, int col) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; + ct->curbkmode=col; +} + +int GetTextColor(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return -1; + return ct->cur_text_color_int; +} + +void SetTextColor(HDC ctx, int col) +{ + HDC__ *ct=(HDC__ *)ctx; + if (!HDC_VALID(ct)) return; + ct->cur_text_color_int = col; + + if (ct->curtextcol) CFRelease(ct->curtextcol); + + ct->curtextcol = CreateColor(col); +} + + +HICON CreateIconIndirect(ICONINFO* iconinfo) +{ + if (WDL_NOT_NORMALLY(!iconinfo || !iconinfo->fIcon)) return 0; + HGDIOBJ__* i=iconinfo->hbmColor; + if (!HGDIOBJ_VALID(i,TYPE_BITMAP) || !i->bitmapptr) return 0; + NSImage* img=i->bitmapptr; + if (!img) return 0; + + HGDIOBJ__* icon=GDP_OBJECT_NEW(); + icon->type=TYPE_BITMAP; + icon->wid=1; + [img retain]; + icon->bitmapptr=img; + return icon; +} + +HICON LoadNamedImage(const char *name, bool alphaFromMask) +{ + NSImage *img=0; + NSString *str=CStringToNSString(name); + if (strstr(name,"/")) + { + img=[[NSImage alloc] initWithContentsOfFile:str]; + } + if (!img) + { + img=[NSImage imageNamed:str]; + if (img) [img retain]; + } + [str release]; + if (!img) + { + return 0; + } + + [img setFlipped:YES]; + if (alphaFromMask) + { + const NSSize sz=[img size]; + const int w = (int)sz.width, h=(int)sz.height; + HDC hdc; + if (w>0 && h>0 && NULL != (hdc=SWELL_CreateMemContext(NULL,w,h))) + { + [NSGraphicsContext saveGraphicsState]; + NSGraphicsContext *gc=[NSGraphicsContext graphicsContextWithGraphicsPort:((struct HDC__*)hdc)->ctx flipped:NO]; + [NSGraphicsContext setCurrentContext:gc]; + [img drawInRect:NSMakeRect(0,0,w,h) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0]; + [NSGraphicsContext restoreGraphicsState]; + + // on yosemite, calling [img TIFFRepresentation] seems to change img somehow for some images, ouch. + // in this case, we should always replace img with newImage (set rcnt=1), but in general + // maybe we shoulnt use alphaFromMask anyhow + NSData *data = [img TIFFRepresentation]; + if (!data) + { + SWELL_DeleteGfxContext(hdc); + goto return_img; + } + + NSImage *newImage=[[NSImage alloc] initWithData:data]; + [newImage setFlipped:YES]; + + const int *fb = (const int *)SWELL_GetCtxFrameBuffer(hdc); + int y,rcnt=0; + [newImage lockFocus]; + CGContextRef myContext = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort]; + for (y=0; y < h; y ++) + { + int x; + for (x = 0; x < w; x++) + { + if ((*fb++ & 0xffffff) == 0xff00ff) + { + CGContextClearRect(myContext,CGRectMake(x,y,1,1)); + rcnt++; + } + } + } + [newImage unlockFocus]; + + SWELL_DeleteGfxContext(hdc); + + if (rcnt) + { + [img release]; + img=newImage; + } + else + [newImage release]; + } + } + +return_img: + HGDIOBJ__ *i=GDP_OBJECT_NEW(); + i->type=TYPE_BITMAP; + i->wid=1; + i->bitmapptr = img; + return i; +} + +void DrawImageInRect(HDC ctx, HICON img, const RECT *r) +{ + HGDIOBJ__ *i = (HGDIOBJ__ *)img; + HDC__ *ct=(HDC__*)ctx; + if (!HDC_VALID(ct) || !HGDIOBJ_VALID(i,TYPE_BITMAP) || !i->bitmapptr) return; + if (WDL_NOT_NORMALLY(!ct->ctx)) return; + //CGContextDrawImage(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top),(CGImage*)i->bitmapptr); + // probably a better way since this ignores the ctx + [NSGraphicsContext saveGraphicsState]; + NSGraphicsContext *gc=[NSGraphicsContext graphicsContextWithGraphicsPort:ct->ctx flipped:NO]; + [NSGraphicsContext setCurrentContext:gc]; + NSImage *nsi=i->bitmapptr; + NSRect rr=NSMakeRect(r->left,r->top,r->right-r->left,r->bottom-r->top); + [nsi setFlipped:YES]; + [nsi drawInRect:rr fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + [nsi setFlipped:NO]; // todo: restore old flippedness? + [NSGraphicsContext restoreGraphicsState]; +// [gc release]; +} + + +BOOL GetObject(HICON icon, int bmsz, void *_bm) +{ + memset(_bm,0,bmsz); + if (WDL_NOT_NORMALLY(bmsz < 2*(int)sizeof(LONG))) return false; + BITMAP *bm=(BITMAP *)_bm; + HGDIOBJ__ *i = (HGDIOBJ__ *)icon; + if (!HGDIOBJ_VALID(i,TYPE_BITMAP)) return false; + NSImage *img = i->bitmapptr; + if (WDL_NOT_NORMALLY(!img)) return false; + bm->bmWidth = (int) ([img size].width+0.5); + bm->bmHeight = (int) ([img size].height+0.5); + if (bmsz >= (int)sizeof(BITMAP)) + { + bm->bmWidthBytes = bm->bmWidth * 4; + bm->bmPlanes = 1; + bm->bmBitsPixel = 32; + bm->bmBits = NULL; + } + + return true; +} + + +void *GetNSImageFromHICON(HICON ico) +{ + HGDIOBJ__ *i = (HGDIOBJ__ *)ico; + if (!HGDIOBJ_VALID(i,TYPE_BITMAP)) return 0; + return i->bitmapptr; +} + +int GetSysColor(int idx) +{ + switch (idx) + { + case COLOR_WINDOW: + case COLOR_BTNFACE: + case COLOR_3DFACE: return SWELL_osx_is_dark_mode(0) ? RGB(37,37,37) : RGB(232,232,232); + case COLOR_BTNTEXT: return SWELL_osx_is_dark_mode(0) ? RGB(255,255,255) : RGB(0,0,0); + case COLOR_SCROLLBAR: return RGB(32,32,32); + case COLOR_3DSHADOW: return RGB(96,96,96); + case COLOR_3DHILIGHT: return RGB(224,224,224); + case COLOR_3DDKSHADOW: return RGB(48,48,48); + case COLOR_INFOBK: return RGB(255,240,200); + case COLOR_INFOTEXT: return RGB(0,0,0); + } + return 0; +} + + +void BitBlt(HDC hdcOut, int x, int y, int w, int h, HDC hdcIn, int xin, int yin, int mode) +{ + StretchBlt(hdcOut,x,y,w,h,hdcIn,xin,yin,w,h,mode); +} + +void StretchBlt(HDC hdcOut, int x, int y, int destw, int desth, HDC hdcIn, int xin, int yin, int w, int h, int mode) +{ + HDC__ *src=(HDC__*)hdcIn; + HDC__ *dest=(HDC__*)hdcOut; + if (w<1 || h<1 || !HDC_VALID(src) || !HDC_VALID(dest) || !src->ownedData || !src->ctx) return; + + const int sw = (int)CGBitmapContextGetWidth(src->ctx); + const int sh = (int)CGBitmapContextGetHeight(src->ctx); + + const int preclip_w=w; + const int preclip_h=h; + + if (xin<0) + { + x-=(xin*destw)/w; + w+=xin; + xin=0; + } + if (yin<0) + { + y-=(yin*desth)/h; + h+=yin; + yin=0; + } + if (xin+w > sw) w=sw-xin; + if (yin+h > sh) h=sh-yin; + + if (w<1||h<1) return; + + if (destw==preclip_w) destw=w; // no scaling, keep width the same + else if (w != preclip_w) destw = (w*destw)/preclip_w; + + if (desth == preclip_h) desth=h; + else if (h != preclip_h) desth = (h*desth)/preclip_h; + + if (destw < 1 || desth < 1) return; + + const bool use_alphachannel = mode == (int)SRCCOPY_USEALPHACHAN; + + unsigned char *p = (unsigned char *)ALIGN_FBUF(src->ownedData); + p += (xin + sw*yin)*4; + +#ifndef SWELL_NO_METAL + + if (dest->metal_ctx) + { + void SWELL_Metal_Blit(void *tex, const unsigned int *buf, int x, int y, int w, int h, int span, bool retina_hint, bool use_alpha); + + const unsigned int *ptr = (const unsigned int *)p; + if (w == destw && h == desth) + SWELL_Metal_Blit(hdcOut->metal_ctx,ptr,x,y,w,h,sw,false, use_alphachannel); + else if (w == destw*2 && h == desth*2) + SWELL_Metal_Blit(hdcOut->metal_ctx,ptr,x*2,y*2,w,h,sw,true, use_alphachannel); + else + { + // Using StretchBlt() to size contents isn't ideal (in Metal mode or in win32), but if the caller insists + const bool retina = w >= destw*2 && h >= desth*2 && SWELL_IsRetinaDC(hdcOut); + if (retina) + { + destw *= 2; + desth *= 2; + x*=2; + y*=2; + } + + // resize a copy of image to destw/desth/destsw/destptr + static WDL_TypedBuf tmp; + const int destspan = (destw+3)&~3; + const unsigned int dx = (w * 65536) / destw, dy = (h * 65536) / desth; + unsigned int *destptr = tmp.ResizeOK(destspan*desth, false); + if (WDL_NOT_NORMALLY(!destptr)) return; + + unsigned int *wr = destptr; + for (int i=0;i>16; + if (WDL_NOT_NORMALLY(yp >= (unsigned int)h)) break; + const unsigned int *rd = ptr + yp*sw; + int xpos = 0; + for (int j=0;j>16); + if (WDL_NOT_NORMALLY(xp >= (unsigned int)w)) break; + wr[j] = rd[xp]; + xpos += dx; + } + wr += destspan; + } + + SWELL_Metal_Blit(hdcOut->metal_ctx,destptr,x,y, destw, desth, destspan, retina, use_alphachannel); + } + + return; + } + +#endif + + if (!dest->ctx) return; + + CGContextRef output = (CGContextRef)dest->ctx; + CGRect outputr = CGRectMake(x,-desth-y,destw,desth); + + +#ifdef SWELL_SUPPORT_OPENGL_BLIT + if (dest->GLgfxctx) + { + NSOpenGLContext *glCtx = (NSOpenGLContext*) dest->GLgfxctx; + NSOpenGLContext *cCtx = [NSOpenGLContext currentContext]; + if (glCtx != cCtx) + { + [glCtx makeCurrentContext]; + } + + glDisable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_RECTANGLE_EXT); + + GLuint texid=0; + glGenTextures(1, &texid); + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texid); + glPixelStorei(GL_UNPACK_ROW_LENGTH, sw); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_RECTANGLE_EXT,0,GL_RGBA8,w,h,0,GL_BGRA,GL_UNSIGNED_INT_8_8_8_8_REV, p); + + glViewport(x,[[glCtx view] bounds].size.height-desth-y,destw,desth); + glBegin(GL_QUADS); + + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-1,1); + + glTexCoord2f(0.0f, h); + glVertex2f(-1,-1); + + glTexCoord2f(w,h); + glVertex2f(1,-1); + + glTexCoord2f(w, 0.0f); + glVertex2f(1,1); + glEnd(); + + glDeleteTextures(1,&texid); + glFlush(); + + if (glCtx != cCtx) [cCtx makeCurrentContext]; + return; + } +#endif + + + + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,p,4*sw*h,NULL); + CGImageRef img = CGImageCreate(w,h,8,32,4*sw,__GetDisplayColorSpace(), + (use_alphachannel?kCGImageAlphaFirst:kCGImageAlphaNoneSkipFirst)|kCGBitmapByteOrder32Host, + provider,NULL,NO,kCGRenderingIntentDefault); + CGDataProviderRelease(provider); + + if (img) + { + CGContextSaveGState(output); + CGContextScaleCTM(output,1.0,-1.0); + + CGContextSetInterpolationQuality(output,kCGInterpolationNone); + CGContextDrawImage(output,outputr,img); + CGContextRestoreGState(output); + + CGImageRelease(img); + } +} + +void SWELL_PushClipRegion(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (HDC_VALID(ct) && ct->ctx) CGContextSaveGState(ct->ctx); +} + +void SWELL_SetClipRegion(HDC ctx, const RECT *r) +{ + HDC__ *ct=(HDC__ *)ctx; + if (HDC_VALID(ct) && ct->ctx) CGContextClipToRect(ct->ctx,CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top)); + +} + +void SWELL_PopClipRegion(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (HDC_VALID(ct) && ct->ctx) CGContextRestoreGState(ct->ctx); +} + +void *SWELL_GetCtxFrameBuffer(HDC ctx) +{ + HDC__ *ct=(HDC__ *)ctx; + if (HDC_VALID(ct)) return ALIGN_FBUF(ct->ownedData); + return 0; +} + + +HDC GetDC(HWND h) +{ + WDL_ASSERT(h); + if (h && [(id)h isKindOfClass:[NSWindow class]]) + { + if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) + { + PAINTSTRUCT ps={0,}; + [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps]; + if (ps.hdc) + { + if ((ps.hdc)->ctx) CGContextSaveGState((ps.hdc)->ctx); + return ps.hdc; + } + } + h=(HWND)[(id)h contentView]; + } + + if (h && [(id)h isKindOfClass:[NSView class]]) + { + if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) + { + PAINTSTRUCT ps={0,}; + [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps]; + if (HDC_VALID((HDC__*)ps.hdc)) + { + if (((HDC__*)ps.hdc)->ctx) CGContextSaveGState((ps.hdc)->ctx); + return ps.hdc; + } + } + +#ifndef SWELL_NO_METAL + if ([(id)h isKindOfClass:[SWELL_hwndChild class]] && [(SWELL_hwndChild *)h swellWantsMetal]) + { + SWELL_hwndChild *wnd = (SWELL_hwndChild*)h; + + wnd->m_metal_dc_dirty = 2; + return SWELL_CreateMetalDC(wnd); + } +#endif + + if ([(NSView*)h lockFocusIfCanDraw]) + { + HDC ret= SWELL_CreateGfxContext([NSGraphicsContext currentContext]); + if (ret) + { + if (ret->ctx) CGContextSaveGState(ret->ctx); + if (!ret->GLgfxctx && [(id)h respondsToSelector:@selector(swellGetGLContext)]) + { + NSOpenGLContext *glctx = (NSOpenGLContext*)[(id)h swellGetGLContext]; + ret->GLgfxctx = glctx; + if (glctx) [glctx setView:(NSView *)h]; + } + } + return ret; + } + } + return 0; +} + +HDC GetWindowDC(HWND h) +{ + WDL_ASSERT(h); + HDC ret=GetDC(h); + if (ret) + { + NSView *v=NULL; + if ([(id)h isKindOfClass:[NSWindow class]]) v=[(id)h contentView]; + else if ([(id)h isKindOfClass:[NSView class]]) v=(NSView *)h; + + if (v) + { + NSRect b=[v bounds]; + float xsc=b.origin.x; + float ysc=b.origin.y; + if ((xsc || ysc) && (ret)->ctx) CGContextTranslateCTM((ret)->ctx,xsc,ysc); + } + } + return ret; +} + +void ReleaseDC(HWND h, HDC hdc) +{ + WDL_ASSERT(h); + if (hdc) + { + if ((hdc)->ctx) CGContextRestoreGState((hdc)->ctx); + } + if (h && [(id)h isKindOfClass:[NSWindow class]]) + { + if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) + { + PAINTSTRUCT ps={0,}; + [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps]; + if (ps.hdc && ps.hdc==hdc) return; + } + h=(HWND)[(id)h contentView]; + } + bool isView=h && [(id)h isKindOfClass:[NSView class]]; + if (isView) + { + if ([(id)h respondsToSelector:@selector(getSwellPaintInfo:)]) + { + PAINTSTRUCT ps={0,}; + [(id)h getSwellPaintInfo:(PAINTSTRUCT *)&ps]; + if (ps.hdc && ps.hdc==hdc) return; + } + } + if (hdc && hdc->GLgfxctx) + { + if ([NSOpenGLContext currentContext] == hdc->GLgfxctx) [NSOpenGLContext clearCurrentContext]; + hdc->GLgfxctx = NULL; + } + + if (hdc) SWELL_DeleteGfxContext(hdc); + if (isView && hdc) + { +#ifndef SWELL_NO_METAL + if ([(id)h isKindOfClass:[SWELL_hwndChild class]] && [(SWELL_hwndChild *)h swellWantsMetal]) + { + SWELL_hwndChild *wnd = (SWELL_hwndChild*)h; + if (wnd->m_metal_dc_dirty == 1) + { + if (WDL_NOT_NORMALLY(wnd->m_use_metal == 1)) + { + NSLog(@"swell-cocoa: metal(1) surface %p had write in GetDC()/ReleaseDC(), this is unsupported, use a metal(2) surface\n",wnd); + } + swell_addMetalDirty(wnd,NULL,true); + } + } + else +#endif + [(NSView *)h unlockFocus]; + } +} + +void SWELL_FillDialogBackground(HDC hdc, const RECT *r, int level) +{ + CGContextRef ctx=(CGContextRef)SWELL_GetCtxGC(hdc); + if (WDL_NORMALLY(ctx)) + { + bool ok = false; + if (SWELL_GDI_GetOSXVersion()>=0x10d0) + { + NSColor *c = [NSColor windowBackgroundColor]; + if ([c respondsToSelector:@selector(CGColor)]) + { + void *(*send_msg)(id, SEL) = (void *(*)(id, SEL))objc_msgSend; + CGContextSetFillColorWithColor(ctx, (CGColorRef)send_msg(c, @selector(CGColor))); + ok = true; + } + } + + if (!ok) + HIThemeSetFill(kThemeBrushDialogBackgroundActive,NULL,ctx,kHIThemeOrientationNormal); + + CGRect rect=CGRectMake(r->left,r->top,r->right-r->left,r->bottom-r->top); + CGContextFillRect(ctx,rect); + } +} + +HGDIOBJ SWELL_CloneGDIObject(HGDIOBJ a) +{ + if (HGDIOBJ_VALID(a)) + { + a->additional_refcnt++; + return a; + } + return NULL; +} + + +HBITMAP CreateBitmap(int width, int height, int numplanes, int bitsperpixel, unsigned char* bits) +{ + int spp = bitsperpixel/8; + Boolean hasa = (bitsperpixel == 32); + Boolean hasp = (numplanes > 1); // won't actually work yet for planar data + NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:0 pixelsWide:width pixelsHigh:height + bitsPerSample:8 samplesPerPixel:spp + hasAlpha:hasa isPlanar:hasp + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:NSAlphaFirstBitmapFormat + bytesPerRow:0 bitsPerPixel:0]; + if (WDL_NOT_NORMALLY(!rep)) return 0; + unsigned char* p = [rep bitmapData]; + const int pspan = (int)[rep bytesPerRow]; // might not be the same as width + + for (int y=0;ytype = TYPE_BITMAP; + obj->wid = 1; // need free + obj->bitmapptr = img; + return obj; +} + + +HIMAGELIST ImageList_CreateEx() +{ + return (HIMAGELIST)new WDL_PtrList; +} + +BOOL ImageList_Remove(HIMAGELIST list, int idx) +{ + WDL_PtrList* imglist=(WDL_PtrList*)list; + if (WDL_NORMALLY(imglist) && idx < imglist->GetSize()) + { + if (idx < 0) + { + int x,n=imglist->GetSize(); + for (x=0;xGet(x); + if (a) DeleteObject(a); + } + imglist->Empty(); + } + else + { + HGDIOBJ__ *a = imglist->Get(idx); + imglist->Set(idx, NULL); + if (a) DeleteObject(a); + } + return TRUE; + } + + return FALSE; +} + +void ImageList_Destroy(HIMAGELIST list) +{ + if (WDL_NOT_NORMALLY(!list)) return; + ImageList_Remove(list, -1); + delete (WDL_PtrList*)list; +} + +int ImageList_ReplaceIcon(HIMAGELIST list, int offset, HICON image) +{ + if (WDL_NOT_NORMALLY(!image || !list)) return -1; + WDL_PtrList *l=(WDL_PtrList *)list; + + HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image; + if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1; + + HGDIOBJ__* icon=GDP_OBJECT_NEW(); + icon->type=TYPE_BITMAP; + icon->wid=1; + icon->bitmapptr = imgsrc->bitmapptr; // no need to duplicate it, can just retain a copy + [icon->bitmapptr retain]; + image = (HICON) icon; + + if (offset<0||offset>=l->GetSize()) + { + l->Add(image); + offset=l->GetSize()-1; + } + else + { + HICON old=l->Get(offset); + l->Set(offset,image); + if (old) DeleteObject(old); + } + return offset; +} + +int ImageList_Add(HIMAGELIST list, HBITMAP image, HBITMAP mask) +{ + if (WDL_NOT_NORMALLY(!image || !list)) return -1; + WDL_PtrList *l=(WDL_PtrList *)list; + + HGDIOBJ__ *imgsrc = (HGDIOBJ__*)image; + if (!HGDIOBJ_VALID(imgsrc,TYPE_BITMAP)) return -1; + + HGDIOBJ__* icon=GDP_OBJECT_NEW(); + icon->type=TYPE_BITMAP; + icon->wid=1; + NSImage *nsimg = [imgsrc->bitmapptr copy]; // caller still owns the image + [nsimg setFlipped:YES]; + icon->bitmapptr = nsimg; + image = (HICON) icon; + + l->Add(image); + return l->GetSize(); +} + +int AddFontResourceEx(LPCTSTR str, DWORD fl, void *pdv) +{ + if (SWELL_GDI_GetOSXVersion() < 0x1060) return 0; + static bool l; + static bool (*_CTFontManagerRegisterFontsForURL)( CFURLRef fontURL, uint32_t scope, CFErrorRef *error ); + if (!l) + { + CFBundleRef b = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreText")); + if (b) + { + *(void **)&_CTFontManagerRegisterFontsForURL = CFBundleGetFunctionPointerForName(b,CFSTR("CTFontManagerRegisterFontsForURL")); + } + + l=true; + } + + if (!_CTFontManagerRegisterFontsForURL) return 0; + + CFStringRef s=(CFStringRef)CStringToNSString(str); + + CFURLRef r=CFURLCreateWithFileSystemPath(NULL,s,kCFURLPOSIXPathStyle,true); + CFErrorRef err=NULL; + const int v = _CTFontManagerRegisterFontsForURL(r, + (fl & FR_PRIVATE) ? 1/*kCTFontManagerScopeProcess*/ : 2/*kCTFontManagerScopeUser*/, + &err)?1:0; + + // release err? don't think so + + CFRelease(s); + CFRelease(r); + return v; +} + +bool SWELL_osx_is_dark_mode(int mode) // mode=0 for enabled, 1=allowed +{ + static char c; + if (!c) + { + NSUserDefaults *def = SWELL_GetOSXVersion() >= 0x10d0 ? [NSUserDefaults standardUserDefaults] : NULL; + c = (def && [def objectForKey:@"NSRequiresAquaSystemAppearance"] && [def boolForKey:@"NSRequiresAquaSystemAppearance"] == NO) ? 1 : -1; + } + if (c<0) return false; + if (mode == 1) return true; + + return [[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"] isEqualToString:@"Dark"]; +} + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-generic-gdk.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-generic-gdk.cpp new file mode 100644 index 000000000..6f88edfbb --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-generic-gdk.cpp @@ -0,0 +1,2949 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + */ + + +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" + +//#define SWELL_GDK_IMPROVE_WINDOWRECT // does not work yet (gdk_window_get_frame_extents() does not seem to be sufficiently reliable) + +#ifdef SWELL_PRELOAD +#define STR(x) #x +#define STR2(x) STR(x) +extern "C" { + char __attribute__ ((visibility ("default"))) SWELL_WANT_LOAD_LIBRARY[] = STR2(SWELL_PRELOAD); +}; +#undef STR +#undef STR2 +#endif + +#ifdef SWELL_TARGET_GDK + +#include "swell-internal.h" +#include "swell-dlggen.h" +#include "../wdlcstring.h" +#include "../wdlutf8.h" + + +#if !defined(SWELL_TARGET_GDK_NO_CURSOR_HACK) + #define SWELL_TARGET_GDK_CURSORHACK + #include +#endif + +#include + +#include +#include + +static void (*_gdk_drag_drop_done)(GdkDragContext *, gboolean); // may not always be available + +static guint32 _gdk_x11_window_get_desktop(GdkWindow *window) +{ + Atom type; + gint format; + gulong nitems=0, bytes_after; + guchar *data; + + if (!window || !gdk_x11_screen_supports_net_wm_hint(gdk_window_get_screen(window), + gdk_atom_intern_static_string("_NET_WM_DESKTOP"))) + return 0; + + XGetWindowProperty(GDK_WINDOW_XDISPLAY(window), GDK_WINDOW_XID(window), + gdk_x11_get_xatom_by_name_for_display(gdk_window_get_display(window), "_NET_WM_DESKTOP"), + 0, G_MAXLONG, false, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &data); + if (type != XA_CARDINAL || nitems<1) return 0; + nitems = *(gulong *)data; + XFree(data); + return (guint32) nitems; +} + +static void _gdk_x11_window_move_to_desktop(GdkWindow *window, guint32 desktop) +{ + XClientMessageEvent xclient; + + if (!window || !gdk_x11_screen_supports_net_wm_hint(gdk_window_get_screen(window), + gdk_atom_intern_static_string("_NET_WM_DESKTOP"))) + return; + + memset (&xclient, 0, sizeof (xclient)); + xclient.type = ClientMessage; + xclient.send_event = true; + xclient.window = GDK_WINDOW_XID(window); + xclient.message_type = gdk_x11_get_xatom_by_name_for_display(gdk_window_get_display(window), "_NET_WM_DESKTOP"); + xclient.format = 32; + xclient.data.l[0] = desktop; + xclient.data.l[1] = 1; + + XSendEvent(GDK_WINDOW_XDISPLAY(window), gdk_x11_get_default_root_xwindow(), false, + SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xclient); +} + +// for m_oswindow_private +#define PRIVATE_NEEDSHOW 1 + +#ifndef SWELL_WINDOWSKEY_GDK_MASK +#define SWELL_WINDOWSKEY_GDK_MASK GDK_MOD4_MASK +#endif + +static int SWELL_gdk_active; +static GdkEvent *s_cur_evt; +static GList *s_program_icon_list; + +static SWELL_OSWINDOW swell_dragsrc_osw; +static DWORD swell_dragsrc_timeout_start; +static HWND swell_dragsrc_hwnd; +static DWORD swell_lastMessagePos; +static int gdk_options; +#define OPTION_KEEP_OWNED_ABOVE 1 +#define OPTION_OWNED_TASKLIST 2 +#define OPTION_BORDERLESS_OVERRIDEREDIRECT 4 +#define OPTION_BORDERLESS_DIALOG 8 +#define OPTION_ALLOW_MAYBE_INACTIVE 16 +#define OPTION_FULLSCREEN_FOR_OWNER_WINDOWS 32 +#define OPTION_FULLSCREEN_DYNAMIC 64 + +static HWND s_ddrop_hwnd; +static POINT s_ddrop_pt; + +static SWELL_CursorResourceIndex *SWELL_curmodule_cursorresource_head; + +static int s_cursor_vis_cnt; + +static HCURSOR s_last_cursor; +static HCURSOR s_last_setcursor; +static SWELL_OSWINDOW s_last_setcursor_oswnd; + +static void *g_swell_touchptr; // last GDK touch sequence +static void *g_swell_touchptr_wnd; // last window of touch sequence, for forcing end of sequence on destroy + +static bool g_swell_mouse_relmode; +static int g_swell_mouse_relmode_curpos_x; +static int g_swell_mouse_relmode_curpos_y; + +static HANDLE s_clipboard_getstate, s_clipboard_setstate; +static GdkAtom s_clipboard_getstate_fmt, s_clipboard_setstate_fmt; + +static WDL_IntKeyedArray m_clip_recs(GlobalFree); +static WDL_PtrList m_clip_curfmts; +static HWND s_clip_hwnd; + +static void swell_gdkEventHandler(GdkEvent *event, gpointer data); + +static int s_last_desktop; +static UINT_PTR s_deactivate_timer; +static guint32 s_force_window_time; +static bool swell_app_is_inactive; + +int swell_is_app_inactive() +{ + return swell_app_is_inactive ? 1 : (gdk_options&OPTION_ALLOW_MAYBE_INACTIVE) && s_deactivate_timer!=0 ? -1 : 0; +} + +static void update_menubar_activations() +{ + if (g_swell_ctheme.menubar_bg == g_swell_ctheme.menubar_bg_inactive && + g_swell_ctheme.menubar_text == g_swell_ctheme.menubar_text_inactive) return; + + HWND h = SWELL_topwindows; + while (h) + { + if (h->m_oswindow && h->m_menu) + { + DrawMenuBar(h); + } + h=h->m_next; + } +} + +static void on_activate(guint32 ftime) +{ + s_force_window_time = ftime; + swell_app_is_inactive=false; + HWND h = SWELL_topwindows; + while (h) + { + if (h->m_oswindow) + { + if (h->m_israised) + gdk_window_set_keep_above(h->m_oswindow,TRUE); + + if (!h->m_enabled) + gdk_window_set_accept_focus(h->m_oswindow,FALSE); + } + + PostMessage(h,WM_ACTIVATEAPP,1,0); + h=h->m_next; + } + s_last_desktop=0; + s_force_window_time = 0; + + update_menubar_activations(); +} + +void swell_gdk_reactivate_app(void) +{ + if (swell_app_is_inactive) + { + SWELL_focused_oswindow=NULL; + on_activate(GDK_CURRENT_TIME); + } +} + +static void on_deactivate() +{ + swell_app_is_inactive=true; + HWND lf = swell_oswindow_to_hwnd(SWELL_focused_oswindow); + s_last_desktop = lf && lf->m_oswindow ? _gdk_x11_window_get_desktop(lf->m_oswindow)+1 : 0; + + HWND h = SWELL_topwindows; + while (h) + { + if (h->m_oswindow) + { + if (h->m_israised) + gdk_window_set_keep_above(h->m_oswindow,FALSE); + if (!h->m_enabled) + gdk_window_set_accept_focus(h->m_oswindow,TRUE); // allow the user to activate app by clicking + } + PostMessage(h,WM_ACTIVATEAPP,0,0); + h=h->m_next; + } + DestroyPopupMenus(); +} + +void swell_oswindow_destroy(HWND hwnd) +{ + if (hwnd && hwnd->m_oswindow) + { + if (SWELL_focused_oswindow == hwnd->m_oswindow) SWELL_focused_oswindow = NULL; + if (g_swell_touchptr && g_swell_touchptr_wnd == hwnd->m_oswindow) + g_swell_touchptr = NULL; + gdk_window_destroy(hwnd->m_oswindow); + hwnd->m_oswindow=NULL; +#ifdef SWELL_LICE_GDI + delete hwnd->m_backingstore; + hwnd->m_backingstore=0; +#endif + + if (swell_app_is_inactive) + { + HWND h = SWELL_topwindows; + while (h) + { + if (h->m_oswindow) break; + h = h->m_next; + } + if (!h) on_activate(10); // arbitrary old timestamp that is nonzero + } + } +} +void swell_oswindow_update_text(HWND hwnd) +{ + if (hwnd && hwnd->m_oswindow) + { + gdk_window_set_title(hwnd->m_oswindow, (char*)hwnd->m_title.Get()); + } +} + +void swell_oswindow_focus(HWND hwnd) +{ + if (!hwnd) + { + SWELL_focused_oswindow = NULL; + update_menubar_activations(); + return; + } + + while (hwnd && !hwnd->m_oswindow) hwnd=hwnd->m_parent; + if (hwnd && !swell_app_is_inactive) + { + gdk_window_raise(hwnd->m_oswindow); + if (hwnd->m_oswindow != SWELL_focused_oswindow) + { + SWELL_focused_oswindow = hwnd->m_oswindow; + gdk_window_focus(hwnd->m_oswindow,GDK_CURRENT_TIME); + update_menubar_activations(); + } + } +} + +void swell_recalcMinMaxInfo(HWND hwnd) +{ + if (!hwnd || !hwnd->m_oswindow || !(hwnd->m_style & WS_CAPTION)) return; + + MINMAXINFO mmi; + memset(&mmi,0,sizeof(mmi)); + if (hwnd->m_style & WS_THICKFRAME) + { + mmi.ptMinTrackSize.x = 20; + mmi.ptMaxSize.x = mmi.ptMaxTrackSize.x = 16384; + mmi.ptMinTrackSize.y = 20; + mmi.ptMaxSize.y = mmi.ptMaxTrackSize.y = 16384; + SendMessage(hwnd,WM_GETMINMAXINFO,0,(LPARAM)&mmi); + } + else + { + RECT r=hwnd->m_position; + mmi.ptMinTrackSize.x = mmi.ptMaxSize.x = mmi.ptMaxTrackSize.x = r.right-r.left; + mmi.ptMinTrackSize.y = mmi.ptMaxSize.y = mmi.ptMaxTrackSize.y = r.bottom-r.top; + } + + GdkGeometry h; + memset(&h,0,sizeof(h)); + h.max_width= mmi.ptMaxSize.x; + h.max_height= mmi.ptMaxSize.y; + h.min_width= mmi.ptMinTrackSize.x; + h.min_height= mmi.ptMinTrackSize.y; + gdk_window_set_geometry_hints(hwnd->m_oswindow,&h,(GdkWindowHints) (GDK_HINT_POS | GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE)); +} + +void SWELL_initargs(int *argc, char ***argv) +{ + if (!SWELL_gdk_active) + { + XInitThreads(); +#if SWELL_TARGET_GDK == 3 + void (*_gdk_set_allowed_backends)(const char *); + + *(void **)&_gdk_drag_drop_done = dlsym(RTLD_DEFAULT,"gdk_drag_drop_done"); + *(void **)&_gdk_set_allowed_backends = dlsym(RTLD_DEFAULT,"gdk_set_allowed_backends"); + + if (_gdk_set_allowed_backends) + _gdk_set_allowed_backends("x11"); +#endif + +#ifdef SWELL_SUPPORT_GTK + SWELL_gdk_active = gtk_init_check(argc,argv) ? 1 : -1; +#else + SWELL_gdk_active = gdk_init_check(argc,argv) ? 1 : -1; +#endif + if (SWELL_gdk_active > 0) + { + char buf[1024]; + GetModuleFileName(NULL,buf,sizeof(buf)); + WDL_remove_filepart(buf); + lstrcatn(buf,"/Resources/main.png",sizeof(buf)); + GdkPixbuf *pb = gdk_pixbuf_new_from_file(buf,NULL); + if (!pb) + { + strcpy(buf+strlen(buf)-3,"ico"); + pb = gdk_pixbuf_new_from_file(buf,NULL); + } + if (pb) s_program_icon_list = g_list_append(s_program_icon_list,pb); + + gdk_event_handler_set(swell_gdkEventHandler,NULL,NULL); + } + } +} + +static bool swell_initwindowsys() +{ + if (!SWELL_gdk_active) + { + // maybe make the main app call this with real parms + int argc=1; + char buf[32]; + strcpy(buf,"blah"); + char *argv[2]; + argv[0] = buf; + argv[1] = buf; + char **pargv = argv; + SWELL_initargs(&argc,&pargv); + } + + return SWELL_gdk_active>0; +} + +#ifdef SWELL_LICE_GDI +class LICE_CairoBitmap : public LICE_IBitmap +{ + public: + LICE_CairoBitmap() + { + m_fb = NULL; + m_allocsize = m_width = m_height = m_span = 0; + m_surf = NULL; + } + virtual ~LICE_CairoBitmap() + { + if (m_surf) cairo_surface_destroy(m_surf); + free(m_fb); + } + + // LICE_IBitmap interface + virtual LICE_pixel *getBits() + { + const UINT_PTR extra=LICE_MEMBITMAP_ALIGNAMT; + return (LICE_pixel *) (((UINT_PTR)m_fb + extra)&~extra); + } + + virtual int getWidth() { return m_width; } + virtual int getHeight() { return m_height; } + virtual int getRowSpan() { return m_span; } + virtual bool resize(int w, int h) + { + if (w<0) w=0; + if (h<0) h=0; + if (w == m_width && h == m_height) return false; + + if (m_surf) cairo_surface_destroy(m_surf); + m_surf = NULL; + + m_span = w ? cairo_format_stride_for_width(CAIRO_FORMAT_RGB24,w)/4 : 0; + const int sz = h * m_span * 4 + LICE_MEMBITMAP_ALIGNAMT; + if (!m_fb || m_allocsize < sz || sz < m_allocsize/4) + { + const int newalloc = m_allocsize= '0' && buf[0] <= '9') return atoi(buf); + return defv; +} + +static void init_options() +{ + if (!gdk_options) + { + gdk_options = 0x40000000; + + if (swell_gdk_option("gdk_owned_windows_keep_above", "auto (default is 1)",1)) + gdk_options|=OPTION_KEEP_OWNED_ABOVE; + + if (swell_gdk_option("gdk_owned_windows_in_tasklist", "auto (default is 0)",0)) + gdk_options|=OPTION_OWNED_TASKLIST; + + switch (swell_gdk_option("gdk_instant_menubar_inactivation", "auto (default is 1 if on Wayland, otherwise 0)",-1)) + { + case -1: + if (getenv("WAYLAND_DISPLAY") == NULL) break; + // fall through + case 1: + gdk_options|=OPTION_ALLOW_MAYBE_INACTIVE; + break; + } + + switch (swell_gdk_option("gdk_borderless_window_mode", "auto (default is 1=dialog hint. 2=override redirect. 0=normal hint)", 1)) + { + case 1: gdk_options|=OPTION_BORDERLESS_DIALOG; break; + case 2: gdk_options|=OPTION_BORDERLESS_OVERRIDEREDIRECT; break; + default: break; + } + + const char *wmname = gdk_x11_screen_get_window_manager_name(gdk_screen_get_default()); + switch (swell_gdk_option("gdk_fullscreen_for_owner_windows", "auto (default is 1 on kwin, otherwise 0)",-1)) + { + case -1: + if (!wmname || strnicmp(wmname,"KWin",4)) break; + // fall through + case 1: + gdk_options |= OPTION_FULLSCREEN_FOR_OWNER_WINDOWS; + break; + } + switch (swell_gdk_option("gdk_fullscreen_dynamic","auto (default is 1 on kwin, otherwise 0)",-1)) + { + case -1: + if (!wmname || strnicmp(wmname,"KWin",4)) break; + // fall through + case 1: + gdk_options |= OPTION_FULLSCREEN_DYNAMIC; + break; + + } + } + +} + +static void swell_hide_owned_windows_transient(HWND hwnd) +{ + if ((gdk_options&OPTION_KEEP_OWNED_ABOVE) && hwnd->m_owned_list) + { + HWND l = SWELL_topwindows; + while (l) + { + if (l->m_oswindow && l->m_owner == hwnd && l->m_visible) + gdk_window_hide(l->m_oswindow); + l = l->m_next; + } + } +} + +static void swell_set_owned_windows_transient(HWND hwnd, bool do_create) +{ + if ((gdk_options&OPTION_KEEP_OWNED_ABOVE) && hwnd->m_owned_list) + { + WDL_PtrList raiselist; + HWND l = SWELL_topwindows; + while (l) + { + if (l->m_owner == hwnd && l->m_visible) + { + if (l->m_oswindow) raiselist.Add(l->m_oswindow); + else if (do_create) swell_oswindow_manage(l,false); + } + l = l->m_next; + } + for (int x = raiselist.GetSize()-1; x>=0; x--) + { + SWELL_OSWINDOW r = (SWELL_OSWINDOW)raiselist.Get(x); + gdk_window_set_transient_for(r,hwnd->m_oswindow); + gdk_window_show_unraised(r); + } + } +} + + + +bool IsModalDialogBox(HWND); + +void swell_oswindow_manage(HWND hwnd, bool wantfocus) +{ + if (!hwnd) return; + + bool isVis = hwnd->m_oswindow != NULL; + bool wantVis = !hwnd->m_parent && hwnd->m_visible; + + if (isVis != wantVis) + { + if (!wantVis) + { + RECT r; + GetWindowRect(hwnd,&r); + swell_hide_owned_windows_transient(hwnd); + swell_oswindow_destroy(hwnd); + hwnd->m_position = r; + } + else + { + if (swell_initwindowsys()) + { + init_options(); + + SWELL_OSWINDOW transient_for=NULL; + if (hwnd->m_owner && (gdk_options&OPTION_KEEP_OWNED_ABOVE)) + { + HWND own = hwnd->m_owner; + while (own->m_parent && !own->m_oswindow) own=own->m_parent; + + if (!own->m_oswindow) + { + if (!IsModalDialogBox(hwnd)) return; // defer + + // if a modal window, parent to any owner up the chain + while (own->m_owner && !own->m_oswindow) + { + own = own->m_owner; + while (own->m_parent && !own->m_oswindow) own=own->m_parent; + } + } + transient_for = own->m_oswindow; + } + + RECT r = hwnd->m_position; + GdkWindowAttr attr={0,}; + attr.title = (char *)hwnd->m_title.Get(); + attr.event_mask = GDK_ALL_EVENTS_MASK|GDK_EXPOSURE_MASK; + attr.x = r.left; + attr.y = r.top; + attr.width = r.right-r.left; + attr.height = r.bottom-r.top; + attr.wclass = GDK_INPUT_OUTPUT; + const char *appname = g_swell_appname; + attr.wmclass_name = (gchar*)appname; + attr.wmclass_class = (gchar*)appname; + attr.window_type = GDK_WINDOW_TOPLEVEL; + hwnd->m_oswindow = gdk_window_new(NULL,&attr,GDK_WA_X|GDK_WA_Y|(appname?GDK_WA_WMCLASS:0)); + + if (hwnd->m_oswindow) + { + bool override_redirect=false; + const bool modal = DialogBoxIsActive() == hwnd; + + if (!(hwnd->m_style & WS_CAPTION)) + { + if (hwnd->m_style != WS_CHILD && !(gdk_options&OPTION_BORDERLESS_OVERRIDEREDIRECT)) + { + if (transient_for) + gdk_window_set_transient_for(hwnd->m_oswindow,transient_for); + gdk_window_set_type_hint(hwnd->m_oswindow, (gdk_options&OPTION_BORDERLESS_DIALOG) ? GDK_WINDOW_TYPE_HINT_DIALOG : GDK_WINDOW_TYPE_HINT_NORMAL); + gdk_window_set_decorations(hwnd->m_oswindow,(GdkWMDecoration) 0); + } + else + { + gdk_window_set_override_redirect(hwnd->m_oswindow,true); + override_redirect=true; + } + if (!SWELL_topwindows || + (SWELL_topwindows==hwnd && !hwnd->m_next)) wantfocus=true; + } + else + { + GdkWindowTypeHint type_hint = GDK_WINDOW_TYPE_HINT_NORMAL; + GdkWMDecoration decor = (GdkWMDecoration) (GDK_DECOR_ALL | GDK_DECOR_MENU); + + if (!(hwnd->m_style&WS_THICKFRAME)) + decor = (GdkWMDecoration) (GDK_DECOR_BORDER|GDK_DECOR_TITLE|GDK_DECOR_MINIMIZE); + + if (transient_for) + { + gdk_window_set_transient_for(hwnd->m_oswindow,transient_for); + if (modal) + gdk_window_set_modal_hint(hwnd->m_oswindow,true); + } + + if (modal) type_hint = GDK_WINDOW_TYPE_HINT_DIALOG; + + gdk_window_set_type_hint(hwnd->m_oswindow,type_hint); + gdk_window_set_decorations(hwnd->m_oswindow,decor); + } + + if (s_force_window_time) + gdk_x11_window_set_user_time(hwnd->m_oswindow,s_force_window_time); + + if (!wantfocus || swell_app_is_inactive) + gdk_window_set_focus_on_map(hwnd->m_oswindow,false); + +#ifdef SWELL_LICE_GDI + if (!hwnd->m_backingstore) hwnd->m_backingstore = new LICE_CairoBitmap; +#endif + if (!override_redirect) + { + if (s_program_icon_list) + gdk_window_set_icon_list(hwnd->m_oswindow,s_program_icon_list); + } + if (hwnd->m_owner && !(gdk_options&OPTION_OWNED_TASKLIST) && !override_redirect) + { + gdk_window_set_skip_taskbar_hint(hwnd->m_oswindow,true); + } + else if (hwnd->m_style == WS_CHILD) + { + // hack: parentless visible window with WS_CHILD set will + // not appear in taskbar + gdk_window_set_skip_taskbar_hint(hwnd->m_oswindow,true); + } + + if (hwnd->m_israised && !swell_app_is_inactive) + gdk_window_set_keep_above(hwnd->m_oswindow,TRUE); + + gdk_window_register_dnd(hwnd->m_oswindow); + + if (hwnd->m_oswindow_fullscreen) + gdk_window_fullscreen(hwnd->m_oswindow); + + if (!swell_app_is_inactive && !s_force_window_time) + gdk_window_show(hwnd->m_oswindow); + else + gdk_window_show_unraised(hwnd->m_oswindow); + + if (s_last_desktop>0) + _gdk_x11_window_move_to_desktop(hwnd->m_oswindow,s_last_desktop-1); + + if (!hwnd->m_oswindow_fullscreen) + { + swell_oswindow_resize(hwnd->m_oswindow,hwnd->m_has_had_position?3:2,r); + } + + swell_set_owned_windows_transient(hwnd, true); + } + } + } + } + if (wantVis) swell_oswindow_update_text(hwnd); +} + +void swell_oswindow_updatetoscreen(HWND hwnd, RECT *rect) +{ +#ifdef SWELL_LICE_GDI + if (hwnd && hwnd->m_backingstore && hwnd->m_oswindow) + { + LICE_IBitmap *bm = hwnd->m_backingstore; + LICE_SubBitmap tmpbm(bm,rect->left,rect->top,rect->right-rect->left,rect->bottom-rect->top); + + GdkRectangle rrr={rect->left,rect->top,rect->right-rect->left,rect->bottom-rect->top}; + gdk_window_begin_paint_rect(hwnd->m_oswindow, &rrr); + + cairo_t * crc = gdk_cairo_create (hwnd->m_oswindow); + cairo_surface_t *temp_surface = (cairo_surface_t*)bm->Extended(0xca140,NULL); + if (temp_surface) cairo_set_source_surface(crc, temp_surface, 0,0); + cairo_paint(crc); + cairo_destroy(crc); + + gdk_window_end_paint(hwnd->m_oswindow); + + if (temp_surface) bm->Extended(0xca140,temp_surface); // release + + } +#endif +} + +#if SWELL_TARGET_GDK == 2 + #define DEF_GKY(x) GDK_##x +#else + #define DEF_GKY(x) GDK_KEY_##x +#endif + +static guint swell_gdkConvertKey(guint key, bool *extended) +{ + switch(key) + { +#define DEF_GK2(h, v) \ + case DEF_GKY(h): *extended = true; return (v); \ + case DEF_GKY(KP_##h): return (v); + + DEF_GK2(Home,VK_HOME) + DEF_GK2(End,VK_END) + DEF_GK2(Up, VK_UP) + DEF_GK2(Down, VK_DOWN) + DEF_GK2(Left, VK_LEFT) + DEF_GK2(Right, VK_RIGHT) + DEF_GK2(Page_Up, VK_PRIOR) + DEF_GK2(Page_Down, VK_NEXT) + DEF_GK2(Insert,VK_INSERT) + DEF_GK2(Delete, VK_DELETE) + +#undef DEF_GK2 + case DEF_GKY(KP_Enter): *extended = true; return VK_RETURN; + case DEF_GKY(Return): return VK_RETURN; + + case DEF_GKY(Escape): return VK_ESCAPE; + case DEF_GKY(BackSpace): return VK_BACK; + case DEF_GKY(ISO_Left_Tab): + case DEF_GKY(Tab): return VK_TAB; + case DEF_GKY(F1): return VK_F1; + case DEF_GKY(F2): return VK_F2; + case DEF_GKY(F3): return VK_F3; + case DEF_GKY(F4): return VK_F4; + case DEF_GKY(F5): return VK_F5; + case DEF_GKY(F6): return VK_F6; + case DEF_GKY(F7): return VK_F7; + case DEF_GKY(F8): return VK_F8; + case DEF_GKY(F9): return VK_F9; + case DEF_GKY(F10): return VK_F10; + case DEF_GKY(F11): return VK_F11; + case DEF_GKY(F12): return VK_F12; + case DEF_GKY(KP_0): return VK_NUMPAD0; + case DEF_GKY(KP_1): return VK_NUMPAD1; + case DEF_GKY(KP_2): return VK_NUMPAD2; + case DEF_GKY(KP_3): return VK_NUMPAD3; + case DEF_GKY(KP_4): return VK_NUMPAD4; + case DEF_GKY(KP_5): return VK_NUMPAD5; + case DEF_GKY(KP_6): return VK_NUMPAD6; + case DEF_GKY(KP_7): return VK_NUMPAD7; + case DEF_GKY(KP_8): return VK_NUMPAD8; + case DEF_GKY(KP_9): return VK_NUMPAD9; + case DEF_GKY(KP_Multiply): return VK_MULTIPLY; + case DEF_GKY(KP_Add): return VK_ADD; + case DEF_GKY(KP_Separator): return VK_SEPARATOR; + case DEF_GKY(KP_Subtract): return VK_SUBTRACT; + case DEF_GKY(KP_Decimal): return VK_DECIMAL; + case DEF_GKY(KP_Divide): return VK_DIVIDE; + case DEF_GKY(Num_Lock): return VK_NUMLOCK; + case DEF_GKY(KP_Begin): return VK_SELECT; + } + return 0; +} + +static LRESULT SendMouseMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (!hwnd || !hwnd->m_wndproc) return -1; + if (!IsWindowEnabled(hwnd)) + { + if (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN || + msg == WM_LBUTTONDBLCLK || msg == WM_RBUTTONDBLCLK || msg == WM_MBUTTONDBLCLK) + { + HWND h = DialogBoxIsActive(); + if (h) SetForegroundWindow(h); + } + return -1; + } + + LRESULT htc=0; + if (msg != WM_MOUSEWHEEL && !GetCapture()) + { + DWORD p=GetMessagePos(); + + htc=hwnd->m_wndproc(hwnd,WM_NCHITTEST,0,p); + if (hwnd->m_hashaddestroy||!hwnd->m_wndproc) + { + return -1; // if somehow WM_NCHITTEST destroyed us, bail + } + + if (htc!=HTCLIENT || swell_window_wants_all_input() == hwnd) + { + if (msg==WM_MOUSEMOVE) return hwnd->m_wndproc(hwnd,WM_NCMOUSEMOVE,htc,p); +// if (msg==WM_MOUSEWHEEL) return hwnd->m_wndproc(hwnd,WM_NCMOUSEWHEEL,htc,p); +// if (msg==WM_MOUSEHWHEEL) return hwnd->m_wndproc(hwnd,WM_NCMOUSEHWHEEL,htc,p); + if (msg==WM_LBUTTONUP) return hwnd->m_wndproc(hwnd,WM_NCLBUTTONUP,htc,p); + if (msg==WM_LBUTTONDOWN) return hwnd->m_wndproc(hwnd,WM_NCLBUTTONDOWN,htc,p); + if (msg==WM_LBUTTONDBLCLK) return hwnd->m_wndproc(hwnd,WM_NCLBUTTONDBLCLK,htc,p); + if (msg==WM_RBUTTONUP) return hwnd->m_wndproc(hwnd,WM_NCRBUTTONUP,htc,p); + if (msg==WM_RBUTTONDOWN) return hwnd->m_wndproc(hwnd,WM_NCRBUTTONDOWN,htc,p); + if (msg==WM_RBUTTONDBLCLK) return hwnd->m_wndproc(hwnd,WM_NCRBUTTONDBLCLK,htc,p); + if (msg==WM_MBUTTONUP) return hwnd->m_wndproc(hwnd,WM_NCMBUTTONUP,htc,p); + if (msg==WM_MBUTTONDOWN) return hwnd->m_wndproc(hwnd,WM_NCMBUTTONDOWN,htc,p); + if (msg==WM_MBUTTONDBLCLK) return hwnd->m_wndproc(hwnd,WM_NCMBUTTONDBLCLK,htc,p); + } + } + + + LRESULT ret=hwnd->m_wndproc(hwnd,msg,wParam,lParam); + + if (msg==WM_LBUTTONUP || msg==WM_RBUTTONUP || msg==WM_MOUSEMOVE || msg==WM_MBUTTONUP) + { + if (!GetCapture() && (hwnd->m_hashaddestroy || !hwnd->m_wndproc || !hwnd->m_wndproc(hwnd,WM_SETCURSOR,(WPARAM)hwnd,htc | (msg<<16)))) + { + SetCursor(SWELL_LoadCursor(IDC_ARROW)); + } + } + + return ret; +} + +static int hex_parse(char c) +{ + if (c >= '0' && c <= '9') return c-'0'; + if (c >= 'A' && c <= 'F') return 10+c-'A'; + if (c >= 'a' && c <= 'f') return 10+c-'a'; + return -1; +} + +static GdkAtom utf8atom() +{ + static GdkAtom tmp; + if (!tmp) tmp = gdk_atom_intern_static_string("UTF8_STRING"); + return tmp; +} +static GdkAtom tgtatom() +{ + static GdkAtom tmp; + if (!tmp) tmp = gdk_atom_intern_static_string("TARGETS"); + return tmp; +} +static GdkAtom urilistatom() +{ + static GdkAtom tmp; + if (!tmp) tmp = gdk_atom_intern_static_string("text/uri-list"); + return tmp; +} + + +static void OnSelectionRequestEvent(GdkEventSelection *b) +{ + //printf("got sel req %s\n",gdk_atom_name(b->target)); + GdkAtom prop=GDK_NONE; + + if (swell_dragsrc_osw && b->window == swell_dragsrc_osw) + { + if (swell_dragsrc_hwnd) + { + if (b->target == tgtatom()) + { + prop = b->property; + GdkAtom list[] = { urilistatom() }; +#if SWELL_TARGET_GDK == 2 + GdkWindow *pw = gdk_window_lookup(b->requestor); + if (!pw) pw = gdk_window_foreign_new(b->requestor); +#else + GdkWindow *pw = b->requestor; +#endif + if (pw) + gdk_property_change(pw,prop,GDK_SELECTION_TYPE_ATOM,32, GDK_PROP_MODE_REPLACE,(guchar*)list,(int) (sizeof(list)/sizeof(list[0]))); + } + SendMessage(swell_dragsrc_hwnd,WM_USER+100,(WPARAM)b,(LPARAM)&prop); + } + } + else if (s_clipboard_setstate) + { + if (b->target == tgtatom()) + { + if (s_clipboard_setstate_fmt) + { + prop = b->property; + GdkAtom list[] = { s_clipboard_setstate_fmt }; +#if SWELL_TARGET_GDK == 2 + GdkWindow *pw = gdk_window_lookup(b->requestor); + if (!pw) pw = gdk_window_foreign_new(b->requestor); +#else + GdkWindow *pw = b->requestor; +#endif + if (pw) + gdk_property_change(pw,prop,GDK_SELECTION_TYPE_ATOM,32, GDK_PROP_MODE_REPLACE,(guchar*)list,(int) (sizeof(list)/sizeof(list[0]))); + } + } + else + { + if (b->target == s_clipboard_setstate_fmt || + (b->target == GDK_TARGET_STRING && s_clipboard_setstate_fmt == utf8atom()) + ) + { + prop = b->property; + int len = GlobalSize(s_clipboard_setstate); + guchar *ptr = (guchar*)s_clipboard_setstate; + + WDL_FastString str; + if (s_clipboard_setstate_fmt == utf8atom()) + { + const char *rd = (const char *)s_clipboard_setstate; + while (*rd) + { + if (!strncmp(rd,"\r\n",2)) + { + str.Append("\n"); + rd+=2; + } + else + str.Append(rd++,1); + } + ptr = (guchar *)str.Get(); + len = str.GetLength(); + } + else if (s_clipboard_setstate_fmt == urilistatom()) + { + if (len > (int)sizeof(DROPFILES)) + { + DROPFILES *hdr = (DROPFILES *)ptr; + if (WDL_NORMALLY(hdr->pFiles < (DWORD)len) && + WDL_NORMALLY(!hdr->fWide) // todo deal with UTF-16 + ) + { + const char *rd = (const char *)ptr; + DWORD rdo = hdr->pFiles; + while (rdo < (DWORD)len && rd[rdo]) + { + const char *fn = rd + rdo; + rdo += strlen(rd+rdo)+1; + str.Append("file://"); + while (*fn) + { + if (isalnum(*fn) || *fn == '.' || *fn == '_' || *fn == '-' || *fn == '/' || *fn == '#') + str.Append(fn,1); + else + str.AppendFormatted(8,"%%%02x",*(unsigned char *)fn); + fn++; + } + str.Append("\r\n"); + } + } + } + + ptr = (guchar *)str.Get(); + len = str.GetLength(); + } + +#if SWELL_TARGET_GDK == 2 + GdkWindow *pw = gdk_window_lookup(b->requestor); + if (!pw) pw = gdk_window_foreign_new(b->requestor); +#else + GdkWindow *pw = b->requestor; +#endif + if (pw) + gdk_property_change(pw,prop,b->target,8, GDK_PROP_MODE_REPLACE,ptr,len); + } + } + } + gdk_selection_send_notify(b->requestor,b->selection,b->target,prop,GDK_CURRENT_TIME); +} + +static void OnExposeEvent(GdkEventExpose *exp) +{ + HWND hwnd = swell_oswindow_to_hwnd(exp->window); + if (!hwnd) return; + +#ifdef SWELL_LICE_GDI + RECT r,cr; + + // don't use GetClientRect(),since we're getting it pre-NCCALCSIZE etc + + cr.left=cr.top=0; + cr.right = hwnd->m_position.right - hwnd->m_position.left; + cr.bottom = hwnd->m_position.bottom - hwnd->m_position.top; + + r.left = exp->area.x; + r.top=exp->area.y; + r.bottom=r.top+exp->area.height; + r.right=r.left+exp->area.width; + + if (!hwnd->m_backingstore) hwnd->m_backingstore = new LICE_CairoBitmap; + + bool forceref = hwnd->m_backingstore->resize(cr.right-cr.left,cr.bottom-cr.top); + if (forceref) r = cr; + + LICE_SubBitmap tmpbm(hwnd->m_backingstore,r.left,r.top,r.right-r.left,r.bottom-r.top); + + if (tmpbm.getWidth()>0 && tmpbm.getHeight()>0) + { + void SWELL_internalLICEpaint(HWND hwnd, LICE_IBitmap *bmout, int bmout_xpos, int bmout_ypos, bool forceref); + SWELL_internalLICEpaint(hwnd, &tmpbm, r.left, r.top, forceref); + + GdkRectangle rrr={r.left,r.top,r.right-r.left,r.bottom-r.top}; + gdk_window_begin_paint_rect(exp->window, &rrr); + + cairo_t *crc = gdk_cairo_create (exp->window); + LICE_IBitmap *bm = hwnd->m_backingstore; + cairo_surface_t *temp_surface = (cairo_surface_t*)bm->Extended(0xca140,NULL); + if (temp_surface) cairo_set_source_surface(crc, temp_surface, 0,0); + cairo_paint(crc); + cairo_destroy(crc); + if (temp_surface) bm->Extended(0xca140,temp_surface); // release + + gdk_window_end_paint(exp->window); + } +#endif +} + +static void OnConfigureEvent(GdkEventConfigure *cfg) +{ + HWND hwnd = swell_oswindow_to_hwnd(cfg->window); + if (!hwnd) return; + int flag=0; + if (cfg->x != hwnd->m_position.left || + cfg->y != hwnd->m_position.top || + !hwnd->m_has_had_position) + { + flag|=1; + hwnd->m_has_had_position = true; + } + if (cfg->width != hwnd->m_position.right-hwnd->m_position.left || + cfg->height != hwnd->m_position.bottom - hwnd->m_position.top) flag|=2; + hwnd->m_position.left = cfg->x; + hwnd->m_position.top = cfg->y; + hwnd->m_position.right = cfg->x + cfg->width; + hwnd->m_position.bottom = cfg->y + cfg->height; + if (flag&1) SendMessage(hwnd,WM_MOVE,0,0); + if (flag&2) SendMessage(hwnd,WM_SIZE,0,0); + if (!hwnd->m_hashaddestroy && hwnd->m_oswindow) swell_recalcMinMaxInfo(hwnd); +} + +static void OnKeyEvent(GdkEventKey *k) +{ + HWND hwnd = swell_oswindow_to_hwnd(k->window); + if (!hwnd) return; + + int modifiers = 0; + if (k->state&GDK_CONTROL_MASK) modifiers|=FCONTROL; + if (k->state&GDK_MOD1_MASK) modifiers|=FALT; + if (k->state&SWELL_WINDOWSKEY_GDK_MASK) modifiers|=FLWIN; + if (k->state&GDK_SHIFT_MASK) modifiers|=FSHIFT; + + UINT msgtype = k->type == GDK_KEY_PRESS ? WM_KEYDOWN : WM_KEYUP; + + bool is_extended = false; + guint kv = swell_gdkConvertKey(k->keyval, &is_extended); + if (kv) + { + modifiers |= FVIRTKEY; + + if (modifiers & FSHIFT) + { + if (k->state&GDK_MOD2_MASK) // numlock on + { + // if shift+numpad home/end is sent while numlock is on, that means it should be treated + // as unmodified home/end + if (!is_extended && kv >= VK_PRIOR && kv <= VK_SELECT) + modifiers &= ~FSHIFT; + } + else + { + // if numpadX is sent while numlock is off, then it should be treated as unmodified + if (kv >= VK_NUMPAD0 && kv <= VK_NUMPAD9) + modifiers &= ~FSHIFT; + } + } + } + else + { + kv = k->keyval; + if (kv >= 'a' && kv <= 'z') + { + kv += 'A'-'a'; + swell_is_likely_capslock = (modifiers&FSHIFT)!=0; + modifiers |= FVIRTKEY; + } + else if (kv >= 'A' && kv <= 'Z') + { + swell_is_likely_capslock = (modifiers&FSHIFT)==0; + modifiers |= FVIRTKEY; + } + else if (kv >= '0' && kv <= '9') + { + modifiers |= FVIRTKEY; + } + else + { + if (kv >= DEF_GKY(Shift_L) || + (kv >= DEF_GKY(ISO_Lock) && + kv <= DEF_GKY(ISO_Last_Group_Lock)) + ) + { + if (kv == DEF_GKY(Shift_L) || kv == DEF_GKY(Shift_R)) kv = VK_SHIFT; + else if (kv == DEF_GKY(Control_L) || kv == DEF_GKY(Control_R)) kv = VK_CONTROL; + else if (kv == DEF_GKY(Alt_L) || kv == DEF_GKY(Alt_R)) kv = VK_MENU; + else if (kv == DEF_GKY(Super_L) || kv == DEF_GKY(Super_R)) kv = VK_LWIN; + else return; // unknown modifie key + + msgtype = k->type == GDK_KEY_PRESS ? WM_SYSKEYDOWN : WM_SYSKEYUP; + modifiers|=FVIRTKEY; + } + else if (kv > 255) + { + guint v = gdk_keyval_to_unicode(kv); + if (v) kv=v; + } + else + { + // treat as ASCII, clear shift flag (?) + modifiers &= ~FSHIFT; + } + } + } + + HWND foc = GetFocusIncludeMenus(); + if (foc && IsChild(hwnd,foc)) hwnd=foc; + else if (foc && foc->m_oswindow && !(foc->m_style&WS_CAPTION)) hwnd=foc; // for menus, event sent to other window due to gdk_window_set_override_redirect() + + if (is_extended) modifiers |= 1<<24; + + MSG msg = { hwnd, msgtype, kv, modifiers, }; + INT_PTR extra_flags = 0; + if (DialogBoxIsActive()) extra_flags |= 1; + if (SWELLAppMain(SWELLAPP_PROCESSMESSAGE,(INT_PTR)&msg,extra_flags)<=0) + SendMessage(hwnd, msg.message, kv, modifiers); +} + +static HWND getMouseTarget(SWELL_OSWINDOW osw, POINT p, const HWND *hwnd_has_osw) +{ + HWND hwnd = GetCapture(); + if (hwnd) return hwnd; + hwnd = hwnd_has_osw ? *hwnd_has_osw : swell_oswindow_to_hwnd(osw); + if (!hwnd || swell_window_wants_all_input() == hwnd) return hwnd; + return ChildWindowFromPoint(hwnd,p); +} + +static void OnMotionEvent(GdkEventMotion *m) +{ + swell_lastMessagePos = MAKELONG(((int)m->x_root&0xffff),((int)m->y_root&0xffff)); + POINT p={(int)m->x, (int)m->y}; + HWND hwnd = getMouseTarget(m->window,p,NULL); + + if (hwnd) + { + POINT p2={(int)m->x_root, (int)m->y_root}; + ScreenToClient(hwnd, &p2); + if (hwnd) hwnd->Retain(); + SendMouseMessage(hwnd, WM_MOUSEMOVE, 0, MAKELPARAM(p2.x, p2.y)); + if (hwnd) hwnd->Release(); + } +} + +static void OnScrollEvent(GdkEventScroll *b) +{ + swell_lastMessagePos = MAKELONG(((int)b->x_root&0xffff),((int)b->y_root&0xffff)); + POINT p={(int)b->x, (int)b->y}; + + HWND hwnd = getMouseTarget(b->window,p,NULL); + if (hwnd) + { + POINT p2={(int)b->x_root, (int)b->y_root}; + // p2 is screen coordinates for WM_MOUSEWHEEL + + int msg=(b->direction == GDK_SCROLL_UP || b->direction == GDK_SCROLL_DOWN) ? WM_MOUSEWHEEL : + (b->direction == GDK_SCROLL_LEFT || b->direction == GDK_SCROLL_RIGHT) ? WM_MOUSEHWHEEL : 0; + + if (msg) + { + int v = (b->direction == GDK_SCROLL_UP || b->direction == GDK_SCROLL_LEFT) ? 120 : -120; + + if (hwnd) hwnd->Retain(); + SendMouseMessage(hwnd, msg, (v<<16), MAKELPARAM(p2.x, p2.y)); + if (hwnd) hwnd->Release(); + } + } +} + +static DWORD s_last_focus_change_time; + +static void OnButtonEvent(GdkEventButton *b) +{ + HWND hwnd = swell_oswindow_to_hwnd(b->window); + if (!hwnd) return; + swell_lastMessagePos = MAKELONG(((int)b->x_root&0xffff),((int)b->y_root&0xffff)); + POINT p={(int)b->x, (int)b->y}; + HWND hwnd2 = getMouseTarget(b->window,p,&hwnd); + + POINT p2={(int)b->x_root, (int)b->y_root}; + ScreenToClient(hwnd2, &p2); + + int msg=WM_LBUTTONDOWN; + if (b->button==2) msg=WM_MBUTTONDOWN; + else if (b->button==3) msg=WM_RBUTTONDOWN; + + if (hwnd2) hwnd2->Retain(); + + if (b->type == GDK_BUTTON_PRESS) + { + HWND oldFocus=GetFocus(); + if (!oldFocus || + oldFocus != hwnd2 || + (GetTickCount()-s_last_focus_change_time) < 500) + { + if (IsWindowEnabled(hwnd2)) + SendMessage(hwnd2,WM_MOUSEACTIVATE,0,0); + } + } + + if (hwnd && hwnd->m_oswindow && SWELL_focused_oswindow != hwnd->m_oswindow) + { + // this should not be necessary, focus is sent via separate events + // (the only time I've ever seen this is when launching a popup menu via the mousedown handler, on the mouseup + // the menu has not yet been focused but the mouse event goes to the popup menu) + SWELL_focused_oswindow = hwnd->m_oswindow; + update_menubar_activations(); + } + + + // for doubleclicks, GDK actually seems to send: + // GDK_BUTTON_PRESS, GDK_BUTTON_RELEASE, + // GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_BUTTON_RELEASE + // win32 expects: + // WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_LBUTTONUP + // what we send: + // WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDOWN, WM_LBUTTONUP, + // WM_LBUTTONDBLCLK, WM_LBUTTONUP + // there is an extra down/up pair, but it should behave fine with most code + // (one hopes) + + if(b->type == GDK_BUTTON_RELEASE) + { + msg++; // convert WM_xBUTTONDOWN to WM_xBUTTONUP + } + else if(b->type == GDK_2BUTTON_PRESS) + { + msg++; // convert WM_xBUTTONDOWN to WM_xBUTTONUP + SendMouseMessage(hwnd2, msg, 0, MAKELPARAM(p2.x, p2.y)); + msg++; // convert WM_xBUTTONUP to WM_xBUTTONDBLCLK + } + + SendMouseMessage(hwnd2, msg, 0, MAKELPARAM(p2.x, p2.y)); + if (hwnd2) hwnd2->Release(); +} + + +static HANDLE urilistToDropFiles(const POINT *pt, const guchar *gptr, gint sz) +{ + HANDLE gobj=GlobalAlloc(0,sz+sizeof(DROPFILES)); + if (!gobj) return NULL; + + DROPFILES *df=(DROPFILES*)gobj; + df->pFiles = sizeof(DROPFILES); + if (pt) df->pt = *pt; + else df->pt.x = df->pt.y = 0; + + df->fNC=FALSE; + df->fWide=FALSE; + guchar *pout = (guchar *)(df+1); + const guchar *rd = gptr; + const guchar *rd_end = rd + sz; + for (;;) + { + while (rd < rd_end && *rd && isspace(*rd)) rd++; + if (rd >= rd_end) break; + + if (rd+7 < rd_end && !strnicmp((const char *)rd,"file://",7)) + { + rd += 7; + int c=0; + while (rd < rd_end && *rd && !isspace(*rd)) + { + int v1,v2; + if (*rd == '%' && rd+2 < rd_end && (v1=hex_parse(rd[1]))>=0 && (v2=hex_parse(rd[2]))>=0) + { + *pout++ = (v1<<4) | v2; + rd+=3; + } + else + { + *pout++ = *rd++; + } + c++; + } + if (c) *pout++=0; + } + else + { + while (rd < rd_end && *rd && !isspace(*rd)) rd++; + } + } + *pout++=0; + *pout++=0; + + return gobj; +} + +static void OnSelectionNotifyEvent(GdkEventSelection *b) +{ + HWND hwnd = swell_oswindow_to_hwnd(b->window); + if (!hwnd) return; + + if (hwnd == s_ddrop_hwnd && b->target == urilistatom()) + { + POINT p = s_ddrop_pt; + HWND cw=hwnd; + RECT r; + GetWindowContentViewRect(hwnd,&r); + if (PtInRect(&r,p)) + { + p.x -= r.left; + p.y -= r.top; + cw = ChildWindowFromPoint(hwnd,p); + } + if (!cw) cw=hwnd; + + guchar *gptr=NULL; + GdkAtom fmt; + gint unitsz=0; + gint sz=gdk_selection_property_get(b->window,&gptr,&fmt,&unitsz); + + if (sz>0 && gptr) + { + POINT pt2 = s_ddrop_pt; + ScreenToClient(cw,&pt2); + HANDLE gobj = urilistToDropFiles(&pt2,gptr,sz); + if (gobj) + { + SendMessage(cw,WM_DROPFILES,(WPARAM)gobj,0); + GlobalFree(gobj); + } + } + + if (gptr) g_free(gptr); + s_ddrop_hwnd=NULL; + return; + } + + s_ddrop_hwnd=NULL; + + if (s_clipboard_getstate) { GlobalFree(s_clipboard_getstate); s_clipboard_getstate=NULL; } + guchar *gptr=NULL; + GdkAtom fmt; + gint unitsz=0; + gint sz=gdk_selection_property_get(b->window,&gptr,&fmt,&unitsz); + if (sz>0 && gptr && (unitsz == 8 || unitsz == 16 || unitsz == 32)) + { + WDL_FastString str; + guchar *ptr = gptr; + if (fmt == urilistatom()) + { + s_clipboard_getstate = urilistToDropFiles(NULL,gptr,sz); + if (s_clipboard_getstate) + s_clipboard_getstate_fmt = fmt; + } + else + { + if (fmt == GDK_TARGET_STRING || fmt == utf8atom()) + { + int lastc=0; + while (sz-->0) + { + int c; + if (unitsz==32) { c = *(unsigned int *)ptr; ptr+=4; } + else if (unitsz==16) { c = *(unsigned short *)ptr; ptr+=2; } + else c = *ptr++; + + if (!c) break; + + if (c == '\n' && lastc != '\r') str.Append("\r",1); + + char bv[8]; + if (fmt != GDK_TARGET_STRING) + { + bv[0] = (char) ((unsigned char)c); + str.Append(bv,1); + } + else + { + WDL_MakeUTFChar(bv,c,sizeof(bv)); + str.Append(bv); + } + + lastc=c; + } + ptr = (guchar*)str.Get(); + sz=str.GetLength()+1; + } + else if (unitsz>8) sz *= (unitsz/8); + + s_clipboard_getstate = GlobalAlloc(0,sz); + if (s_clipboard_getstate) + { + memcpy(s_clipboard_getstate,ptr,sz); + s_clipboard_getstate_fmt = fmt; + } + } + } + if (gptr) g_free(gptr); +} + +static void OnDropStartEvent(GdkEventDND *e) +{ + HWND hwnd = swell_oswindow_to_hwnd(e->window); + if (!hwnd) return; + + GdkDragContext *ctx = e->context; + if (ctx) + { + POINT p = { (int)e->x_root, (int)e->y_root }; + s_ddrop_hwnd = hwnd; + s_ddrop_pt = p; + + GdkAtom srca = gdk_drag_get_selection(ctx); + gdk_selection_convert(e->window,srca,urilistatom(),e->time); + gdk_drop_finish(ctx,TRUE,e->time); + } +} + +static bool is_our_oswindow(GdkWindow *w) +{ + while (w) + { + HWND hwnd = swell_oswindow_to_hwnd(w); + if (hwnd) return true; + w = gdk_window_get_effective_parent(w); + } + return false; + +} + +static void deactivateTimer(HWND hwnd, UINT uMsg, UINT_PTR tm, DWORD dwt) +{ + KillTimer(NULL,s_deactivate_timer); + s_deactivate_timer=0; + if (swell_app_is_inactive) return; + GdkWindow *window = gdk_screen_get_active_window(gdk_screen_get_default()); + if (!is_our_oswindow(window)) + on_deactivate(); + + update_menubar_activations(); +} + +extern SWELL_OSWINDOW swell_ignore_focus_oswindow; +extern DWORD swell_ignore_focus_oswindow_until; + +static void swell_gdkEventHandler(GdkEvent *evt, gpointer data) +{ + GdkEvent *oldEvt = s_cur_evt; + s_cur_evt = evt; + + switch (evt->type) + { + case GDK_FOCUS_CHANGE: + { + bool do_menus = false; + GdkEventFocus *fc = (GdkEventFocus *)evt; + if (s_deactivate_timer) + { + KillTimer(NULL,s_deactivate_timer); + s_deactivate_timer=0; + do_menus = true; + } + if (fc->in && is_our_oswindow(fc->window)) + { + s_last_focus_change_time = GetTickCount(); + swell_on_toplevel_raise(fc->window); + if (swell_ignore_focus_oswindow != fc->window || + (GetTickCount()-swell_ignore_focus_oswindow_until) < 0x10000000) + { + SWELL_focused_oswindow = fc->window; + update_menubar_activations(); + } + if (swell_app_is_inactive) + { + on_activate(0); + } + } + else if (!swell_app_is_inactive) + { + s_deactivate_timer = SetTimer(NULL,0,200,deactivateTimer); + if (gdk_options & OPTION_ALLOW_MAYBE_INACTIVE) + do_menus = true; + } + if (do_menus) + update_menubar_activations(); + } + break; + case GDK_SELECTION_REQUEST: + OnSelectionRequestEvent((GdkEventSelection *)evt); + break; + + case GDK_DELETE: + { + HWND hwnd = swell_oswindow_to_hwnd(((GdkEventAny*)evt)->window); + if (hwnd && IsWindowEnabled(hwnd) && !SendMessage(hwnd,WM_CLOSE,0,0)) + SendMessage(hwnd,WM_COMMAND,IDCANCEL,0); + } + break; + case GDK_EXPOSE: // paint! GdkEventExpose... + OnExposeEvent((GdkEventExpose *)evt); + break; + case GDK_CONFIGURE: // size/move, GdkEventConfigure + OnConfigureEvent((GdkEventConfigure*)evt); + break; + case GDK_WINDOW_STATE: /// GdkEventWindowState for min/max + //printf("minmax\n"); + break; + case GDK_GRAB_BROKEN: + if (swell_oswindow_to_hwnd(((GdkEventAny*)evt)->window)) + { + if (swell_captured_window) + { + SendMessage(swell_captured_window,WM_CAPTURECHANGED,0,0); + swell_captured_window=0; + } + } + break; + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + swell_dlg_destroyspare(); + OnKeyEvent((GdkEventKey *)evt); + break; +#ifdef GDK_AVAILABLE_IN_3_4 + case GDK_TOUCH_BEGIN: + case GDK_TOUCH_UPDATE: + case GDK_TOUCH_END: + case GDK_TOUCH_CANCEL: + { + GdkEventTouch *e = (GdkEventTouch *)evt; + static guint32 touchptr_lasttime; + bool doubletap = false; + if (evt->type == GDK_TOUCH_BEGIN && !g_swell_touchptr) + { + DWORD now = e->time; + doubletap = touchptr_lasttime && + now >= touchptr_lasttime && + now < touchptr_lasttime+350; + touchptr_lasttime = now; + g_swell_touchptr = e->sequence; + g_swell_touchptr_wnd = e->window; + } + + if (!e->sequence || e->sequence != g_swell_touchptr) + { + touchptr_lasttime=0; + break; + } + + if (e->type == GDK_TOUCH_UPDATE) + { + GdkEventMotion m; + memset(&m,0,sizeof(m)); + m.type = GDK_MOTION_NOTIFY; + m.window = e->window; + m.time = e->time; + m.x = e->x; + m.y = e->y; + m.axes = e->axes; + m.state = e->state; + m.device = e->device; + m.x_root = e->x_root; + m.y_root = e->y_root; + OnMotionEvent(&m); + } + else + { + GdkEventButton but; + memset(&but,0,sizeof(but)); + if (e->type == GDK_TOUCH_BEGIN) + { + but.type = doubletap ? GDK_2BUTTON_PRESS:GDK_BUTTON_PRESS; + } + else + { + but.type = GDK_BUTTON_RELEASE; + g_swell_touchptr = NULL; + } + but.window = e->window; + but.time = e->time; + but.x = e->x; + but.y = e->y; + but.axes = e->axes; + but.state = e->state; + but.device = e->device; + but.button = 1; + but.x_root = e->x_root; + but.y_root = e->y_root; + swell_dlg_destroyspare(); + OnButtonEvent(&but); + } + } + break; +#endif + case GDK_MOTION_NOTIFY: + gdk_event_request_motions((GdkEventMotion *)evt); + OnMotionEvent((GdkEventMotion *)evt); + break; + case GDK_SCROLL: + OnScrollEvent((GdkEventScroll*)evt); + break; + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + swell_dlg_destroyspare(); + OnButtonEvent((GdkEventButton*)evt); + break; + case GDK_SELECTION_NOTIFY: + OnSelectionNotifyEvent((GdkEventSelection *)evt); + break; + case GDK_DRAG_ENTER: + case GDK_DRAG_MOTION: + if (swell_oswindow_to_hwnd(((GdkEventAny*)evt)->window)) + { + GdkEventDND *e = (GdkEventDND *)evt; + if (e->context) + { + gdk_drag_status(e->context,GDK_ACTION_COPY,e->time); + //? gdk_drop_reply(e->context,TRUE,e->time); + } + } + break; + case GDK_DRAG_LEAVE: + case GDK_DRAG_STATUS: + case GDK_DROP_FINISHED: + break; + case GDK_DROP_START: + OnDropStartEvent((GdkEventDND *)evt); + break; + + default: + //printf("msg: %d\n",evt->type); + break; + } +#ifdef SWELL_SUPPORT_GTK + gtk_main_do_event(evt); +#endif + s_cur_evt = oldEvt; +} + +void SWELL_RunEvents() +{ + if (SWELL_gdk_active>0) + { +#if 0 && defined(SWELL_SUPPORT_GTK) + // does not seem to be necessary + while (gtk_events_pending()) + gtk_main_iteration(); +#else + +#if SWELL_TARGET_GDK == 2 + gdk_window_process_all_updates(); +#endif + + GMainContext *ctx=g_main_context_default(); + while (g_main_context_iteration(ctx,FALSE)) + { + GdkEvent *evt; + while (gdk_events_pending() && (evt = gdk_event_get())) + { + swell_gdkEventHandler(evt,(gpointer)1); + gdk_event_free(evt); + } + } +#endif + } +} + +bool swell_gdk_set_fullscreen(HWND hwnd, int fs) // fs=2 if window might have owned children +{ + if (!hwnd) return false; + if (fs==2 && (gdk_options & OPTION_FULLSCREEN_FOR_OWNER_WINDOWS)!=OPTION_FULLSCREEN_FOR_OWNER_WINDOWS) return false; + + const int stylemask = WS_BORDER|WS_CAPTION|WS_THICKFRAME; + if (!hwnd->m_oswindow || !(gdk_options & OPTION_FULLSCREEN_DYNAMIC)) + { + hwnd->m_oswindow_fullscreen = fs ? WS_VISIBLE | (hwnd->m_style & stylemask) : 0; + return true; // request caller hide/show + } + + // OPTION_FULLSCREEN_DYNAMIC and window exists, modify state + if (fs) + { + hwnd->m_oswindow_fullscreen = WS_VISIBLE | (hwnd->m_style & stylemask); + hwnd->m_style &= ~stylemask; + gdk_window_fullscreen(hwnd->m_oswindow); + } + else + { + hwnd->m_style |= (hwnd->m_oswindow_fullscreen & stylemask); + hwnd->m_oswindow_fullscreen = 0; + gdk_window_unfullscreen(hwnd->m_oswindow); + } + return false; +} + +void swell_oswindow_update_style(HWND hwnd, LONG oldstyle) +{ + const LONG val = hwnd->m_style, ret = oldstyle; + if (hwnd->m_oswindow && ((ret^val)& WS_CAPTION)) + { + swell_hide_owned_windows_transient(hwnd); + + gdk_window_hide(hwnd->m_oswindow); + if (val & WS_CAPTION) + { + if (val & WS_THICKFRAME) + gdk_window_set_decorations(hwnd->m_oswindow,(GdkWMDecoration) (GDK_DECOR_ALL | GDK_DECOR_MENU)); + else + gdk_window_set_decorations(hwnd->m_oswindow,(GdkWMDecoration) (GDK_DECOR_BORDER|GDK_DECOR_TITLE|GDK_DECOR_MINIMIZE)); + } + else + { + gdk_window_set_decorations(hwnd->m_oswindow,(GdkWMDecoration) 0); + } + hwnd->m_oswindow_private |= PRIVATE_NEEDSHOW; + } +} + +void swell_oswindow_update_enable(HWND hwnd) +{ + if (hwnd->m_oswindow && !swell_app_is_inactive) + gdk_window_set_accept_focus(hwnd->m_oswindow,hwnd->m_enabled); +} + +int SWELL_SetWindowLevel(HWND hwnd, int newlevel) +{ + int rv=0; + if (hwnd) + { + rv = hwnd->m_israised ? 1 : 0; + hwnd->m_israised = newlevel>0; + if (hwnd->m_oswindow) gdk_window_set_keep_above(hwnd->m_oswindow,newlevel>0 && !swell_app_is_inactive); + } + return rv; +} + +void SWELL_GetViewPort(RECT *r, const RECT *sourcerect, bool wantWork) +{ + if (swell_initwindowsys()) + { + GdkScreen *defscr = gdk_screen_get_default(); + if (!defscr) { r->left=r->top=0; r->right=r->bottom=1024; return; } + gint idx = sourcerect ? gdk_screen_get_monitor_at_point(defscr, + (sourcerect->left+sourcerect->right)/2, + (sourcerect->top+sourcerect->bottom)/2) : 0; + GdkRectangle rc={0,0,1024,1024}; +#if SWELL_TARGET_GDK != 2 + if (wantWork) + gdk_screen_get_monitor_workarea(defscr,idx,&rc); + else +#endif + gdk_screen_get_monitor_geometry(defscr,idx,&rc); + r->left=rc.x; + r->top = rc.y; + r->right=rc.x+rc.width; + r->bottom=rc.y+rc.height; + return; + } + r->left=r->top=0; + r->right=1024; + r->bottom=768; +} + + +bool GetWindowRect(HWND hwnd, RECT *r) +{ + if (!hwnd) return false; + if (hwnd->m_oswindow) + { +#ifdef SWELL_GDK_IMPROVE_WINDOWRECT + GdkRectangle gr; + gdk_window_get_frame_extents(hwnd->m_oswindow,&gr); + + r->left=gr.x; + r->top=gr.y; + r->right=gr.x + gr.width; + r->bottom = gr.y + gr.height; +#else + // this is wrong (returns client rect in screen coordinates), but gdk_window_get_frame_extents() doesn't seem to work + gint x=hwnd->m_position.left,y=hwnd->m_position.top; + gdk_window_get_root_origin(hwnd->m_oswindow,&x,&y); + r->left=x; + r->top=y; + r->right=x + hwnd->m_position.right - hwnd->m_position.left; + r->bottom = y + hwnd->m_position.bottom - hwnd->m_position.top; +#endif + + return true; + } + + r->left=r->top=0; + ClientToScreen(hwnd,(LPPOINT)r); + r->right = r->left + hwnd->m_position.right - hwnd->m_position.left; + r->bottom = r->top + hwnd->m_position.bottom - hwnd->m_position.top; + return true; +} + +void swell_oswindow_begin_resize(SWELL_OSWINDOW wnd) +{ + // make sure window is resizable (hints will be re-set on upcoming CONFIGURE event) + gdk_window_set_geometry_hints(wnd,NULL,(GdkWindowHints) 0); +} + +void swell_oswindow_resize(SWELL_OSWINDOW wnd, int reposflag, RECT f) +{ +#ifdef SWELL_GDK_IMPROVE_WINDOWRECT + if (reposflag & 2) + { + // increase size to include titlebars etc + GdkRectangle gr; + gdk_window_get_frame_extents(wnd,&gr); + gint cw=gr.width, ch=gr.height; + gdk_window_get_geometry(wnd,NULL,NULL,&cw,&ch); + // when it matters, this seems to always make gr.height=ch, which is pointless + f.right -= gr.width - cw; + f.bottom -= gr.height - ch; + } +#endif + if ((reposflag&3)==3) gdk_window_move_resize(wnd,f.left,f.top,f.right-f.left,f.bottom-f.top); + else if (reposflag&2) gdk_window_resize(wnd,f.right-f.left,f.bottom-f.top); + else if (reposflag&1) gdk_window_move(wnd,f.left,f.top); +} + +void swell_oswindow_postresize(HWND hwnd, RECT f) +{ + if (hwnd->m_oswindow && (hwnd->m_oswindow_private&PRIVATE_NEEDSHOW) && !hwnd->m_oswindow_fullscreen) + { + gdk_window_show(hwnd->m_oswindow); + if (hwnd->m_style & WS_CAPTION) gdk_window_unmaximize(hwnd->m_oswindow); // fixes Kwin + swell_oswindow_resize(hwnd->m_oswindow,3,f); // fixes xfce + hwnd->m_oswindow_private &= ~PRIVATE_NEEDSHOW; + + swell_set_owned_windows_transient(hwnd,false); + } +} + +void UpdateWindow(HWND hwnd) +{ +#if SWELL_TARGET_GDK == 2 + if (hwnd) + { + while (hwnd && !hwnd->m_oswindow) hwnd=hwnd->m_parent; + if (hwnd && hwnd->m_oswindow) gdk_window_process_updates(hwnd->m_oswindow,true); + } +#endif +} + +void swell_oswindow_invalidate(HWND hwnd, const RECT *r) +{ + GdkRectangle gdkr; + if (r) + { + gdkr.x = r->left; + gdkr.y = r->top; + gdkr.width = r->right-r->left; + gdkr.height = r->bottom-r->top; + } + + gdk_window_invalidate_rect(hwnd->m_oswindow,r ? &gdkr : NULL,true); +} + + + +bool OpenClipboard(HWND hwndDlg) +{ + RegisterClipboardFormat(NULL); + s_clip_hwnd=hwndDlg ? hwndDlg : SWELL_topwindows; + if (s_clipboard_getstate) + { + GlobalFree(s_clipboard_getstate); + s_clipboard_getstate = NULL; + } + s_clipboard_getstate_fmt = NULL; + + return true; +} + +static HANDLE req_clipboard(GdkAtom type) +{ + if (s_clipboard_getstate_fmt == type) return s_clipboard_getstate; + + HWND h = s_clip_hwnd; + while (h && !h->m_oswindow) h = h->m_parent; + + if (h && SWELL_gdk_active > 0) + { + if (s_clipboard_getstate) + { + GlobalFree(s_clipboard_getstate); + s_clipboard_getstate=NULL; + } + gdk_selection_convert(h->m_oswindow,GDK_SELECTION_CLIPBOARD,type,GDK_CURRENT_TIME); + + GMainContext *ctx=g_main_context_default(); + DWORD startt = GetTickCount(); + for (;;) + { + while (!s_clipboard_getstate && g_main_context_iteration(ctx,FALSE)) + { + GdkEvent *evt; + while (!s_clipboard_getstate && gdk_events_pending() && (evt = gdk_event_get())) + { + if (evt->type == GDK_SELECTION_NOTIFY || evt->type == GDK_SELECTION_REQUEST) + swell_gdkEventHandler(evt,(gpointer)1); + gdk_event_free(evt); + } + } + + if (s_clipboard_getstate) + { + if (s_clipboard_getstate_fmt == type) return s_clipboard_getstate; + return NULL; + } + + if ((GetTickCount()-startt) > 500) break; + Sleep(10); + } + } + return NULL; +} + +void CloseClipboard() +{ + s_clip_hwnd=NULL; +} + +UINT EnumClipboardFormats(UINT lastfmt) +{ + if (lastfmt == CF_TEXT) return CF_HDROP; + if (!lastfmt) + { + // checking this causes issues (reentrancy, I suppose?) + //if (req_clipboard(utf8atom())) + return CF_TEXT; + } + if (lastfmt == CF_HDROP) lastfmt = 0; + + int x=0; + for (;;) + { + int fmt=0; + if (!m_clip_recs.Enumerate(x++,&fmt)) return 0; + if (lastfmt == 0) return fmt; + + if ((UINT)fmt == lastfmt) return m_clip_recs.Enumerate(x++,&fmt) ? fmt : 0; + } +} + +HANDLE GetClipboardData(UINT type) +{ + RegisterClipboardFormat(NULL); + if (type == CF_TEXT) + return req_clipboard(utf8atom()); + + if (type == CF_HDROP) + return req_clipboard(urilistatom()); + + return m_clip_recs.Get(type); +} + + +void EmptyClipboard() +{ + m_clip_recs.DeleteAll(); +} + +void SetClipboardData(UINT type, HANDLE h) +{ + RegisterClipboardFormat(NULL); + if (type == CF_TEXT || type == CF_HDROP) + { + if (s_clipboard_setstate) { GlobalFree(s_clipboard_setstate); s_clipboard_setstate=NULL; } + s_clipboard_setstate_fmt=NULL; + static GdkWindow *w; + if (!w) + { + GdkWindowAttr attr={0,}; + attr.title = (char *)"swell clipboard"; + attr.event_mask = GDK_ALL_EVENTS_MASK; + attr.wclass = GDK_INPUT_ONLY; + attr.window_type = GDK_WINDOW_TOPLEVEL; + w = gdk_window_new(NULL,&attr,0); + } + if (w) + { + s_clipboard_setstate_fmt = type == CF_HDROP ? urilistatom() : utf8atom(); + s_clipboard_setstate = h; + gdk_selection_owner_set(w,GDK_SELECTION_CLIPBOARD,GDK_CURRENT_TIME,TRUE); + } + return; + } + if (h) m_clip_recs.Insert(type,h); + else m_clip_recs.Delete(type); +} + +UINT RegisterClipboardFormat(const char *desc) +{ + if (!m_clip_curfmts.GetSize()) + { + m_clip_curfmts.Add(strdup("SWELL__CF_TEXT")); + m_clip_curfmts.Add(strdup("SWELL__CF_HDROP")); + } + + if (!desc || !*desc) return 0; + int x; + const int n = m_clip_curfmts.GetSize(); + for(x=0;xx=0; + pt->y=0; + if (SWELL_gdk_active>0) + { +//#if SWELL_TARGET_GDK == 3 +// GdkDevice *dev=NULL; +// if (s_cur_evt) dev = gdk_event_get_device(s_cur_evt); +// if (!dev) dev = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); +// if (dev) gdk_device_get_position(dev,NULL,&pt->x,&pt->y); +//#else + gdk_display_get_pointer(gdk_display_get_default(),NULL,&pt->x,&pt->y,NULL); +//#endif + } +} + + +WORD GetAsyncKeyState(int key) +{ + if (SWELL_gdk_active>0) + { + GdkModifierType mod=(GdkModifierType)0; + HWND h = GetFocus(); + while (h && !h->m_oswindow) h = h->m_parent; +//#if SWELL_TARGET_GDK == 3 +// GdkDevice *dev=NULL; +// if (s_cur_evt) dev = gdk_event_get_device(s_cur_evt); +// if (!dev) dev = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())); +// if (dev) gdk_window_get_device_position(h? h->m_oswindow : gdk_get_default_root_window(),dev, NULL, NULL,&mod); +//#else + gdk_window_get_pointer(h? h->m_oswindow : gdk_get_default_root_window(),NULL,NULL,&mod); +//#endif + + if (key == VK_LBUTTON) return ((mod&GDK_BUTTON1_MASK)||g_swell_touchptr)?0x8000:0; + if (key == VK_MBUTTON) return (mod&GDK_BUTTON2_MASK)?0x8000:0; + if (key == VK_RBUTTON) return (mod&GDK_BUTTON3_MASK)?0x8000:0; + + if (key == VK_CONTROL) return (mod&GDK_CONTROL_MASK)?0x8000:0; + if (key == VK_MENU) return (mod&GDK_MOD1_MASK)?0x8000:0; + if (key == VK_SHIFT) return (mod&GDK_SHIFT_MASK)?0x8000:0; + if (key == VK_LWIN) return (mod&SWELL_WINDOWSKEY_GDK_MASK)?0x8000:0; + } + return 0; +} + +DWORD GetMessagePos() +{ + return swell_lastMessagePos; +} + +struct bridgeState { + bridgeState(bool needrep, GdkWindow *_w, Window _nw, Display *_disp, GdkWindow *_curpar); + ~bridgeState(); + + GdkWindow *w; + Window native_w; + Display *native_disp; + GdkWindow *cur_parent; + + bool lastvis; + bool need_reparent; + RECT lastrect; + + GLXContext gl_ctx; +}; + +static bridgeState *s_last_gl_ctx; + +static WDL_PtrList filter_windows; +bridgeState::~bridgeState() +{ + if (gl_ctx) + { + if (s_last_gl_ctx == this) + { + glXMakeCurrent(native_disp,None, NULL); + s_last_gl_ctx = NULL; + } + + glXDestroyContext(native_disp,gl_ctx); + gl_ctx = NULL; + } + filter_windows.DeletePtr(this); + if (w) + { + if (!need_reparent) + { + // if this window is a child of another window, it will get destroyed by the hierarchy unless it is fully + // released (we could do g_object_unref() again here, but reparenting feels safer in this context) + gdk_window_reparent(w,NULL,0,0); + } + g_object_unref(G_OBJECT(w)); + XDestroyWindow(native_disp,native_w); + } +} +bridgeState::bridgeState(bool needrep, GdkWindow *_w, Window _nw, Display *_disp, GdkWindow *_curpar) +{ + gl_ctx = NULL; + w=_w; + native_w=_nw; + native_disp=_disp; + lastvis=false; + need_reparent=needrep; + cur_parent = _curpar; + memset(&lastrect,0,sizeof(lastrect)); + filter_windows.Add(this); +} + +static LRESULT xbridgeProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_DESTROY: + if (hwnd && hwnd->m_private_data) + { + bridgeState *bs = (bridgeState*)hwnd->m_private_data; + hwnd->m_private_data = 0; + delete bs; + } + break; + case WM_TIMER: + + if (wParam == 1010) + { + // callers can SetTimer(hwnd_container,1010,X,NULL) and have the child X window resized to fit the parent + // they are only resized once, but it is deferred by timer until the window is actually there. + // (if the window was created by another connection to the X server than ours, the window might not yet + // be valid on the X server). + bridgeState *bs = (bridgeState*)hwnd->m_private_data; + + RECT r; + GetClientRect(hwnd,&r); + if (r.right>0 && r.bottom>0 && bs) + { + Window root, par, *list=NULL; + unsigned int nlist=0; + // if a plug-in created a window on a separate X11 connection, it might not be valid yet. + if (XQueryTree(bs->native_disp,bs->native_w,&root,&par,&list, &nlist)) + { + if (!list || !nlist) + { + if (list) XFree(list); + return 0; + } + XSizeHints *hints = XAllocSizeHints(); + if (hints) + { + long hints_ret=0; + XGetWMNormalHints(bs->native_disp,list[0],hints,&hints_ret); + + if (hints->flags&PMinSize) + { + if (r.right < hints->min_width) r.right = hints->min_width; + if (r.bottom < hints->min_height) r.bottom = hints->min_height; + } + if (hints->flags&PMaxSize) + { + if (hints->max_width > 0 && r.right > hints->max_width) r.right = hints->max_width; + if (hints->max_height > 0 && r.bottom > hints->max_height) r.bottom = hints->max_height; + } + XFree(hints); + } + + XResizeWindow(bs->native_disp,list[0],r.right,r.bottom); + XFree(list); + } + } + KillTimer(hwnd,wParam); + } + + if (wParam != 1) break; + case WM_MOVE: + case WM_SIZE: + if (hwnd && hwnd->m_private_data) + { + bridgeState *bs = (bridgeState*)hwnd->m_private_data; + if (bs->w) + { + HWND h = hwnd->m_parent; + RECT tr = hwnd->m_position; + while (h) + { + RECT cr = h->m_position; + if (h->m_oswindow) + { + cr.right -= cr.left; + cr.bottom -= cr.top; + cr.left=cr.top=0; + } + + if (h->m_wndproc) + { + NCCALCSIZE_PARAMS p = {{ cr }}; + h->m_wndproc(h,WM_NCCALCSIZE,0,(LPARAM)&p); + cr = p.rgrc[0]; + } + tr.left += cr.left; + tr.top += cr.top; + tr.right += cr.left; + tr.bottom += cr.top; + + if (tr.left < cr.left) tr.left=cr.left; + if (tr.top < cr.top) tr.top = cr.top; + if (tr.right > cr.right) tr.right = cr.right; + if (tr.bottom > cr.bottom) tr.bottom = cr.bottom; + + if (h->m_oswindow) break; + h=h->m_parent; + } + + // todo: need to periodically check to see if the plug-in has resized its window + bool vis = IsWindowVisible(hwnd); + if (vis) + { +#if SWELL_TARGET_GDK == 2 + gint w=0,hh=0,d=0; + gdk_window_get_geometry(bs->w,NULL,NULL,&w,&hh,&d); +#else + gint w=0,hh=0; + gdk_window_get_geometry(bs->w,NULL,NULL,&w,&hh); +#endif + if (w > bs->lastrect.right-bs->lastrect.left) + { + bs->lastrect.right = bs->lastrect.left + w; + tr.right++; // workaround "bug" in GDK -- if bs->w was resized via Xlib, GDK won't resize it unless it thinks the size changed + } + if (hh > bs->lastrect.bottom-bs->lastrect.top) + { + bs->lastrect.bottom = bs->lastrect.top + hh; + tr.bottom++; // workaround "bug" in GDK -- if bs->w was resized via Xlib, GDK won't resize it unless it thinks the size changed + } + } + + if (h && h->m_oswindow != bs->cur_parent) + bs->need_reparent = true; + + if (h && (bs->need_reparent || (vis != bs->lastvis) || (vis&&memcmp(&tr,&bs->lastrect,sizeof(RECT))))) + { + if (bs->lastvis && !vis) + { + gdk_window_hide(bs->w); + bs->lastvis = false; + } + + if (bs->need_reparent) + { + gdk_window_reparent(bs->w,h->m_oswindow,tr.left,tr.top); + gdk_window_resize(bs->w, tr.right-tr.left,tr.bottom-tr.top); + bs->lastrect=tr; + + bs->cur_parent = h->m_oswindow; + bs->need_reparent=false; + if (vis && bs->lastvis) gdk_window_show(bs->w); + } + else if (memcmp(&tr,&bs->lastrect,sizeof(RECT))) + { + bs->lastrect = tr; + gdk_window_move_resize(bs->w,tr.left,tr.top, tr.right-tr.left, tr.bottom-tr.top); + } + if (vis && !bs->lastvis) + { + gdk_window_show(bs->w); + gdk_window_raise(bs->w); + bs->lastvis = true; + } + } + } + } + break; + } + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + +static GdkFilterReturn filterCreateShowProc(GdkXEvent *xev, GdkEvent *event, gpointer data) +{ + const XEvent *xevent = (XEvent *)xev; + if (xevent && xevent->type == CreateNotify) + { + for (int x=0;xnative_w == xevent->xany.window && bs->native_disp == xevent->xany.display) + { + //gint w=0,hh=0; + //gdk_window_get_geometry(bs->w,NULL,NULL,&w,&hh); + XMapWindow(bs->native_disp, xevent->xcreatewindow.window); + //XResizeWindow(bs->native_disp, xevent->xcreatewindow.window,w,hh); + return GDK_FILTER_REMOVE; + } + } + } + return GDK_FILTER_CONTINUE; +} + +static const char * const bridge_class_name = "__swell_xbridgewndclass"; +HWND SWELL_CreateXBridgeWindow(HWND viewpar, void **wref, const RECT *r) +{ + HWND hwnd = NULL; + *wref = NULL; + + GdkWindow *ospar = NULL; + HWND hpar = viewpar; + while (hpar) + { + ospar = hpar->m_oswindow; + if (ospar) break; + hpar = hpar->m_parent; + } + + bool need_reparent=false; + + if (!ospar) + { + need_reparent = true; + ospar = gdk_screen_get_root_window(gdk_screen_get_default()); + } + + Display *disp = gdk_x11_display_get_xdisplay(gdk_window_get_display(ospar)); + Window w = XCreateWindow(disp,GDK_WINDOW_XID(ospar),0,0,r->right-r->left,r->bottom-r->top,0,CopyFromParent, InputOutput, CopyFromParent, 0, NULL); + GdkWindow *gdkw = w ? gdk_x11_window_foreign_new_for_display(gdk_display_get_default(),w) : NULL; + + hwnd = new HWND__(viewpar,0,r,NULL, true, xbridgeProc); + bridgeState *bs = gdkw ? new bridgeState(need_reparent,gdkw,w,disp, ospar) : NULL; + hwnd->m_classname = bridge_class_name; + hwnd->m_private_data = (INT_PTR) bs; + if (gdkw) + { + *wref = (void *) w; + + XSelectInput(disp, w, StructureNotifyMask | SubstructureNotifyMask); + + static bool filt_add; + if (!filt_add) + { + filt_add=true; + gdk_window_add_filter(NULL, filterCreateShowProc, NULL); + } + SetTimer(hwnd,1,100,NULL); + if (!need_reparent) SendMessage(hwnd,WM_SIZE,0,0); + } + return hwnd; +} + +struct dropSourceInfo { + dropSourceInfo() + { + srclist=NULL; srccount=0; srcfn=NULL; callback=NULL; + state=0; + dragctx=NULL; + } + ~dropSourceInfo() + { + free(srcfn); + if (dragctx) + { + if (_gdk_drag_drop_done) _gdk_drag_drop_done(dragctx,state!=0); + g_object_unref(dragctx); + } + } + + const char **srclist; + int srccount; + // or + void (*callback)(const char *); + char *srcfn; + + int state; + + GdkDragContext *dragctx; +}; + +static void encode_uri(WDL_FastString *s, const char *rd) +{ + while (*rd) + { + // unsure if UTF-8 chars should be urlencoded or allowed? + if (*rd < 0 || (!isalnum(*rd) && *rd != '-' && *rd != '_' && *rd != '.' && *rd != '/')) + { + char buf[8]; + snprintf(buf,sizeof(buf),"%%%02x",(int)(unsigned char)*rd); + s->Append(buf); + } + else s->Append(rd,1); + + rd++; + } +} + + +static LRESULT WINAPI dropSourceWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + dropSourceInfo *inf = (dropSourceInfo*)hwnd->m_private_data; + switch (msg) + { + case WM_CREATE: + if (!swell_dragsrc_osw) + { + GdkWindowAttr attr={0,}; + attr.title = (char *)"swell drag source"; + attr.event_mask = GDK_ALL_EVENTS_MASK; + attr.wclass = GDK_INPUT_ONLY; + attr.window_type = GDK_WINDOW_TOPLEVEL; + swell_dragsrc_osw = gdk_window_new(NULL,&attr,0); + } + if (swell_dragsrc_osw) + { + inf->dragctx = gdk_drag_begin(swell_dragsrc_osw, g_list_append(NULL,urilistatom())); + } + SetCapture(hwnd); + break; + case WM_MOUSEMOVE: + if (inf->dragctx) + { + POINT p; + GetCursorPos(&p); + GdkWindow *w = NULL; + GdkDragProtocol proto; + gdk_drag_find_window_for_screen(inf->dragctx,NULL,gdk_screen_get_default(),p.x,p.y,&w,&proto); + // todo: need to update gdk_drag_context_get_drag_window() + // (or just SetCursor() a drag and drop cursor) + if (w) + { + gdk_drag_motion(inf->dragctx,w,proto,p.x,p.y,GDK_ACTION_COPY,GDK_ACTION_COPY,GDK_CURRENT_TIME); + } + } + break; + case WM_LBUTTONUP: + if (inf->dragctx && !inf->state) + { + inf->state=1; + GdkAtom sel = gdk_drag_get_selection(inf->dragctx); + if (sel) gdk_selection_owner_set(swell_dragsrc_osw,sel,GDK_CURRENT_TIME,TRUE); + gdk_drag_drop(inf->dragctx,GDK_CURRENT_TIME); + if (!sel) + { + sel = gdk_drag_get_selection(inf->dragctx); + if (sel) gdk_selection_owner_set(swell_dragsrc_osw,sel,GDK_CURRENT_TIME,TRUE); + } + swell_dragsrc_timeout_start = GetTickCount(); + return 0; + } + ReleaseCapture(); + break; + case WM_USER+100: + if (wParam && lParam) + { + GdkAtom *aOut = (GdkAtom *)lParam; + GdkEventSelection *evt = (GdkEventSelection*)wParam; + + if (evt->target == urilistatom()) + { + WDL_FastString s; + if (inf->srclist && inf->srccount) + { + for (int x=0;xsrccount;x++) + { + if (x) s.Append("\n"); + s.Append("file://"); + encode_uri(&s,inf->srclist[x]); + } + } + else if (inf->callback && inf->srcfn && inf->state) + { + inf->callback(inf->srcfn); + s.Append("file://"); + encode_uri(&s,inf->srcfn); + } + + if (s.GetLength()) + { + *aOut = evt->property; +#if SWELL_TARGET_GDK == 2 + GdkWindow *pw = gdk_window_lookup(evt->requestor); + if (!pw) pw = gdk_window_foreign_new(evt->requestor); +#else + GdkWindow *pw = evt->requestor; +#endif + if (pw) + gdk_property_change(pw,*aOut,evt->target,8, GDK_PROP_MODE_REPLACE,(guchar*)s.Get(),s.GetLength()); + } + } + + if (inf->state) ReleaseCapture(); + } + break; + + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + + +void SWELL_InitiateDragDrop(HWND hwnd, RECT* srcrect, const char* srcfn, void (*callback)(const char* dropfn)) +{ + dropSourceInfo info; + info.srcfn = strdup(srcfn); + info.callback = callback; + RECT r={0,}; + HWND__ *h = new HWND__(NULL,0,&r,NULL,false,NULL,dropSourceWndProc, NULL); + swell_dragsrc_timeout_start = 0; + swell_dragsrc_hwnd=h; + h->m_private_data = (INT_PTR) &info; + dropSourceWndProc(h,WM_CREATE,0,0); + while (GetCapture()==h) + { + SWELL_RunEvents(); + Sleep(10); + if (swell_dragsrc_timeout_start && (GetTickCount()-swell_dragsrc_timeout_start) > 500) ReleaseCapture(); + } + + swell_dragsrc_hwnd=NULL; + DestroyWindow(h); +} + +// owner owns srclist, make copies here etc +void SWELL_InitiateDragDropOfFileList(HWND hwnd, RECT *srcrect, const char **srclist, int srccount, HICON icon) +{ + dropSourceInfo info; + info.srclist = srclist; + info.srccount = srccount; + RECT r={0,}; + HWND__ *h = new HWND__(NULL,0,&r,NULL,false,NULL,dropSourceWndProc, NULL); + swell_dragsrc_timeout_start = 0; + swell_dragsrc_hwnd=h; + h->m_private_data = (INT_PTR) &info; + dropSourceWndProc(h,WM_CREATE,0,0); + while (GetCapture()==h) + { + SWELL_RunEvents(); + Sleep(10); + if (swell_dragsrc_timeout_start && (GetTickCount()-swell_dragsrc_timeout_start) > 500) ReleaseCapture(); + } + + swell_dragsrc_hwnd=NULL; + DestroyWindow(h); +} + +void SWELL_FinishDragDrop() { } + + +bool SWELL_IsCursorVisible() +{ + return s_cursor_vis_cnt>=0; +} + + +void SWELL_SetCursor(HCURSOR curs) +{ + if (s_last_setcursor == curs && SWELL_focused_oswindow == s_last_setcursor_oswnd) return; + + s_last_setcursor=curs; + s_last_setcursor_oswnd = SWELL_focused_oswindow; + if (SWELL_focused_oswindow) + { + gdk_window_set_cursor(SWELL_focused_oswindow,(GdkCursor *)curs); +#ifdef SWELL_TARGET_GDK_CURSORHACK + if (GetCapture()) + { + // workaround for a GDK behavior: + // gdkwindow.c, gdk_window_set_cursor_internal() has a line: + // >>> if (_gdk_window_event_parent_of (window, pointer_info->window_under_pointer)) + // this should also allow setting the cursor if window is in a "grabbing" state + GdkDisplay *gdkdisp = gdk_display_get_default(); +#if SWELL_TARGET_GDK == 2 + if (gdkdisp && gdk_display_get_window_at_pointer(gdkdisp,NULL,NULL) != SWELL_focused_oswindow) +#else + GdkDevice *dev = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdkdisp)); + if (dev && gdk_device_get_window_at_position(dev,NULL,NULL) != SWELL_focused_oswindow) +#endif + { + Display *disp = gdk_x11_display_get_xdisplay(gdkdisp); + Window wn = GDK_WINDOW_XID(SWELL_focused_oswindow); +#if SWELL_TARGET_GDK == 2 + gint devid=2; // hardcoded default pointing device +#else + gint devid = gdk_x11_device_get_id(dev); +#endif + if (disp && wn) + { + if (curs) + XIDefineCursor(disp,devid,wn, gdk_x11_cursor_get_xcursor((GdkCursor*)curs)); + else + XIUndefineCursor(disp,devid,wn); + } + } + } +#endif // SWELL_TARGET_GDK_CURSORHACK + } +} + +HCURSOR SWELL_GetCursor() +{ + return s_last_setcursor; +} +HCURSOR SWELL_GetLastSetCursor() +{ + return s_last_setcursor; +} + +int SWELL_ShowCursor(BOOL bShow) +{ + s_cursor_vis_cnt += (bShow?1:-1); + if (s_cursor_vis_cnt==-1 && !bShow) + { + gint x1, y1; + #if SWELL_TARGET_GDK == 3 + GdkDevice *dev = gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (gdk_display_get_default ())); + gdk_device_get_position (dev, NULL, &x1, &y1); + #else + gdk_display_get_pointer(gdk_display_get_default(), NULL, &x1, &y1, NULL); + #endif + g_swell_mouse_relmode_curpos_x = x1; + g_swell_mouse_relmode_curpos_y = y1; + s_last_cursor = GetCursor(); + SetCursor((HCURSOR)gdk_cursor_new_for_display(gdk_display_get_default(),GDK_BLANK_CURSOR)); + //g_swell_mouse_relmode=true; + } + if (s_cursor_vis_cnt==0 && bShow) + { + SetCursor(s_last_cursor); + g_swell_mouse_relmode=false; + if (!g_swell_touchptr) + { + #if SWELL_TARGET_GDK == 3 + gdk_device_warp(gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())), + gdk_screen_get_default(), + g_swell_mouse_relmode_curpos_x, g_swell_mouse_relmode_curpos_y); + #else + gdk_display_warp_pointer(gdk_display_get_default(),gdk_screen_get_default(), g_swell_mouse_relmode_curpos_x, g_swell_mouse_relmode_curpos_y); + #endif + } + } + return s_cursor_vis_cnt; +} + +BOOL SWELL_SetCursorPos(int X, int Y) +{ + if (g_swell_mouse_relmode || g_swell_touchptr) return false; + + #if SWELL_TARGET_GDK == 3 + gdk_device_warp(gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())), + gdk_screen_get_default(), + X, Y); + #else + gdk_display_warp_pointer(gdk_display_get_default(),gdk_screen_get_default(), X, Y); + #endif + return true; +} + +static void getHotSpotForFile(const char *fn, POINT *pt) +{ + FILE *fp = WDL_fopenA(fn,"rb"); + if (!fp) return; + unsigned char buf[32]; + if (fread(buf,1,6,fp)==6 && !buf[0] && !buf[1] && buf[2] == 2 && buf[3] == 0 && buf[4] == 1 && buf[5] == 0) + { + if (fread(buf,1,16,fp)==16) + { + pt->x = buf[4]|(buf[5]<<8); + pt->y = buf[6]|(buf[7]<<8); + } + } + fclose(fp); +} + +HCURSOR SWELL_LoadCursorFromFile(const char *fn) +{ + GdkPixbuf *pb = gdk_pixbuf_new_from_file(fn,NULL); + if (pb) + { + POINT hs = {0,}; + getHotSpotForFile(fn,&hs); + GdkCursor *curs = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),pb,hs.x,hs.y); + g_object_unref(pb); + return (HCURSOR) curs; + } + return NULL; +} + +HCURSOR SWELL_LoadCursor(const char *_idx) +{ + GdkCursorType def = GDK_LEFT_PTR; + if (_idx == IDC_NO) def = GDK_PIRATE; + else if (_idx == IDC_SIZENWSE) def = GDK_BOTTOM_LEFT_CORNER; + else if (_idx == IDC_SIZENESW) def = GDK_BOTTOM_RIGHT_CORNER; + else if (_idx == IDC_SIZEALL) def = GDK_FLEUR; + else if (_idx == IDC_SIZEWE) def = GDK_RIGHT_SIDE; + else if (_idx == IDC_SIZENS) def = GDK_TOP_SIDE; + else if (_idx == IDC_ARROW) def = GDK_LEFT_PTR; + else if (_idx == IDC_HAND) def = GDK_HAND1; + else if (_idx == IDC_UPARROW) def = GDK_CENTER_PTR; + else if (_idx == IDC_IBEAM) def = GDK_XTERM; + else + { + SWELL_CursorResourceIndex *p = SWELL_curmodule_cursorresource_head; + while (p) + { + if (p->resid == _idx) + { + if (p->cachedCursor) return p->cachedCursor; + // todo: load from p->resname, into p->cachedCursor, p->hotspot + char buf[1024]; + GetModuleFileName(NULL,buf,sizeof(buf)); + WDL_remove_filepart(buf); + snprintf_append(buf,sizeof(buf),"/Resources/%s.cur",p->resname); + GdkPixbuf *pb = gdk_pixbuf_new_from_file(buf,NULL); + if (pb) + { + getHotSpotForFile(buf,&p->hotspot); + GdkCursor *curs = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),pb,p->hotspot.x,p->hotspot.y); + g_object_unref(pb); + return (p->cachedCursor = (HCURSOR) curs); + } + } + p=p->_next; + } + } + + HCURSOR hc= (HCURSOR)gdk_cursor_new_for_display(gdk_display_get_default(),def); + + return hc; +} + +void SWELL_Register_Cursor_Resource(const char *idx, const char *name, int hotspot_x, int hotspot_y) +{ + SWELL_CursorResourceIndex *ri = (SWELL_CursorResourceIndex*)malloc(sizeof(SWELL_CursorResourceIndex)); + ri->hotspot.x = hotspot_x; + ri->hotspot.y = hotspot_y; + ri->resname=name; + ri->cachedCursor=0; + ri->resid = idx; + ri->_next = SWELL_curmodule_cursorresource_head; + SWELL_curmodule_cursorresource_head = ri; +} + +int SWELL_KeyToASCII(int wParam, int lParam, int *newflags) +{ + return 0; +} + +void swell_scaling_init(bool no_auto_hidpi) +{ + #if SWELL_TARGET_GDK == 3 + + if (!no_auto_hidpi && g_swell_ui_scale == 256) + { + int (*gsf)(void*); + void * (*gpm)(GdkDisplay *); + *(void **)&gsf = dlsym(RTLD_DEFAULT,"gdk_monitor_get_scale_factor"); + *(void **)&gpm = dlsym(RTLD_DEFAULT,"gdk_display_get_primary_monitor"); + + if (gpm && gsf) + { + GdkDisplay *gdkdisp = gdk_display_get_default(); + if (gdkdisp) + { + void *m = gpm(gdkdisp); + if (m) + { + int sf = gsf(m); + if (sf > 1 && sf < 8) + g_swell_ui_scale = sf*256; + } + } + } + } + + if (g_swell_ui_scale != 256) + { + GdkDisplay *gdkdisp = gdk_display_get_default(); + if (gdkdisp) + { + void (*p)(GdkDisplay*, gint); + *(void **)&p = dlsym(RTLD_DEFAULT,"gdk_x11_display_set_window_scale"); + if (p) p(gdkdisp,1); + } + } + #endif +} + +BOOL EnumDisplayMonitors(HDC hdc,const LPRECT r,MONITORENUMPROC proc,LPARAM lParam) +{ + GdkScreen *defscr = gdk_screen_get_default(); + const int nmon = gdk_screen_get_n_monitors(defscr); + for (int x = 0; x < nmon; x ++) + { + GdkRectangle rc={0,0,1024,1024}; + gdk_screen_get_monitor_geometry(defscr,x,&rc); + RECT screen_rect, tmp = { rc.x, rc.y,rc.x+rc.width , rc.y+rc.height }; + if (r) + { + if (!IntersectRect(&screen_rect,r,&tmp)) + continue; + } + else + { + screen_rect = tmp; + } + + if (!proc((HMONITOR)(INT_PTR) (x+1),hdc,&screen_rect,lParam)) break; + } + return TRUE; +} +BOOL GetMonitorInfo(HMONITOR hmon, void *inf) +{ + GdkScreen *defscr = gdk_screen_get_default(); + const int nmon = gdk_screen_get_n_monitors(defscr); + const int monidx = ((int) (INT_PTR) hmon)-1; + if (monidx<0 || monidx >= nmon) return FALSE; + + MONITORINFOEX *a = (MONITORINFOEX*)inf; + if (a->cbSize < sizeof(MONITORINFO)) return FALSE; + a->dwFlags = 0; + GdkRectangle rc={0,0,1024,1024}; + gdk_screen_get_monitor_geometry(defscr,monidx,&rc); + RECT tmp = { rc.x, rc.y,rc.x+rc.width , rc.y+rc.height }; + a->rcMonitor = a->rcWork = tmp; + + if (a->cbSize > sizeof(MONITORINFO)) + { + const int maxlen = (int) (a->cbSize - sizeof(MONITORINFO)); + const char *s = gdk_screen_get_monitor_plug_name(defscr,monidx); + if (!s) return FALSE; + lstrcpyn_safe(a->szDevice,s,maxlen); + } + + return TRUE; +} + + + +void SWELL_SetViewGL(HWND h, char wantGL) +{ + // only works for X bridge windows + if (h && h->m_classname == bridge_class_name && h->m_private_data) + { + bridgeState *bs = (bridgeState*)h->m_private_data; + if (wantGL && !bs->gl_ctx) + { + static GLint att[] = { GLX_RGBA, None }; + XVisualInfo* vi = glXChooseVisual(bs->native_disp, 0, att); + bs->gl_ctx = glXCreateContext(bs->native_disp,vi,NULL,1); + } + else if (!wantGL && bs->gl_ctx) + { + if (s_last_gl_ctx == bs) + { + glXMakeCurrent(bs->native_disp,None, NULL); + s_last_gl_ctx = NULL; + } + + glXDestroyContext(bs->native_disp,bs->gl_ctx); + bs->gl_ctx = NULL; + } + } +} + +bool SWELL_GetViewGL(HWND h) +{ + return h && h->m_classname == bridge_class_name && h->m_private_data && + ((bridgeState*)h->m_private_data)->gl_ctx != NULL; +} + +bool SWELL_SetGLContextToView(HWND h) +{ + if (h) + { + if (h->m_classname == bridge_class_name && h->m_private_data) + { + bridgeState *bs = (bridgeState*)h->m_private_data; + if (bs->gl_ctx) + { + glXMakeCurrent(bs->native_disp, bs->native_w, bs->gl_ctx); + s_last_gl_ctx = bs; + return true; + } + } + return false; + } + + if (s_last_gl_ctx) + { + glXMakeCurrent(s_last_gl_ctx->native_disp,None, NULL); + s_last_gl_ctx = NULL; + } + return true; +} + + + +#endif +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-generic-headless.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-generic-headless.cpp new file mode 100644 index 000000000..61c2b1c6c --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-generic-headless.cpp @@ -0,0 +1,316 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + */ + + +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" + +#ifndef SWELL_TARGET_GDK + +#include "swell-internal.h" +#include "swell-dlggen.h" +#include "../wdlcstring.h" + +void swell_oswindow_destroy(HWND hwnd) +{ + if (hwnd && hwnd->m_oswindow) + { + if (SWELL_focused_oswindow == hwnd->m_oswindow) SWELL_focused_oswindow = NULL; + hwnd->m_oswindow=NULL; +#ifdef SWELL_LICE_GDI + delete hwnd->m_backingstore; + hwnd->m_backingstore=0; +#endif + } +} +void swell_oswindow_update_text(HWND hwnd) +{ + if (hwnd) printf("SWELL: swt '%s'\n",hwnd->m_title.Get()); + +} + +void swell_oswindow_focus(HWND hwnd) +{ + if (!hwnd) + { + SWELL_focused_oswindow = NULL; + } + while (hwnd && !hwnd->m_oswindow) hwnd=hwnd->m_parent; + if (hwnd && hwnd->m_oswindow != SWELL_focused_oswindow) + { + SWELL_focused_oswindow = hwnd->m_oswindow; + } +} + +void swell_recalcMinMaxInfo(HWND hwnd) +{ +} + + +void SWELL_initargs(int *argc, char ***argv) +{ +} + +void swell_oswindow_updatetoscreen(HWND hwnd, RECT *rect) +{ +} + +void swell_oswindow_manage(HWND hwnd, bool wantfocus) +{ + if (!hwnd) return; + + bool isVis = !!hwnd->m_oswindow; + bool wantVis = !hwnd->m_parent && hwnd->m_visible; + + if (isVis != wantVis) + { + if (!wantVis) swell_oswindow_destroy(hwnd); + else + { + // generic implementation + hwnd->m_oswindow = hwnd; + if (wantfocus) swell_oswindow_focus(hwnd); + } + } + if (wantVis) swell_oswindow_update_text(hwnd); +} + +void SWELL_RunEvents() +{ +} + + +void swell_oswindow_update_style(HWND hwnd, LONG oldstyle) +{ +} + +void swell_oswindow_update_enable(HWND hwnd) +{ +} + +int SWELL_SetWindowLevel(HWND hwnd, int newlevel) +{ + int rv=0; + if (hwnd) + { + rv = hwnd->m_israised ? 1 : 0; + hwnd->m_israised = newlevel>0; + } + return rv; +} + +void SWELL_GetViewPort(RECT *r, const RECT *sourcerect, bool wantWork) +{ + r->left=r->top=0; + r->right=1024; + r->bottom=768; +} + + +bool GetWindowRect(HWND hwnd, RECT *r) +{ + if (!hwnd) return false; + if (hwnd->m_oswindow) + { + *r = hwnd->m_position; + return true; + } + + r->left=r->top=0; + ClientToScreen(hwnd,(LPPOINT)r); + r->right = r->left + hwnd->m_position.right - hwnd->m_position.left; + r->bottom = r->top + hwnd->m_position.bottom - hwnd->m_position.top; + return true; +} + +void swell_oswindow_begin_resize(SWELL_OSWINDOW wnd) { } +void swell_oswindow_resize(SWELL_OSWINDOW wnd, int reposflag, RECT f) { } +void swell_oswindow_postresize(HWND hwnd, RECT f) { } +void swell_oswindow_invalidate(HWND hwnd, const RECT *r) { } + +void UpdateWindow(HWND hwnd) { } + +static WDL_IntKeyedArray m_clip_recs(GlobalFree); +static WDL_PtrList m_clip_curfmts; + +bool OpenClipboard(HWND hwndDlg) +{ + RegisterClipboardFormat(NULL); + return true; +} + +void CloseClipboard() { } + +UINT EnumClipboardFormats(UINT lastfmt) +{ + int x=0; + for (;;) + { + int fmt=0; + if (!m_clip_recs.Enumerate(x++,&fmt)) return 0; + if (lastfmt == 0) return fmt; + + if ((UINT)fmt == lastfmt) return m_clip_recs.Enumerate(x++,&fmt) ? fmt : 0; + } +} + +HANDLE GetClipboardData(UINT type) +{ + return m_clip_recs.Get(type); +} + + +void EmptyClipboard() +{ + m_clip_recs.DeleteAll(); +} + +void SetClipboardData(UINT type, HANDLE h) +{ + if (h) m_clip_recs.Insert(type,h); + else m_clip_recs.Delete(type); +} + +UINT RegisterClipboardFormat(const char *desc) +{ + if (!m_clip_curfmts.GetSize()) + { + m_clip_curfmts.Add(strdup("SWELL__CF_TEXT")); + m_clip_curfmts.Add(strdup("SWELL__CF_HDROP")); + } + if (!desc || !*desc) return 0; + int x; + const int n = m_clip_curfmts.GetSize(); + for(x=0;xx=0; + pt->y=0; +} + + +WORD GetAsyncKeyState(int key) +{ + return 0; +} + + +DWORD GetMessagePos() +{ + return 0; +} + +HWND SWELL_CreateXBridgeWindow(HWND viewpar, void **wref, const RECT *r) +{ + *wref = NULL; + return NULL; +} + + + +void SWELL_InitiateDragDrop(HWND hwnd, RECT* srcrect, const char* srcfn, void (*callback)(const char* dropfn)) +{ +} + +// owner owns srclist, make copies here etc +void SWELL_InitiateDragDropOfFileList(HWND hwnd, RECT *srcrect, const char **srclist, int srccount, HICON icon) +{ +} + +void SWELL_FinishDragDrop() { } + +static HCURSOR m_last_setcursor; + +void SWELL_SetCursor(HCURSOR curs) +{ + m_last_setcursor=curs; +} + +HCURSOR SWELL_GetCursor() +{ + return m_last_setcursor; +} +HCURSOR SWELL_GetLastSetCursor() +{ + return m_last_setcursor; +} + +static int m_curvis_cnt; +bool SWELL_IsCursorVisible() +{ + return m_curvis_cnt>=0; +} + +int SWELL_ShowCursor(BOOL bShow) +{ + m_curvis_cnt += (bShow?1:-1); + return m_curvis_cnt; +} + +BOOL SWELL_SetCursorPos(int X, int Y) +{ + return false; +} + +HCURSOR SWELL_LoadCursorFromFile(const char *fn) +{ + return NULL; +} + + +static SWELL_CursorResourceIndex *SWELL_curmodule_cursorresource_head; + +HCURSOR SWELL_LoadCursor(const char *_idx) +{ + return NULL; +} + + + +void SWELL_Register_Cursor_Resource(const char *idx, const char *name, int hotspot_x, int hotspot_y) +{ + SWELL_CursorResourceIndex *ri = (SWELL_CursorResourceIndex*)malloc(sizeof(SWELL_CursorResourceIndex)); + ri->hotspot.x = hotspot_x; + ri->hotspot.y = hotspot_y; + ri->resname=name; + ri->cachedCursor=0; + ri->resid = idx; + ri->_next = SWELL_curmodule_cursorresource_head; + SWELL_curmodule_cursorresource_head = ri; +} + +int SWELL_KeyToASCII(int wParam, int lParam, int *newflags) +{ + return 0; +} + +int swell_is_app_inactive() { return 0; } + +#endif +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-ini.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-ini.cpp new file mode 100644 index 000000000..25cfb9207 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-ini.cpp @@ -0,0 +1,603 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + This file implements basic win32 GetPrivateProfileString / etc support. + It works by caching reads, but writing through on every write that is required (to ensure + that updates take, especially when being updated from multiple modules who have their own + cache of the .ini file). + + It is threadsafe, but in theory if two processes are trying to access the same ini, + results may go a bit unpredictable (but in general the file should NOT get corrupted, + we hope). + +*/ + +#ifndef SWELL_PROVIDED_BY_APP + + +#include "swell.h" +#include "../assocarray.h" +#include "../wdlcstring.h" +#include "../mutex.h" +#include "../queue.h" +#include +#include +#include + +static void deleteStringKeyedArray(WDL_StringKeyedArray *p) { delete p; } + +struct iniFileContext +{ + iniFileContext() : m_sections(false,deleteStringKeyedArray) + { + m_curfn=NULL; + m_lastaccesscnt=0; + m_curfn_time=0; + m_curfn_sz=0; + } + ~iniFileContext() { } + + WDL_UINT64 m_lastaccesscnt; + time_t m_curfn_time; + int m_curfn_sz; + char *m_curfn; + + WDL_StringKeyedArray< WDL_StringKeyedArray * > m_sections; + +}; + +#define NUM_OPEN_CONTEXTS 32 +static iniFileContext s_ctxs[NUM_OPEN_CONTEXTS]; +static WDL_Mutex m_mutex; + +static time_t getfileupdtimesize(const char *fn, int *szOut) +{ + struct stat st; + *szOut = 0; + if (!fn || !fn[0] || stat(fn,&st)) return 0; + *szOut = (int)st.st_size; + return st.st_mtime; +} + +static bool fgets_to_typedbuf(WDL_TypedBuf *buf, FILE *fp) +{ + int rdpos=0; + while (rdpos < 1024*1024*32) + { + if (buf->GetSize()Resize(rdpos+8192); + if (buf->GetSize()Get()+rdpos; + *p=0; + if (!fgets(p,buf->GetSize()-rdpos,fp) || !*p) break; + while (*p) p++; + if (p[-1] == '\r' || p[-1] == '\n') break; + + rdpos = (int) (p - buf->Get()); + } + return buf->GetSize()>0 && buf->Get()[0]; +} + +// return true on success +static iniFileContext *GetFileContext(const char *name) +{ + static WDL_UINT64 acc_cnt; + int best_z = 0; + char fntemp[512]; + if (!name || !strstr(name,"/")) + { + extern char *g_swell_defini; + if (g_swell_defini) + { + lstrcpyn_safe(fntemp,g_swell_defini,sizeof(fntemp)); + } + else + { + const char *p = getenv("HOME"); + snprintf(fntemp,sizeof(fntemp),"%s/.libSwell.ini", + p && *p ? p : "/tmp"); + } + if (name && *name) + { + WDL_remove_fileext(fntemp); + snprintf_append(fntemp,sizeof(fntemp),"_%s%s",name, + stricmp(WDL_get_fileext(name),".ini")?".ini":""); + } + name = fntemp; + } + + { + int w; + WDL_UINT64 bestcnt = 0; + bestcnt--; + + for (w=0;wm_lastaccesscnt=++acc_cnt; + + int sz=0; + if (!ctx->m_curfn || stricmp(ctx->m_curfn,name) || ctx->m_curfn_time != getfileupdtimesize(ctx->m_curfn,&sz) || sz != ctx->m_curfn_sz) + { + ctx->m_sections.DeleteAll(); +// printf("reinitting to %s\n",name); + if (!ctx->m_curfn || stricmp(ctx->m_curfn,name)) + { + free(ctx->m_curfn); + ctx->m_curfn=strdup(name); + } + FILE *fp = WDL_fopenA(name,"r"); + + if (!fp) + { + ctx->m_curfn_time=0; + ctx->m_curfn_sz=0; + return ctx; // allow to proceed (empty file) + } + + flock(fileno(fp),LOCK_SH); + + // parse .ini file + WDL_StringKeyedArray *cursec=NULL; + + int lcnt=0; + for (;;) + { + static WDL_TypedBuf _buf; + if (!fgets_to_typedbuf(&_buf,fp)) break; + + char *buf = _buf.Get(); + if (!ctx->m_sections.GetSize()) + { + lcnt += strlen(buf); + if (lcnt > 256*1024) break; // dont bother reading more than 256kb if no section encountered + } + char *p=buf; + + while (*p) p++; + + if (p>buf) + { + p--; + while (p >= buf && (*p==' ' || *p == '\r' || *p == '\n' || *p == '\t')) p--; + p[1]=0; + } + p=buf; + while (*p == ' ' || *p == '\t') p++; + if (p[0] == '[') + { + char *p2=p; + while (*p2 && *p2 != ']') p2++; + if (*p2) + { + *p2=0; + if (cursec) cursec->Resort(); + + if (p[1]) + { + cursec = ctx->m_sections.Get(p+1); + if (!cursec) + { + cursec = new WDL_StringKeyedArray(false,WDL_StringKeyedArray::freecharptr); + ctx->m_sections.Insert(p+1,cursec); + } + else cursec->DeleteAll(); + } + else cursec=0; + } + } + else if (cursec) + { + char *t=strstr(p,"="); + if (t) + { + *t++=0; + if (*p) + cursec->AddUnsorted(p,strdup(t)); + } + } + } + ctx->m_curfn_time = getfileupdtimesize(name,&ctx->m_curfn_sz); + flock(fileno(fp),LOCK_UN); + fclose(fp); + + if (cursec) cursec->Resort(); + } + return ctx; +} + +static void WriteBackFile(iniFileContext *ctx) +{ + if (!ctx||!ctx->m_curfn) return; + char newfn[1024]; + lstrcpyn_safe(newfn,ctx->m_curfn,sizeof(newfn)-8); + { + char *p=newfn; + while (*p) p++; + while (p>newfn && p[-1] != '/') p--; + char lc = '.'; + while (*p) + { + char c = *p; + *p++ = lc; + lc = c; + } + *p++ = lc; + strcpy(p,".new"); + } + + FILE *fp = WDL_fopenA(newfn,"w"); + if (!fp) return; + + flock(fileno(fp),LOCK_EX); + + int x; + for (x = 0; ; x ++) + { + const char *secname=NULL; + WDL_StringKeyedArray * cursec = ctx->m_sections.Enumerate(x,&secname); + if (!cursec || !secname) break; + + fprintf(fp,"[%s]\n",secname); + int y; + for (y=0;;y++) + { + const char *keyname = NULL; + const char *keyvalue = cursec->Enumerate(y,&keyname); + if (!keyvalue || !keyname) break; + if (*keyname) fprintf(fp,"%s=%s\n",keyname,keyvalue); + } + fprintf(fp,"\n"); + } + + fflush(fp); + flock(fileno(fp),LOCK_UN); + fclose(fp); + + if (!rename(newfn,ctx->m_curfn)) + { + ctx->m_curfn_time = getfileupdtimesize(ctx->m_curfn,&ctx->m_curfn_sz); + } + else + { + // error updating, hmm how to handle this? + } +} + +BOOL WritePrivateProfileSection(const char *appname, const char *strings, const char *fn) +{ + if (!appname) return FALSE; + WDL_MutexLock lock(&m_mutex); + iniFileContext *ctx = GetFileContext(fn); + if (!ctx) return FALSE; + + WDL_StringKeyedArray * cursec = ctx->m_sections.Get(appname); + if (!cursec) + { + if (!*strings) return TRUE; + + cursec = new WDL_StringKeyedArray(false,WDL_StringKeyedArray::freecharptr); + ctx->m_sections.Insert(appname,cursec); + } + else cursec->DeleteAll(); + + if (*strings) + { + while (*strings) + { + char buf[8192]; + lstrcpyn_safe(buf,strings,sizeof(buf)); + char *p = buf; + while (*p && *p != '=') p++; + if (*p) + { + *p++=0; + cursec->Insert(buf,strdup(strings + (p-buf))); + } + + strings += strlen(strings)+1; + } + } + WriteBackFile(ctx); + + return TRUE; +} + + +BOOL WritePrivateProfileString(const char *appname, const char *keyname, const char *val, const char *fn) +{ + if (!appname || (keyname && !*keyname)) return FALSE; +// printf("writing %s %s %s %s\n",appname,keyname,val,fn); + WDL_MutexLock lock(&m_mutex); + + iniFileContext *ctx = GetFileContext(fn); + if (!ctx) return FALSE; + + if (!keyname) + { + if (ctx->m_sections.Get(appname)) + { + ctx->m_sections.Delete(appname); + WriteBackFile(ctx); + } + } + else + { + WDL_StringKeyedArray * cursec = ctx->m_sections.Get(appname); + if (!val) + { + if (cursec && cursec->Get(keyname)) + { + cursec->Delete(keyname); + WriteBackFile(ctx); + } + } + else + { + const char *p; + if (!cursec || !(p=cursec->Get(keyname)) || strcmp(p,val)) + { + if (!cursec) + { + cursec = new WDL_StringKeyedArray(false,WDL_StringKeyedArray::freecharptr); + ctx->m_sections.Insert(appname,cursec); + } + cursec->Insert(keyname,strdup(val)); + WriteBackFile(ctx); + } + } + + } + + return TRUE; +} + +static void lstrcpyn_trimmed(char* dest, const char* src, int len) +{ + if (len<1) return; + // Mimic Win32 behavior of stripping quotes and whitespace + while (*src==' ' || *src=='\t') ++src; // Strip beginning whitespace + + const char *end = src; + if (*end) while (end[1]) end++; + + while (end >= src && (*end==' ' || *end=='\t')) --end; // Strip end whitespace + + if (end > src && ((*src=='\"' && *end=='\"') || (*src=='\'' && *end=='\''))) + { + // Strip initial set of "" or '' + ++src; + --end; + } + + int newlen = (int) (end-src+2); + if (newlen < 1) newlen = 1; + else if (newlen > len) newlen = len; + + lstrcpyn_safe(dest, src, newlen); +} + +DWORD GetPrivateProfileSection(const char *appname, char *strout, DWORD strout_len, const char *fn) +{ + WDL_MutexLock lock(&m_mutex); + + if (!strout || strout_len<2) + { + if (strout && strout_len==1) *strout=0; + return 0; + } + iniFileContext *ctx= GetFileContext(fn); + int szOut=0; + WDL_StringKeyedArray *cursec = ctx ? ctx->m_sections.Get(appname) : NULL; + + if (ctx && cursec) + { + int x; + for(x=0;xGetSize();x++) + { + const char *kv = NULL; + const char *val = cursec->Enumerate(x,&kv); + if (val && kv) + { + int l; + +#define WRSTR(v) \ + l = (int)strlen(v); \ + if (l > (int)strout_len - szOut - 2) l = (int)strout_len - 2 - szOut; \ + if (l>0) { memcpy(strout+szOut,v,l); szOut+=l; } + + WRSTR(kv) + WRSTR("=") +#undef WRSTR + + lstrcpyn_trimmed(strout+szOut, val, (int)strout_len - szOut - 2); + szOut += strlen(strout+szOut); + + l=1; + if (l > (int)strout_len - szOut - 1) l = (int)strout_len - 1 - szOut; + if (l>0) { memset(strout+szOut,0,l); szOut+=l; } + if (szOut >= (int)strout_len-1) + { + strout[strout_len-1]=0; + return strout_len-2; + } + } + } + } + strout[szOut]=0; + if (!szOut) strout[1]=0; + return szOut; +} + +DWORD GetPrivateProfileString(const char *appname, const char *keyname, const char *def, char *ret, int retsize, const char *fn) +{ + WDL_MutexLock lock(&m_mutex); + +// printf("getprivateprofilestring: %s\n",fn); + iniFileContext *ctx= GetFileContext(fn); + + if (ctx) + { + if (!appname||!keyname) + { + WDL_Queue tmpbuf; + if (!appname) + { + int x; + for (x = 0;; x ++) + { + const char *secname=NULL; + if (!ctx->m_sections.Enumerate(x,&secname) || !secname) break; + if (*secname) tmpbuf.Add(secname,(int)strlen(secname)+1); + } + } + else + { + WDL_StringKeyedArray *cursec = ctx->m_sections.Get(appname); + if (cursec) + { + int y; + for (y = 0; ; y ++) + { + const char *k=NULL; + if (!cursec->Enumerate(y,&k)||!k) break; + if (*k) tmpbuf.Add(k,(int)strlen(k)+1); + } + } + } + + int sz=tmpbuf.GetSize()-1; + if (sz<0) + { + ret[0]=ret[1]=0; + return 0; + } + if (sz > retsize-2) sz=retsize-2; + memcpy(ret,tmpbuf.Get(),sz); + ret[sz]=ret[sz+1]=0; + + return (DWORD)sz; + } + + WDL_StringKeyedArray *cursec = ctx->m_sections.Get(appname); + if (cursec) + { + const char *val = cursec->Get(keyname); + if (val) + { + lstrcpyn_trimmed(ret,val,retsize); + return (DWORD)strlen(ret); + } + } + } +// printf("def %s %s %s %s\n",appname,keyname,def,fn); + lstrcpyn_safe(ret,def?def:"",retsize); + return (DWORD)strlen(ret); +} + +int GetPrivateProfileInt(const char *appname, const char *keyname, int def, const char *fn) +{ + char buf[512]; + GetPrivateProfileString(appname,keyname,"",buf,sizeof(buf),fn); + if (buf[0]) + { + int a=atoi(buf); + if (a||buf[0]=='0') return a; + } + return def; +} + +static bool __readbyte(char *src, unsigned char *out) +{ + unsigned char cv=0; + int s=4; + while(s>=0) + { + if (*src >= '0' && *src <= '9') cv += (*src-'0')<= 'a' && *src <= 'f') cv += (*src-'a' + 10)<= 'A' && *src <= 'F') cv += (*src-'A' + 10)<0) + { + if (!__readbyte(src,&cv)) break; + *bufout++ = cv; + sum += cv; + src+=2; + } + ret = bufsz<0 && __readbyte(src,&cv) && cv==sum; + } + free(tmp); + //printf("getprivateprofilestruct returning %d\n",ret); + return ret; +} + +BOOL WritePrivateProfileStruct(const char *appname, const char *keyname, const void *buf, int bufsz, const char *fn) +{ + if (!keyname || !buf) return WritePrivateProfileString(appname,keyname,(const char *)buf,fn); + char *tmp=(char *)malloc((bufsz+1)*2+1); + if (!tmp) return 0; + char *p = tmp; + unsigned char sum=0; + unsigned char *src=(unsigned char *)buf; + while (bufsz-- > 0) + { + sprintf(p,"%02X",*src); + sum+=*src++; + p+=2; + } + sprintf(p,"%02X",sum); + + BOOL ret=WritePrivateProfileString(appname,keyname,tmp,fn); + free(tmp); + return ret; +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-internal.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-internal.h new file mode 100644 index 000000000..63b39d509 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-internal.h @@ -0,0 +1,1333 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. +*/ + +#ifndef _SWELL_INTERNAL_H_ +#define _SWELL_INTERNAL_H_ + +#include "../ptrlist.h" + +class SWELL_ListView_Row +{ +public: + SWELL_ListView_Row() : m_param(0), m_imageidx(0), m_tmp(0) { } + ~SWELL_ListView_Row() { m_vals.Empty(true,free); } + WDL_PtrList m_vals; + + LPARAM m_param; + int m_imageidx; + int m_tmp; // Cocoa uses this temporarily, generic uses it as a mask (1= selected) +}; + +struct HTREEITEM__; + +#ifdef SWELL_TARGET_OSX + +#if 0 + // at some point we should enable this and use it in most SWELL APIs that call Cocoa code... + #define SWELL_BEGIN_TRY @try { + #define SWELL_END_TRY(x) } @catch (NSException *ex) { NSLog(@"SWELL exception in %s:%d :: %@:%@\n",__FILE__,__LINE__,[ex name], [ex reason]); x } +#else + #define SWELL_BEGIN_TRY + #define SWELL_END_TRY(x) +#endif + +#define __SWELL_PREFIX_CLASSNAME3(a,b) a##b +#define __SWELL_PREFIX_CLASSNAME2(a,b) __SWELL_PREFIX_CLASSNAME3(a,b) +#define __SWELL_PREFIX_CLASSNAME(cname) __SWELL_PREFIX_CLASSNAME2(SWELL_APP_PREFIX,cname) + +// this defines interfaces to internal swell classes +#define SWELL_hwndChild __SWELL_PREFIX_CLASSNAME(_hwnd) +#define SWELL_hwndCarbonHost __SWELL_PREFIX_CLASSNAME(_hwndcarbonhost) + +#define SWELL_ModelessWindow __SWELL_PREFIX_CLASSNAME(_modelesswindow) +#define SWELL_ModalDialog __SWELL_PREFIX_CLASSNAME(_dialogbox) + +#define SWELL_TextField __SWELL_PREFIX_CLASSNAME(_textfield) +#define SWELL_ListView __SWELL_PREFIX_CLASSNAME(_listview) +#define SWELL_TreeView __SWELL_PREFIX_CLASSNAME(_treeview) +#define SWELL_TabView __SWELL_PREFIX_CLASSNAME(_tabview) +#define SWELL_ProgressView __SWELL_PREFIX_CLASSNAME(_progind) +#define SWELL_TextView __SWELL_PREFIX_CLASSNAME(_textview) +#define SWELL_BoxView __SWELL_PREFIX_CLASSNAME(_box) +#define SWELL_Button __SWELL_PREFIX_CLASSNAME(_button) +#define SWELL_PopUpButton __SWELL_PREFIX_CLASSNAME(_pub) +#define SWELL_ComboBox __SWELL_PREFIX_CLASSNAME(_cbox) + +#define SWELL_StatusCell __SWELL_PREFIX_CLASSNAME(_statuscell) +#define SWELL_ListViewCell __SWELL_PREFIX_CLASSNAME(_listviewcell) +#define SWELL_ODListViewCell __SWELL_PREFIX_CLASSNAME(_ODlistviewcell) +#define SWELL_ODButtonCell __SWELL_PREFIX_CLASSNAME(_ODbuttoncell) +#define SWELL_ImageButtonCell __SWELL_PREFIX_CLASSNAME(_imgbuttoncell) + +#define SWELL_FocusRectWnd __SWELL_PREFIX_CLASSNAME(_drawfocusrectwnd) + +#define SWELL_DataHold __SWELL_PREFIX_CLASSNAME(_sdh) +#define SWELL_ThreadTmp __SWELL_PREFIX_CLASSNAME(_thread) +#define SWELL_PopupMenuRecv __SWELL_PREFIX_CLASSNAME(_trackpopupmenurecv) + +#define SWELL_TimerFuncTarget __SWELL_PREFIX_CLASSNAME(_tft) + + +#define SWELL_Menu __SWELL_PREFIX_CLASSNAME(_menu) + +#ifdef __OBJC__ + +#ifndef MAC_OS_X_VERSION_10_7 +typedef struct _NSDraggingSession NSDraggingSession; +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +typedef int NSInteger; +typedef unsigned int NSUInteger; +#endif + +@interface SWELL_Menu : NSMenu +{ +} +-(void)dealloc; +- (id)copyWithZone:(NSZone *)zone; +@end + +@interface SWELL_DataHold : NSObject +{ + @public + void *m_data; +} +-(id) initWithVal:(void *)val; +-(void *) getValue; +@end + +@interface SWELL_TimerFuncTarget : NSObject +{ + TIMERPROC m_cb; + HWND m_hwnd; + UINT_PTR m_timerid; +} +-(id) initWithId:(UINT_PTR)tid hwnd:(HWND)h callback:(TIMERPROC)cb; +-(void)SWELL_Timer:(id)sender; +@end + +typedef struct OwnedWindowListRec +{ + NSWindow *hwnd; + struct OwnedWindowListRec *_next; +} OwnedWindowListRec; + +typedef struct WindowPropRec +{ + char *name; // either <64k or a strdup'd name + void *data; + struct WindowPropRec *_next; +} WindowPropRec; + + + +@interface SWELL_TextField : NSTextField +{ + @public + bool m_last_dark_mode; + bool m_ctlcolor_set; + bool m_disable_menu; +} +- (id) init; +- (void)setNeedsDisplay:(BOOL)flag; +- (void)setNeedsDisplayInRect:(NSRect)rect; +- (void)drawRect:(NSRect)rect; +- (void)initColors:(int)darkmode; // -1 to not update darkmode but trigger update of colors +- (void)swellDisableContextMenu:(bool)dis; +- (NSMenu *)textView:(NSTextView *)view menu:(NSMenu *)menu forEvent:(NSEvent *)event atIndex:(NSUInteger)charIndex; +@end + +@interface SWELL_TabView : NSTabView +{ + NSInteger m_tag; + id m_dest; +} +@end + +@interface SWELL_ProgressView : NSProgressIndicator +{ + NSInteger m_tag; +} +@end + +@interface SWELL_ListViewCell : NSTextFieldCell +{ +} +-(NSColor *)highlightColorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView; +- (NSRect)drawingRectForBounds:(NSRect)rect; +@end + +@interface SWELL_StatusCell : SWELL_ListViewCell +{ + NSImage *status; +} +-(id)initNewCell; +-(void)setStatusImage:(NSImage *)img; +- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView; +@end + +@interface SWELL_TreeView : NSOutlineView +{ + @public + bool m_fakerightmouse; + LONG style; + WDL_PtrList *m_items; + NSColor *m_fgColor; + NSMutableArray *m_selColors; +} +-(id) init; +-(void) dealloc; +-(bool) findItem:(HTREEITEM)item parOut:(HTREEITEM__ **)par idxOut:(int *)idx; +-(void)mouseDown:(NSEvent *)theEvent; +-(void)mouseDragged:(NSEvent *)theEvent; +-(void)mouseUp:(NSEvent *)theEvent; +- (void)rightMouseUp:(NSEvent *)theEvent; +- (void)highlightSelectionInClipRect:(NSRect)theClipRect; + +// data source +-(NSInteger) outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item; +- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item; +- (id)outlineView:(NSOutlineView *)outlineView + child:(NSInteger)index + ofItem:(id)item; +- (id)outlineView:(NSOutlineView *)outlineView + objectValueForTableColumn:(NSTableColumn *)tableColumn + byItem:(id)item; + + + +- (BOOL)outlineView:(NSOutlineView *)outlineView + writeItems:(NSArray *)items + toPasteboard:(NSPasteboard *)pasteboard; +- (BOOL)outlineView:(NSOutlineView *)outlineView + acceptDrop:(id)info + item:(id)item + childIndex:(NSInteger)index; +- (void)outlineView:(NSOutlineView *)outlineView + draggingSession:(NSDraggingSession *)session + endedAtPoint:(NSPoint)screenPoint + operation:(NSDragOperation)operation; +- (void)outlineView:(NSOutlineView *)outlineView + draggingSession:(NSDraggingSession *)session + willBeginAtPoint:(NSPoint)screenPoint + forItems:(NSArray *)draggedItems; +- (NSDragOperation)outlineView:(NSOutlineView *)outlineView + validateDrop:(id)info + proposedItem:(id)item + proposedChildIndex:(NSInteger)index; + +@end + +@interface SWELL_ListView : NSTableView +{ + int m_leftmousemovecnt; + bool m_fakerightmouse; + @public + LONG style; + int ownermode_cnt; + int m_start_item; + int m_start_subitem; + int m_start_item_clickmode; + + int m_lbMode; + WDL_PtrList *m_items; + WDL_PtrList *m_cols; + WDL_PtrList *m_status_imagelist; + int m_status_imagelist_type; + int m_fastClickMask; + NSColor *m_fgColor; + NSMutableArray *m_selColors; + + // these are for the new yosemite mouse handling code + int m_last_plainly_clicked_item, m_last_shift_clicked_item; + +} +-(LONG)getSwellStyle; +-(void)setSwellStyle:(LONG)st; +-(int)getSwellNotificationMode; +-(void)setSwellNotificationMode:(int)lbMode; +-(NSInteger)columnAtPoint:(NSPoint)pt; +-(int)getColumnPos:(int)idx; // get current position of column that was originally at idx +-(int)getColumnIdx:(int)pos; // get original index of column that is currently at position + +-(BOOL)accessibilityPerformShowMenu; +@end + +@interface SWELL_ImageButtonCell : NSButtonCell +{ +} +- (NSRect)drawTitle:(NSAttributedString *)title withFrame:(NSRect)frame inView:(NSView *)controlView; +@end + +@interface SWELL_ODButtonCell : NSButtonCell +{ +} +@end + +@interface SWELL_ODListViewCell : NSCell +{ + SWELL_ListView *m_ownctl; + int m_lastidx; +} +-(void)setOwnerControl:(SWELL_ListView *)t; +-(void)setItemIdx:(int)idx; +@end + +@interface SWELL_Button : NSButton +{ + void *m_swellGDIimage; + LONG_PTR m_userdata; + int m_radioflags; +} +-(int)swellGetRadioFlags; +-(void)swellSetRadioFlags:(int)f; +-(LONG_PTR)getSwellUserData; +-(void)setSwellUserData:(LONG_PTR)val; +-(void)setSwellGDIImage:(void *)par; +-(void *)getSwellGDIImage; +@end + +@interface SWELL_TextView : NSTextView +{ + NSInteger m_tag; + bool m_disable_menu; +} +-(id)init; +-(NSInteger) tag; +-(void) setTag:(NSInteger)tag; +- (void)swellDisableContextMenu:(bool)dis; +- (bool)swellWantsContextMenu; +@end + +@interface SWELL_BoxView : NSBox +{ + NSInteger m_tag; +@public + int m_etch_mode; // if nonzero, SS_ETCHEDHORZ etc +} +-(NSInteger) tag; +-(void) setTag:(NSInteger)tag; +-(void) drawRect:(NSRect)r; +-(int) swellIsEtchBox; +@end + +@interface SWELL_FocusRectWnd : NSView +{ +} +@end + +@interface SWELL_ThreadTmp : NSObject +{ +@public + void *a, *b; +} +-(void)bla:(id)obj; +@end + + + +@interface SWELL_hwndChild : NSView // +{ +@public + int m_enabled; // -1 if preventing focus + DLGPROC m_dlgproc; + WNDPROC m_wndproc; + LONG_PTR m_userdata; + LONG_PTR m_extradata[32]; + NSInteger m_tag; + int m_isfakerightmouse; + char m_hashaddestroy; // 2 = WM_DESTROY has finished completely + HMENU m_menu; + BOOL m_flip; + bool m_supports_ddrop; + bool m_paintctx_used; + HDC m_paintctx_hdc; + WindowPropRec *m_props; + NSRect m_paintctx_rect; + BOOL m_isopaque; + char m_titlestr[1024]; + unsigned int m_create_windowflags; + NSOpenGLContext *m_glctx; + char m_isdirty; // &1=self needs redraw, &2=children may need redraw + char m_allow_nomiddleman; + id m_lastTopLevelOwner; // save a copy of the owner, if any + id m_access_cacheptrs[6]; + const char *m_classname; + +// only used if not SWELL_NO_METAL + char m_use_metal; // 1=normal mode, 2=full pipeline (GetDC() etc support). -1 is for non-metal async layered mode. -2 for non-metal non-async layered + + // metal state (if used) + char m_metal_dc_dirty; // used to track state during paint or getdc/releasedc. set to 1 if dirty, 2 if GetDC() but no write yet + char m_metal_gravity; // &1=resizing left, &2=resizing top + bool m_metal_retina; // last-retina-state, triggered to true by StretchBlt() with a 2:1 ratio + + bool m_metal_in_needref_list; + RECT m_metal_in_needref_rect; + NSRect m_metal_lastframe; + + id m_metal_texture; // id -- owned if in full pipeline mode, otherwise reference to m_metal_drawable + id m_metal_pipelineState; // id -- only used in full pipeline mode + id m_metal_commandQueue; // id -- only used in full pipeline mode + id m_metal_drawable; // id -- only used in normal mode + id m_metal_device; // id -- set to last-used-device + DWORD m_metal_device_lastchkt; + +} +- (id)initChild:(SWELL_DialogResourceIndex *)resstate Parent:(NSView *)parent dlgProc:(DLGPROC)dlgproc Param:(LPARAM)par; +- (LRESULT)onSwellMessage:(UINT)msg p1:(WPARAM)wParam p2:(LPARAM)lParam; +-(HANDLE)swellExtendedDragOp:(id )sender retGlob:(BOOL)retG; +- (const char *)onSwellGetText; +-(void)onSwellSetText:(const char *)buf; +-(LONG)swellGetExtendedStyle; +-(void)swellSetExtendedStyle:(LONG)st; +-(HMENU)swellGetMenu; +-(BOOL)swellHasBeenDestroyed; +-(void)swellSetMenu:(HMENU)menu; +-(LONG_PTR)getSwellUserData; +-(void)setSwellUserData:(LONG_PTR)val; +-(void)setOpaque:(bool)isOpaque; +-(LPARAM)getSwellExtraData:(int)idx; +-(void)setSwellExtraData:(int)idx value:(LPARAM)val; +-(void)setSwellWindowProc:(WNDPROC)val; +-(WNDPROC)getSwellWindowProc; +-(void)setSwellDialogProc:(DLGPROC)val; +-(DLGPROC)getSwellDialogProc; + +- (NSArray*) namesOfPromisedFilesDroppedAtDestination:(NSURL*)droplocation; + +-(void) getSwellPaintInfo:(PAINTSTRUCT *)ps; +- (int)swellCapChangeNotify; +-(unsigned int)swellCreateWindowFlags; + +-(bool)swellCanPostMessage; +-(int)swellEnumProps:(PROPENUMPROCEX)proc lp:(LPARAM)lParam; +-(void *)swellGetProp:(const char *)name wantRemove:(BOOL)rem; +-(int)swellSetProp:(const char *)name value:(void *)val ; +-(NSOpenGLContext *)swellGetGLContext; +- (void) setEnabledSwellNoFocus; +-(const char *)getSwellClass; + +// NSAccessibility + +// attribute methods +//- (NSArray *)accessibilityAttributeNames; +- (id)accessibilityAttributeValue:(NSString *)attribute; +//- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute; +//- (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute; + +// parameterized attribute methods +//- (NSArray *)accessibilityParameterizedAttributeNames; +//- (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter; + +// action methods +//- (NSArray *)accessibilityActionNames; +//- (NSString *)accessibilityActionDescription:(NSString *)action; +//- (void)accessibilityPerformAction:(NSString *)action; + +// Return YES if the UIElement doesn't show up to the outside world - i.e. its parent should return the UIElement's children as its own - cutting the UIElement out. E.g. NSControls are ignored when they are single-celled. +- (BOOL)accessibilityIsIgnored; + +// Returns the deepest descendant of the UIElement hierarchy that contains the point. You can assume the point has already been determined to lie within the receiver. Override this method to do deeper hit testing within a UIElement - e.g. a NSMatrix would test its cells. The point is bottom-left relative screen coordinates. +- (id)accessibilityHitTest:(NSPoint)point; + +// Returns the UI Element that has the focus. You can assume that the search for the focus has already been narrowed down to the reciever. Override this method to do a deeper search with a UIElement - e.g. a NSMatrix would determine if one of its cells has the focus. +- (id)accessibilityFocusedUIElement; + +-(void) swellOnControlDoubleClick:(id)sender; + +#ifdef MAC_OS_X_VERSION_10_8 +// for radio button with the OSX 10.8+ SDK, see comment in SWELL_MakeControl +-(void) onSwellCommand0:(id)sender; +-(void) onSwellCommand2:(id)sender; +-(void) onSwellCommand3:(id)sender; +-(void) onSwellCommand4:(id)sender; +-(void) onSwellCommand5:(id)sender; +-(void) onSwellCommand6:(id)sender; +-(void) onSwellCommand7:(id)sender; +#endif + +-(BOOL) swellWantsMetal; +-(void) swellDrawMetal:(const RECT *)forRect; +@end + +@interface SWELL_ModelessWindow : NSWindow +{ +@public + NSSize lastFrameSize; + id m_owner; + OwnedWindowListRec *m_ownedwnds; + BOOL m_enabled; + int m_wantraiseamt; + bool m_wantInitialKeyWindowOnShow; +} +- (id)initModeless:(SWELL_DialogResourceIndex *)resstate Parent:(HWND)parent dlgProc:(DLGPROC)dlgproc Param:(LPARAM)par outputHwnd:(HWND *)hwndOut forceStyles:(unsigned int)smask; +- (id)initModelessForChild:(HWND)child owner:(HWND)owner styleMask:(unsigned int)smask; +- (void)keyDown:(NSEvent *)event; +- (void)swellDestroyAllOwnedWindows; +- (void)swellRemoveOwnedWindow:(NSWindow *)wnd; +- (void)swellAddOwnedWindow:(NSWindow*)wnd; +- (void)swellSetOwner:(id)owner; +- (id)swellGetOwner; +- (void **)swellGetOwnerWindowHead; +-(void)swellDoDestroyStuff; +-(void)swellResetOwnedWindowLevels; + +-(void)toggleFullScreen:(id)sender; +@end + +@interface SWELL_ModalDialog : NSPanel +{ + NSSize lastFrameSize; + id m_owner; + OwnedWindowListRec *m_ownedwnds; + + int m_rv; + bool m_hasrv; + BOOL m_enabled; +} +- (id)initDialogBox:(SWELL_DialogResourceIndex *)resstate Parent:(HWND)parent dlgProc:(DLGPROC)dlgproc Param:(LPARAM)par; +- (void)swellDestroyAllOwnedWindows; +- (void)swellRemoveOwnedWindow:(NSWindow *)wnd; +- (void)swellAddOwnedWindow:(NSWindow*)wnd; +- (void)swellSetOwner:(id)owner; +- (id)swellGetOwner; +- (void **)swellGetOwnerWindowHead; +-(void)swellDoDestroyStuff; + +-(void)swellSetModalRetVal:(int)r; +-(int)swellGetModalRetVal; +-(bool)swellHasModalRetVal; +@end + +#ifndef SWELL_NO_METAL +void swell_removeMetalDirty(SWELL_hwndChild *slf); +void swell_updateAllMetalDirty(void); +void swell_addMetalDirty(SWELL_hwndChild *slf, const RECT *r, bool isReleaseDC=false); +HDC SWELL_CreateMetalDC(SWELL_hwndChild *); +#endif + + +@interface SWELL_hwndCarbonHost : SWELL_hwndChild +#ifdef MAC_OS_X_VERSION_10_7 + +#endif +{ +@public + NSWindow *m_cwnd; + + bool m_whileresizing; + void* m_wndhandler; // won't compile if declared EventHandlerRef, wtf + void* m_ctlhandler; // not sure if these need to be separate but cant hurt + bool m_wantallkeys; +} +-(BOOL)swellIsCarbonHostingView; +-(void)swellDoRepos; +@end + + +@interface SWELL_PopupMenuRecv : NSObject +{ + int m_act; + HWND cbwnd; +} +-(id) initWithWnd:(HWND)wnd; +-(void) onSwellCommand:(id)sender; +-(int) isCommand; +- (void)menuNeedsUpdate:(NSMenu *)menu; + +@end + +@interface SWELL_PopUpButton : NSPopUpButton +{ + LONG m_style; +} +-(void)setSwellStyle:(LONG)style; +-(LONG)getSwellStyle; +@end + +@interface SWELL_ComboBox : NSComboBox +{ +@public + LONG m_style; + WDL_PtrList *m_ids; + int m_ignore_selchg; // used to track the last set selection state, to avoid getting feedback notifications + bool m_disable_menu; +} +-(id)init; +-(void)dealloc; +-(void)setSwellStyle:(LONG)style; +-(LONG)getSwellStyle; +- (void)swellDisableContextMenu:(bool)dis; +- (NSMenu *)textView:(NSTextView *)view menu:(NSMenu *)menu forEvent:(NSEvent *)event atIndex:(NSUInteger)charIndex; +@end + + + +// GDI internals + +#ifndef __AVAILABILITYMACROS__ +#error __AVAILABILITYMACROS__ not defined, include AvailabilityMacros.h! +#endif + +// 10.4 doesn't support CoreText, so allow ATSUI if targetting 10.4 SDK +#ifndef MAC_OS_X_VERSION_10_5 + // 10.4 SDK + #define SWELL_NO_CORETEXT + #define SWELL_ATSUI_TEXT_SUPPORT +#elif !defined(__LP64__) + #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + #ifndef MAC_OS_X_VERSION_10_9 // not sure when ATSUI was dropped completely, definitely gone in 10.13! + #define SWELL_ATSUI_TEXT_SUPPORT + #endif + #endif +#endif + +#ifndef MAC_OS_X_VERSION_10_11 + // metal requires a recent SDK + #define SWELL_NO_METAL +#endif + +struct HGDIOBJ__ +{ + int type; + + int additional_refcnt; // refcnt of 0 means one owner (if >0, additional owners) + + // used by pen/brush + CGColorRef color; + int wid; + int color_int; + NSImage *bitmapptr; + + NSMutableDictionary *__old_fontdict; // unused, for ABI compat + // + // if ATSUI used, meaning IsCoreTextSupported() returned false + ATSUStyle atsui_font_style; + + float font_rotation; + + bool _infreelist; + struct HGDIOBJ__ *_next; + + // if using CoreText to draw text + void *ct_FontRef; +}; + +struct HDC__ { + CGContextRef ctx; + void *ownedData; // always use via SWELL_GetContextFrameBuffer() (which performs necessary alignment) + void *metal_ctx; // SWELL_hwndChild, only used if not SWELL_NO_METAL + HGDIOBJ__ *curpen; + HGDIOBJ__ *curbrush; + HGDIOBJ__ *curfont; + + NSColor *__old_nstextcol; // provided for ABI compat, but unused + int cur_text_color_int; // text color as int + + int curbkcol; + int curbkmode; + float lastpos_x,lastpos_y; + + void *GLgfxctx; // optionally set + bool _infreelist; + struct HDC__ *_next; + + CGColorRef curtextcol; // text color as CGColor +}; + + + + + +// some extras so we can call functions available only on some OSX versions without warnings, and with the correct types +#define SWELL_DelegateExtensions __SWELL_PREFIX_CLASSNAME(_delext) +#define SWELL_ViewExtensions __SWELL_PREFIX_CLASSNAME(_viewext) +#define SWELL_AppExtensions __SWELL_PREFIX_CLASSNAME(_appext) +#define SWELL_WindowExtensions __SWELL_PREFIX_CLASSNAME(_wndext) +#define SWELL_TableColumnExtensions __SWELL_PREFIX_CLASSNAME(_tcolext) + +@interface SWELL_WindowExtensions : NSWindow +-(void)setCollectionBehavior:(NSUInteger)a; +@end +@interface SWELL_ViewExtensions : NSView +-(void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)vr rectIsVisibleRectForView:(NSView*)v topView:(NSView *)v2; +@end + +@interface SWELL_DelegateExtensions : NSObject +-(bool)swellPostMessage:(HWND)dest msg:(int)message wp:(WPARAM)wParam lp:(LPARAM)lParam; +-(void)swellPostMessageClearQ:(HWND)dest; +-(void)swellPostMessageTick:(id)sender; +@end + +@interface SWELL_AppExtensions : NSApplication +-(NSUInteger)presentationOptions; +-(void)setPresentationOptions:(NSUInteger)o; +@end +@interface SWELL_TableColumnExtensions : NSTableColumn +-(BOOL)isHidden; +-(void)setHidden:(BOOL)h; +@end + + + + +#else + // compat when compiling targetting OSX but not in objectiveC mode + struct SWELL_DataHold; +#endif // !__OBJC__ + +// 10.4 sdk just uses "float" +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + #ifdef __LP64__ + typedef double CGFloat; + #else + typedef float CGFloat; +#endif + +#endif + + +#define NSPOINT_TO_INTS(pt) (int)floor((pt).x+0.5), (int)floor((pt).y+0.5) + +#ifdef __OBJC__ +static WDL_STATICFUNC_UNUSED void NSPOINT_TO_POINT(POINT *p, const NSPoint &pt) +{ + p->x = (int)floor(pt.x+0.5); + p->y = (int)floor((pt).y+0.5); +} +static WDL_STATICFUNC_UNUSED void NSRECT_TO_RECT(RECT *r, const NSRect &tr) +{ + r->left=(int)floor(tr.origin.x+0.5); + r->right=(int)floor(tr.origin.x+tr.size.width+0.5); + r->top=(int)floor(tr.origin.y+0.5); + r->bottom=(int)floor(tr.origin.y+tr.size.height+0.5); +} +#endif + + +#ifdef SWELL_IMPLEMENT_GETOSXVERSION + +SWELL_IMPLEMENT_GETOSXVERSION int SWELL_GetOSXVersion() +{ + static SInt32 v; + if (!v) + { + if (NSAppKitVersionNumber >= 1266.0) + { + if (NSAppKitVersionNumber >= 2100.0) + v = 0x1200; + else if (NSAppKitVersionNumber >= 2022.0) + v = 0x1100; + else if (NSAppKitVersionNumber >= 1894.0) + v = 0x10e0; + else if (NSAppKitVersionNumber >= 1639.10) + v = 0x10d0; + else if (NSAppKitVersionNumber >= 1560) + v = 0x10c0; + else if (NSAppKitVersionNumber >= 1404.0) + v = 0x10b0; + else + v = 0x10a0; // 10.10+ Gestalt(gsv) return 0x109x, so we bump this to 0x10a0 + } + else + { + SInt32 a = 0x1040; + Gestalt(gestaltSystemVersion,&a); + v=a; + } + } + return v; +} + +#endif + + +#elif defined(SWELL_TARGET_GDK) + + +#ifdef SWELL_SUPPORT_GTK +#include +#endif + +#include +#include +#include + + +#else +// generic + +#endif // end generic + +struct HTREEITEM__ +{ + HTREEITEM__(); + ~HTREEITEM__(); + bool FindItem(HTREEITEM it, HTREEITEM__ **parOut, int *idxOut); + +#ifdef SWELL_TARGET_OSX + SWELL_DataHold *m_dh; +#else + int m_state; // TVIS_EXPANDED, for ex +#endif + + bool m_haschildren; + char *m_value; + WDL_PtrList m_children; // only used in tree mode + LPARAM m_param; +}; + + +#ifndef SWELL_TARGET_OSX + +#include "../wdlstring.h" + +#ifdef SWELL_LICE_GDI +#include "../lice/lice.h" +#endif +#include "../assocarray.h" + +LRESULT SwellDialogDefaultWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + +#ifdef SWELL_TARGET_GDK +typedef GdkWindow *SWELL_OSWINDOW; +#else +typedef void *SWELL_OSWINDOW; // maps to the HWND__ itself on visible, non-GDK, top level windows +#endif + +struct HWND__ +{ + HWND__(HWND par, int wID=0, const RECT *wndr=NULL, const char *label=NULL, bool visible=false, WNDPROC wndproc=NULL, DLGPROC dlgproc=NULL, HWND ownerWindow=NULL); + ~HWND__(); // DO NOT USE!!! We would make this private but it breaks PtrList using it on gcc. + + // using this API prevents the HWND from being valid -- it'll still get its resources destroyed via DestroyWindow() though. + // DestroyWindow() does cleanup, then the final Release(). + void Retain() { m_refcnt++; } + void Release() { if (!--m_refcnt) delete this; } + + const char *m_classname; + + SWELL_OSWINDOW m_oswindow; + + WDL_FastString m_title; + + HWND__ *m_children, *m_parent, *m_next, *m_prev; + HWND__ *m_owner, *m_owned_list, *m_owned_next, *m_owned_prev; + HWND__ *m_focused_child; // only valid if hwnd itself is in focus chain, and must be validated before accessed + RECT m_position; + UINT m_id; + int m_style, m_exstyle; + INT_PTR m_userdata; + WNDPROC m_wndproc; + DLGPROC m_dlgproc; + INT_PTR m_extra[64]; + INT_PTR m_private_data; // used by internal controls + + bool m_visible; + char m_hashaddestroy; // 1 in destroy, 2 full destroy + bool m_enabled; + bool m_wantfocus; + + bool m_israised; + bool m_has_had_position; + int m_oswindow_fullscreen; // may contain preserved style flags + + int m_refcnt; + int m_oswindow_private; // private state for generic-gtk or whatever + + HMENU m_menu; + HFONT m_font; + + WDL_StringKeyedArray m_props; + +#ifdef SWELL_LICE_GDI + void *m_paintctx; // temporarily set for calls to WM_PAINT + + bool m_child_invalidated; // if a child is invalidated + bool m_invalidated; // set to true on direct invalidate. todo RECT instead? + + LICE_IBitmap *m_backingstore; // if NULL, unused (probably only should use on top level windows, but support caching?) +#endif +}; + +struct HMENU__ +{ + HMENU__() { m_refcnt=1; sel_vis = -1; } + + void Retain() { m_refcnt++; } + void Release() { if (!--m_refcnt) delete this; } + + WDL_PtrList items; + int sel_vis; // for mouse/keyboard nav + int m_refcnt; + + HMENU__ *Duplicate(); + static void freeMenuItem(void *p); + +private: + ~HMENU__() { items.Empty(true,freeMenuItem); } +}; + + +struct HGDIOBJ__ +{ + int type; + int additional_refcnt; // refcnt of 0 means one owner (if >0, additional owners) + + int color; + int wid; + + float alpha; + + struct HGDIOBJ__ *_next; + bool _infreelist; + void *typedata; // font: FT_Face, bitmap: LICE_IBitmap +}; + + +struct HDC__ { +#ifdef SWELL_LICE_GDI + LICE_IBitmap *surface; // owned by context. can be (and usually is, if clipping is desired) LICE_SubBitmap + POINT surface_offs; // offset drawing into surface by this amount + + RECT dirty_rect; // in surface coordinates, used for GetWindowDC()/GetDC()/etc + bool dirty_rect_valid; +#else + void *ownedData; // for mem contexts, support a null rendering +#endif + + HGDIOBJ__ *curpen; + HGDIOBJ__ *curbrush; + HGDIOBJ__ *curfont; + + int cur_text_color_int; // text color as int + + int curbkcol; + int curbkmode; + float lastpos_x,lastpos_y; + + struct HDC__ *_next; + bool _infreelist; +}; + +HWND DialogBoxIsActive(void); +void DestroyPopupMenus(void); +HWND ChildWindowFromPoint(HWND h, POINT p); +HWND GetFocusIncludeMenus(); + +void SWELL_RunEvents(); + +bool swell_isOSwindowmenu(SWELL_OSWINDOW osw); + +void swell_on_toplevel_raise(SWELL_OSWINDOW wnd); // called by swell-generic-gdk when a window is focused + +HWND swell_oswindow_to_hwnd(SWELL_OSWINDOW w); +void swell_oswindow_focus(HWND hwnd); +void swell_oswindow_update_style(HWND hwnd, LONG oldstyle); +void swell_oswindow_update_enable(HWND hwnd); +void swell_oswindow_update_text(HWND hwnd); +void swell_oswindow_begin_resize(SWELL_OSWINDOW wnd); +void swell_oswindow_resize(SWELL_OSWINDOW wnd, int reposflag, RECT f); +void swell_oswindow_postresize(HWND hwnd, RECT f); +void swell_oswindow_invalidate(HWND hwnd, const RECT *r); +void swell_oswindow_destroy(HWND hwnd); +void swell_oswindow_manage(HWND hwnd, bool wantfocus); +void swell_oswindow_updatetoscreen(HWND hwnd, RECT *rect); +HWND swell_window_wants_all_input(); // window with an active drag of menubar will have this set, to route all mouse events to nonclient area of window +int swell_delegate_menu_message(HWND src, LPARAM lParam, int msg, bool screencoords); // menubar/menus delegate to submenus during drag. + +void swell_dlg_destroyspare(); + +extern bool swell_is_likely_capslock; // only used when processing dit events for a-zA-Z +extern const char *g_swell_appname; +extern SWELL_OSWINDOW SWELL_focused_oswindow; // top level window which has focus (might not map to a HWND__!) +extern HWND swell_captured_window; +extern HWND SWELL_topwindows; // front of list = most recently active + +int swell_is_app_inactive(); // returns >0 if definitely inactive, -1 if maybe + +#ifdef _DEBUG +void VALIDATE_HWND_LIST(HWND list, HWND par); +#else +#define VALIDATE_HWND_LIST(list, par) do { } while (0) +#endif + + +#endif // !OSX + +HDC SWELL_CreateGfxContext(void *); + +// GDP internals +#define TYPE_PEN 1 +#define TYPE_BRUSH 2 +#define TYPE_FONT 3 +#define TYPE_BITMAP 4 + +typedef struct +{ + void *instptr; +#ifdef __APPLE__ + void *bundleinstptr; +#endif + int refcnt; + +#ifndef SWELL_EXTRA_MINIMAL + int (*SWELL_dllMain)(HINSTANCE, DWORD,LPVOID); //last parm=SWELLAPI_GetFunc + BOOL (*dllMain)(HINSTANCE, DWORD, LPVOID); +#endif + void *lastSymbolRequested; +} SWELL_HINSTANCE; + + +enum +{ + INTERNAL_OBJECT_START= 0x1000001, + INTERNAL_OBJECT_THREAD, + INTERNAL_OBJECT_EVENT, + INTERNAL_OBJECT_FILE, + INTERNAL_OBJECT_EXTERNALSOCKET, // socket not owned by us + INTERNAL_OBJECT_SOCKETEVENT, + INTERNAL_OBJECT_NSTASK, + INTERNAL_OBJECT_PID, + INTERNAL_OBJECT_END +}; + +typedef struct +{ + int type; // INTERNAL_OBJECT_* + int count; // reference count +} SWELL_InternalObjectHeader; + +typedef struct +{ + SWELL_InternalObjectHeader hdr; + DWORD (*threadProc)(LPVOID); + void *threadParm; + pthread_t pt; + DWORD retv; + bool done; +} SWELL_InternalObjectHeader_Thread; + +typedef struct +{ + SWELL_InternalObjectHeader hdr; + + pthread_mutex_t mutex; + pthread_cond_t cond; + + bool isSignal; + bool isManualReset; + +} SWELL_InternalObjectHeader_Event; + + +// used for both INTERNAL_OBJECT_EXTERNALSOCKET and INTERNAL_OBJECT_SOCKETEVENT. +// if EXTERNALSOCKET, socket[1] ignored and autoReset ignored. +typedef struct +{ + SWELL_InternalObjectHeader hdr; + int socket[2]; + bool autoReset; +} SWELL_InternalObjectHeader_SocketEvent; + +typedef struct +{ + SWELL_InternalObjectHeader hdr; + + FILE *fp; +} SWELL_InternalObjectHeader_File; + +typedef struct +{ + SWELL_InternalObjectHeader hdr; + void *task; +} SWELL_InternalObjectHeader_NSTask; + +typedef struct +{ + SWELL_InternalObjectHeader hdr; + int pid; + int done, result; +} SWELL_InternalObjectHeader_PID; + +bool IsRightClickEmulateEnabled(); + +#ifdef SWELL_INTERNAL_HTREEITEM_IMPL + +HTREEITEM__::HTREEITEM__() +{ + m_param=0; + m_value=0; + m_haschildren=false; +#ifdef SWELL_TARGET_OSX + m_dh = [[SWELL_DataHold alloc] initWithVal:this]; +#else + m_state=0; +#endif +} +HTREEITEM__::~HTREEITEM__() +{ + free(m_value); + m_children.Empty(true); +#ifdef SWELL_TARGET_OSX + if (m_dh) + { + m_dh->m_data = NULL; + [m_dh release]; + } +#endif +} + + +bool HTREEITEM__::FindItem(HTREEITEM it, HTREEITEM__ **parOut, int *idxOut) +{ + int a=m_children.Find((HTREEITEM__*)it); + if (a>=0) + { + if (parOut) *parOut=this; + if (idxOut) *idxOut=a; + return true; + } + int x; + const int n=m_children.GetSize(); + for (x = 0; x < n; x ++) + { + if (m_children.Get(x)->FindItem(it,parOut,idxOut)) return true; + } + return false; +} + +#endif + +#ifdef SWELL_INTERNAL_MERGESORT_IMPL + +static int __listview_sortfunc(void *p1, void *p2, int (*compar)(LPARAM val1, LPARAM val2, LPARAM userval), LPARAM userval) +{ + SWELL_ListView_Row *a = *(SWELL_ListView_Row **)p1; + SWELL_ListView_Row *b = *(SWELL_ListView_Row **)p2; + return compar(a->m_param,b->m_param,userval); +} + + +static void __listview_mergesort_internal(void *base, size_t nmemb, size_t size, + int (*compar)(LPARAM val1, LPARAM val2, LPARAM userval), + LPARAM parm, + char *tmpspace) +{ + char *b1,*b2; + size_t n1, n2; + + if (nmemb < 2) return; + + n1 = nmemb / 2; + b1 = (char *) base; + n2 = nmemb - n1; + b2 = b1 + (n1 * size); + + if (nmemb>2) + { + __listview_mergesort_internal(b1, n1, size, compar, parm, tmpspace); + __listview_mergesort_internal(b2, n2, size, compar, parm, tmpspace); + } + + char *p = tmpspace; + + do + { + if (__listview_sortfunc(b1, b2, compar,parm) > 0) + { + memcpy(p, b2, size); + b2 += size; + n2--; + } + else + { + memcpy(p, b1, size); + b1 += size; + n1--; + } + p += size; + } + while (n1 > 0 && n2 > 0); + + if (n1 > 0) memcpy(p, b1, n1 * size); + memcpy(base, tmpspace, (nmemb - n2) * size); +} + + +#endif + +#ifndef SWELL_TARGET_OSX + +#define SWELL_GENERIC_THEMESIZEDEFS(f,fd) \ + f(default_font_size, 12) \ + f(menubar_height, 17) \ + f(menubar_font_size, 13) \ + f(menubar_spacing_width, 8) \ + f(menubar_margin_width, 6) \ + f(scrollbar_width, 14) \ + f(smscrollbar_width, 16) \ + f(scrollbar_min_thumb_height, 4) \ + f(combo_height, 20) \ + + +#define SWELL_GENERIC_THEMEDEFS(f,fd) \ + SWELL_GENERIC_THEMESIZEDEFS(f,fd) \ + f(_3dface,RGB(192,192,192)) \ + f(_3dshadow,RGB(96,96,96)) \ + f(_3dhilight,RGB(224,224,224)) \ + f(_3ddkshadow,RGB(48,48,48)) \ + fd(button_bg,RGB(192,192,192),_3dface) \ + f(button_text,RGB(0,0,0)) \ + f(button_text_disabled, RGB(128,128,128)) \ + fd(button_shadow, RGB(96,96,96), _3dshadow) \ + fd(button_hilight, RGB(224,224,224), _3dhilight) \ + f(checkbox_text,RGB(0,0,0)) \ + f(checkbox_text_disabled, RGB(128,128,128)) \ + f(checkbox_fg, RGB(0,0,0)) \ + f(checkbox_inter, RGB(192,192,192)) \ + f(checkbox_bg, RGB(255,255,255)) \ + f(scrollbar,RGB(32,32,32)) \ + f(scrollbar_fg, RGB(160,160,160)) \ + f(scrollbar_bg, RGB(224,224,224)) \ + f(edit_cursor,RGB(0,128,255)) \ + f(edit_bg,RGB(255,255,255)) \ + f(edit_bg_disabled,RGB(224,224,224)) \ + f(edit_text,RGB(0,0,0)) \ + f(edit_text_disabled, RGB(128,128,128)) \ + f(edit_bg_sel,RGB(128,192,255)) \ + f(edit_text_sel,RGB(255,255,255)) \ + fd(edit_hilight, RGB(224,224,224), _3dhilight) \ + fd(edit_shadow, RGB(96,96,96), _3dshadow) \ + f(info_bk,RGB(255,240,200)) \ + f(info_text,RGB(0,0,0)) \ + fd(menu_bg, RGB(192,192,192), _3dface) \ + fd(menu_shadow, RGB(96,96,96), _3dshadow) \ + fd(menu_hilight, RGB(224,224,224), _3dhilight) \ + fd(menu_text, RGB(0,0,0), button_text) \ + fd(menu_text_disabled, RGB(224,224,224), _3dhilight) \ + fd(menu_bg_sel, RGB(0,0,0), menu_text) \ + fd(menu_text_sel, RGB(224,224,224), menu_bg) \ + f(menu_scroll, RGB(64,64,64)) \ + fd(menu_scroll_arrow, RGB(96,96,96), _3dshadow) \ + fd(menu_submenu_arrow, RGB(96,96,96), menu_text) \ + fd(menu_submenu_arrow_sel, RGB(96,96,96), menu_bg) \ + fd(menubar_bg, RGB(192,192,192), menu_bg) \ + fd(menubar_bg_inactive, RGB(192,192,192), menubar_bg) \ + fd(menubar_text, RGB(0,0,0), menu_text) \ + fd(menubar_text_inactive, RGB(0,0,0), menubar_text) \ + fd(menubar_text_disabled, RGB(224,224,224), menu_text_disabled) \ + fd(menubar_bg_sel, RGB(0,0,0), menu_bg_sel) \ + fd(menubar_text_sel, RGB(224,224,224), menu_text_sel) \ + f(trackbar_track, RGB(224,224,224)) \ + f(trackbar_mark, RGB(96,96,96)) \ + f(trackbar_knob, RGB(48,48,48)) \ + f(progress,RGB(0,128,255)) \ + fd(label_text, RGB(0,0,0), button_text) \ + fd(label_text_disabled, RGB(128,128,128), button_text_disabled) \ + fd(combo_text, RGB(0,0,0), button_text) \ + fd(combo_text_disabled, RGB(128,128,128), button_text_disabled) \ + fd(combo_bg, RGB(192,192,192), _3dface) \ + f(combo_bg2, RGB(255,255,255)) \ + fd(combo_shadow, RGB(96,96,96), _3dshadow) \ + fd(combo_hilight, RGB(224,224,224), _3dhilight) \ + fd(combo_arrow, RGB(96,96,96), _3dshadow) \ + fd(combo_arrow_press, RGB(224,224,224), _3dhilight) \ + f(listview_bg, RGB(255,255,255)) \ + f(listview_bg_sel, RGB(128,128, 255)) \ + f(listview_text, RGB(0,0,0)) \ + fd(listview_text_sel, RGB(0,0,0), listview_text) \ + fd(listview_grid, RGB(224,224,224), _3dhilight) \ + f(listview_hdr_arrow,RGB(96,96,96)) \ + fd(listview_shadow, RGB(96,96,96), _3dshadow) \ + fd(listview_hilight, RGB(224,224,224), _3dhilight) \ + fd(listview_hdr_shadow, RGB(96,96,96), _3dshadow) \ + fd(listview_hdr_hilight, RGB(224,224,224), _3dhilight) \ + fd(listview_hdr_bg, RGB(192,192,192), _3dface) \ + fd(listview_hdr_text, RGB(0,0,0), button_text) \ + f(treeview_text,RGB( 0,0,0)) \ + f(treeview_bg, RGB(255,255,255)) \ + f(treeview_bg_sel, RGB(128,128,255)) \ + f(treeview_text_sel, RGB(0,0,0)) \ + f(treeview_arrow, RGB(96,96,96)) \ + fd(treeview_shadow, RGB(96,96,96), _3dshadow) \ + fd(treeview_hilight, RGB(224,224,224), _3dhilight) \ + fd(tab_shadow, RGB(96,96,96), _3dshadow) \ + fd(tab_hilight, RGB(224,224,224), _3dhilight) \ + fd(tab_text, RGB(0,0,0), button_text) \ + f(focusrect,RGB(255,0,0)) \ + f(group_text,RGB(0,0,0)) \ + fd(group_shadow, RGB(96,96,96), _3dshadow) \ + fd(group_hilight, RGB(224,224,224), _3dhilight) \ + f(focus_hilight, RGB(140,190,233)) \ + + + +struct swell_colortheme { +#define __def_theme_ent(x,c) int x; +#define __def_theme_ent_fb(x,c,fb) int x; +SWELL_GENERIC_THEMEDEFS(__def_theme_ent,__def_theme_ent_fb) +#undef __def_theme_ent +#undef __def_theme_ent_fb +}; + +#define SWELL_UI_SCALE(x) (((x)*g_swell_ui_scale)/256) +void swell_scaling_init(bool no_auto_hidpi); +extern int g_swell_ui_scale; +extern swell_colortheme g_swell_ctheme; +extern const char *g_swell_deffont_face; +HFONT SWELL_GetDefaultFont(void); + +#endif + + +static WDL_STATICFUNC_UNUSED int ext_valid_for_extlist(const char *thisext, const char *extlist) +{ + if (!thisext || *thisext != '.' || !extlist) return -1; + int txlen = (int)strlen(thisext), witem = 0; + while (*extlist) + { + while (*extlist) extlist++; // description + extlist++; + + while (*extlist) + { + while (*extlist == ' ' || *extlist == ';') extlist++; + if (*extlist == '*') + { + if (!strnicmp(extlist+1,thisext,txlen) && + (extlist[1+txlen] == ';' ||extlist[1+txlen] == 0)) + return witem; + } + while (*extlist && *extlist != ';') extlist++; + if (*extlist) extlist++; + } + + while (*extlist) extlist++; + extlist++; + witem++; + } + return -1; +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-kb-generic.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-kb-generic.cpp new file mode 100644 index 000000000..822b4d264 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-kb-generic.cpp @@ -0,0 +1,26 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic key and mouse cursor querying, as well as a key to windows key translation function. + + */ + + +// everything has moved to swell-generic-*.cpp diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-kb.mm b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-kb.mm new file mode 100644 index 000000000..f8de6bfcb --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-kb.mm @@ -0,0 +1,693 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic key and mouse cursor querying, as well as a mac key to windows key translation function. + + */ + +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" +#include "swell-dlggen.h" +#include "../wdltypes.h" +#import +#import + + + +static int MacKeyCodeToVK(int code, int *flag) +{ + switch (code) + { + case 51: return VK_BACK; + case 65: return VK_DECIMAL; + case 67: return VK_MULTIPLY; + case 69: return VK_ADD; + case 71: return VK_NUMLOCK; + case 75: return VK_DIVIDE; + case 76: *flag |= 1<<24; return VK_RETURN; + case 78: return VK_SUBTRACT; + case 81: return VK_SEPARATOR; + case 82: return VK_NUMPAD0; + case 83: return VK_NUMPAD1; + case 84: return VK_NUMPAD2; + case 85: return VK_NUMPAD3; + case 86: return VK_NUMPAD4; + case 87: return VK_NUMPAD5; + case 88: return VK_NUMPAD6; + case 89: return VK_NUMPAD7; + case 91: return VK_NUMPAD8; + case 92: return VK_NUMPAD9; + case 96: return VK_F5; + case 97: return VK_F6; + case 98: return VK_F7; + case 99: return VK_F3; + case 100: return VK_F8; + case 101: return VK_F9; + case 109: return VK_F10; + case 103: return VK_F11; + case 111: return VK_F12; + case 114: *flag |= 1<<24; return VK_INSERT; + case 115: *flag |= 1<<24; return VK_HOME; + case 117: *flag |= 1<<24; return VK_DELETE; + case 116: *flag |= 1<<24; return VK_PRIOR; + case 118: return VK_F4; + case 119: *flag |= 1<<24; return VK_END; + case 120: return VK_F2; + case 121: *flag |= 1<<24; return VK_NEXT; + case 122: return VK_F1; + case 123: *flag |= 1<<24; return VK_LEFT; + case 124: *flag |= 1<<24; return VK_RIGHT; + case 125: *flag |= 1<<24; return VK_DOWN; + case 126: *flag |= 1<<24; return VK_UP; + case 0x69: return VK_F13; + case 0x6B: return VK_F14; + case 0x71: return VK_F15; + case 0x6A: return VK_F16; + } + return 0; +} + +bool IsRightClickEmulateEnabled(); + +#ifdef MAC_OS_X_VERSION_10_5 + +static int charFromVcode(int keyCode) // used for getting the root char (^, `) from dead keys on other keyboards, + // only used when using MacKeyToWindowsKeyEx() with mode=1, for now +{ + static char loaded; + static TISInputSourceRef (*_TISCopyCurrentKeyboardInputSource)( void); + static void* (*_TISGetInputSourceProperty) ( TISInputSourceRef inputSource, CFStringRef propertyKey); + + if (!loaded) + { + loaded++; + CFBundleRef b = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon")); + if (b) + { + *(void **)&_TISGetInputSourceProperty = CFBundleGetFunctionPointerForName(b,CFSTR("TISGetInputSourceProperty")); + *(void **)&_TISCopyCurrentKeyboardInputSource = CFBundleGetFunctionPointerForName(b,CFSTR("TISCopyCurrentKeyboardInputSource")); + } + } + if (!_TISCopyCurrentKeyboardInputSource || !_TISGetInputSourceProperty) return 0; + + TISInputSourceRef currentKeyboard = _TISCopyCurrentKeyboardInputSource(); + CFDataRef uchr = (CFDataRef)_TISGetInputSourceProperty(currentKeyboard, CFSTR("TISPropertyUnicodeKeyLayoutData")); + const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr); + + if(keyboardLayout) + { + UInt32 deadKeyState = 0; + UniCharCount maxStringLength = 255; + UniCharCount actualStringLength = 0; + UniChar unicodeString[maxStringLength]; + + OSStatus status = UCKeyTranslate(keyboardLayout, + keyCode, kUCKeyActionDown, 0, + LMGetKbdType(), 0, + &deadKeyState, + maxStringLength, + &actualStringLength, unicodeString); + + if (actualStringLength == 0 && deadKeyState) + { + status = UCKeyTranslate(keyboardLayout, + kVK_Space, kUCKeyActionDown, 0, + LMGetKbdType(), 0, + &deadKeyState, + maxStringLength, + &actualStringLength, unicodeString); + } + if(actualStringLength > 0 && status == noErr) return unicodeString[0]; + } + return 0; +} +#endif + +int SWELL_MacKeyToWindowsKeyEx(void *nsevent, int *flags, int mode) +{ + NSEvent *theEvent = (NSEvent *)nsevent; + if (!theEvent) theEvent = [NSApp currentEvent]; + + const NSInteger mod=[theEvent modifierFlags]; + + int flag=0; + if (mod & NSShiftKeyMask) flag|=FSHIFT; + if (mod & NSCommandKeyMask) flag|=FCONTROL; // todo: this should be command once we figure it out + if (mod & NSAlternateKeyMask) flag|=FALT; + if ((mod&NSControlKeyMask) && !IsRightClickEmulateEnabled()) flag|=FLWIN; + + int rawcode=[theEvent keyCode]; + + int code=MacKeyCodeToVK(rawcode,&flag); + if (!code) + { + NSString *str=NULL; + if (mode == 1) str=[theEvent characters]; + + if (!str || ![str length]) str=[theEvent charactersIgnoringModifiers]; + + if (!str || ![str length]) + { + #ifdef MAC_OS_X_VERSION_10_5 + if (mode==1) code=charFromVcode(rawcode); + #endif + if (!code) + { + code = 1024+rawcode; // raw code + flag|=FVIRTKEY; + } + } + else + { + code=[str characterAtIndex:0]; + if (code >= NSF1FunctionKey && code <= NSF24FunctionKey) + { + flag|=FVIRTKEY; + code += VK_F1 - NSF1FunctionKey; + } + else + { + if (code >= 'a' && code <= 'z') code+='A'-'a'; + if (code == 25 && (flag&FSHIFT)) code=VK_TAB; + if (isalnum(code)||code==' ' || code == '\r' || code == '\n' || code ==27 || code == VK_TAB) flag|=FVIRTKEY; + } + } + } + else + { + flag|=FVIRTKEY; + if (code==8) code='\b'; + } + + if (!(flag&FVIRTKEY)) flag&=~FSHIFT; + + if (flags) *flags=flag; + return code; +} + +int SWELL_MacKeyToWindowsKey(void *nsevent, int *flags) +{ + if (!nsevent) return 0; + return SWELL_MacKeyToWindowsKeyEx(nsevent,flags,0); +} + +int SWELL_KeyToASCII(int wParam, int lParam, int *newflags) +{ + if (wParam >= '0' && wParam <= '9' && lParam == (FSHIFT|FVIRTKEY)) + { + *newflags = lParam&~(FSHIFT|FVIRTKEY); + if (!(lParam & (FCONTROL|FLWIN))) switch (wParam) + { + case '1': return '!'; + case '2': return '@'; + case '3': return '#'; + case '4': return '$'; + case '5': return '%'; + case '6': return '^'; + case '7': return '&'; + case '8': return '*'; + case '9': return '('; + case '0': return ')'; + } + } + return 0; +} + + +WORD GetAsyncKeyState(int key) +{ + CGEventFlags state=0; + if (key == VK_LBUTTON || key == VK_RBUTTON || key == VK_MBUTTON) + { + state=GetCurrentEventButtonState(); + } + else + { + state=CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState); + } + + if ((key == VK_LBUTTON && (state&1)) || + (key == VK_RBUTTON && (state&2)) || + (key == VK_MBUTTON && (state&4)) || + (key == VK_SHIFT && (state&kCGEventFlagMaskShift)) || + (key == VK_CONTROL && (state&kCGEventFlagMaskCommand)) || + (key == VK_MENU && (state&kCGEventFlagMaskAlternate)) || + (key == VK_LWIN && !IsRightClickEmulateEnabled() && (state&kCGEventFlagMaskControl))) + { + return 0x8000; + } + + return 0; +} + + +static SWELL_CursorResourceIndex *SWELL_curmodule_cursorresource_head; + +static NSCursor* MakeCursorFromData(unsigned char* data, int hotspot_x, int hotspot_y) +{ + NSCursor *c=NULL; + NSBitmapImageRep* bmp = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:0 + pixelsWide:16 + pixelsHigh:16 + bitsPerSample:8 + samplesPerPixel:2 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSCalibratedWhiteColorSpace + bytesPerRow:(16*2) + bitsPerPixel:16]; + + if (bmp) + { + unsigned char* p = [bmp bitmapData]; + if (p) + { + int i; + for (i = 0; i < 16*16; ++i) + { + // tried 4 bits per sample and memcpy, didn't work + p[2*i] = (data[i]&0xF0) | data[i]>>4; + p[2*i+1] = (data[i]<<4) | (data[i]&0xf); + } + + NSImage *img = [[NSImage alloc] init]; + if (img) + { + [img addRepresentation:bmp]; + NSPoint hs = NSMakePoint(hotspot_x, hotspot_y); + c = [[NSCursor alloc] initWithImage:img hotSpot:hs]; + [img release]; + } + } + [bmp release]; + } + return c; +} + +static NSCursor* MakeSWELLSystemCursor(const char *id) +{ + // bytemaps are (white<<4)|(alpha) + const unsigned char B = 0xF; + const unsigned char W = 0xFF; + const unsigned char G = 0xF8; + + static NSCursor* carr[4] = { 0, 0, 0, 0 }; + + NSCursor** pc=0; + if (id == IDC_SIZEALL) pc = &carr[0]; + else if (id == IDC_SIZENWSE) pc = &carr[1]; + else if (id == IDC_SIZENESW) pc = &carr[2]; + else if (id == IDC_NO) pc = &carr[3]; + else return 0; + + if (!(*pc)) + { + if (id == IDC_SIZEALL) + { + static unsigned char p[16*16] = + { + 0, 0, 0, 0, 0, 0, G, W, W, G, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, G, W, B, B, W, G, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, W, B, B, B, B, W, 0, 0, 0, 0, 0, + 0, 0, 0, 0, G, B, B, B, B, B, B, G, 0, 0, 0, 0, + 0, 0, 0, G, 0, 0, W, B, B, W, 0, 0, G, 0, 0, 0, + 0, G, W, B, 0, 0, W, B, B, W, 0, 0, B, W, G, 0, + G, W, B, B, W, W, W, B, B, W, W, W, B, B, W, G, + W, B, B, B, B, B, B, B, B, B, B, B, B, B, B, W, + W, B, B, B, B, B, B, B, B, B, B, B, B, B, B, W, + G, W, B, B, W, W, W, B, B, W, W, W, B, B, W, G, + 0, G, W, B, 0, 0, W, B, B, W, 0, 0, B, W, G, 0, + 0, 0, 0, G, 0, 0, W, B, B, W, 0, 0, G, 0, 0, 0, + 0, 0, 0, 0, G, B, B, B, B, B, B, G, 0, 0, 0, 0, + 0, 0, 0, 0, 0, W, B, B, B, B, W, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, G, W, B, B, W, G, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, G, W, W, G, 0, 0, 0, 0, 0, 0, + }; + *pc = MakeCursorFromData(p, 8, 8); + } + else if (id == IDC_SIZENWSE || id == IDC_SIZENESW) + { + static unsigned char p[16*16] = + { + W, W, W, W, W, W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + W, G, G, G, W, G, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + W, G, B, W, G, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + W, G, W, B, W, G, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + W, W, G, W, B, W, G, 0, 0, 0, 0, 0, 0, 0, 0, 0, + W, G, 0, G, W, B, W, G, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, G, W, B, W, G, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, G, W, B, W, G, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, G, W, B, W, G, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, G, W, B, W, G, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, G, W, B, W, G, 0, G, W, + 0, 0, 0, 0, 0, 0, 0, 0, 0, G, W, B, W, G, W, W, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, G, W, B, W, G, W, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, G, W, B, G, W, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, G, W, G, G, G, W, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, W, W, W, W, W, W, + }; + if (id == IDC_SIZENESW) + { + int x, y; + for (y = 0; y < 16; ++y) + { + for (x = 0; x < 8; ++x) + { + unsigned char tmp = p[16*y+x]; + p[16*y+x] = p[16*y+16-x-1]; + p[16*y+16-x-1] = tmp; + } + } + } + *pc = MakeCursorFromData(p, 8, 8); + if (id == IDC_SIZENESW) // swap back! + { + int x, y; + for (y = 0; y < 16; ++y) + { + for (x = 0; x < 8; ++x) + { + unsigned char tmp = p[16*y+x]; + p[16*y+x] = p[16*y+16-x-1]; + p[16*y+16-x-1] = tmp; + } + } + } + } + else if (id == IDC_NO) + { + static unsigned char p[16*16] = + { + 0, 0, 0, 0, G, W, W, W, W, W, W, G, 0, 0, 0, 0, + 0, 0, G, W, W, B, B, B, B, B, B, W, W, G, 0, 0, + 0, G, W, B, B, B, W, W, W, W, B, B, B, W, G, 0, + 0, W, B, B, W, W, G, 0, 0, G, W, G, B, B, W, 0, + G, W, B, W, G, 0, 0, 0, 0, G, W, B, G, B, W, G, + W, B, B, W, 0, 0, 0, 0, G, W, B, W, W, B, B, W, + W, B, W, G, 0, 0, 0, G, W, B, W, G, G, W, B, W, + W, B, W, 0, 0, 0, G, W, B, W, G, 0, 0, W, B, W, + W, B, W, 0, 0, G, W, B, W, G, 0, 0, 0, W, B, W, + W, B, W, G, G, W, B, W, G, 0, 0, 0, G, W, B, W, + W, B, B, W, W, B, W, G, 0, 0, 0, 0, W, B, B, W, + G, W, B, G, B, W, G, 0, 0, 0, 0, G, W, B, W, G, + 0, W, B, B, G, W, G, 0, 0, G, W, W, B, B, W, 0, + 0, G, W, B, B, B, W, W, W, W, B, B, B, W, G, 0, + 0, 0, G, W, W, B, B, B, B, B, B, W, W, G, 0, 0, + 0, 0, 0, 0, G, W, W, W, W, W, W, G, 0, 0, 0, 0, + }; + *pc = MakeCursorFromData(p, 8, 8); + } + } + + return *pc; +} + +static NSImage *swell_imageFromCursorString(const char *name, POINT *hotSpot) +{ + NSImage *img=NULL; + FILE *fp = NULL; + bool isFullFn=false; + + if (!strstr(name,"/") && strlen(name)<1024) + { + char tmpn[4096]; + GetModuleFileName(NULL,tmpn,(DWORD)(sizeof(tmpn)-128-strlen(name))); + strcat(tmpn,"/Contents/Resources/"); + strcat(tmpn,name); + strcat(tmpn,".cur"); + fp = WDL_fopenA(tmpn,"rb"); + } + else + { + isFullFn=true; + if (strlen(name)>4 && !stricmp(name+strlen(name)-4,".cur")) fp = WDL_fopenA(name,"rb"); + } + + if (fp) + { + unsigned char buf[4096]; + if (fread(buf,1,6,fp)==6 && !buf[0] && !buf[1] && buf[2] == 2 && buf[3] == 0 && buf[4] == 1 && buf[5] == 0) + { + static char tempfn[512]; + if (!tempfn[0]) + { + GetTempPath(256,tempfn); + snprintf(tempfn+strlen(tempfn),256,"swellcur%x%x.ico", timeGetTime(),(int)getpid()); + } + + FILE *outfp = WDL_fopenA(tempfn,"wb"); + if (outfp) + { + bool wantLoad=false; + buf[2]=1; // change to .ico + fwrite(buf,1,6,outfp); + + fread(buf,1,16,fp); + int xHot = buf[4]|(buf[5]<<8); + int yHot = buf[6]|(buf[7]<<8); + + buf[4]=1; buf[5]=0; // 1 color plane + buf[6]=0; buf[7]=0; // 0 for pixel depth means "auto" + + if (!buf[3]) + { + fwrite(buf,1,16,outfp); + for (;;) + { + size_t a = fread(buf,1,sizeof(buf),fp); + if (a<1) break; + fwrite(buf,1,a,outfp); + } + wantLoad=true; + } + + fclose(outfp); + if (wantLoad) + { + NSString *str = (NSString *)SWELL_CStringToCFString(tempfn); + img = [[NSImage alloc] initWithContentsOfFile:str]; + [str release]; + if (img && hotSpot) + { + hotSpot->x = xHot; + hotSpot->y = yHot; + } + // printf("loaded converted ico for %s %s %d\n",tempfn,name,!!img); + } + unlink(tempfn); + } + + } + + fclose(fp); + } + + if (!img) // fall back + { + NSString *str = (NSString *)SWELL_CStringToCFString(name); + + if (isFullFn) img = [[NSImage alloc] initWithContentsOfFile:str]; + else + { + img = [NSImage imageNamed:str]; + if (img) [img retain]; + } + [str release]; + } + + return img; +} + + +HCURSOR SWELL_LoadCursorFromFile(const char *fn) +{ + POINT hotspot={0,}; + NSImage *img = swell_imageFromCursorString(fn,&hotspot); + if (img) + { + HCURSOR ret=(HCURSOR)[[NSCursor alloc] initWithImage:img hotSpot:NSMakePoint(hotspot.x,hotspot.y)]; + [img release]; + return ret; + } + return NULL; +} + +// todo: support for loading from file +HCURSOR SWELL_LoadCursor(const char *_idx) +{ + if (_idx == IDC_NO||_idx==IDC_SIZENWSE || _idx == IDC_SIZENESW || _idx == IDC_SIZEALL) return (HCURSOR) MakeSWELLSystemCursor(_idx); + if (_idx == IDC_SIZEWE) return (HCURSOR)[NSCursor resizeLeftRightCursor]; + if (_idx == IDC_SIZENS) return (HCURSOR)[NSCursor resizeUpDownCursor]; + if (_idx == IDC_ARROW) return (HCURSOR)[NSCursor arrowCursor]; + if (_idx == IDC_HAND) return (HCURSOR)[NSCursor openHandCursor]; + if (_idx == IDC_UPARROW) return (HCURSOR)[NSCursor resizeUpCursor]; + if (_idx == IDC_IBEAM) return (HCURSOR)[NSCursor IBeamCursor]; + + // search registered cursor list + SWELL_CursorResourceIndex *p = SWELL_curmodule_cursorresource_head; + while (p) + { + if (p->resid == _idx) + { + if (p->cachedCursor) return p->cachedCursor; + + NSImage *img = swell_imageFromCursorString(p->resname,&p->hotspot); + if (img) + { + p->cachedCursor=(HCURSOR)[[NSCursor alloc] initWithImage:img hotSpot:NSMakePoint(p->hotspot.x,p->hotspot.y)]; + [img release]; + return p->cachedCursor; + } + } + p=p->_next; + } + return 0; +} + +static HCURSOR m_last_setcursor; + +void SWELL_SetCursor(HCURSOR curs) +{ + if (curs && [(id) curs isKindOfClass:[NSCursor class]]) + { + m_last_setcursor=curs; + [(NSCursor *)curs set]; + } + else + { + m_last_setcursor=NULL; + [[NSCursor arrowCursor] set]; + } +} + +HCURSOR SWELL_GetCursor() +{ + return (HCURSOR)[NSCursor currentCursor]; +} +HCURSOR SWELL_GetLastSetCursor() +{ + return m_last_setcursor; +} + +static POINT g_swell_mouse_relmode_curpos; // stored in osx-native coordinates (y=0=top of screen) +static bool g_swell_mouse_relmode; + + + +void GetCursorPos(POINT *pt) +{ + if (g_swell_mouse_relmode) + { + *pt=g_swell_mouse_relmode_curpos; + return; + } + NSPoint localpt=[NSEvent mouseLocation]; + pt->x=(int)floor(localpt.x); + pt->y=-(int)floor(-localpt.y); // floor() is used with negative sign, effectively ceil(), because screen coordinates are flipped and everywhere else we use nonflipped rounding +} + +DWORD GetMessagePos() +{ + if (g_swell_mouse_relmode) + { + return MAKELONG((int)g_swell_mouse_relmode_curpos.x,(int)g_swell_mouse_relmode_curpos.y); + } + NSPoint localpt=[NSEvent mouseLocation]; + return MAKELONG((int)floor(localpt.x), -(int)floor(-localpt.y)); // floor() is used with negative sign, effectively ceil(), because screen coordinates are flipped and everywhere else we use nonflipped rounding +} + + +NSPoint swellProcessMouseEvent(int msg, NSView *view, NSEvent *event) +{ + if (g_swell_mouse_relmode && msg==WM_MOUSEMOVE) // event will have relative coordinates + { + int idx=(int)[event deltaX]; + int idy=(int)[event deltaY]; + g_swell_mouse_relmode_curpos.x += idx; + g_swell_mouse_relmode_curpos.y -= idy; + } + if (g_swell_mouse_relmode) + { + POINT p=g_swell_mouse_relmode_curpos; + ScreenToClient((HWND)view,&p); + return NSMakePoint(p.x,p.y); + } + NSPoint localpt=[event locationInWindow]; + return [view convertPoint:localpt fromView:nil]; +} + +static int m_curvis_cnt; +bool SWELL_IsCursorVisible() +{ + return m_curvis_cnt>=0; +} +int SWELL_ShowCursor(BOOL bShow) +{ + m_curvis_cnt += (bShow?1:-1); + if (m_curvis_cnt==-1 && !bShow) + { + GetCursorPos(&g_swell_mouse_relmode_curpos); + CGDisplayHideCursor(kCGDirectMainDisplay); + CGAssociateMouseAndMouseCursorPosition(false); + g_swell_mouse_relmode=true; + } + if (m_curvis_cnt==0 && bShow) + { + CGDisplayShowCursor(kCGDirectMainDisplay); + CGAssociateMouseAndMouseCursorPosition(true); + g_swell_mouse_relmode=false; + SetCursorPos(g_swell_mouse_relmode_curpos.x,g_swell_mouse_relmode_curpos.y); + } + return m_curvis_cnt; +} + + +BOOL SWELL_SetCursorPos(int X, int Y) +{ + if (g_swell_mouse_relmode) + { + g_swell_mouse_relmode_curpos.x=X; + g_swell_mouse_relmode_curpos.y=Y; + return TRUE; + } + + const int h = (int)CGDisplayPixelsHigh(CGMainDisplayID()); + CGPoint pos=CGPointMake(X,h-Y); + return CGWarpMouseCursorPosition(pos)==kCGErrorSuccess; +} + +void SWELL_Register_Cursor_Resource(const char *idx, const char *name, int hotspot_x, int hotspot_y) +{ + SWELL_CursorResourceIndex *ri = (SWELL_CursorResourceIndex*)malloc(sizeof(SWELL_CursorResourceIndex)); + ri->hotspot.x = hotspot_x; + ri->hotspot.y = hotspot_y; + ri->resname=name; + ri->cachedCursor=0; + ri->resid = idx; + ri->_next = SWELL_curmodule_cursorresource_head; + SWELL_curmodule_cursorresource_head = ri; +} + + + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-menu-generic.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-menu-generic.cpp new file mode 100644 index 000000000..bbded1e87 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-menu-generic.cpp @@ -0,0 +1,1387 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic windows menu API + + */ + +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" +#include "swell-menugen.h" + +#include "swell-internal.h" + +#include "../ptrlist.h" +#include "../wdlcstring.h" + +static bool MenuIsStringType(const MENUITEMINFO *inf) +{ + return inf->fType == MFT_STRING || inf->fType == MFT_RADIOCHECK; +} + +HMENU__ *HMENU__::Duplicate() +{ + HMENU__ *p = new HMENU__; + int x; + for (x = 0; x < items.GetSize(); x ++) + { + MENUITEMINFO *s = items.Get(x); + MENUITEMINFO *inf = (MENUITEMINFO*)calloc(sizeof(MENUITEMINFO),1); + + *inf = *s; + if (inf->dwTypeData && MenuIsStringType(inf)) inf->dwTypeData=strdup(inf->dwTypeData); + if (inf->hSubMenu) inf->hSubMenu = inf->hSubMenu->Duplicate(); + + p->items.Add(inf); + } + return p; +} + +void HMENU__::freeMenuItem(void *p) +{ + MENUITEMINFO *inf = (MENUITEMINFO *)p; + if (!inf) return; + if (inf->hSubMenu) inf->hSubMenu->Release(); + if (MenuIsStringType(inf)) free(inf->dwTypeData); + free(inf); +} + +static MENUITEMINFO *GetMenuItemByID(HMENU menu, int id, bool searchChildren=true) +{ + if (WDL_NOT_NORMALLY(!menu)) return 0; + int x; + for (x = 0; x < menu->items.GetSize(); x ++) + if (menu->items.Get(x)->wID == (UINT)id) return menu->items.Get(x); + + if (searchChildren) for (x = 0; x < menu->items.GetSize(); x ++) + { + if (menu->items.Get(x)->hSubMenu) + { + MENUITEMINFO *ret = GetMenuItemByID(menu->items.Get(x)->hSubMenu,id,true); + if (ret) return ret; + } + } + + return 0; +} + +bool SetMenuItemText(HMENU hMenu, int idx, int flag, const char *text) +{ + MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? ((flag & MF_BYPOSITION) ? hMenu->items.Get(idx) : GetMenuItemByID(hMenu,idx)) : NULL; + if (!item) return false; + + if (MenuIsStringType(item)) free(item->dwTypeData); + else item->fType = MFT_STRING; + item->dwTypeData=strdup(text?text:""); + + return true; +} + +bool EnableMenuItem(HMENU hMenu, int idx, int en) +{ + MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? ((en & MF_BYPOSITION) ? hMenu->items.Get(idx) : GetMenuItemByID(hMenu,idx)) : NULL; + if (!item) return false; + + int mask = MF_ENABLED|MF_DISABLED|MF_GRAYED; + item->fState &= ~mask; + item->fState |= en&mask; + + return true; +} + +bool CheckMenuItem(HMENU hMenu, int idx, int chk) +{ + MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? ((chk & MF_BYPOSITION) ? hMenu->items.Get(idx) : GetMenuItemByID(hMenu,idx)) : NULL; + if (!item) return false; + + int mask = MF_CHECKED; + item->fState &= ~mask; + item->fState |= chk&mask; + + return true; +} +HMENU SWELL_GetCurrentMenu() +{ + return NULL; // not osx +} +void SWELL_SetCurrentMenu(HMENU hmenu) +{ +} + +HMENU GetSubMenu(HMENU hMenu, int pos) +{ + MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? hMenu->items.Get(pos) : NULL; + if (item) return item->hSubMenu; + return 0; +} + +int GetMenuItemCount(HMENU hMenu) +{ + if (WDL_NORMALLY(hMenu)) return hMenu->items.GetSize(); + return 0; +} + +int GetMenuItemID(HMENU hMenu, int pos) +{ + MENUITEMINFO *item = WDL_NORMALLY(hMenu) ? hMenu->items.Get(pos) : NULL; + if (!item) + { + WDL_ASSERT(pos==0); // do not assert if GetMenuItemID(0) is called on an empty menu + return -1; + } + if (item->hSubMenu) return -1; + return item->wID; +} + +bool SetMenuItemModifier(HMENU hMenu, int idx, int flag, int code, unsigned int mask) +{ + return false; +} + +HMENU CreatePopupMenu() +{ + return new HMENU__; +} +HMENU CreatePopupMenuEx(const char *title) +{ + return CreatePopupMenu(); +} + +void DestroyMenu(HMENU hMenu) +{ + if (WDL_NORMALLY(hMenu)) hMenu->Release(); +} + +int AddMenuItem(HMENU hMenu, int pos, const char *name, int tagid) +{ + if (WDL_NOT_NORMALLY(!hMenu)) return -1; + MENUITEMINFO *inf = (MENUITEMINFO*)calloc(1,sizeof(MENUITEMINFO)); + inf->wID = tagid; + inf->fType = MFT_STRING; + inf->dwTypeData = strdup(name?name:""); + hMenu->items.Insert(pos,inf); + return 0; +} + +bool DeleteMenu(HMENU hMenu, int idx, int flag) +{ + if (WDL_NOT_NORMALLY(!hMenu)) return false; + if (flag&MF_BYPOSITION) + { + if (hMenu->items.Get(idx)) + { + hMenu->items.Delete(idx,true,HMENU__::freeMenuItem); + return true; + } + return false; + } + else + { + int x; + int cnt=0; + for (x=0;xitems.GetSize(); x ++) + { + if (!hMenu->items.Get(x)->hSubMenu && hMenu->items.Get(x)->wID == (UINT)idx) + { + hMenu->items.Delete(x--,true,HMENU__::freeMenuItem); + cnt++; + } + } + if (!cnt) + { + for (x=0;xitems.GetSize(); x ++) + { + if (hMenu->items.Get(x)->hSubMenu) cnt += DeleteMenu(hMenu->items.Get(x)->hSubMenu,idx,flag)?1:0; + } + } + return !!cnt; + } +} + + +BOOL SetMenuItemInfo(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi) +{ + if (WDL_NOT_NORMALLY(!hMenu)) return 0; + MENUITEMINFO *item = byPos ? hMenu->items.Get(pos) : GetMenuItemByID(hMenu, pos, true); + if (!item) return 0; + + if ((mi->fMask & MIIM_SUBMENU) && mi->hSubMenu != item->hSubMenu) + { + if (item->hSubMenu) item->hSubMenu->Release(); + item->hSubMenu = mi->hSubMenu; + } + if (mi->fMask & MIIM_TYPE) + { + const bool wasString = MenuIsStringType(item), isString = MenuIsStringType(mi); + if (wasString != isString) + { + if (wasString) free(item->dwTypeData); + item->dwTypeData=NULL; + } + + if (mi->fType == MFT_BITMAP) item->dwTypeData = mi->dwTypeData; + else if (isString && mi->dwTypeData) + { + free(item->dwTypeData); + item->dwTypeData = strdup(mi->dwTypeData); + } + item->fType = mi->fType; + } + + if (mi->fMask & MIIM_STATE) item->fState = mi->fState; + if (mi->fMask & MIIM_ID) item->wID = mi->wID; + if (mi->fMask & MIIM_DATA) item->dwItemData = mi->dwItemData; + if ((mi->fMask & MIIM_BITMAP) && mi->cbSize >= sizeof(*mi)) item->hbmpItem = mi->hbmpItem; + + return true; +} + +BOOL GetMenuItemInfo(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi) +{ + if (WDL_NOT_NORMALLY(!hMenu)) return 0; + MENUITEMINFO *item = byPos ? hMenu->items.Get(pos) : GetMenuItemByID(hMenu, pos, true); + if (!item) return 0; + + if (mi->fMask & MIIM_TYPE) + { + mi->fType = item->fType; + if (MenuIsStringType(mi) && mi->dwTypeData && mi->cch) + { + lstrcpyn_safe(mi->dwTypeData,item->dwTypeData?item->dwTypeData:"",mi->cch); + } + else if (item->fType == MFT_BITMAP) mi->dwTypeData = item->dwTypeData; + } + + if (mi->fMask & MIIM_DATA) mi->dwItemData = item->dwItemData; + if (mi->fMask & MIIM_STATE) mi->fState = item->fState; + if (mi->fMask & MIIM_ID) mi->wID = item->wID; + if (mi->fMask & MIIM_SUBMENU) mi->hSubMenu = item->hSubMenu; + if ((mi->fMask & MIIM_BITMAP) && mi->cbSize >= sizeof(*mi)) mi->hbmpItem = item->hbmpItem; + + return 1; + +} + +void SWELL_InsertMenu(HMENU menu, int pos, unsigned int flag, UINT_PTR idx, const char *str) +{ + MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING, + (flag & ~MF_BYPOSITION),(flag&MF_POPUP) ? 0 : (UINT)idx,NULL,NULL,NULL,0,(char *)str}; + + if (flag&MF_POPUP) + { + mi.hSubMenu = (HMENU)idx; + mi.fMask |= MIIM_SUBMENU; + mi.fState &= ~MF_POPUP; + } + + if (flag&MF_SEPARATOR) + { + mi.fMask=MIIM_TYPE; + mi.fType=MFT_SEPARATOR; + mi.fState &= ~MF_SEPARATOR; + } + + if (flag&MF_BITMAP) + { + mi.fType=MFT_BITMAP; + mi.fState &= ~MF_BITMAP; + } + + InsertMenuItem(menu,pos,(flag&MF_BYPOSITION) ? TRUE : FALSE, &mi); +} + +void InsertMenuItem(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi) +{ + if (WDL_NOT_NORMALLY(!hMenu)) return; + int ni=hMenu->items.GetSize(); + + if (!byPos) + { + int x; + for (x=0;xitems.Get(x)->wID != (UINT)pos; x++); + pos = x; + } + if (pos < 0 || pos > ni) pos=ni; + + MENUITEMINFO *inf = (MENUITEMINFO*)calloc(sizeof(MENUITEMINFO),1); + inf->fType = mi->fType; + if (MenuIsStringType(inf)) + { + inf->dwTypeData = strdup(mi->dwTypeData?mi->dwTypeData:""); + } + else if (mi->fType == MFT_BITMAP) + { + inf->dwTypeData = mi->dwTypeData; + } + else if (mi->fType == MFT_SEPARATOR) + { + } + if (mi->fMask&MIIM_SUBMENU) inf->hSubMenu = mi->hSubMenu; + if (mi->fMask & MIIM_STATE) inf->fState = mi->fState; + if (mi->fMask & MIIM_DATA) inf->dwItemData = mi->dwItemData; + if (mi->fMask & MIIM_ID) inf->wID = mi->wID; + if ((mi->fMask & MIIM_BITMAP) && mi->cbSize >= sizeof(*mi)) inf->hbmpItem = mi->hbmpItem; + + hMenu->items.Insert(pos,inf); +} + + +void SWELL_SetMenuDestination(HMENU menu, HWND hwnd) +{ + // only needed for Cocoa +} + +extern RECT g_trackpopup_yroot; +static POINT m_trackingPt, m_trackingPt2; +static int m_trackingMouseFlag; +static int m_trackingFlags,m_trackingRet; +static HWND m_trackingPar; +static WDL_PtrList m_trackingMenus; // each HWND as userdata = HMENU + +int swell_delegate_menu_message(HWND src, LPARAM lParam, int msg, bool screencoords) +{ + static bool _reent; + if (_reent) return 0; + + _reent = true; + + POINT sp = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + if (!screencoords) ClientToScreen(src,&sp); + + for (int x = m_trackingMenus.GetSize()-1; x>=0; x--) + { + HWND sw = m_trackingMenus.Get(x); + if (!sw) continue; + + if (sw == src) break; // stop searching (don't delegate to parent) + + RECT r; + GetWindowRect(sw,&r); + if (PtInRect(&r,sp)) + { + POINT p = sp; + ScreenToClient(sw,&p); + SendMessage(sw,msg,0,MAKELPARAM(p.x,p.y)); + _reent = false; + return 1; + } + } + + _reent = false; + return 0; +} + +bool swell_isOSwindowmenu(SWELL_OSWINDOW osw) +{ + int x = m_trackingMenus.GetSize(); + if (osw) while (--x>=0) + { + HWND__ *p = m_trackingMenus.Get(x); + if (p->m_oswindow == osw) return true; + } + return false; +} + +int menuBarNavigate(int dir); // -1 if no menu bar active, 0 if did nothing, 1 if navigated +HWND GetFocusIncludeMenus(void); +static DWORD swell_menu_ignore_mousemove_from; + +static LRESULT WINAPI submenuWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static int lcol, rcol, mcol, top_margin, separator_ht, text_ht_pad, bitmap_ht_pad, scroll_margin, item_bm_pad; + if (!lcol) + { + lcol=SWELL_UI_SCALE(30); rcol=SWELL_UI_SCALE(18); mcol=SWELL_UI_SCALE(10); + top_margin=SWELL_UI_SCALE(4); separator_ht=SWELL_UI_SCALE(8); + text_ht_pad=SWELL_UI_SCALE(4); bitmap_ht_pad=SWELL_UI_SCALE(4); + scroll_margin=SWELL_UI_SCALE(10); + item_bm_pad = SWELL_UI_SCALE(4); + } + switch (uMsg) + { + case WM_CREATE: + hwnd->m_classname = "__SWELL_MENU"; + hwnd->m_style = WS_CHILD; + m_trackingMenus.Add(hwnd); + SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam); + + if (m_trackingPar && !(m_trackingFlags&TPM_NONOTIFY)) + SendMessage(m_trackingPar,WM_INITMENUPOPUP,(WPARAM)lParam,0); + + { + HDC hdc = GetDC(hwnd); + HMENU__ *menu = (HMENU__*)lParam; + int ht = 0, wid=SWELL_UI_SCALE(100),wid2=0; + int x; + for (x=0; x < menu->items.GetSize(); x++) + { + MENUITEMINFO *inf = menu->items.Get(x); + BITMAP bm2={0,}; + if (inf->hbmpItem) + GetObject(inf->hbmpItem,sizeof(bm2),&bm2); + + if (MenuIsStringType(inf)) + { + RECT r={0,}; + const char *str = inf->dwTypeData; + if (!str || !*str) str="XXXXX"; + const char *pt2 = strstr(str,"\t"); + DrawText(hdc,str,pt2 ? (int)(pt2-str) : -1,&r,DT_CALCRECT|DT_SINGLELINE); + if (r.bottom < bm2.bmHeight) r.bottom = bm2.bmHeight; + if (bm2.bmWidth) r.right += bm2.bmWidth + item_bm_pad; + + if (r.right > wid) wid=r.right; + ht += r.bottom + text_ht_pad; + + if (pt2) + { + r.right=r.left; + DrawText(hdc,pt2+1,-1,&r,DT_CALCRECT|DT_SINGLELINE); + if (r.right > wid2) wid2=r.right; + } + } + else if (inf->fType == MFT_BITMAP) + { + BITMAP bm={16,16}; + if (inf->dwTypeData) GetObject((HBITMAP)inf->dwTypeData,sizeof(bm),&bm); + if (bm.bmHeight < bm2.bmHeight) bm.bmHeight = bm2.bmHeight; + if (bm2.bmWidth) bm.bmWidth += bm2.bmWidth + item_bm_pad; + if (bm.bmWidth > wid) wid = bm.bmWidth; + + ht += bm.bmHeight + bitmap_ht_pad; + } + else + { + // treat as separator, ignore bm2 + ht += separator_ht; + } + } + wid+=lcol+rcol + (wid2?wid2+mcol:0); + ReleaseDC(hwnd,hdc); + + const RECT ref={m_trackingPt.x, m_trackingPt.y, m_trackingPt.x, m_trackingPt.y }; + RECT vp, tr={m_trackingPt.x,m_trackingPt.y, m_trackingPt.x+wid+SWELL_UI_SCALE(4),m_trackingPt.y+ht+top_margin * 2}; + SWELL_GetViewPort(&vp,&ref,true); + vp.bottom -= 8; + + if (g_trackpopup_yroot.bottom > g_trackpopup_yroot.top && + g_trackpopup_yroot.bottom > vp.top && + g_trackpopup_yroot.top < vp.bottom) + { + const int req_h = ht*9/8+32; + if (vp.bottom - g_trackpopup_yroot.bottom < req_h && g_trackpopup_yroot.top - vp.top >= req_h) + { + vp.bottom = g_trackpopup_yroot.top; + } + } + + if (tr.bottom > vp.bottom) { tr.top += vp.bottom-tr.bottom; tr.bottom=vp.bottom; } + if (tr.right > vp.right) + { + if ((vp.right - m_trackingPt2.x) < (m_trackingPt2.x - vp.left)) + { + tr.left = m_trackingPt2.x - (tr.right-tr.left); + tr.right = m_trackingPt2.x; + } + else + { + tr.left += vp.right-tr.right; tr.right=vp.right; + } + } + + if (tr.left < vp.left) { tr.right += vp.left-tr.left; tr.left=vp.left; } + if (tr.top < vp.top) { tr.bottom += vp.top-tr.top; tr.top=vp.top; } + if (tr.bottom > vp.bottom) tr.bottom=vp.bottom; + if (tr.right > vp.right) tr.right=vp.right; + + SetWindowPos(hwnd,NULL,tr.left,tr.top,tr.right-tr.left,tr.bottom-tr.top,SWP_NOZORDER); + + hwnd->m_extra[0] = 0; // Y scroll offset + hwnd->m_extra[1] = 0; // is currently autoscrolling to sel_vis + hwnd->m_extra[2] = 0; // remaining items out of view, if any + } + + ShowWindow(hwnd,SW_SHOW); + SetFocus(hwnd); + SetTimer(hwnd,1,100,NULL); + SetTimer(hwnd,2,15,NULL); + break; + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT cr; + GetClientRect(hwnd,&cr); + HBRUSH br = CreateSolidBrush(g_swell_ctheme.menu_bg); + HBRUSH br2 = CreateSolidBrushAlpha(g_swell_ctheme.menu_scroll,0.5f); + HBRUSH br3 = CreateSolidBrush(g_swell_ctheme.menu_scroll_arrow); + HBRUSH br_submenu_arrow = CreateSolidBrush(g_swell_ctheme.menu_submenu_arrow); + HBRUSH br_submenu_arrow_sel = CreateSolidBrush(g_swell_ctheme.menu_submenu_arrow_sel); + HPEN pen = CreatePen(PS_SOLID,0,g_swell_ctheme.menu_shadow); + HPEN pen2 = CreatePen(PS_SOLID,0,g_swell_ctheme.menu_hilight); + HGDIOBJ oldbr = SelectObject(ps.hdc,br); + HGDIOBJ oldpen = SelectObject(ps.hdc,pen2); + Rectangle(ps.hdc,cr.left,cr.top,cr.right,cr.bottom); + SetBkMode(ps.hdc,TRANSPARENT); + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + int ypos = top_margin; + + int extra_left = 0; + bool want_autoscroll_down = false; + const int ni = menu->items.GetSize(); + int x; + for (x=wdl_max((int)hwnd->m_extra[0],0); x < ni; x++) + { + if (ypos >= cr.bottom) + { + extra_left = ni-x; + break; + } + MENUITEMINFO *inf = menu->items.Get(x); + if (WDL_NOT_NORMALLY(!inf)) break; + + RECT r={lcol,ypos,cr.right, }; + bool dis = !!(inf->fState & MF_GRAYED); + BITMAP bm={16,16}, bm2={0,}; + if (inf->hbmpItem) + GetObject(inf->hbmpItem,sizeof(bm2),&bm2); + + if (MenuIsStringType(inf)) + { + const char *str = inf->dwTypeData; + if (!str || !*str) str="XXXXX"; + RECT mr={0,}; + DrawText(ps.hdc,str,-1,&mr,DT_CALCRECT|DT_SINGLELINE); + + ypos += wdl_max(mr.bottom,bm2.bmHeight) + text_ht_pad; + r.bottom = ypos; + } + else if (inf->fType == MFT_BITMAP) + { + if (inf->dwTypeData) GetObject((HBITMAP)inf->dwTypeData,sizeof(bm),&bm); + + ypos += wdl_max(bm.bmHeight,bm2.bmHeight) + bitmap_ht_pad; + r.bottom = ypos; + + } + else + { + dis=true; + ypos += separator_ht; + r.bottom = ypos; + } + + if (x == menu->sel_vis && !dis) + { + HBRUSH brs=CreateSolidBrush(g_swell_ctheme.menu_bg_sel); + RECT r2=r; + r2.left = cr.left + 1; + r2.right = r2.right - 1; + FillRect(ps.hdc,&r2,brs); + DeleteObject(brs); + SetTextColor(ps.hdc,g_swell_ctheme.menu_text_sel); + } + else + { + SetTextColor(ps.hdc, + dis ? g_swell_ctheme.menu_text_disabled : + g_swell_ctheme.menu_text); + } + + if (bm2.bmWidth) + { + RECT tr = r; + tr.right = tr.left + bm2.bmWidth; + DrawImageInRect(ps.hdc,inf->hbmpItem,&tr); + + r.left += bm2.bmWidth + item_bm_pad; + } + + if (MenuIsStringType(inf)) + { + const char *str = inf->dwTypeData; + if (!str) str=""; + const char *pt2 = strstr(str,"\t"); + + if (*str) + { + DrawText(ps.hdc,str,pt2 ? (int)(pt2-str) : -1,&r,DT_VCENTER|DT_SINGLELINE); + if (pt2) + { + RECT tr=r; tr.right-=rcol; + DrawText(ps.hdc,pt2+1,-1,&tr,DT_VCENTER|DT_SINGLELINE|DT_RIGHT); + } + } + } + else if (inf->fType == MFT_BITMAP) + { + if (inf->dwTypeData) + { + RECT tr = r; + tr.top += bitmap_ht_pad/2; + tr.right = tr.left + bm.bmWidth; + tr.bottom = tr.top + bm.bmHeight; + DrawImageInRect(ps.hdc,(HBITMAP)inf->dwTypeData,&tr); + } + } + else + { + SelectObject(ps.hdc,pen2); + int margin = rcol / 2; + int y = r.top/2+r.bottom/2, left = cr.left+margin, right = r.right-margin; + MoveToEx(ps.hdc,left,y,NULL); + LineTo(ps.hdc,right,y); + SelectObject(ps.hdc,pen); + + y++; + MoveToEx(ps.hdc,left,y,NULL); + LineTo(ps.hdc,right,y); + } + if (inf->hSubMenu) + { + const int sz = (r.bottom-r.top)/5, xp = r.right - rcol/2 - sz, yp = (r.top + r.bottom)/2; + + POINT pts[3] = { + {xp, yp-sz}, + {xp, yp+sz}, + {xp + sz,yp} + }; + HGDIOBJ oldPen = SelectObject(ps.hdc,GetStockObject(NULL_PEN)); + + if (x == menu->sel_vis && !dis) + SelectObject(ps.hdc,br_submenu_arrow_sel); + else + SelectObject(ps.hdc,br_submenu_arrow); + + Polygon(ps.hdc,pts,3); + + SelectObject(ps.hdc,oldPen); + + + } + if (inf->fState&MF_CHECKED) + { + int col; + if (x == menu->sel_vis && !dis) + col = g_swell_ctheme.menu_text_sel; + else + col = dis ? g_swell_ctheme.menu_text_disabled : g_swell_ctheme.menu_text; + + HPEN tpen = CreatePen(PS_SOLID,0, col); + HBRUSH tbr = CreateSolidBrush(col); + HGDIOBJ oldBrush = SelectObject(ps.hdc,tbr); + HGDIOBJ oldPen = SelectObject(ps.hdc,tpen); + const int sz = (wdl_min(lcol, r.bottom-r.top) - SWELL_UI_SCALE(10)); + const int xo = SWELL_UI_SCALE(4), yo = (r.bottom+r.top)/2 - sz/2; + if (inf->fType&MFT_RADIOCHECK) + { + Ellipse(ps.hdc, xo, yo, xo+sz, yo+sz); + } + else + { + static const unsigned char coords[12] = { 0, 76, 12, 64, 40, 92, 40, 118, 116, 16, 128, 28, }; + for (int pass=0;pass<2;pass++) + { + POINT pts[4]; + for (int i=0;i<4; i ++) + { + pts[i].x = lcol/2 + sz * ((int)coords[i*2+pass*4] - 64) / 128; + pts[i].y = (r.bottom+r.top)/2 + sz * ((int)coords[i*2+pass*4+1] - 64) / 128; + } + Polygon(ps.hdc,pts,4); + } + } + + SelectObject(ps.hdc,oldPen); + SelectObject(ps.hdc,oldBrush); + DeleteObject(tpen); + DeleteObject(tbr); + } + if ((r.top+ypos)/2 > cr.bottom) + { + extra_left = ni-x; + } + if (ypos > cr.bottom && x == menu->sel_vis) + { + want_autoscroll_down = true; + extra_left = ni-x; + } + } + hwnd->m_extra[1] = want_autoscroll_down || x <= menu->sel_vis; + hwnd->m_extra[2] = extra_left; + + + // lower scroll indicator + int mid=(cr.right-cr.left)/2; + SelectObject(ps.hdc,GetStockObject(NULL_PEN)); + SelectObject(ps.hdc,br3); + POINT pts[3]; + const int smm = SWELL_UI_SCALE(2); + const int smh = scroll_margin-smm*2; + if (extra_left>0) + { + RECT fr = {cr.left, cr.bottom-scroll_margin, cr.right,cr.bottom}; + FillRect(ps.hdc,&fr,br2); + pts[0].x = mid; pts[0].y = cr.bottom - smm; + pts[1].x = mid-smh; pts[1].y = pts[0].y - smh; + pts[2].x = mid+smh; pts[2].y = pts[1].y; + Polygon(ps.hdc,pts,3); + } + // upper scroll indicator + if (hwnd->m_extra[0] > 0) + { + RECT fr = {cr.left, cr.top, cr.right, cr.top+scroll_margin}; + FillRect(ps.hdc,&fr,br2); + + pts[0].x = mid; pts[0].y = cr.top + smm; + pts[1].x = mid-smh; pts[1].y = pts[0].y + smh; + pts[2].x = mid+smh; pts[2].y = pts[1].y; + Polygon(ps.hdc,pts,3); + } + + SelectObject(ps.hdc,oldbr); + SelectObject(ps.hdc,oldpen); + DeleteObject(br); + DeleteObject(br2); + DeleteObject(br3); + DeleteObject(br_submenu_arrow); + DeleteObject(br_submenu_arrow_sel); + DeleteObject(pen); + DeleteObject(pen2); + EndPaint(hwnd,&ps); + } + } + break; + case WM_TIMER: + if (wParam==1) + { + HWND h = GetFocusIncludeMenus(); + if (h!=hwnd) + { + int a = h ? m_trackingMenus.Find(h) : -1; + if (a<0 || a < m_trackingMenus.Find(hwnd)) + { + if (m_trackingMouseFlag && m_trackingMenus.Get(0)) + { + SetFocus(m_trackingMenus.Get(0)); + m_trackingMouseFlag=0; + } + else DestroyWindow(hwnd); + } + } + } + else if (wParam == 2) + { + // menu scroll + RECT tr; + GetWindowRect(hwnd,&tr); + + POINT curM; + GetCursorPos(&curM); + const bool xmatch = (curM.x >= tr.left && curM.x < tr.right); + if (xmatch || (hwnd->m_extra[1] && hwnd->m_extra[2])) + { + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + int xFirst = wdl_max(hwnd->m_extra[0],0); + const bool ymatch = curM.y >= tr.bottom-scroll_margin && curM.y < tr.bottom+scroll_margin; + if (hwnd->m_extra[2] && (hwnd->m_extra[1] || ymatch)) + { + if (hwnd->m_extra[1] && menu->sel_vis > 0) + { + const int li = menu->items.GetSize() - (int)hwnd->m_extra[2]; + const int incr = (menu->sel_vis - li)/2 + 2; + xFirst += wdl_max(incr,1); + } + else + xFirst++; + hwnd->m_extra[0]=xFirst; + hwnd->m_extra[1]=hwnd->m_extra[2]=0; + if (ymatch) menu->sel_vis=-1; + InvalidateRect(hwnd,NULL,FALSE); + } + else if (xFirst > 0 && curM.y >= tr.top-scroll_margin && curM.y < tr.top+scroll_margin) + { + hwnd->m_extra[0]=--xFirst; + menu->sel_vis=-1; + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + break; + case WM_KEYUP: + return 1; + case WM_MOUSEWHEEL: + { + RECT tr; + GetWindowRect(hwnd,&tr); + + POINT curM; + GetCursorPos(&curM); + const bool xmatch = (curM.x >= tr.left && curM.x < tr.right); + const bool ymatch = curM.y >= tr.bottom-scroll_margin && curM.y < tr.bottom+scroll_margin; + if (!xmatch || !ymatch) // if mouse is over scroll area, ignore + { + int amt = ((short)HIWORD(wParam))/-40; + const int xFirst = wdl_max(hwnd->m_extra[0],0); + if (amt > hwnd->m_extra[2]) amt = (int) hwnd->m_extra[2]; + if (amt < 0 ? xFirst > 0 : amt > 0) + { + hwnd->m_extra[0]=wdl_max(xFirst+amt,0); + if (amt>0) + { + hwnd->m_extra[2] -= amt; + if (hwnd->m_extra[2]<0) hwnd->m_extra[2]=0; + } + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + menu->sel_vis=-1; // clear selection + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + return 1; + + case WM_KEYDOWN: + + if (wParam >= VK_SPACE && wParam < 0x80) + { + // ignore mousemoves immediately after most keys + swell_menu_ignore_mousemove_from = GetTickCount(); + } + + if (wParam == VK_ESCAPE || wParam == VK_LEFT) + { + HWND l = m_trackingMenus.Get(m_trackingMenus.Find(hwnd)-1); + if (l) SetFocus(l); + else + { + if (wParam != VK_LEFT || menuBarNavigate(-1) < 0) + DestroyWindow(hwnd); + } + } + else if (wParam == VK_RETURN || wParam == VK_RIGHT) + { + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (wParam == VK_RIGHT) + { + MENUITEMINFO *inf = menu->items.Get(menu->sel_vis); + if (!inf || !inf->hSubMenu) + { + menuBarNavigate(1); + return 1; + } + } + SendMessage(hwnd,WM_USER+100,1,menu->sel_vis); + } + else if (wParam == VK_UP || wParam == VK_PRIOR) + { + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + int l = menu->sel_vis; + for (int i= wParam == VK_UP ? 0 : 9; i>=0; i--) + { + int mc = menu->items.GetSize(); + while (mc--) + { + if (l<1) + { + if (wParam != VK_UP) break; + l = menu->items.GetSize(); + } + MENUITEMINFO *inf = menu->items.Get(--l); + if (!inf) break; + if (!(inf->fState & MF_GRAYED) && inf->fType != MFT_SEPARATOR) + { + menu->sel_vis=l; + break; + } + } + } + if (menu->sel_vis < hwnd->m_extra[0]) + hwnd->m_extra[0] = menu->sel_vis; + InvalidateRect(hwnd,NULL,FALSE); + } + else if (wParam == VK_DOWN || wParam == VK_NEXT) + { + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + int l = menu->sel_vis; + const int n =menu->items.GetSize()-1; + for (int i = wParam == VK_DOWN ? 0 : 9; i>=0; i--) + { + int mc = n+1; + while (mc--) + { + if (l>=n) + { + if (wParam != VK_DOWN) break; + l=-1; + hwnd->m_extra[0]=0; + } + MENUITEMINFO *inf = menu->items.Get(++l); + if (!inf) break; + if (!(inf->fState & MF_GRAYED) && inf->fType != MFT_SEPARATOR) + { + menu->sel_vis=l; + break; + } + } + } + InvalidateRect(hwnd,NULL,FALSE); + } + else if (wParam == VK_END) + { + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + int l = menu->items.GetSize(); + while (l > 0) + { + MENUITEMINFO *inf = menu->items.Get(--l); + if (!inf) break; + if (!(inf->fState & MF_GRAYED) && inf->fType != MFT_SEPARATOR) + { + menu->sel_vis=l; + break; + } + } + if (menu->sel_vis < hwnd->m_extra[0]) + hwnd->m_extra[0] = menu->sel_vis; + InvalidateRect(hwnd,NULL,FALSE); + } + else if (wParam == VK_HOME) + { + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + int l = 0; + while (l < menu->items.GetSize()) + { + MENUITEMINFO *inf = menu->items.Get(l++); + if (!inf) break; + if (!(inf->fState & MF_GRAYED) && inf->fType != MFT_SEPARATOR) + { + menu->sel_vis=l-1; + break; + } + } + if (menu->sel_vis < hwnd->m_extra[0]) + hwnd->m_extra[0] = menu->sel_vis; + InvalidateRect(hwnd,NULL,FALSE); + } + else if ((lParam & FVIRTKEY) && ( + (wParam >= 'A' && wParam <= 'Z') || + (wParam >= '0' && wParam <= '9'))) + { + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + const int n=menu->items.GetSize(); + + int offs = menu->sel_vis+1; + if (offs<0||offs>=n) offs=0; + int matchcnt=0; + for(int x=0;xitems.Get(offs); + if (MenuIsStringType(inf) && + !(inf->fState & MF_GRAYED) && + inf->dwTypeData) + { + const char *p = inf->dwTypeData; + bool is_prefix_mode = xsel_vis); + } + break; + } + + if (is_prefix_mode) while (*p) + { + if (*p++ == '&') + { + if (*p != '&') break; + p++; + } + } + + if (*p > 0 && (WPARAM)toupper(*p) == wParam) + { + if (!matchcnt++) + { + menu->sel_vis = offs; + if (menu->sel_vis < hwnd->m_extra[0]) + hwnd->m_extra[0] = menu->sel_vis; + InvalidateRect(hwnd,NULL,FALSE); + } + if (!is_prefix_mode) break; + } + } + if (++offs >= n) offs=0; + } + } + + return 1; + case WM_DESTROY: + { + int a = m_trackingMenus.Find(hwnd); + m_trackingMenus.Delete(a); + if (m_trackingMenus.Get(a)) DestroyWindow(m_trackingMenus.Get(a)); + RemoveProp(hwnd,"SWELL_MenuOwner"); + } + break; + case WM_USER+100: + if (wParam == 1 || wParam == 2 || wParam == 3 || wParam == 4) + { + int which = (int) lParam; + int item_ypos = which; + + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + + int ht = top_margin; + HDC hdc=GetDC(hwnd); + if (wParam > 1) which = -1; + else item_ypos = 0; + for (int x=wdl_max(hwnd->m_extra[0],0); x < (menu->items.GetSize()); x++) + { + if (wParam == 1 && which == x) { item_ypos = ht; break; } + MENUITEMINFO *inf = menu->items.Get(x); + int lastht = ht; + BITMAP bm2={0,}; + if (inf->hbmpItem) + GetObject(inf->hbmpItem,sizeof(bm2),&bm2); + + if (MenuIsStringType(inf)) + { + RECT r={0,}; + const char *str = inf->dwTypeData; + if (!str || !*str) str="XXXXX"; + const char *pt2 = strstr(str,"\t"); + DrawText(hdc,str,pt2 ? (int)(pt2-str) : -1,&r,DT_CALCRECT|DT_SINGLELINE); + ht += wdl_max(r.bottom,bm2.bmHeight) + text_ht_pad; + } + else if (inf->fType == MFT_BITMAP) + { + BITMAP bm={16,16}; + if (inf->dwTypeData) GetObject((HBITMAP)inf->dwTypeData,sizeof(bm),&bm); + ht += wdl_max(bm.bmHeight,bm2.bmHeight) + bitmap_ht_pad; + } + else + { + ht += separator_ht; + } + if (wParam > 1 && item_ypos < ht) + { + item_ypos = lastht; + which = x; + if (wParam == 4 && inf->hSubMenu) + { + HWND nextmenu = m_trackingMenus.Get(m_trackingMenus.Find(hwnd)+1); + if (!nextmenu || GetWindowLongPtr(nextmenu,GWLP_USERDATA) != (LPARAM)inf->hSubMenu) + { + wParam = 1; // activate if not already visible + menu->sel_vis = which; + } + } + break; + } + } + ReleaseDC(hwnd,hdc); + if (wParam == 3 || wParam == 4) + { + MENUITEMINFO *inf = menu->items.Get(which); + HWND next = m_trackingMenus.Get(m_trackingMenus.Find(hwnd)+1); + if (next && inf && (!inf->hSubMenu || (LPARAM)inf->hSubMenu != GetWindowLongPtr(next,GWLP_USERDATA))) DestroyWindow(next); + menu->sel_vis = which; + return 0; + } + + MENUITEMINFO *inf = menu->items.Get(which); + + if (inf) + { + if (inf->fState&MF_GRAYED){ } + else if (inf->hSubMenu) + { + const int nextidx = m_trackingMenus.Find(hwnd)+1; + HWND hh = m_trackingMenus.Get(nextidx); + + inf->hSubMenu->sel_vis=-1; + + if (hh) + { + m_trackingMenus.Delete(nextidx); + int a = m_trackingMenus.GetSize(); + while (a > nextidx) DestroyWindow(m_trackingMenus.Get(--a)); + } + else + { + hh = new HWND__(NULL,0,NULL,"menu",false,submenuWndProc,NULL, hwnd); + SetProp(hh,"SWELL_MenuOwner",GetProp(hwnd,"SWELL_MenuOwner")); + } + + RECT r; + GetClientRect(hwnd,&r); + m_trackingPt.x=r.right; + m_trackingPt.y=item_ypos; + m_trackingPt2.x=r.left; + m_trackingPt2.y=item_ypos; + ClientToScreen(hwnd,&m_trackingPt); + ClientToScreen(hwnd,&m_trackingPt2); + + submenuWndProc(hh, WM_CREATE,0,(LPARAM)inf->hSubMenu); + InvalidateRect(hwnd,NULL,FALSE); + } + else if (inf->wID) m_trackingRet = inf->wID; + } + } + return 0; + case WM_MOUSEMOVE: + { + if ((GetTickCount() - swell_menu_ignore_mousemove_from)<200) return 0; + + if (swell_delegate_menu_message(hwnd, lParam,uMsg, false)) + return 0; + + RECT r; + GetClientRect(hwnd,&r); + HMENU__ *menu = (HMENU__*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + const int oldsel = menu->sel_vis; + if (GET_X_LPARAM(lParam)>=r.left && GET_X_LPARAM(lParam)= r.right - rcol*2 ? 4 : 3; + SendMessage(hwnd,WM_USER+100,mode,GET_Y_LPARAM(lParam)); + } + else menu->sel_vis = -1; + if (oldsel != menu->sel_vis) InvalidateRect(hwnd,NULL,FALSE); + } + return 0; + case WM_LBUTTONUP: + case WM_RBUTTONUP: + { + if (swell_delegate_menu_message(hwnd, lParam, uMsg, false)) + return 0; + + RECT r; + GetClientRect(hwnd,&r); + if (GET_X_LPARAM(lParam)>=r.left && GET_X_LPARAM(lParam)Retain(); + m_trackingPar=hwnd; + m_trackingFlags=flags; + m_trackingRet=-1; + m_trackingPt2.x=m_trackingPt.x=xpos; + m_trackingPt2.y=m_trackingPt.y=ypos; + m_trackingMouseFlag = 0; + if (GetAsyncKeyState(VK_LBUTTON)) m_trackingMouseFlag |= 1; + if (GetAsyncKeyState(VK_RBUTTON)) m_trackingMouseFlag |= 2; + if (GetAsyncKeyState(VK_MBUTTON)) m_trackingMouseFlag |= 4; + +// HWND oldFoc = GetFocus(); + // bool oldFoc_child = oldFoc && (IsChild(hwnd,oldFoc) || oldFoc == hwnd || oldFoc==GetParent(hwnd)); + + if (hwnd) + { + hwnd->Retain(); + swell_ignore_focus_oswindow = hwnd->m_oswindow; + swell_ignore_focus_oswindow_until = GetTickCount()+500; + } + + + if (r && r->left == (1<<30) && r->top == (1<<30) && !r->right) + hMenu->sel_vis = r->bottom; + else + hMenu->sel_vis=-1; + + if (!resvd || resvd == 0xbeee) swell_menu_ignore_mousemove_from = GetTickCount(); + + HWND hh=new HWND__(NULL,0,NULL,"menu",false,submenuWndProc,NULL, hwnd); + + submenuWndProc(hh,WM_CREATE,0,(LPARAM)hMenu); + + SetProp(hh,"SWELL_MenuOwner",(HANDLE)hwnd); + + while (m_trackingRet<0 && m_trackingMenus.GetSize()) + { + void SWELL_RunMessageLoop(); + SWELL_RunMessageLoop(); + Sleep(10); + } + + int x=m_trackingMenus.GetSize()-1; + while (x>=0) + { + HWND h = m_trackingMenus.Get(x); + m_trackingMenus.Delete(x); + if (h) DestroyWindow(h); + x--; + } + +// if (oldFoc_child) SetFocus(oldFoc); + + if (!(flags&TPM_RETURNCMD) && m_trackingRet>0) + SendMessage(hwnd,WM_COMMAND,m_trackingRet,0); + + if (hwnd) hwnd->Release(); + + swell_ignore_focus_oswindow = NULL; + hMenu->Release(); + + m_trackingPar = NULL; + + if (flags & TPM_RETURNCMD) return m_trackingRet>0?m_trackingRet:0; + + return (resvd|1)!=0xbeef || m_trackingRet>0; +} + + + + +void SWELL_Menu_AddMenuItem(HMENU hMenu, const char *name, int idx, unsigned int flags) +{ + MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING, + (UINT)((flags)?MFS_GRAYED:0),(UINT)idx,NULL,NULL,NULL,0,(char *)name}; + if (!name) + { + mi.fType = MFT_SEPARATOR; + mi.fMask&=~(MIIM_STATE|MIIM_ID); + } + InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi); +} + + +SWELL_MenuResourceIndex *SWELL_curmodule_menuresource_head; // todo: move to per-module thingy + +static SWELL_MenuResourceIndex *resById(SWELL_MenuResourceIndex *head, const char *resid) +{ + SWELL_MenuResourceIndex *p=head; + while (p) + { + if (p->resid == resid) return p; + p=p->_next; + } + return 0; +} + +HMENU SWELL_LoadMenu(SWELL_MenuResourceIndex *head, const char *resid) +{ + SWELL_MenuResourceIndex *p; + + if (!(p=resById(head,resid))) return 0; + HMENU hMenu=CreatePopupMenu(); + if (hMenu) p->createFunc(hMenu); + return hMenu; +} + +HMENU SWELL_DuplicateMenu(HMENU menu) +{ + if (WDL_NOT_NORMALLY(!menu)) return 0; + return menu->Duplicate(); +} + +BOOL SetMenu(HWND hwnd, HMENU menu) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + HMENU oldmenu = hwnd->m_menu; + + hwnd->m_menu = menu; + + if (!hwnd->m_parent && !!hwnd->m_menu != !!oldmenu) + { + WNDPROC oldwc = hwnd->m_wndproc; + hwnd->m_wndproc = DefWindowProc; + RECT r; + GetWindowRect(hwnd,&r); + + if (oldmenu) r.bottom -= g_swell_ctheme.menubar_height; // hack: we should WM_NCCALCSIZE before and after, really + else r.bottom += g_swell_ctheme.menubar_height; + + SetWindowPos(hwnd,NULL,0,0,r.right-r.left,r.bottom-r.top,SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE); + hwnd->m_wndproc = oldwc; + // resize + } + + return TRUE; +} + +HMENU GetMenu(HWND hwnd) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + return hwnd->m_menu; +} + +void DrawMenuBar(HWND hwnd) +{ + if (WDL_NORMALLY(hwnd) && hwnd->m_menu) + { + RECT r; + GetClientRect(hwnd,&r); + r.top = - g_swell_ctheme.menubar_height; + r.bottom=0; + InvalidateRect(hwnd,&r,FALSE); + } +} + + +// copied from swell-menu.mm, can have a common impl someday +int SWELL_GenerateMenuFromList(HMENU hMenu, const void *_list, int listsz) +{ + SWELL_MenuGen_Entry *list = (SWELL_MenuGen_Entry *)_list; + const int l1=strlen(SWELL_MENUGEN_POPUP_PREFIX); + while (listsz>0) + { + int cnt=1; + if (!list->name) SWELL_Menu_AddMenuItem(hMenu,NULL,-1,0); + else if (!strcmp(list->name,SWELL_MENUGEN_ENDPOPUP)) return list + 1 - (SWELL_MenuGen_Entry *)_list; + else if (!strncmp(list->name,SWELL_MENUGEN_POPUP_PREFIX,l1)) + { + MENUITEMINFO mi={sizeof(mi),MIIM_SUBMENU|MIIM_STATE|MIIM_TYPE,MFT_STRING,0,0,CreatePopupMenuEx(list->name+l1),NULL,NULL,0,(char *)list->name+l1}; + cnt += SWELL_GenerateMenuFromList(mi.hSubMenu,list+1,listsz-1); + InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi); + } + else SWELL_Menu_AddMenuItem(hMenu,list->name,list->idx,list->flags); + + list+=cnt; + listsz -= cnt; + } + return list + 1 - (SWELL_MenuGen_Entry *)_list; +} +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-menu.mm b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-menu.mm new file mode 100644 index 000000000..b5bd39aba --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-menu.mm @@ -0,0 +1,956 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic windows menu API to interface an NSMenu + + */ + +#ifndef SWELL_PROVIDED_BY_APP + +#import + +#include "swell.h" +#include "swell-menugen.h" + +#include "swell-internal.h" + + +static void __filtnametobuf(char *out, const char *in, int outsz) +{ + while (*in && outsz>1) + { + if (*in == '\t') break; + if (*in == '&') + { + in++; + } + *out++=*in++; + outsz--; + } + *out=0; +} + + + +bool SetMenuItemText(HMENU hMenu, int idx, int flag, const char *text) +{ + NSMenu *menu=(NSMenu *)hMenu; + if (WDL_NOT_NORMALLY(!menu)) return false; + + NSMenuItem *item; + if (flag & MF_BYPOSITION) item=[menu itemAtIndex:idx]; + else item =[menu itemWithTag:idx]; + if (!item) + { + if (!(flag & MF_BYPOSITION)) + { + const int n = (int) [menu numberOfItems]; + for (int x = 0; x < n; x ++) + { + item=[menu itemAtIndex:x]; + if (item && [item hasSubmenu]) + { + NSMenu *m=[item submenu]; + if (m && SetMenuItemText((HMENU)m,idx,flag,text)) return true; + } + } + } + return false; + } + char buf[1024]; + __filtnametobuf(buf,text?text:"",sizeof(buf)); + NSString *label=(NSString *)SWELL_CStringToCFString(buf); + + [item setTitle:label]; + if ([item hasSubmenu] && [item submenu]) [[item submenu] setTitle:label]; + + [label release]; + return true; +} + +bool EnableMenuItem(HMENU hMenu, int idx, int en) +{ + NSMenu *menu=(NSMenu *)hMenu; + if (WDL_NOT_NORMALLY(!menu)) return false; + + NSMenuItem *item; + if (en & MF_BYPOSITION) item=[menu itemAtIndex:idx]; + else item =[menu itemWithTag:idx]; + if (!item) + { + if (!(en & MF_BYPOSITION)) + { + const int n=(int)[menu numberOfItems]; + for (int x = 0; x < n; x ++) + { + item=[menu itemAtIndex:x]; + if (item && [item hasSubmenu]) + { + NSMenu *m=[item submenu]; + if (m && EnableMenuItem((HMENU)m,idx,en)) return true; + } + } + } + return false; + } + [item setEnabled:((en&MF_GRAYED)?NO:YES)]; + return true; +} + +bool CheckMenuItem(HMENU hMenu, int idx, int chk) +{ + NSMenu *menu=(NSMenu *)hMenu; + if (WDL_NOT_NORMALLY(!menu)) return false; + + NSMenuItem *item; + if (chk & MF_BYPOSITION) item=[menu itemAtIndex:idx]; + else item =[menu itemWithTag:idx]; + if (!item) + { + if (!(chk & MF_BYPOSITION)) + { + const int n=(int)[menu numberOfItems]; + for (int x = 0; x < n; x ++) + { + item=[menu itemAtIndex:x]; + if (item && [item hasSubmenu]) + { + NSMenu *m=[item submenu]; + if (m && CheckMenuItem((HMENU)m,idx,chk)) return true; + } + } + } + return false; + } + [item setState:((chk&MF_CHECKED)?NSOnState:NSOffState)]; + + return true; +} +HMENU SWELL_GetCurrentMenu() +{ + return (HMENU)[NSApp mainMenu]; +} + +extern int g_swell_terminating; + +void SWELL_SetCurrentMenu(HMENU hmenu) +{ + if (WDL_NORMALLY(hmenu && [(id)hmenu isKindOfClass:[NSMenu class]])) + { + if (!g_swell_terminating) [NSApp setMainMenu:(NSMenu *)hmenu]; + } +} + +HMENU GetSubMenu(HMENU hMenu, int pos) +{ + NSMenu *menu=(NSMenu *)hMenu; + WDL_ASSERT(menu != NULL); + + NSMenuItem *item=menu && pos >=0 && pos < [menu numberOfItems] ? [menu itemAtIndex:pos] : 0; + if (item && [item hasSubmenu]) return (HMENU)[item submenu]; + return 0; +} + +int GetMenuItemCount(HMENU hMenu) +{ + NSMenu *menu=(NSMenu *)hMenu; + WDL_ASSERT(menu != NULL); + return (int)[menu numberOfItems]; +} + +int GetMenuItemID(HMENU hMenu, int pos) +{ + NSMenu *menu=(NSMenu *)hMenu; + if (pos < 0 || pos >= (int)[menu numberOfItems]) + { + WDL_ASSERT(pos==0); // don't assert if GetMenuItemID(0) is called on an empty menu + return 0; + } + + NSMenuItem *item=[menu itemAtIndex:pos]; + if (item) + { + if ([item hasSubmenu]) return -1; + return (int)[item tag]; + } + return 0; +} + +bool SetMenuItemModifier(HMENU hMenu, int idx, int flag, int code, unsigned int mask) +{ + if (WDL_NOT_NORMALLY(hMenu == NULL)) return false; + + NSMenu *menu=(NSMenu *)hMenu; + + NSMenuItem *item; + if (flag & MF_BYPOSITION) item=[menu itemAtIndex:idx]; + else item =[menu itemWithTag:idx]; + if (!item) + { + if (!(flag & MF_BYPOSITION)) + { + const int n = (int)[menu numberOfItems]; + for (int x = 0; x < n; x ++) + { + item=[menu itemAtIndex:x]; + if (item && [item hasSubmenu]) + { + NSMenu *m=[item submenu]; + if (m && SetMenuItemModifier((HMENU)m,idx,flag,code,mask)) return true; + } + } + } + return false; + } + + bool suppressShift = false; + unichar arrowKey = 0; + + if (code >= 'A' && code <= 'Z') + { + arrowKey = (mask & FSHIFT) ? code : (code + 'a' - 'A'); + suppressShift=true; + } + else if ((code>='0' && code <= '9') || + code== ' ' || + (!(mask&FVIRTKEY) && (code >= '!' && code <= '~'))) + { + arrowKey=code; + } + else if (code >= VK_F1 && code <= VK_F24) + { + arrowKey = NSF1FunctionKey + code - VK_F1; + } + else switch (code&0xff) + { + #define DEFKP(wink,mack) case wink: arrowKey = mack; break; + DEFKP(VK_UP,NSUpArrowFunctionKey) + DEFKP(VK_DOWN,NSDownArrowFunctionKey) + DEFKP(VK_LEFT,NSLeftArrowFunctionKey) + DEFKP(VK_RIGHT,NSRightArrowFunctionKey) + DEFKP(VK_INSERT,NSInsertFunctionKey) + DEFKP(VK_DELETE,NSDeleteCharacter) + DEFKP(VK_BACK,NSBackspaceCharacter) + DEFKP(VK_HOME,NSHomeFunctionKey) + DEFKP(VK_END,NSEndFunctionKey) + DEFKP(VK_NEXT,NSPageDownFunctionKey) + DEFKP(VK_PRIOR,NSPageUpFunctionKey) + DEFKP(VK_SUBTRACT,'-') + DEFKP(VK_RETURN,'\r') + DEFKP(VK_TAB,'\t') + DEFKP(VK_ESCAPE,27) + // hmm numpad enter, what to do: DEFKP(VK_RETURN|32768, '\r') + } + + unsigned int mask2=0; + if (mask&FALT) mask2|=NSAlternateKeyMask; + if (!suppressShift) if (mask&FSHIFT) mask2|=NSShiftKeyMask; + if (mask&FCONTROL) mask2|=NSCommandKeyMask; + if (mask&FLWIN) mask2|=NSControlKeyMask; + + [item setKeyEquivalentModifierMask:mask2]; + [item setKeyEquivalent:arrowKey?[NSString stringWithCharacters:&arrowKey length:1]:@""]; + return true; +} + +// #define SWELL_MENU_ACCOUNTING + +#ifdef SWELL_MENU_ACCOUNTING +struct menuTmp +{ + NSMenu *menu; + NSString *lbl; +}; + +WDL_PtrList allMenus; +#endif + +@implementation SWELL_Menu +- (id)copyWithZone:(NSZone *)zone +{ + id rv = [super copyWithZone:zone]; +#ifdef SWELL_MENU_ACCOUNTING + if (rv) + { + menuTmp *mt = new menuTmp; + mt->menu=(NSMenu *)rv; + NSString *lbl = [(SWELL_Menu *)rv title]; + mt->lbl = lbl; + [lbl retain]; + allMenus.Add(mt); + NSLog(@"copy menu, new count=%d lbl=%@\n",allMenus.GetSize(),lbl); + } +#endif + return rv; +} +-(void)dealloc +{ +#ifdef SWELL_MENU_ACCOUNTING + int x; + bool f=false; + for(x=0;xmenu == self) + { + NSLog(@"dealloc menu, found self %@\n",allMenus.Get(x)->lbl); + allMenus.Delete(x); + f=true; + break; + } + } + + NSLog(@"dealloc menu, new count=%d %@\n",allMenus.GetSize(), [self title]); + if (!f) + { + NSLog(@"deleting unfound menu!!\n"); + } +#endif + [super dealloc]; +} +@end + +HMENU CreatePopupMenu() +{ + return CreatePopupMenuEx(NULL); +} +HMENU CreatePopupMenuEx(const char *title) +{ + SWELL_Menu *m; + if (title) + { + char buf[1024]; + __filtnametobuf(buf,title,sizeof(buf)); + NSString *lbl=(NSString *)SWELL_CStringToCFString(buf); + m=[[SWELL_Menu alloc] initWithTitle:lbl]; +#ifdef SWELL_MENU_ACCOUNTING + menuTmp *mt = new menuTmp; + mt->menu=m; + mt->lbl = lbl; + [lbl retain]; + allMenus.Add(mt); + NSLog(@"alloc menu, new count=%d lbl=%@\n",allMenus.GetSize(),lbl); +#endif + [lbl release]; + } + else + { + m=[[SWELL_Menu alloc] init]; +#ifdef SWELL_MENU_ACCOUNTING + menuTmp *mt = new menuTmp; + mt->menu=m; + mt->lbl = @""; + allMenus.Add(mt); + NSLog(@"alloc menu, new count=%d lbl=%@\n",allMenus.GetSize(),@""); +#endif + } + [m setAutoenablesItems:NO]; + + return (HMENU)m; +} + +void DestroyMenu(HMENU hMenu) +{ + if (WDL_NORMALLY(hMenu)) + { + SWELL_SetMenuDestination(hMenu,NULL); + NSMenu *m=(NSMenu *)hMenu; + [m release]; + } +} + + + +int AddMenuItem(HMENU hMenu, int pos, const char *name, int tagid) +{ + if (WDL_NOT_NORMALLY(!hMenu)) return -1; + NSMenu *m=(NSMenu *)hMenu; + NSString *label=(NSString *)SWELL_CStringToCFString(name); + NSMenuItem *item=[m insertItemWithTitle:label action:NULL keyEquivalent:@"" atIndex:pos]; + [label release]; + [item setTag:tagid]; + [item setEnabled:YES]; + return 0; +} + +bool DeleteMenu(HMENU hMenu, int idx, int flag) +{ + if (WDL_NOT_NORMALLY(!hMenu)) return false; + NSMenu *m=(NSMenu *)hMenu; + NSMenuItem *item=NULL; + + if (flag&MF_BYPOSITION) + { + if (idx >=0 && idx < [m numberOfItems]) + item=[m itemAtIndex:idx]; + if (!item) return false; + } + else + { + item=[m itemWithTag:idx]; + if (!item) + { + const int n = (int) [m numberOfItems]; + for (int x=0;xfMask & MIIM_TYPE) + { + if (mi->fType == MFT_STRING && mi->dwTypeData) + { + char buf[1024]; + __filtnametobuf(buf,mi->dwTypeData?mi->dwTypeData:"(null)",sizeof(buf)); + NSString *label=(NSString *)SWELL_CStringToCFString(buf); + + [item setTitle:label]; + + if ([item hasSubmenu]) + { + NSMenu *subm=[item submenu]; + if (subm) [subm setTitle:label]; + } + + [label release]; + } + } + if (mi->fMask & MIIM_SUBMENU) + { + NSMenu *oldMenu = [item hasSubmenu] ? [item submenu] : NULL; + NSMenu *newMenu = (NSMenu*)mi->hSubMenu; + if (oldMenu != newMenu) + { + if (oldMenu) [oldMenu retain]; // we do not destroy the old menu, caller responsibility + + if (newMenu) [newMenu setTitle:[item title]]; + [m setSubmenu:newMenu forItem:item]; + if (newMenu) [newMenu release]; // let the parent menu free it + } + } + + if (mi->fMask & MIIM_STATE) + { + [item setState:((mi->fState&MFS_CHECKED)?NSOnState:NSOffState)]; + [item setEnabled:((mi->fState&MFS_GRAYED)?NO:YES)]; + } + if (mi->fMask & MIIM_ID) + { + [item setTag:mi->wID]; + } + if (mi->fMask & MIIM_DATA) + { + SWELL_DataHold* newh = [[SWELL_DataHold alloc] initWithVal:(void*)mi->dwItemData]; + [item setRepresentedObject:newh]; + [newh release]; + } + + return true; +} + +BOOL GetMenuItemInfo(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi) +{ + if (WDL_NOT_NORMALLY(!hMenu)) return 0; + NSMenu *m=(NSMenu *)hMenu; + NSMenuItem *item; + if (byPos) + { + item=[m itemAtIndex:pos]; + } + else item=[m itemWithTag:pos]; + + if (!item) + { + if (!byPos) + { + const int n = (int)[m numberOfItems]; + for (int x = 0; x < n; x ++) + { + item=[m itemAtIndex:x]; + if (item && [item hasSubmenu]) + { + NSMenu *m1=[item submenu]; + if (m1 && GetMenuItemInfo((HMENU)m1,pos,byPos,mi)) return true; + } + } + } + return 0; + } + + if (mi->fMask & MIIM_TYPE) + { + if ([item isSeparatorItem]) mi->fType = MFT_SEPARATOR; + else + { + mi->fType = MFT_STRING; + if (mi->dwTypeData && mi->cch) + { + mi->dwTypeData[0]=0; + SWELL_CFStringToCString([item title], (char *)mi->dwTypeData, mi->cch); + } + } + } + + if (mi->fMask & MIIM_DATA) + { + SWELL_DataHold *h=[item representedObject]; + mi->dwItemData = (INT_PTR)(h && [h isKindOfClass:[SWELL_DataHold class]]? [h getValue] : 0); + } + + if (mi->fMask & MIIM_STATE) + { + mi->fState=0; + if ([item state]) mi->fState|=MFS_CHECKED; + if (![item isEnabled]) mi->fState|=MFS_GRAYED; + } + + if (mi->fMask & MIIM_ID) + { + mi->wID = (unsigned int)[item tag]; + } + + if(mi->fMask & MIIM_SUBMENU) + { + mi->hSubMenu = (HMENU) ([item hasSubmenu] ? [item submenu] : NULL); + } + + return 1; + +} + +void SWELL_InsertMenu(HMENU menu, int pos, unsigned int flag, UINT_PTR idx, const char *str) +{ + MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING, + (flag & ~MF_BYPOSITION),(flag&MF_POPUP) ? 0 : (unsigned int)idx,NULL,NULL,NULL,0,(char *)str}; + + if (flag&MF_POPUP) + { + mi.hSubMenu = (HMENU)idx; + mi.fMask |= MIIM_SUBMENU; + mi.fState &= ~MF_POPUP; + } + + if (flag&MF_SEPARATOR) + { + mi.fMask=MIIM_TYPE; + mi.fType=MFT_SEPARATOR; + mi.fState &= ~MF_SEPARATOR; + } + + if (flag&MF_BITMAP) + { + mi.fType=MFT_BITMAP; + mi.fState &= ~MF_BITMAP; + } + + InsertMenuItem(menu,pos,(flag&MF_BYPOSITION) ? TRUE : FALSE, &mi); +} + + +void InsertMenuItem(HMENU hMenu, int pos, BOOL byPos, MENUITEMINFO *mi) +{ + if (WDL_NOT_NORMALLY(!hMenu)) return; + NSMenu *m=(NSMenu *)hMenu; + NSMenuItem *item; + int ni = (int)[m numberOfItems]; + + if (!byPos) + { + pos = (int)[m indexOfItemWithTag:pos]; + } + if (pos < 0 || pos > ni) pos=ni; + + NSString *label=0; + if (mi->fType == MFT_STRING) + { + char buf[1024]; + __filtnametobuf(buf,mi->dwTypeData?mi->dwTypeData:"(null)",sizeof(buf)); + label=(NSString *)SWELL_CStringToCFString(buf); + item=[m insertItemWithTitle:label action:NULL keyEquivalent:@"" atIndex:pos]; + } + else if (mi->fType == MFT_BITMAP) + { + item=[m insertItemWithTitle:@"(no image)" action:NULL keyEquivalent:@"" atIndex:pos]; + if (mi->dwTypeData) + { + NSImage *i=(NSImage *)GetNSImageFromHICON((HICON)mi->dwTypeData); + if (i) + { + [item setImage:i]; + [item setTitle:@""]; + } + } + } + else + { + item = [NSMenuItem separatorItem]; + [m insertItem:item atIndex:pos]; + } + + if ((mi->fMask & MIIM_SUBMENU) && mi->hSubMenu) + { + if (label) [(NSMenu *)mi->hSubMenu setTitle:label]; + [m setSubmenu:(NSMenu *)mi->hSubMenu forItem:item]; + [((NSMenu *)mi->hSubMenu) release]; // let the parent menu free it + } + if (label) [label release]; + + if (!ni) [m setAutoenablesItems:NO]; + [item setEnabled:YES]; + + if (mi->fMask & MIIM_STATE) + { + if (mi->fState&MFS_GRAYED) + { + [item setEnabled:NO]; + } + if (mi->fState&MFS_CHECKED) + { + [item setState:NSOnState]; + } + } + + if (mi->fMask & MIIM_DATA) + { + SWELL_DataHold *h=[[SWELL_DataHold alloc] initWithVal:(void*)mi->dwItemData]; + [item setRepresentedObject:h]; + [h release]; + } + else + { + [item setRepresentedObject:nil]; + } + + if (mi->fMask & MIIM_ID) + { + [item setTag:mi->wID]; + } + + int i; + ni = (int)[m numberOfItems]; + // try to find a valid action/target + for (i = 0; i < ni; i ++) + { + NSMenuItem *fi=[m itemAtIndex:i]; + if (fi && fi != item) + { + SEL act = [fi action]; + id tgt = [fi target]; + if (act || tgt) + { + if (act) [item setAction:act]; + if (tgt) [item setTarget:tgt]; + break; + } + } + if (i == 5 && ni > 14) i = ni-6; // only look at first and last 6 items or so + } +} + + + +@implementation SWELL_PopupMenuRecv +-(id) initWithWnd:(HWND)wnd +{ + if ((self = [super init])) + { + cbwnd=wnd; + m_act=0; + } + return self; +} + +-(void) onSwellCommand:(id)sender +{ + int tag=(int) [sender tag]; + if (tag) + m_act=tag; +} + +-(int) isCommand +{ + return m_act; +} + +- (void)menuNeedsUpdate:(NSMenu *)menu +{ + if (cbwnd) + { + SendMessage(cbwnd,WM_INITMENUPOPUP,(WPARAM)menu,0); + SWELL_SetMenuDestination((HMENU)menu,(HWND)self); + } +} + +@end + +static void SWELL_SetMenuDestinationInt(NSMenu *m, HWND hwnd, bool is_top_level, bool do_skip_sub) +{ + [m setDelegate:(id)hwnd]; + const int n = (int)[m numberOfItems]; + for (int x = 0; x < n; x++) + { + NSMenuItem *item=[m itemAtIndex:x]; + if (item) + { + if ([item hasSubmenu]) + { + NSMenu *mm=[item submenu]; + if (mm) + { + if (do_skip_sub) + { + id del = [mm delegate]; + NSString *cn = del ? [del className] : NULL; + if (cn) + { + char buf[1024]; + SWELL_CFStringToCString(cn, buf, sizeof(buf)); + if (strstr(buf,"NSServices")) continue; + } + } + SWELL_SetMenuDestinationInt(mm,hwnd,false, is_top_level && !x); + } + } + else + { + if ([item tag]) + { + [item setTarget:(id)hwnd]; + if (hwnd) [item setAction:@selector(onSwellCommand:)]; + } + } + } + } +} + +void SWELL_SetMenuDestination(HMENU menu, HWND hwnd) +{ + if (WDL_NOT_NORMALLY(!menu || (hwnd && ![(id)hwnd respondsToSelector:@selector(onSwellCommand:)]))) return; + + NSMenu *m = (NSMenu *)menu, *par = [m supermenu]; + + bool do_skip_sub = false; + if (par && ![par supermenu] && [par numberOfItems]>0) + { + NSMenuItem *item = [par itemAtIndex:0]; + do_skip_sub = item && [item hasSubmenu] && [item submenu] == m; + } + SWELL_SetMenuDestinationInt(m,hwnd,!par,do_skip_sub); +} + +int TrackPopupMenu(HMENU hMenu, int flags, int xpos, int ypos, int resvd, HWND hwnd, const RECT *r) +{ + ReleaseCapture(); // match win32 -- TrackPopupMenu() ends any captures + if (WDL_NORMALLY(hMenu)) + { + NSMenu *m=(NSMenu *)hMenu; + NSView *v=(NSView *)hwnd; + if (v && [v isKindOfClass:[NSWindow class]]) v=[(NSWindow *)v contentView]; + if (!v) v=[[NSApp mainWindow] contentView]; + if (!v) return 0; + + NSEvent *event = [NSApp currentEvent]; + + { + //create a new event at these coordinates, faking it + NSWindow *w = [v window]; + NSPoint pt = NSMakePoint(xpos, ypos); + pt=[w convertScreenToBase:pt]; + pt.y -= 4; + NSInteger wn = [w windowNumber]; // event ? [event windowNumber] : [w windowNumber]; + NSTimeInterval ts = event ? [event timestamp] : 0; + NSGraphicsContext *gctx = event ? [event context] : 0; + event = [NSEvent otherEventWithType:NSApplicationDefined location:pt modifierFlags:0 timestamp:ts windowNumber:wn context:gctx subtype:0 data1:0 data2:0]; + } + + SWELL_PopupMenuRecv *recv = [[SWELL_PopupMenuRecv alloc] initWithWnd:((flags & TPM_NONOTIFY) ? 0 : hwnd)]; + + SWELL_SetMenuDestination((HMENU)m,(HWND)recv); + + if (!(flags&TPM_NONOTIFY)&&hwnd) + { + SendMessage(hwnd,WM_INITMENUPOPUP,(WPARAM)m,0); + SWELL_SetMenuDestination((HMENU)m,(HWND)recv); + } + + [NSMenu popUpContextMenu:m withEvent:event forView:v]; + + int ret=[recv isCommand]; + SWELL_SetMenuDestination((HMENU)m,(HWND)NULL); + [recv release]; + + if (ret<=0) return 0; + + if (flags & TPM_RETURNCMD) return ret; + + if (hwnd) SendMessage(hwnd,WM_COMMAND,ret,0); + + return 1; + } + return 0; +} + + + + +void SWELL_Menu_AddMenuItem(HMENU hMenu, const char *name, int idx, unsigned int flags) +{ + MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING, + (unsigned int) ((flags)?MFS_GRAYED:0),(unsigned int)idx,NULL,NULL,NULL,0,(char *)name}; + if (!name) + { + mi.fType = MFT_SEPARATOR; + mi.fMask&=~(MIIM_STATE|MIIM_ID); + } + InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi); +} + +int SWELL_GenerateMenuFromList(HMENU hMenu, const void *_list, int listsz) +{ + SWELL_MenuGen_Entry *list = (SWELL_MenuGen_Entry *)_list; + const int l1=strlen(SWELL_MENUGEN_POPUP_PREFIX); + while (listsz>0) + { + int cnt=1; + if (!list->name) SWELL_Menu_AddMenuItem(hMenu,NULL,-1,0); + else if (!strcmp(list->name,SWELL_MENUGEN_ENDPOPUP)) break; + else if (!strncmp(list->name,SWELL_MENUGEN_POPUP_PREFIX,l1)) + { + MENUITEMINFO mi={sizeof(mi),MIIM_SUBMENU|MIIM_STATE|MIIM_TYPE,MFT_STRING,0,0,CreatePopupMenuEx(list->name+l1),NULL,NULL,0,(char *)list->name+l1}; + cnt += SWELL_GenerateMenuFromList(mi.hSubMenu,list+1,listsz-1); + InsertMenuItem(hMenu,GetMenuItemCount(hMenu),TRUE,&mi); + } + else SWELL_Menu_AddMenuItem(hMenu,list->name,list->idx,list->flags); + + list+=cnt; + listsz -= cnt; + } + return (int) (list + 1 - (SWELL_MenuGen_Entry *)_list); +} + + +SWELL_MenuResourceIndex *SWELL_curmodule_menuresource_head; // todo: move to per-module thingy + +static SWELL_MenuResourceIndex *resById(SWELL_MenuResourceIndex *head, const char *resid) +{ + SWELL_MenuResourceIndex *p=head; + while (p) + { + if (p->resid == resid) return p; + p=p->_next; + } + return 0; +} + +HMENU SWELL_LoadMenu(SWELL_MenuResourceIndex *head, const char *resid) +{ + SWELL_MenuResourceIndex *p; + + if (!(p=resById(head,resid))) return 0; + HMENU hMenu=CreatePopupMenu(); + if (hMenu) p->createFunc(hMenu); + return hMenu; +} + +HMENU SWELL_DuplicateMenu(HMENU menu) +{ + if (WDL_NOT_NORMALLY(!menu)) return 0; + NSMenu *ret = (NSMenu *)[(NSMenu *)menu copy]; + return (HMENU)ret; +} + +BOOL SetMenu(HWND hwnd, HMENU menu) +{ + if (WDL_NOT_NORMALLY(!hwnd||![(id)hwnd respondsToSelector:@selector(swellSetMenu:)])) return FALSE; + if (g_swell_terminating) return FALSE; + + [(id)hwnd swellSetMenu:(HMENU)menu]; + NSWindow *nswnd = (NSWindow *)hwnd; + if ([nswnd isKindOfClass:[NSWindow class]] || + ([nswnd isKindOfClass:[NSView class]] && (nswnd=[(NSView *)nswnd window]) && hwnd == (HWND)[nswnd contentView])) + { + if ([NSApp keyWindow]==nswnd && + [NSApp mainMenu] != (NSMenu *)menu) + { + [NSApp setMainMenu:(NSMenu *)menu]; + if (menu) SendMessage(hwnd,WM_INITMENUPOPUP,(WPARAM)menu,0); // find a better place for this! TODO !!! + } + } + + return TRUE; +} + +HMENU GetMenu(HWND hwnd) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return NULL; + if ([(id)hwnd isKindOfClass:[NSWindow class]]) hwnd = (HWND)[(NSWindow *)hwnd contentView]; + if ([(id)hwnd respondsToSelector:@selector(swellGetMenu)]) return (HMENU) [(id)hwnd swellGetMenu]; + return NULL; +} + +void DrawMenuBar(HWND hwnd) +{ +} + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-menugen.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-menugen.h new file mode 100644 index 000000000..63dfbfd0e --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-menugen.h @@ -0,0 +1,93 @@ +#ifndef _SWELL_MENUGEN_H_ +#define _SWELL_MENUGEN_H_ + + +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + Dynamic menu generation + + Usage: + + See: swell_resgen.php etc + + */ + + +#include "swell.h" + + +#ifdef BEGIN +#undef BEGIN +#endif + +#ifdef END +#undef END +#endif + + +typedef struct SWELL_MenuResourceIndex +{ + const char *resid; + void (*createFunc)(HMENU hMenu); + struct SWELL_MenuResourceIndex *_next; +} SWELL_MenuResourceIndex; +extern SWELL_MenuResourceIndex *SWELL_curmodule_menuresource_head; + + +#define SWELL_MENUGEN_POPUP_PREFIX "/.BO^O:" +#define SWELL_MENUGEN_ENDPOPUP "EN%%%^:" +struct SWELL_MenuGen_Entry +{ + const char *name; // will begin with SWELL_MENUGEN_POPUP_PREFIX on submenus, and will be SWELL_MENUGEN_ENDPOPUP at the end of a submenu + unsigned short idx; + unsigned short flags; +}; + +class SWELL_MenuGenHelper +{ + public: + SWELL_MenuResourceIndex m_rec; + SWELL_MenuGenHelper(SWELL_MenuResourceIndex **h, void (*cf)(HMENU), int recid) + { + m_rec.resid=MAKEINTRESOURCE(recid); + m_rec.createFunc=cf; + m_rec._next=*h; + *h = &m_rec; + } +}; + +#define SWELL_DEFINE_MENU_RESOURCE_BEGIN(recid) \ + static void __swell_menu_cf__##recid(HMENU hMenu); \ + static SWELL_MenuGenHelper __swell_menu_cf_helper__##recid(&SWELL_curmodule_menuresource_head, __swell_menu_cf__##recid, recid); \ + static void __swell_menu_cf__##recid(HMENU hMenu) { static const SWELL_MenuGen_Entry list[]={{NULL,0,0 + +#define SWELL_DEFINE_MENU_RESOURCE_END(recid) } }; SWELL_GenerateMenuFromList(hMenu,list+1,sizeof(list)/sizeof(list[0])-1); } + + +#define GRAYED 1 +#define INACTIVE 2 +#define POPUP }, { SWELL_MENUGEN_POPUP_PREFIX +#define MENUITEM }, { +#define SEPARATOR NULL, 0xffff +#define BEGIN +#define END }, { SWELL_MENUGEN_ENDPOPUP + +#endif//_SWELL_MENUGEN_H_ + diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-misc-generic.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-misc-generic.cpp new file mode 100644 index 000000000..3302d62b7 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-misc-generic.cpp @@ -0,0 +1,97 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. +*/ + +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" +#include "swell-internal.h" + +#ifndef __APPLE__ +#include +#include +#endif + +bool IsRightClickEmulateEnabled() +{ + return false; +} + +void SWELL_EnableRightClickEmulate(BOOL enable) +{ +} + + +HANDLE SWELL_CreateProcessFromPID(int pid) +{ + SWELL_InternalObjectHeader_PID *buf = (SWELL_InternalObjectHeader_PID*)malloc(sizeof(SWELL_InternalObjectHeader_PID)); + buf->hdr.type = INTERNAL_OBJECT_PID; + buf->hdr.count = 1; + buf->pid = (int) pid; + buf->done = buf->result = 0; + return (HANDLE) buf; +} + +HANDLE SWELL_CreateProcess(const char *exe, int nparams, const char **params) +{ + void swell_cleanupZombies(); + swell_cleanupZombies(); + + const pid_t pid = fork(); + if (pid == 0) + { + char **pp = (char **)calloc(nparams+2,sizeof(char*)); + pp[0] = strdup(exe); + for (int x=0;xhdr.type != INTERNAL_OBJECT_PID|| !hdr->pid) return -1; + if (hdr->done) return hdr->result; + + int wstatus=0; + pid_t v = waitpid((pid_t)hdr->pid,&wstatus,WNOHANG); + if (v <= 0) return -2; + hdr->done = 1; + return hdr->result = WEXITSTATUS(wstatus); +} + + +#ifndef SWELL_TARGET_GDK +BOOL EnumDisplayMonitors(HDC hdc,const LPRECT r,MONITORENUMPROC proc,LPARAM lParam) +{ + return FALSE; +} +BOOL GetMonitorInfo(HMONITOR hmon, void *inf) +{ + return FALSE; +} +#endif + + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-misc.mm b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-misc.mm new file mode 100644 index 000000000..992b73e6d --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-misc.mm @@ -0,0 +1,887 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. +*/ + +#ifndef SWELL_PROVIDED_BY_APP + +//#import +#import +#include +#include +#include "swell.h" +#define SWELL_IMPLEMENT_GETOSXVERSION +#include "swell-internal.h" + +#include "../mutex.h" + +HWND g_swell_only_timerhwnd; + +@implementation SWELL_TimerFuncTarget + +-(id) initWithId:(UINT_PTR)tid hwnd:(HWND)h callback:(TIMERPROC)cb +{ + if ((self = [super init])) + { + m_hwnd=h; + m_cb=cb; + m_timerid = tid; + } + return self; +} +-(void)SWELL_Timer:(id)sender +{ + if (g_swell_only_timerhwnd && m_hwnd != g_swell_only_timerhwnd) return; + + m_cb(m_hwnd,WM_TIMER,m_timerid,GetTickCount()); +} +@end + +@implementation SWELL_DataHold +-(id) initWithVal:(void *)val +{ + if ((self = [super init])) + { + m_data=val; + } + return self; +} +-(void *) getValue +{ + return m_data; +} +@end + +void SWELL_CFStringToCString(const void *str, char *buf, int buflen) +{ + NSString *s = (NSString *)str; + if (buflen>0) *buf=0; + if (buflen <= 1 || !s || [s getCString:buf maxLength:buflen encoding:NSUTF8StringEncoding]) return; // should always work, I'd hope (ambiguous documentation ugh) + + NSData *data = [s dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES]; + if (!data) + { + [s getCString:buf maxLength:buflen encoding:NSASCIIStringEncoding]; + return; + } + int len = (int)[data length]; + if (len > buflen-1) len=buflen-1; + [data getBytes:buf length:len]; + buf[len]=0; +} + +void *SWELL_CStringToCFString(const char *str) +{ + if (!str) str=""; + void *ret; + + ret=(void *)CFStringCreateWithCString(NULL,str,kCFStringEncodingUTF8); + if (ret) return ret; + ret=(void*)CFStringCreateWithCString(NULL,str,kCFStringEncodingASCII); + return ret; +} + +void SWELL_MakeProcessFront(HANDLE h) +{ + SWELL_InternalObjectHeader_NSTask *buf = (SWELL_InternalObjectHeader_NSTask*)h; + if (buf && buf->hdr.type == INTERNAL_OBJECT_NSTASK && buf->task) + { + ProcessSerialNumber psn; + + int pid=[(id)buf->task processIdentifier]; + // try for 1sec to get the PSN, if it fails + int n = 20; + while (GetProcessForPID(pid,&psn) != noErr && n-- > 0) + { + Sleep(50); + } + if (n>0) SetFrontProcess(&psn); + } +} + +void SWELL_ReleaseNSTask(void *p) +{ + NSTask *a =(NSTask*)p; + [a release]; +} +DWORD SWELL_WaitForNSTask(void *p, DWORD msTO) +{ + NSTask *a =(NSTask*)p; + const DWORD t = GetTickCount(); + do + { + if (![a isRunning]) return WAIT_OBJECT_0; + if (msTO) Sleep(1); + } + while (msTO && (GetTickCount()-t) < msTO); + + return [a isRunning] ? WAIT_TIMEOUT : WAIT_OBJECT_0; +} + +HANDLE SWELL_CreateProcessIO(const char *exe, int nparams, const char **params, bool redirectIO) +{ + NSString *ex = (NSString *)SWELL_CStringToCFString(exe); + NSMutableArray *ar = [[NSMutableArray alloc] initWithCapacity:nparams]; + + int x; + for (x=0;x hdr.type = INTERNAL_OBJECT_NSTASK; + buf->hdr.count=1; + buf->task = tsk; + return buf; +} + +HANDLE SWELL_CreateProcess(const char *exe, int nparams, const char **params) +{ + return SWELL_CreateProcessIO(exe,nparams,params,false); +} + + +int SWELL_GetProcessExitCode(HANDLE hand) +{ + int rv=0; + SWELL_InternalObjectHeader_NSTask *hdr=(SWELL_InternalObjectHeader_NSTask*)hand; + if (!hdr || hdr->hdr.type != INTERNAL_OBJECT_NSTASK || !hdr->task) return -1; + @try { + if ([(NSTask *)hdr->task isRunning]) rv=-3; + else rv = [(NSTask *)hdr->task terminationStatus]; + } + @catch (id ex) { + rv=-2; + } + return rv; +} + +int SWELL_TerminateProcess(HANDLE hand) +{ + int rv=0; + SWELL_InternalObjectHeader_NSTask *hdr=(SWELL_InternalObjectHeader_NSTask*)hand; + if (!hdr || hdr->hdr.type != INTERNAL_OBJECT_NSTASK || !hdr->task) return -1; + @try + { + [(NSTask *)hdr->task terminate]; + } + @catch (id ex) { + rv=-2; + } + return rv; +} +int SWELL_ReadWriteProcessIO(HANDLE hand, int w/*stdin,stdout,stderr*/, char *buf, int bufsz) +{ + SWELL_InternalObjectHeader_NSTask *hdr=(SWELL_InternalObjectHeader_NSTask*)hand; + if (!hdr || hdr->hdr.type != INTERNAL_OBJECT_NSTASK || !hdr->task) return 0; + NSTask *tsk = (NSTask*)hdr->task; + NSPipe *pipe = NULL; + bool async_mode = false; + if (w & (1<<24)) + { + async_mode = true; + w &= ~ (1<<24); + } + switch (w) + { + case 0: pipe = [tsk standardInput]; break; + case 1: pipe = [tsk standardOutput]; break; + case 2: pipe = [tsk standardError]; break; + } + if (!pipe || ![pipe isKindOfClass:[NSPipe class]]) return 0; + + NSFileHandle *fh = w!=0 ? [pipe fileHandleForReading] : [pipe fileHandleForWriting]; + if (!fh) return 0; + if (w==0) + { + if (bufsz>0) + { + NSData *d = [NSData dataWithBytes:buf length:bufsz]; + @try + { + if (d) [fh writeData:d]; + else bufsz=0; + } + @catch (id ex) { bufsz=0; } + + return bufsz; + } + } + else + { + if (async_mode) + { + int handle = [fh fileDescriptor]; + if (handle >= 0) + { + struct pollfd pl = { handle, POLLIN }; + if (poll(&pl,1,0)<1) return 0; + + return read(handle,buf,bufsz); + } + } + NSData *d = NULL; + @try + { + d = [fh readDataOfLength:(bufsz < 1 ? 32768 : bufsz)]; + } + @catch (id ex) { } + + if (!d || bufsz < 1) return d ? (int)[d length] : 0; + int l = (int)[d length]; + if (l > bufsz) l = bufsz; + [d getBytes:buf length:l]; + return l; + } + + return 0; +} + + +@implementation SWELL_ThreadTmp +-(void)bla:(id)obj +{ + if (a) + { + DWORD (*func)(void *); + *(void **)(&func) = a; + func(b); + } + [NSThread exit]; +} +@end + +void SWELL_EnsureMultithreadedCocoa() +{ + static int a; + if (!a) + { + a++; + if (![NSThread isMultiThreaded]) // force cocoa into multithreaded mode + { + SWELL_ThreadTmp *t=[[SWELL_ThreadTmp alloc] init]; + t->a=0; + t->b=0; + [NSThread detachNewThreadSelector:@selector(bla:) toTarget:t withObject:t]; + /// [t release]; + } + } +} + +void CreateThreadNS(void *TA, DWORD stackSize, DWORD (*ThreadProc)(LPVOID), LPVOID parm, DWORD cf, DWORD *tidOut) +{ + SWELL_ThreadTmp *t=[[SWELL_ThreadTmp alloc] init]; + t->a=(void*)ThreadProc; + t->b=parm; + return [NSThread detachNewThreadSelector:@selector(bla:) toTarget:t withObject:t]; +} + + +// used by swell.cpp (lazy these should go elsewhere) +void *SWELL_InitAutoRelease() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + return (void *)pool; +} +void SWELL_QuitAutoRelease(void *p) +{ + if (p) + [(NSAutoreleasePool*)p release]; +} + +void SWELL_RunEvents() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + int x=100; + while (x-- > 0) + { + NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0.001] inMode:NSDefaultRunLoopMode dequeue:YES]; + if (!event) break; + [NSApp sendEvent:event]; + } + [pool release]; +} + +// timer stuff +typedef struct TimerInfoRec +{ + UINT_PTR timerid; + HWND hwnd; + NSTimer *timer; + struct TimerInfoRec *_next; +} TimerInfoRec; +static TimerInfoRec *m_timer_list; +static WDL_Mutex m_timermutex; +#ifndef SWELL_NO_POSTMESSAGE +static pthread_t m_pmq_mainthread; +static void SWELL_pmq_settimer(HWND h, UINT_PTR timerid, UINT rate, TIMERPROC tProc); +#endif + +UINT_PTR SetTimer(HWND hwnd, UINT_PTR timerid, UINT rate, TIMERPROC tProc) +{ + if (!hwnd && !tProc) return 0; // must have either callback or hwnd + + if (hwnd && !timerid) return 0; + +#ifndef SWELL_NO_POSTMESSAGE + if (timerid != ~(UINT_PTR)0 && m_pmq_mainthread && pthread_self()!=m_pmq_mainthread) + { + SWELL_pmq_settimer(hwnd,timerid,(rate==(UINT)-1)?((UINT)-2):rate,tProc); + return timerid; + } +#endif + + + if (hwnd && ![(id)hwnd respondsToSelector:@selector(SWELL_Timer:)]) + { + if (![(id)hwnd isKindOfClass:[NSWindow class]]) return 0; + hwnd=(HWND)[(id)hwnd contentView]; + if (![(id)hwnd respondsToSelector:@selector(SWELL_Timer:)]) return 0; + } + + WDL_MutexLock lock(&m_timermutex); + TimerInfoRec *rec=NULL; + if (hwnd||timerid) + { + rec = m_timer_list; + while (rec) + { + if (rec->timerid == timerid && rec->hwnd == hwnd) // works for both kinds + break; + rec=rec->_next; + } + } + + bool recAdd=false; + if (!rec) + { + rec=(TimerInfoRec*)malloc(sizeof(TimerInfoRec)); + recAdd=true; + } + else + { + [rec->timer invalidate]; + rec->timer=0; + } + + rec->timerid=timerid; + rec->hwnd=hwnd; + + if (!hwnd || tProc) + { + // set timer to this unique ptr + if (!hwnd) timerid = rec->timerid = (UINT_PTR)rec; + + SWELL_TimerFuncTarget *t = [[SWELL_TimerFuncTarget alloc] initWithId:timerid hwnd:hwnd callback:tProc]; + rec->timer = [NSTimer scheduledTimerWithTimeInterval:(wdl_max(rate,1)*0.001) target:t selector:@selector(SWELL_Timer:) + userInfo:t repeats:YES]; + [t release]; + + } + else + { + SWELL_DataHold *t=[[SWELL_DataHold alloc] initWithVal:(void *)timerid]; + rec->timer = [NSTimer scheduledTimerWithTimeInterval:(wdl_max(rate,1)*0.001) target:(id)hwnd selector:@selector(SWELL_Timer:) + userInfo:t repeats:YES]; + + [t release]; + } + [[NSRunLoop currentRunLoop] addTimer:rec->timer forMode:(NSString*)kCFRunLoopCommonModes]; + + if (recAdd) + { + rec->_next=m_timer_list; + m_timer_list=rec; + } + + return timerid; +} +void SWELL_RunRunLoop(int ms) +{ + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:ms*0.001]]; +} +void SWELL_RunRunLoopEx(int ms, HWND hwndOnlyTimer) +{ + HWND h=g_swell_only_timerhwnd; + g_swell_only_timerhwnd = hwndOnlyTimer; + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:ms*0.001]]; + if (g_swell_only_timerhwnd == hwndOnlyTimer) g_swell_only_timerhwnd = h; +} + +BOOL KillTimer(HWND hwnd, UINT_PTR timerid) +{ + if (!hwnd && !timerid) return FALSE; + + WDL_MutexLock lock(&m_timermutex); +#ifndef SWELL_NO_POSTMESSAGE + if (timerid != ~(UINT_PTR)0 && m_pmq_mainthread && pthread_self()!=m_pmq_mainthread) + { + SWELL_pmq_settimer(hwnd,timerid,~(UINT)0,NULL); + return TRUE; + } +#endif + BOOL rv=FALSE; + + // don't allow removing all global timers + if (timerid!=~(UINT_PTR)0 || hwnd) + { + TimerInfoRec *rec = m_timer_list, *lrec=NULL; + while (rec) + { + + if (rec->hwnd == hwnd && (timerid==~(UINT_PTR)0 || rec->timerid == timerid)) + { + TimerInfoRec *nrec = rec->_next; + + // remove self from list + if (lrec) lrec->_next = nrec; + else m_timer_list = nrec; + + [rec->timer invalidate]; + free(rec); + + rv=TRUE; + if (timerid!=~(UINT_PTR)0) break; + + rec=nrec; + } + else + { + lrec=rec; + rec=rec->_next; + } + } + } + return rv; +} + + +#ifndef SWELL_NO_POSTMESSAGE + +///////// PostMessage emulation + +// implementation of postmessage stuff + + + +typedef struct PMQ_rec +{ + HWND hwnd; + UINT msg; + WPARAM wParam; + LPARAM lParam; + + struct PMQ_rec *next; + bool is_special_timer; // if set, then msg=interval(-1 for kill),wParam=timer id, lParam = timerproc +} PMQ_rec; + +static WDL_Mutex *m_pmq_mutex; +static PMQ_rec *m_pmq, *m_pmq_empty, *m_pmq_tail; +static int m_pmq_size; +static id m_pmq_timer; +#define MAX_POSTMESSAGE_SIZE 1024 + +void SWELL_Internal_PostMessage_Init() +{ + if (m_pmq_mutex) return; + id del = [NSApp delegate]; + if (!del || ![del respondsToSelector:@selector(swellPostMessageTick:)]) return; + + m_pmq_mainthread=pthread_self(); + m_pmq_mutex = new WDL_Mutex; + + m_pmq_timer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:(id)del selector:@selector(swellPostMessageTick:) userInfo:nil repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:m_pmq_timer forMode:(NSString*)kCFRunLoopCommonModes]; + // [ release]; + // set a timer to the delegate +} + + +void SWELL_MessageQueue_Flush() +{ + if (!m_pmq_mutex) return; + + m_pmq_mutex->Enter(); + int max_amt = m_pmq_size; + PMQ_rec *p=m_pmq; + if (p) + { + m_pmq = p->next; + if (m_pmq_tail == p) m_pmq_tail=NULL; + m_pmq_size--; + } + m_pmq_mutex->Leave(); + + // process out queue + while (p) + { + if (p->is_special_timer) + { + if (p->msg == ~(UINT)0) KillTimer(p->hwnd,p->wParam); + else SetTimer(p->hwnd,p->wParam,p->msg,(TIMERPROC)p->lParam); + } + else + { + if ([(id)p->hwnd respondsToSelector:@selector(swellCanPostMessage)] && [(id)p->hwnd swellCanPostMessage]) + SendMessage(p->hwnd,p->msg,p->wParam,p->lParam); + } + + m_pmq_mutex->Enter(); + // move this message to empty list + p->next=m_pmq_empty; + m_pmq_empty = p; + + // get next queued message (if within limits) + p = (--max_amt > 0) ? m_pmq : NULL; + if (p) + { + m_pmq = p->next; + if (m_pmq_tail == p) m_pmq_tail=NULL; + m_pmq_size--; + } + m_pmq_mutex->Leave(); + } +} + +void SWELL_Internal_PMQ_ClearAllMessages(HWND hwnd) +{ + if (!m_pmq_mutex) return; + + m_pmq_mutex->Enter(); + PMQ_rec *p=m_pmq; + PMQ_rec *lastrec=NULL; + while (p) + { + if (hwnd && p->hwnd != hwnd) { lastrec=p; p=p->next; } + else + { + PMQ_rec *next=p->next; + + p->next=m_pmq_empty; // add p to empty list + m_pmq_empty=p; + m_pmq_size--; + + + if (p==m_pmq_tail) m_pmq_tail=lastrec; // update tail + + if (lastrec) p = lastrec->next = next; + else p = m_pmq = next; + } + } + m_pmq_mutex->Leave(); +} + +static void SWELL_pmq_settimer(HWND h, UINT_PTR timerid, UINT rate, TIMERPROC tProc) +{ + if (!h||!m_pmq_mutex) return; + WDL_MutexLock lock(m_pmq_mutex); + + PMQ_rec *rec=m_pmq; + while (rec) + { + if (rec->is_special_timer && rec->hwnd == h && rec->wParam == timerid) + { + rec->msg = rate; // adjust to new rate + rec->lParam = (LPARAM)tProc; + return; + } + rec=rec->next; + } + + rec=m_pmq_empty; + if (rec) m_pmq_empty=rec->next; + else rec=(PMQ_rec*)malloc(sizeof(PMQ_rec)); + rec->next=0; + rec->hwnd=h; + rec->msg=rate; + rec->wParam=timerid; + rec->lParam=(LPARAM)tProc; + rec->is_special_timer=true; + + if (m_pmq_tail) m_pmq_tail->next=rec; + else + { + PMQ_rec *p=m_pmq; + while (p && p->next) p=p->next; // shouldnt happen unless m_pmq is NULL As well but why not for safety + if (p) p->next=rec; + else m_pmq=rec; + } + m_pmq_tail=rec; + m_pmq_size++; +} + +BOOL PostMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (WDL_NORMALLY(m_pmq_mutex != NULL)) + { + return SWELL_Internal_PostMessage(hwnd,message,wParam,lParam); + } + + // legacy passthrough to delegate if caller is using its own swell impl, not threadsafe + id del=[NSApp delegate]; + if (del && [del respondsToSelector:@selector(swellPostMessage:msg:wp:lp:)]) + return !![(SWELL_DelegateExtensions*)del swellPostMessage:hwnd msg:message wp:wParam lp:lParam]; + return FALSE; +} + +void SWELL_MessageQueue_Clear(HWND h) +{ + if (WDL_NORMALLY(m_pmq_mutex != NULL)) + { + SWELL_Internal_PMQ_ClearAllMessages(h); + } + else + { + id del=[NSApp delegate]; + if (del && [del respondsToSelector:@selector(swellPostMessageClearQ:)]) + [(SWELL_DelegateExtensions*)del swellPostMessageClearQ:h]; + } +} + +BOOL SWELL_Internal_PostMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (!hwnd||!m_pmq_mutex) return FALSE; + + BOOL ret=FALSE; + m_pmq_mutex->Enter(); + + if (m_pmq_empty||m_pmq_sizenext; + else rec=(PMQ_rec*)malloc(sizeof(PMQ_rec)); + rec->next=0; + rec->hwnd=hwnd; + rec->msg=msg; + rec->wParam=wParam; + rec->lParam=lParam; + rec->is_special_timer=false; + + if (m_pmq_tail) m_pmq_tail->next=rec; + else + { + PMQ_rec *p=m_pmq; + while (p && p->next) p=p->next; // shouldnt happen unless m_pmq is NULL As well but why not for safety + if (p) p->next=rec; + else m_pmq=rec; + } + m_pmq_tail=rec; + m_pmq_size++; + + ret=TRUE; + } + + m_pmq_mutex->Leave(); + + return ret; +} +#endif + +static bool s_rightclickemulate=true; + +bool IsRightClickEmulateEnabled() +{ + return s_rightclickemulate; +} + +void SWELL_EnableRightClickEmulate(BOOL enable) +{ + s_rightclickemulate=enable; +} + +int g_swell_terminating; +void SWELL_PostQuitMessage(void *sender) +{ + g_swell_terminating=true; + + [NSApp terminate:(id)sender]; +} + + +#ifndef MAC_OS_X_VERSION_10_9 +typedef uint64_t NSActivityOptions; +enum +{ + NSActivityIdleDisplaySleepDisabled = (1ULL << 40), + NSActivityIdleSystemSleepDisabled = (1ULL << 20), + NSActivitySuddenTerminationDisabled = (1ULL << 14), + NSActivityAutomaticTerminationDisabled = (1ULL << 15), + NSActivityUserInitiated = (0x00FFFFFFULL | NSActivityIdleSystemSleepDisabled), + NSActivityUserInitiatedAllowingIdleSystemSleep = (NSActivityUserInitiated & ~NSActivityIdleSystemSleepDisabled), + NSActivityBackground = 0x000000FFULL, + NSActivityLatencyCritical = 0xFF00000000ULL, +}; + + +@interface NSProcessInfo (reaperhostadditions) +- (id)beginActivityWithOptions:(NSActivityOptions)options reason:(NSString *)reason; +- (void)endActivity:(id)activity; + +@end + +#endif + +void SWELL_DisableAppNap(int disable) +{ + if (!g_swell_terminating && floor(NSFoundationVersionNumber) > 945.00) // 10.9+ + { + static int cnt; + static id obj; + + cnt += disable; + + @try + { + if (cnt > 0) + { + if (!obj) + { + const NSActivityOptions v = NSActivityLatencyCritical | NSActivityIdleSystemSleepDisabled; + + // beginActivityWithOptions returns an autoreleased object + obj = [[NSProcessInfo processInfo] beginActivityWithOptions:v reason:@"SWELL_DisableAppNap"]; + if (obj) [obj retain]; + } + } + else + { + id a = obj; + if (a) + { + // in case we crash somehow, dont' want obj sticking around pointing to a stale object + obj = NULL; + [[NSProcessInfo processInfo] endActivity:a]; + [a release]; // apparently releasing this is enough, without the endActivity call, but the docs are quite vague + } + } + } + @catch (NSException *exception) { + } + @catch (id ex) { + } + } +} + + +BOOL EnumDisplayMonitors(HDC hdc, const LPRECT r, MONITORENUMPROC proc,LPARAM lParam) +{ + // ignores hdc + NSArray *screens = [NSScreen screens]; + const int ns = [screens count]; + for (int x = 0; x < ns; x ++) + { + NSScreen *mon = [screens objectAtIndex:x]; + if (mon) + { + NSRect tr=[mon frame]; + RECT screen_rect,tmp; + NSRECT_TO_RECT(&tmp,tr); + if (r) + { + if (!IntersectRect(&screen_rect,r,&tmp)) + continue; + } + else + { + screen_rect = tmp; + } + if (!proc((HMONITOR)mon,hdc,&screen_rect,lParam)) break; + } + } + + return TRUE; +} + +BOOL GetMonitorInfo(HMONITOR hmon, void *inf) +{ + if (!hmon) return FALSE; + MONITORINFOEX *a = (MONITORINFOEX*)inf; + if (a->cbSize < sizeof(MONITORINFO)) return FALSE; + + NSScreen *mon = (NSScreen *)hmon; + NSRect tr=[mon frame]; + NSRECT_TO_RECT(&a->rcMonitor,tr); + tr = [mon visibleFrame]; + NSRECT_TO_RECT(&a->rcWork,tr); + a->dwFlags = 0; + + if (a->cbSize > sizeof(MONITORINFO)) + { + const int maxlen = (int) (a->cbSize - sizeof(MONITORINFO)); + const int displayID = [[[mon deviceDescription] valueForKey:@"NSScreenNumber"] intValue]; + snprintf(a->szDevice,maxlen,"DisplayID %d",displayID); + + + static bool init; + static CFDictionaryRef (*_IODisplayCreateInfoDictionary)(io_service_t framebuffer, IOOptionBits options); + + if (!init) + { + init = true; + void *lib = dlopen("/System/Library/Frameworks/IOKit.framework/Versions/Current/IOKit",RTLD_LAZY); + if (lib) + *(void **)&_IODisplayCreateInfoDictionary = dlsym(lib,"IODisplayCreateInfoDictionary"); + } + + if (_IODisplayCreateInfoDictionary) + { + NSDictionary *deviceInfo = (NSDictionary *)_IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName); + NSDictionary *names = [deviceInfo objectForKey:@"DisplayProductName"]; + if ([names count] > 0) + { + NSString *s = [names objectForKey:[[names allKeys] objectAtIndex:0]]; + if (s) SWELL_CFStringToCString(s,a->szDevice,maxlen); + } + [deviceInfo release]; + } + } + + return TRUE; +} + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-miscdlg-generic.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-miscdlg-generic.cpp new file mode 100644 index 000000000..b41f42db7 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-miscdlg-generic.cpp @@ -0,0 +1,2062 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic APIs for browsing for files, directories, and messageboxes. + + These APIs don't all match the Windows equivelents, but are close enough to make it not too much trouble. + + */ + + +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" +#include "swell-internal.h" +#include "swell-dlggen.h" + +#include "../wdlcstring.h" +#include "../assocarray.h" +#include "../ptrlist.h" +#include +#include + +#include "../lineparse.h" +#define WDL_HASSTRINGS_EXPORT static +#include "../has_strings.h" + + +#ifndef SWELL_BROWSE_RECENT_SIZE +#define SWELL_BROWSE_RECENT_SIZE 12 +#endif +static WDL_PtrList s_browse_rcu, s_browse_rcu_tmp; +static int recent_size() { return s_browse_rcu.GetSize() + s_browse_rcu_tmp.GetSize(); } + +static void recent_addtocb(HWND hwnd) +{ + int x; + for (x=0;x=SWELL_BROWSE_RECENT_SIZE) + s_browse_rcu.Delete(SWELL_BROWSE_RECENT_SIZE,true,free); + s_browse_rcu.Insert(0,strdup(path)); + } + + for (x=0;x<=s_browse_rcu.GetSize();x++) + { + char tmp[64]; + snprintf(tmp,sizeof(tmp),"path%d",x); + WritePrivateProfileString(".swell_recent_path",tmp, s_browse_rcu.Get(x),""); + } +} +static void recent_read() +{ + s_browse_rcu_tmp.Empty(true,free); + if (s_browse_rcu.GetSize()) return; + int x; + for (x=0;x 0 && date64 < WDL_INT64_CONST(0x793406fff)) + { + struct tm *a=localtime(&date); + if (a) strftime(buf,bufsz,"%c",a); + } + } + + void format_size(char *buf, int bufsz) + { + if (type == 1) + { + lstrcpyn_safe(buf,"",bufsz); + } + else + { + static const char *tab[]={ "bytes","KB","MB","GB" }; + int lf=0; + WDL_INT64 s=size; + if (s<1024) + { + snprintf(buf,bufsz,"%d %s",(int)s,tab[0]); + } + else + { + int w = 1; + do { w++; lf = (int)(s&1023); s/=1024; } while (s >= 1024 && w<4); + snprintf(buf,bufsz,"%d.%d %s",(int)s,(int)((lf*10.0)/1024.0+0.5),tab[w-1]); + } + } + } + + char *format_all(char *buf, int bufsz) + { + char dstr[128],sstr[128]; + format_date(dstr,sizeof(dstr)); + format_size(sstr,sizeof(sstr)); + snprintf(buf,bufsz,"%s\t%s\t%s",WDL_get_filepart(name),dstr,sstr); + return buf; + } + + }; + + void viewlist_clear() + { + rec *l = viewlist_store.Get(); + const int n = viewlist_store.GetSize(); + for (int x = 0; x < n; x ++) free(l[x].name); + viewlist_store.Resize(0); + viewlist.Empty(); + } + WDL_TypedBuf viewlist_store; + WDL_PtrList viewlist; + + bool show_hidden; + + void viewlist_sort(const char *filter) + { + if (filter) + { + viewlist.Empty(); + LineParser lp; + const bool no_filter = !*filter || !WDL_makeSearchFilter(filter,&lp); + for (int x=0;xformat_all(tmp,sizeof(tmp)),&lp)) + viewlist.Add(r); + } + } + s_sortrev = sortrev; + if (viewlist.GetSize()>1) + qsort(viewlist.GetList(), viewlist.GetSize(),sizeof(rec*), + sortcol == 1 ? sortFunc_sz : + sortcol == 2 ? sortFunc_date : + sortFunc_fn); + } + static int sortFunc_fn(const void *_a, const void *_b) + { + const rec *a = *(const rec * const*)_a, *b = *(const rec * const*)_b; + int d = a->type - b->type; + if (d) return d; + d = stricmp(a->name,b->name); + return s_sortrev ? -d : d; + } + static int sortFunc_date(const void *_a, const void *_b) + { + const rec *a = *(const rec * const*)_a, *b = *(const rec * const*)_b; + if (a->date != b->date) return s_sortrev ? (a->date>b->date?-1:1) : (a->date>b->date?1:-1); + return stricmp(a->name,b->name); + } + static int sortFunc_sz(const void *_a, const void *_b) + { + const rec *a = *(const rec * const *)_a, *b = *(const rec * const *)_b; + int d = a->type - b->type; + if (d) return s_sortrev ? -d : d; + if (a->size != b->size) return s_sortrev ? (a->size>b->size?-1:1) : (a->size>b->size?1:-1); + return stricmp(a->name,b->name); + } + + + void scan_path(const char *path, const char *filterlist, bool dir_only) + { + viewlist_clear(); + DIR *dir = opendir(path); + if (!dir) return; + char tmp[2048]; + struct dirent *ent; + while (NULL != (ent = readdir(dir))) + { + if (ent->d_name[0] == '.') + { + if (ent->d_name[1] == 0 || ent->d_name[1] == '.' || !show_hidden) continue; + } + bool is_dir = (ent->d_type == DT_DIR); + if (ent->d_type == DT_UNKNOWN) + { + snprintf(tmp,sizeof(tmp),"%s/%s",path,ent->d_name); + DIR *d = opendir(tmp); + if (d) { is_dir = true; closedir(d); } + } + else if (ent->d_type == DT_LNK) + { + snprintf(tmp,sizeof(tmp),"%s/%s",path,ent->d_name); + char *rp = realpath(tmp,NULL); + if (rp) + { + DIR *d = opendir(rp); + if (d) { is_dir = true; closedir(d); } + free(rp); + } + } + if (!dir_only || is_dir) + { + if (filterlist && *filterlist && !is_dir) + { + const char *f = filterlist; + while (*f) + { + const char *nf = f; + while (*nf && *nf != ';') nf++; + if (*f != '*') + { + const char *nw = f; + while (nw < nf && *nw != '*') nw++; + + if ((nw!=nf || f+strlen(ent->d_name) == nw) && !strncasecmp(ent->d_name,f,nw-f)) + { + // matched leading text + if (nw == nf) break; + f = nw; + } + } + + if (*f == '*') + { + f++; + if (!*f || *f == ';' || (*f == '.' && f[1] == '*')) break; + size_t l = strlen(ent->d_name); + if (f+l > nf && !strncasecmp(ent->d_name + l - (nf-f), f,nf-f)) break; + } + f = nf; + while (*f == ';') f++; + } + if (!*f) continue; // did not match + } + snprintf(tmp,sizeof(tmp),"%s/%s",path,ent->d_name); + + struct stat st={0,}; + stat(tmp,&st); + + rec r = { st.st_size, st.st_mtime, strdup(ent->d_name), is_dir?1:2 } ; + viewlist_store.Add(&r,1); + } + } + // sort viewlist + + closedir(dir); + } +}; + +char BrowseFile_State::s_sortrev; + +static void preprocess_user_path(char *buf, int bufsz) +{ + if (buf[0] == '~') + { + char *tmp = strdup(buf+1); + if (buf[1] == '/' || !buf[1]) + { + char *p = getenv("HOME"); + if (p && *p) snprintf(buf,bufsz,"%s%s",p,tmp); + } + else + { + snprintf(buf,bufsz,"/home/%s",tmp); // if someone wants to write code to lookup homedirs, please, go right ahead! + } + free(tmp); + } +} + +static LRESULT WINAPI swellFileSelectProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + enum { IDC_EDIT=0x100, IDC_LABEL, IDC_CHILD, IDC_DIR, IDC_LIST, IDC_EXT, IDC_PARENTBUTTON, IDC_FILTER, ID_SHOW_HIDDEN }; + enum { WM_UPD=WM_USER+100 }; + const int maxPathLen = 2048; + const char *multiple_files = "(multiple files)"; + switch (uMsg) + { + case WM_CREATE: + if (lParam) // swell-specific + { + SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc); + SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellFileSelectProc); + + SetWindowLong(hwnd,GWL_STYLE, GetWindowLong(hwnd,GWL_STYLE)|WS_THICKFRAME); + + SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam); + BrowseFile_State *parms = (BrowseFile_State *)lParam; + + char tmp[1024]; + recent_read(); + + recent_add_tmp(parms->initialdir); + + if (parms->initialfile && *parms->initialfile != '.') + { + lstrcpyn_safe(tmp,parms->initialfile,sizeof(tmp)); + WDL_remove_filepart(tmp); + recent_add_tmp(tmp); + } + + if (parms->caption) SetWindowText(hwnd,parms->caption); + + SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false); + + HWND edit = SWELL_MakeEditField(IDC_EDIT, 0,0,0,0, 0); + SWELL_MakeButton(0, + parms->mode == BrowseFile_State::OPENDIR ? "Choose directory" : + parms->mode == BrowseFile_State::SAVE ? "Save" : "Open", + IDOK,0,0,0,0, 0); + + SWELL_MakeButton(0, "Cancel", IDCANCEL,0,0,0,0, 0); + HWND dir = SWELL_MakeCombo(IDC_DIR, 0,0,0,0, 0); + SWELL_MakeButton(0, "..", IDC_PARENTBUTTON, 0,0,0,0, 0); + SWELL_MakeEditField(IDC_FILTER, 0,0,0,0, 0); + + const char *ent = parms->mode == BrowseFile_State::OPENDIR ? "dir_browser" : "file_browser"; + GetPrivateProfileString(".swell",ent,"", tmp,sizeof(tmp),""); + int x=0,y=0,w=0,h=0, c1=0,c2=0,c3=0,extraflag=0; + int flag = SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER; + if (tmp[0] && + sscanf(tmp,"%d %d %d %d %d %d %d %d",&x,&y,&w,&h,&c1,&c2,&c3,&extraflag) >= 4) + flag &= ~SWP_NOMOVE; + if (w < 100) w=SWELL_UI_SCALE(600); + if (h < 100) h=SWELL_UI_SCALE(400); + if (extraflag&1) + parms->show_hidden=true; + + if (c1 + c2 + c3 < w/2) + { + c1=SWELL_UI_SCALE(280); + c2=SWELL_UI_SCALE(120); + c3=SWELL_UI_SCALE(140); + } + + HWND list = SWELL_MakeControl("",IDC_LIST,"SysListView32",LVS_REPORT|LVS_SHOWSELALWAYS| + (parms->mode == BrowseFile_State::OPENMULTI ? 0 : LVS_SINGLESEL)| + LVS_OWNERDATA|WS_BORDER|WS_TABSTOP,0,0,0,0,0); + if (list) + { + LVCOLUMN c={LVCF_TEXT|LVCF_WIDTH, 0, c1, (char*)"Filename" }; + ListView_InsertColumn(list,0,&c); + c.cx = c2; + c.pszText = (char*) "Size"; + ListView_InsertColumn(list,1,&c); + c.cx = c3; + c.pszText = (char*) "Date"; + ListView_InsertColumn(list,2,&c); + HWND hdr = ListView_GetHeader(list); + HDITEM hi; + memset(&hi,0,sizeof(hi)); + hi.mask = HDI_FORMAT; + hi.fmt = parms->sortrev ? HDF_SORTDOWN : HDF_SORTUP; + Header_SetItem(hdr,parms->sortcol,&hi); + } + HWND extlist = (parms->extlist && *parms->extlist) ? SWELL_MakeCombo(IDC_EXT, 0,0,0,0, CBS_DROPDOWNLIST) : NULL; + if (extlist) + { + const char *p = parms->extlist; + while (*p) + { + const char *rd=p; + p += strlen(p)+1; + if (!*p) break; + int a = SendMessage(extlist,CB_ADDSTRING,0,(LPARAM)rd); + SendMessage(extlist,CB_SETITEMDATA,a,(LPARAM)p); + p += strlen(p)+1; + } + + int sel = 0; + if (parms->initialfile && *parms->initialfile) + { + sel = ext_valid_for_extlist(WDL_get_fileext(parms->initialfile), parms->extlist); + if (sel<0) sel=0; + } + SendMessage(extlist,CB_SETCURSEL,sel,0); + } + + SWELL_MakeLabel(-1,parms->mode == BrowseFile_State::OPENDIR ? "Directory: " : "File:",IDC_LABEL, 0,0,0,0, 0); + + if (BFSF_Templ_dlgid && BFSF_Templ_dlgproc) + { + HWND dlg = SWELL_CreateDialog(BFSF_Templ_reshead, BFSF_Templ_dlgid, hwnd, BFSF_Templ_dlgproc, 0); + if (dlg) SetWindowLong(dlg,GWL_ID,IDC_CHILD); + BFSF_Templ_dlgproc=0; + BFSF_Templ_dlgid=0; + } + + SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false); + + if (edit && dir) + { + char buf[maxPathLen]; + const char *filepart = ""; + if (parms->initialfile && *parms->initialfile && *parms->initialfile != '.') + { + lstrcpyn_safe(buf,parms->initialfile,sizeof(buf)); + char *p = (char *)WDL_get_filepart(buf); + if (p > buf) + { + p[-1]=0; + filepart = p; + } + else + { + filepart = parms->initialfile; + goto get_dir; + } + } + else + { +get_dir: + if (parms->initialdir && *parms->initialdir && strcmp(parms->initialdir,".")) + { + lstrcpyn_safe(buf,parms->initialdir,sizeof(buf)); + } + else if (!getcwd(buf,sizeof(buf))) + buf[0]=0; + } + + SetWindowText(edit,filepart); + SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); + } + + if (list) SetWindowPos(list,HWND_BOTTOM,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); + SetWindowPos(hwnd,NULL,x,y, w,h, flag); + SendMessage(hwnd,WM_UPD,1,0); + SendMessage(edit,EM_SETSEL,0,(LPARAM)-1); + SetFocus(edit); + } + break; + case WM_DESTROY: + { + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (parms) + { + RECT r; + GetWindowRect(hwnd,&r); + HWND list = GetDlgItem(hwnd,IDC_LIST); + const int c1 = ListView_GetColumnWidth(list,0); + const int c2 = ListView_GetColumnWidth(list,1); + const int c3 = ListView_GetColumnWidth(list,2); + char tmp[128]; + int extraflag=0; + if (parms->show_hidden) extraflag|=1; + snprintf(tmp,sizeof(tmp),"%d %d %d %d %d %d %d %d",r.left,r.top,r.right-r.left,r.bottom-r.top,c1,c2,c3,extraflag); + const char *ent = parms->mode == BrowseFile_State::OPENDIR ? "dir_browser" : "file_browser"; + WritePrivateProfileString(".swell",ent, tmp, ""); + } + } + break; + case WM_UPD: + switch (wParam) + { + case IDC_DIR: // update directory combo box -- destroys buffer pointed to by lParam + if (lParam) + { + char *path = (char*)lParam; + HWND combo=GetDlgItem(hwnd,IDC_DIR); + SendMessage(combo,CB_RESETCONTENT,0,0); + WDL_remove_trailing_dirchars(path); + while (path[0]) + { + SendMessage(combo,CB_ADDSTRING,0,(LPARAM)path); + WDL_remove_filepart(path); + WDL_remove_trailing_dirchars(path); + } + SendMessage(combo,CB_ADDSTRING,0,(LPARAM)"/"); + recent_addtocb(combo); + SendMessage(combo,CB_SETCURSEL,0,0); + } + break; + case IDC_EXT: + case 1: + { + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (parms) + { + SetDlgItemText(hwnd,IDC_FILTER,""); + KillTimer(hwnd,1); + + char buf[maxPathLen]; + const char *filt = NULL; + HWND ext = GetDlgItem(hwnd,IDC_EXT); + if (ext) + { + LRESULT aidx = SendMessage(ext,CB_GETCURSEL,0,0); + if (aidx != CB_ERR) + { + filt = (const char *)SendMessage(ext,CB_GETITEMDATA,aidx,0); + if (wParam == IDC_EXT && parms->extlist && *parms->extlist) + { + GetDlgItemText(hwnd,IDC_EDIT,buf,sizeof(buf)); + + if (buf[0]) + { + const char *erd = filt; + if (*erd == '*' && erd[1] == '.' && erd[2] && erd[2] != '*') + { + const char *a = (erd+=1); + while (*erd && *erd != ';') erd++; + if (erd > a+1) + { + if (ext_valid_for_extlist(WDL_get_fileext(buf),parms->extlist)>=0) + { + WDL_remove_fileext(buf); + } + else + { + char *p = buf; + while (*p) p++; + while (p > buf && (p[-1] == ' ' || p[-1] == '.' || p[-1] == '/' || p[-1] == '\\')) p--; + *p=0; + } + if (buf[0]) + { + snprintf_append(buf,sizeof(buf),"%.*s",(int)(erd-a),a); + SetDlgItemText(hwnd,IDC_EDIT,buf); + } + } + } + } + } + } + } + + GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf)); + preprocess_user_path(buf,sizeof(buf)); + + if (buf[0]) parms->scan_path(buf, filt, parms->mode == BrowseFile_State::OPENDIR); + else parms->viewlist_clear(); + HWND list = GetDlgItem(hwnd,IDC_LIST); + ListView_SetItemCount(list, 0); // clear selection + + parms->viewlist_sort(""); + ListView_SetItemCount(list, parms->viewlist.GetSize()); + ListView_RedrawItems(list,0, parms->viewlist.GetSize()); + } + } + break; + } + break; + case WM_GETMINMAXINFO: + { + LPMINMAXINFO p=(LPMINMAXINFO)lParam; + p->ptMinTrackSize.x = 300; + p->ptMinTrackSize.y = 300; + } + break; + case WM_SIZE: + { + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + // reposition controls + RECT r; + GetClientRect(hwnd,&r); + const int buth = SWELL_UI_SCALE(24), cancelbutw = SWELL_UI_SCALE(50), okbutw = SWELL_UI_SCALE(parms->mode == BrowseFile_State::OPENDIR ? 120 : 50); + const int xborder = SWELL_UI_SCALE(4), yborder=SWELL_UI_SCALE(8); + const int fnh = SWELL_UI_SCALE(20), fnlblw = SWELL_UI_SCALE(parms->mode == BrowseFile_State::OPENDIR ? 70 : 50); + const int ypad = SWELL_UI_SCALE(4); + + int ypos = r.bottom - ypad - buth; + int xpos = r.right; + SetWindowPos(GetDlgItem(hwnd,IDCANCEL), NULL, xpos -= cancelbutw + xborder, ypos, cancelbutw,buth, SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hwnd,IDOK), NULL, xpos -= okbutw + xborder, ypos, okbutw,buth, SWP_NOZORDER|SWP_NOACTIVATE); + + HWND emb = GetDlgItem(hwnd,IDC_CHILD); + if (emb) + { + RECT sr; + GetClientRect(emb,&sr); + if (ypos > r.bottom-ypad-sr.bottom) ypos = r.bottom-ypad-sr.bottom; + SetWindowPos(emb,NULL, xborder,ypos, xpos - xborder*2, sr.bottom, SWP_NOZORDER|SWP_NOACTIVATE); + ShowWindow(emb,SW_SHOWNA); + } + + HWND filt = GetDlgItem(hwnd,IDC_EXT); + if (filt) + { + SetWindowPos(filt, NULL, xborder*2 + fnlblw, ypos -= fnh + yborder, r.right-fnlblw-xborder*3, fnh, SWP_NOZORDER|SWP_NOACTIVATE); + } + + SetWindowPos(GetDlgItem(hwnd,IDC_EDIT), NULL, xborder*2 + fnlblw, ypos -= fnh + yborder, r.right-fnlblw-xborder*3, fnh, SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hwnd,IDC_LABEL), NULL, xborder, ypos, fnlblw, fnh, SWP_NOZORDER|SWP_NOACTIVATE); + const int comboh = g_swell_ctheme.combo_height; + const int filterw = wdl_max(r.right/8, SWELL_UI_SCALE(50)); + SetWindowPos(GetDlgItem(hwnd,IDC_DIR), NULL, xborder, yborder/2, + r.right-xborder*4 - comboh - filterw, comboh, SWP_NOZORDER|SWP_NOACTIVATE); + + SetWindowPos(GetDlgItem(hwnd,IDC_PARENTBUTTON),NULL, + r.right-xborder*2-comboh - filterw,yborder/2, + comboh,comboh,SWP_NOZORDER|SWP_NOACTIVATE); + + SetWindowPos(GetDlgItem(hwnd,IDC_FILTER),NULL, + r.right-xborder-filterw,yborder/2 + (comboh-fnh)/2, + filterw,fnh,SWP_NOZORDER|SWP_NOACTIVATE); + + SetWindowPos(GetDlgItem(hwnd,IDC_LIST), NULL, xborder, g_swell_ctheme.combo_height+yborder, r.right-xborder*2, ypos - (g_swell_ctheme.combo_height+yborder) - yborder, SWP_NOZORDER|SWP_NOACTIVATE); + } + break; + case WM_TIMER: + if (wParam == 1) + { + KillTimer(hwnd,1); + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (parms) + { + char buf[128]; + GetDlgItemText(hwnd,IDC_FILTER,buf,sizeof(buf)); + parms->viewlist_sort(buf); + HWND list = GetDlgItem(hwnd,IDC_LIST); + ListView_SetItemCount(list, parms->viewlist.GetSize()); + ListView_RedrawItems(list,0, parms->viewlist.GetSize()); + } + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_FILTER: + if (HIWORD(wParam) == EN_CHANGE) + { + KillTimer(hwnd,1); + SetTimer(hwnd,1,250,NULL); + } + return 0; + case IDC_EXT: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + SendMessage(hwnd,WM_UPD,IDC_EXT,0); + } + return 0; + case IDC_PARENTBUTTON: + { + int a = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCURSEL,0,0); + int cbcnt = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCOUNT,0,0); + if (a>=0 && a < cbcnt - recent_size()) + { + SendDlgItemMessage(hwnd,IDC_DIR,CB_SETCURSEL,a+1,0); + } + else + { + char buf[maxPathLen]; + GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf)); + preprocess_user_path(buf,sizeof(buf)); + WDL_remove_filepart(buf); + if (a>=0) + SendMessage(hwnd,WM_UPD,IDC_DIR,(LPARAM)buf); + else + SetDlgItemText(hwnd,IDC_DIR,buf); + } + SendMessage(hwnd,WM_UPD,1,0); + } + return 0; + case IDC_DIR: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + int a = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCURSEL,0,0); + int cbcnt = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCOUNT,0,0); + if (a>=cbcnt - recent_size()) + { + char buf[maxPathLen]; + GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf)); + preprocess_user_path(buf,sizeof(buf)); + SendMessage(hwnd,WM_UPD,IDC_DIR,(LPARAM)buf); + } + SendMessage(hwnd,WM_UPD,1,0); + } + return 0; + case IDCANCEL: EndDialog(hwnd,0); return 0; + case IDOK: + { + char buf[maxPathLen], msg[2048]; + GetDlgItemText(hwnd,IDC_DIR,buf,sizeof(buf)); + preprocess_user_path(buf,sizeof(buf)); + + if (GetFocus() == GetDlgItem(hwnd,IDC_DIR)) + { + DIR *dir = opendir(buf); + if (!dir) + { + //snprintf(msg,sizeof(msg),"Path does not exist:\r\n\r\n%s",buf); + //MessageBox(hwnd,msg,"Path not found",MB_OK); + return 0; + } + closedir(dir); + + SendMessage(hwnd,WM_UPD,1,0); + SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); + HWND e = GetDlgItem(hwnd,IDC_EDIT); + SendMessage(e,EM_SETSEL,0,(LPARAM)-1); + SetFocus(e); + return 0; + } + + size_t buflen = strlen(buf); + if (!buflen) strcpy(buf,"/"); + else + { + if (buflen > sizeof(buf)-2) buflen = sizeof(buf)-2; + if (buf[buflen-1]!='/') { buf[buflen++] = '/'; buf[buflen]=0; } + } + GetDlgItemText(hwnd,IDC_EDIT,msg,sizeof(msg)); + preprocess_user_path(msg,sizeof(msg)); + + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + int cnt; + if (parms->mode == BrowseFile_State::OPENMULTI && (cnt=ListView_GetSelectedCount(GetDlgItem(hwnd,IDC_LIST)))>1 && (!*msg || !strcmp(msg,multiple_files))) + { + recent_write(buf); + HWND list = GetDlgItem(hwnd,IDC_LIST); + WDL_TypedBuf fs; + fs.Set(buf,strlen(buf)+1); + int a = ListView_GetNextItem(list,-1,LVNI_SELECTED); + while (a != -1 && fs.GetSize() < 4096*4096 && cnt--) + { + if (a < 0 || a >= parms->viewlist.GetSize()) break; + const struct BrowseFile_State::rec *rec = parms->viewlist.Get(a); + if (!rec) break; + + fs.Add(rec->name,strlen(rec->name)+1); + a = ListView_GetNextItem(list,a,LVNI_SELECTED); + } + fs.Add("",1); + + parms->fnout = (char*)malloc(fs.GetSize()); + if (parms->fnout) memcpy(parms->fnout,fs.Get(),fs.GetSize()); + + EndDialog(hwnd,1); + return 0; + } + else + { + if (msg[0] == '.' && (msg[1] == '.' || msg[1] == 0)) + { + if (msg[1] == '.') + { + int a = (int) SendDlgItemMessage(hwnd,IDC_DIR,CB_GETCURSEL,0,0); + if (a>=0) SendDlgItemMessage(hwnd,IDC_DIR,CB_SETCURSEL,a+1,0); + } + SetDlgItemText(hwnd,IDC_EDIT,""); + SendMessage(hwnd,WM_UPD,1,0); + return 0; + } + else if (msg[0] == '/') lstrcpyn_safe(buf,msg,sizeof(buf)); + else lstrcatn(buf,msg,sizeof(buf)); + } + + switch (parms->mode) + { + case BrowseFile_State::OPENDIR: + if (!buf[0]) return 0; + else if (msg[0]) + { + // navigate to directory if filepart set +treatAsDir: + DIR *dir = opendir(buf); + if (!dir) + { + snprintf(msg,sizeof(msg),"Error opening directory:\r\n\r\n%.1000s\r\n\r\nCreate?",buf); + if (MessageBox(hwnd,msg,"Create directory?",MB_OKCANCEL)==IDCANCEL) return 0; + CreateDirectory(buf,NULL); + dir=opendir(buf); + } + if (!dir) { MessageBox(hwnd,"Error creating directory","Error",MB_OK); return 0; } + closedir(dir); + SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); + SetDlgItemText(hwnd,IDC_EDIT,""); + SendMessage(hwnd,WM_UPD,1,0); + + return 0; + } + else + { + DIR *dir = opendir(buf); + if (!dir) return 0; + closedir(dir); + } + break; + case BrowseFile_State::SAVE: + if (!buf[0]) return 0; + else + { + struct stat st={0,}; + DIR *dir = opendir(buf); + if (dir) + { + closedir(dir); + SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); + SetDlgItemText(hwnd,IDC_EDIT,""); + SendMessage(hwnd,WM_UPD,1,0); + return 0; + } + if (buf[strlen(buf)-1] == '/') goto treatAsDir; + + const char *extlist = parms->extlist; + if (extlist && *extlist && ext_valid_for_extlist(WDL_get_fileext(buf),extlist) < 0) + { + const char *erd = extlist+strlen(extlist)+1; + LRESULT combo_sel; + HWND combo = GetDlgItem(hwnd, IDC_EXT); + if (combo && (combo_sel=SendMessage(combo,CB_GETCURSEL,0,0)) != CB_ERR) + { + const char *filt = (const char *)SendMessage(combo,CB_GETITEMDATA,combo_sel,0); + if (filt && *filt == '*' && filt[1] == '.' && filt[2] && filt[2] != '*') erd = filt; + } + + if (*erd == '*' && erd[1] == '.' && erd[2] && erd[2] != '*') // add default extension + { + const char *a = (erd+=1); + while (*erd && *erd != ';') erd++; + if (erd > a+1) snprintf_append(buf,sizeof(buf),"%.*s",(int)(erd-a),a); + } + } + if (!stat(buf,&st)) + { + snprintf(msg,sizeof(msg),"File exists:\r\n\r\n%.1000s\r\n\r\nOverwrite?",buf); + if (MessageBox(hwnd,msg,"Overwrite file?",MB_OKCANCEL)==IDCANCEL) return 0; + } + } + break; + default: + if (!buf[0]) return 0; + else + { + struct stat st={0,}; + DIR *dir = opendir(buf); + if (dir) + { + closedir(dir); + SendMessage(hwnd, WM_UPD, IDC_DIR, (LPARAM)buf); + SetDlgItemText(hwnd,IDC_EDIT,""); + SendMessage(hwnd,WM_UPD,1,0); + return 0; + } + if (stat(buf,&st)) + { + //snprintf(msg,sizeof(msg),"File does not exist:\r\n\r\n%s",buf); + //MessageBox(hwnd,msg,"File not found",MB_OK); + return 0; + } + } + break; + } + if (parms->fnout) + { + lstrcpyn_safe(parms->fnout,buf,parms->fnout_sz); + } + else + { + size_t l = strlen(buf); + parms->fnout = (char*)calloc(l+2,1); + memcpy(parms->fnout,buf,l); + } + if (parms->mode != BrowseFile_State::OPENDIR) + WDL_remove_filepart(buf); + recent_write(buf); + } + EndDialog(hwnd,1); + return 0; + case ID_SHOW_HIDDEN: + { + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + parms->show_hidden = !parms->show_hidden; + SendMessage(hwnd,WM_UPD,1,0); + } + return 0; + } + break; + case WM_NOTIFY: + { + LPNMHDR l=(LPNMHDR)lParam; + if (l->code == LVN_GETDISPINFO) + { + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + NMLVDISPINFO *lpdi = (NMLVDISPINFO*) lParam; + const int idx=lpdi->item.iItem; + if (l->idFrom == IDC_LIST && parms) + { + struct BrowseFile_State::rec *rec = parms->viewlist.Get(idx); + if (rec && rec->name) + { + if (lpdi->item.mask&LVIF_TEXT) + { + switch (lpdi->item.iSubItem) + { + case 0: + lstrcpyn_safe(lpdi->item.pszText,rec->name,lpdi->item.cchTextMax); + break; + case 1: + rec->format_size(lpdi->item.pszText,lpdi->item.cchTextMax); + break; + case 2: + rec->format_date(lpdi->item.pszText,lpdi->item.cchTextMax); + break; + } + } + } + } + } + else if (l->code == LVN_ODFINDITEM) + { + } + else if (l->code == LVN_ITEMCHANGED) + { + const int selidx = ListView_GetNextItem(l->hwndFrom, -1, LVNI_SELECTED); + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (selidx>=0 && parms) + { + if (parms->mode == BrowseFile_State::OPENMULTI && ListView_GetSelectedCount(l->hwndFrom)>1) + { + SetDlgItemText(hwnd,IDC_EDIT,multiple_files); + } + else + { + struct BrowseFile_State::rec *rec = parms->viewlist.Get(selidx); + if (rec) + { + SetDlgItemText(hwnd,IDC_EDIT,rec->name); + } + } + } + } + else if (l->code == NM_DBLCLK) + { + SendMessage(hwnd,WM_COMMAND,IDOK,0); + } + else if (l->code == LVN_COLUMNCLICK) + { + NMLISTVIEW* lv = (NMLISTVIEW*) l; + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + parms->sortrev=!parms->sortrev; + char lcol = parms->sortcol; + if ((int)parms->sortcol != lv->iSubItem) + { + parms->sortcol = (char)lv->iSubItem; + parms->sortrev=0; + } + + HWND hdr = ListView_GetHeader(l->hwndFrom); + HDITEM hi; + memset(&hi,0,sizeof(hi)); + hi.mask = HDI_FORMAT; + Header_SetItem(hdr,lcol,&hi); + hi.fmt = parms->sortrev ? HDF_SORTDOWN : HDF_SORTUP; + Header_SetItem(hdr,parms->sortcol,&hi); + + parms->viewlist_sort(NULL); + ListView_RedrawItems(l->hwndFrom,0, parms->viewlist.GetSize()); + } + } + break; + case WM_KEYDOWN: + if (lParam == FVIRTKEY && wParam == VK_F5) + { + SendMessage(hwnd,WM_UPD,1,0); + return 1; + } + else if (lParam == (FVIRTKEY|FCONTROL) && wParam == 'H') + { + SendMessage(hwnd,WM_COMMAND,ID_SHOW_HIDDEN,0); + return 1; + } + else if (lParam == FVIRTKEY && wParam == VK_BACK && + GetFocus() == GetDlgItem(hwnd,IDC_LIST)) + { + SendMessage(hwnd,WM_COMMAND,IDC_PARENTBUTTON,0); + return 1; + } + else if ((lParam&0xff) == FVIRTKEY && wParam == VK_RETURN && + GetFocus() == GetDlgItem(hwnd,IDC_LIST)) + { + SendMessage(hwnd,WM_COMMAND,IDOK,0); + return 1; + } + return 0; + case WM_CONTEXTMENU: + { + BrowseFile_State *parms = (BrowseFile_State *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + HMENU menu = CreatePopupMenu(); + SWELL_InsertMenu(menu,0,MF_BYPOSITION|(parms->show_hidden ? MF_CHECKED:MF_UNCHECKED), ID_SHOW_HIDDEN, "Show files/directories beginning with ."); + POINT p; + GetCursorPos(&p); + TrackPopupMenu(menu,0,p.x,p.y,0,hwnd,NULL); + DestroyMenu(menu); + } + return 1; + } + return 0; +} + +// return true +bool BrowseForSaveFile(const char *text, const char *initialdir, const char *initialfile, const char *extlist, + char *fn, int fnsize) +{ + BrowseFile_State state( text, initialdir, initialfile, extlist, BrowseFile_State::SAVE, fn, fnsize ); + if (!DialogBoxParam(NULL,NULL,GetForegroundWindow(),swellFileSelectProc,(LPARAM)&state)) return false; + + return true; +} + +bool BrowseForDirectory(const char *text, const char *initialdir, char *fn, int fnsize) +{ + BrowseFile_State state( text, initialdir, initialdir, NULL, BrowseFile_State::OPENDIR, fn, fnsize ); + return !!DialogBoxParam(NULL,NULL,GetForegroundWindow(),swellFileSelectProc,(LPARAM)&state); +} + + +char *BrowseForFiles(const char *text, const char *initialdir, + const char *initialfile, bool allowmul, const char *extlist) +{ + BrowseFile_State state( text, initialdir, initialfile, extlist, + allowmul ? BrowseFile_State::OPENMULTI : BrowseFile_State::OPEN, NULL, 0 ); + return DialogBoxParam(NULL,NULL,GetForegroundWindow(),swellFileSelectProc,(LPARAM)&state) ? state.fnout : NULL; +} + +static const char *mbidtostr(int idx) +{ + switch (idx) + { + case IDOK: return "OK"; + case IDCANCEL: return "Cancel"; + case IDYES: return "Yes"; + case IDNO: return "No"; + case IDRETRY: return "Retry"; + case IDABORT: return "Abort"; + case IDIGNORE: return "Ignore"; + default: + WDL_ASSERT(idx == 0); + return ""; + } +} + +static LRESULT WINAPI swellMessageBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + enum { IDC_LABEL=0x100 }; + const int button_spacing = 8; + switch (uMsg) + { + case WM_CREATE: + if (lParam) // swell-specific + { + SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc); + SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellMessageBoxProc); + void **parms = (void **)lParam; + if (parms[1]) SetWindowText(hwnd,(const char*)parms[1]); + + + int b1=IDOK, b2=0,b3=0; + + const int fmode = (int)(INT_PTR)parms[2]; + switch (fmode & 0xf) + { + case MB_ABORTRETRYIGNORE: + b1 = IDABORT; + b2 = IDRETRY; + b3 = IDIGNORE; + break; + case MB_RETRYCANCEL: + b1 = IDRETRY; + // fallthrough + case MB_OKCANCEL: + b2 = IDCANCEL; + break; + case MB_YESNOCANCEL: + b3 = IDCANCEL; + // fallthrough + case MB_YESNO: + b1 = IDYES; + b2 = IDNO; + break; + } + + SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false); + RECT labsize = {0,0,300,20}; + HWND lab = SWELL_MakeLabel(-1,parms[0] ? (const char *)parms[0] : "", IDC_LABEL, 0,0,10,10,SS_CENTER); //we'll resize this manually + HDC dc=GetDC(lab); + if (lab && parms[0]) + { + DrawText(dc,(const char *)parms[0],-1,&labsize,DT_CALCRECT|DT_NOPREFIX);// if dc isnt valid yet, try anyway + } + + const int sc10 = SWELL_UI_SCALE(10); + const int sc8 = SWELL_UI_SCALE(8); + labsize.top += sc10; + labsize.bottom += sc10 + sc8; + + RECT vp; + SWELL_GetViewPort(&vp,NULL,true); + vp.bottom -= vp.top; + if (labsize.bottom > vp.bottom*7/8) + labsize.bottom = vp.bottom*7/8; + + + int x; + int button_height=0, button_total_w=0;; + const int bspace = SWELL_UI_SCALE(button_spacing); + int button_sizes[3]; + const int nbuttons = b3 ? 3 : b2 ? 2 : 1; + for (x = 0; x < nbuttons; x ++) + { + const int idx = x==0?b1:x==1?b2:b3; + RECT r={0,0,35,12}; + DrawText(dc,mbidtostr(idx),-1,&r,DT_CALCRECT|DT_NOPREFIX|DT_SINGLELINE); + button_sizes[x] = r.right-r.left + sc10; + button_total_w += button_sizes[x] + (x ? bspace : 0); + if (r.bottom-r.top+sc10 > button_height) button_height = r.bottom-r.top+sc10; + } + + if (labsize.right < button_total_w+sc8*2) labsize.right = button_total_w+sc8*2; + + int xpos = labsize.right/2 - button_total_w/2; + const int def_id = (fmode & MB_DEFBUTTON3)&&b3 ? b3 : (fmode & MB_DEFBUTTON2)&&b2 ? b2 : b1; + + for (x = 0; x < nbuttons; x ++) + { + const int idx = x==0?b1:x==1?b2:b3; + SWELL_MakeButton(idx==def_id,mbidtostr(idx),idx,xpos,labsize.bottom,button_sizes[x],button_height,0); + xpos += button_sizes[x] + bspace; + } + + if (dc) ReleaseDC(lab,dc); + SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false); + SetWindowPos(hwnd,NULL,0,0, + labsize.right + sc8*2,labsize.bottom + button_height + sc8,SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE); + if (lab) SetWindowPos(lab,NULL,sc8,0,labsize.right,labsize.bottom,SWP_NOACTIVATE|SWP_NOZORDER); + SetFocus(GetDlgItem(hwnd,def_id)); + } + break; + case WM_SIZE: + { + RECT r; + GetClientRect(hwnd,&r); + HWND h = GetWindow(hwnd,GW_CHILD); + int n = 10, w[8]; + HWND tab[8],lbl=NULL; + int tabsz=0, bxwid=0, button_height=0; + while (h && n-- && tabsz<8) + { + int idx = GetWindowLong(h,GWL_ID); + if (idx == IDCANCEL || idx == IDOK || idx == IDNO || idx == IDYES || idx == IDRETRY) + { + RECT tr; + GetClientRect(h,&tr); + tab[tabsz] = h; + w[tabsz++] = tr.right - tr.left; + button_height = tr.bottom-tr.top; + bxwid += tr.right-tr.left; + } else if (idx==IDC_LABEL) lbl=h; + h = GetWindow(h,GW_HWNDNEXT); + } + const int bspace = SWELL_UI_SCALE(button_spacing), sc8 = SWELL_UI_SCALE(8); + if (lbl) SetWindowPos(lbl,NULL,sc8,0,r.right,r.bottom - sc8 - button_height, SWP_NOZORDER|SWP_NOACTIVATE); + int xo = r.right/2 - (bxwid + (tabsz-1)*bspace)/2; + for (int x=0; x255) ir=255; + if (ig<0) ig=0; else if (ig>255) ig=255; + if (ib<0) ib=0; else if (ib>255) ib=255; + return RGB(ir,ig,ib); +} + + +static void _RGB2HSV(double r, double g, double b, double *h, double *s, double *v) +{ + const double maxrgb=wdl_max(wdl_max(r,g),b); + const double df = maxrgb - wdl_min(wdl_min(r,g),b); + double d=r-g, degoffs = 240.0; + + if (g > r) + { + if (g > b) + { + degoffs=120; + d=b-r; + } + } + else if (r > b) + { + degoffs=0.0; + d=g-b; + } + + *v = maxrgb; + if (df) + { + degoffs += (d*60)/df; + if (degoffs<0.0) degoffs+=360.0; + else if (degoffs >= 360.0) degoffs-=360.0; + *h = degoffs; + *s = (df*256)/(maxrgb+1); + } + else + *h = *s = 0; +} + +static LRESULT WINAPI swellColorSelectProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static int s_reent,s_vmode; + static int wndw, custsz, buth, border, butw, edh, edlw, edew, vsize, psize, yt; + if (!wndw) + { + wndw = SWELL_UI_SCALE(400); + custsz = SWELL_UI_SCALE(20); + butw = SWELL_UI_SCALE(50); + buth = SWELL_UI_SCALE(24); + border = SWELL_UI_SCALE(4); + edh = SWELL_UI_SCALE(20); + edlw = SWELL_UI_SCALE(16); + edew = SWELL_UI_SCALE(40); + vsize = SWELL_UI_SCALE(40); + psize = border+edlw + edew; + yt = border + psize + border + (edh + border)*6; + } + + const int customperrow = (wndw-border)/(custsz+border); + switch (uMsg) + { + case WM_CREATE: + if (lParam) // swell-specific + { + SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc); + SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellColorSelectProc); + SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam); + + SetWindowText(hwnd,"Choose Color"); + + SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false); + + SWELL_MakeButton(0, "OK", IDOK,0,0,0,0, 0); + SWELL_MakeButton(0, "Cancel", IDCANCEL,0,0,0,0, 0); + SWELL_MakeLabel(0, "(right click a custom color to save)", 0x500, 0,0,0,0, 0); + + static const char * const lbl[] = { "R","G","B","H","S","V"}; + for (int x=0;x<6;x++) + { + SWELL_MakeLabel(0,lbl[x], 0x100+x, 0,0,0,0, 0); + SWELL_MakeEditField(0x200+x, 0,0,0,0, 0); + } + + ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false); + int nrows = ((cs?cs->ncustom : 0 ) + customperrow-1)/wdl_max(customperrow,1); + SetWindowPos(hwnd,NULL,0,0, wndw, + yt + buth + border + nrows * (custsz+border), + SWP_NOZORDER|SWP_NOMOVE); + SendMessage(hwnd,WM_USER+100,0,3); + } + break; + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + { + ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (!cs) break; + RECT r; + GetClientRect(hwnd,&r); + const int xt = r.right - edew - edlw - border*3; + + const int y = GET_Y_LPARAM(lParam); + const int x = GET_X_LPARAM(lParam); + if (x < xt && y < yt) + { + s_vmode = x >= xt-vsize; + SetCapture(hwnd); + // fall through + } + else + { + if (cs->custom && cs->ncustom && y >= yt && y < r.bottom - buth - border) + { + int row = (y-yt) / (custsz+border), rowoffs = (y-yt) % (custsz+border); + if (rowoffs < custsz) + { + int col = (x-border) / (custsz+border), coloffs = (x-border) % (custsz+border); + if (coloffs < custsz) + { + col += customperrow*row; + if (col >= 0 && col < cs->ncustom) + { + if (uMsg == WM_LBUTTONDOWN) + { + _RGB2HSV(GetRValue(cs->custom[col]),GetGValue(cs->custom[col]),GetBValue(cs->custom[col]),&cs->h,&cs->s,&cs->v); + SendMessage(hwnd,WM_USER+100,0,3); + } + else + { + cs->custom[col] = _HSV2RGBV(cs->h,cs->s,cs->v); + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + } + } + break; + } + // fall through + } + case WM_MOUSEMOVE: + if (GetCapture()==hwnd) + { + RECT r; + GetClientRect(hwnd,&r); + const int xt = r.right - edew - edlw - border*3; + ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (!cs) break; + int var = 255 - (GET_Y_LPARAM(lParam) - border)*256 / (yt-border*2); + if (var<0)var=0; + else if (var>255)var=255; + if (s_vmode) + { + if (var != cs->v) + { + cs->v=var; + SendMessage(hwnd,WM_USER+100,0,3); + } + } + else + { + int hue = (GET_X_LPARAM(lParam) - border)*360 / (xt-border - vsize); + if (hue<0) hue=0; + else if (hue>359) hue=359; + if (cs->h != hue || cs->s != var) + { + cs->h=hue; + cs->s=var; + SendMessage(hwnd,WM_USER+100,0,3); + } + } + } + break; + case WM_LBUTTONUP: + case WM_RBUTTONUP: + ReleaseCapture(); + break; + case WM_PAINT: + { + PAINTSTRUCT ps; + ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (cs && BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + const int xt = r.right - edew - edlw - border*3; + if (cs->custom && cs->ncustom) + { + int ypos = yt; + int xpos = border; + for (int x = 0; x < cs->ncustom; x ++) + { + HBRUSH br = CreateSolidBrush(cs->custom[x]); + RECT tr={xpos,ypos,xpos+custsz, ypos+custsz }; + FillRect(ps.hdc,&tr,br); + DeleteObject(br); + + xpos += border+custsz; + if (xpos+custsz >= r.right) + { + xpos=border; + ypos += border + custsz; + } + } + } + + { + HBRUSH br = CreateSolidBrush(_HSV2RGBV(cs->h,cs->s,cs->v)); + RECT tr={r.right - border - psize, border, r.right-border, border+psize}; + FillRect(ps.hdc,&tr,br); + DeleteObject(br); + } + + if (!cs->bm) cs->bm = new LICE_SysBitmap(xt-border,yt-border); + else cs->bm->resize(xt-border,yt-border); + + int x1 = xt - border - vsize; + int var = cs->v; + + const int ysz = yt-border*2; + const int vary = ysz - 1 - (ysz * cs->v)/256; + + for (int y = 0; y < ysz; y ++) + { + LICE_pixel *wr = cs->bm->getBits() + cs->bm->getRowSpan() * y; + const int sat = 255 - y*256/ysz; + double xx=0.0, dx=384.0/x1; + int x; + for (x = 0; x < x1; x++) + { + *wr++ = LICE_HSV2Pix((int)(xx+0.5),sat,var,255); + xx+=dx; + } + LICE_pixel p = LICE_HSV2Pix(cs->h * 384.0/360.0,cs->s,sat ^ (y==vary ? 128 : 0),255); + for (;x < xt-border;x++) *wr++ = p; + } + LICE_pixel p = LICE_HSV2Pix((int)(cs->h+0.5),(int)(cs->s+0.5),((int)(128.5+cs->v))&255,255); + const int saty = ysz - 1 - (int) (ysz * cs->s + 0.5)/256; + const int huex = (x1*cs->h)/360; + LICE_Line(cs->bm,huex,saty-4,huex,saty+4,p,.75f,LICE_BLIT_MODE_COPY,false); + LICE_Line(cs->bm,huex-4,saty,huex+4,saty,p,.75f,LICE_BLIT_MODE_COPY,false); + + BitBlt(ps.hdc,border,border,xt-border,ysz,cs->bm->getDC(),0,0,SRCCOPY); + + EndPaint(hwnd,&ps); + } + } + + break; + case WM_GETMINMAXINFO: + { + LPMINMAXINFO p=(LPMINMAXINFO)lParam; + p->ptMinTrackSize.x = 300; + p->ptMinTrackSize.y = 300; + } + break; + case WM_USER+100: + { + ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (cs) + { + double t[6]; + t[3] = cs->h; + t[4] = cs->s; + t[5] = cs->v; + _HSV2RGB(t[3],t[4],t[5],t,t+1,t+2); + s_reent++; + for (int x=0;x<6;x++) if (lParam & ((x<3)?1:2)) SetDlgItemInt(hwnd,0x200+x,(int) (t[x]+0.5),FALSE); + s_reent--; + InvalidateRect(hwnd,NULL,FALSE); + } + } + break; + case WM_SIZE: + { + RECT r; + GetClientRect(hwnd,&r); + int tx = r.right - edew-edlw-border*2, ty = border*2 + psize; + for (int x=0;x<6;x++) + { + SetWindowPos(GetDlgItem(hwnd,0x100+x),NULL,tx, ty, edlw, edh, SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hwnd,0x200+x),NULL,tx+edlw+border, ty, edew, edh, SWP_NOZORDER|SWP_NOACTIVATE); + ty += border+edh; + } + + r.right -= border + butw; + r.bottom -= border + buth; + SetWindowPos(GetDlgItem(hwnd,IDCANCEL), NULL, r.right, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE); + r.right -= border*2 + butw; + SetWindowPos(GetDlgItem(hwnd,IDOK), NULL, r.right, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE); + + SetWindowPos(GetDlgItem(hwnd,0x500), NULL, border, r.bottom, r.right-border*2, buth, SWP_NOZORDER|SWP_NOACTIVATE); + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDCANCEL: + EndDialog(hwnd,0); + break; + case IDOK: + EndDialog(hwnd,1); + break; + case 0x200: + case 0x201: + case 0x202: + case 0x203: + case 0x204: + case 0x205: + if (!s_reent) + { + const bool ishsv = LOWORD(wParam) >= 0x203; + int offs = ishsv ? 0x203 : 0x200; + BOOL t = FALSE; + double h = GetDlgItemInt(hwnd,offs++,&t,FALSE); + if (!t) break; + double s = GetDlgItemInt(hwnd,offs++,&t,FALSE); + if (!t) break; + double v = GetDlgItemInt(hwnd,offs++,&t,FALSE); + if (!t) break; + if (s<0) s=0; else if (s>255) s=255; + if (v<0) v=0; else if (v>255) v=255; + if (h<0) h=0; + + if (!ishsv) + { + if (h>255) h=255; + _RGB2HSV(h,s,v,&h,&s,&v); + } + else + { + if (h>360) h=360; + } + + ChooseColor_State *cs = (ChooseColor_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (cs) + { + cs->h = h; + cs->s = s; + cs->v = v; + } + SendMessage(hwnd,WM_USER+100,0,ishsv?1:2); + } + break; + } + break; + + } + return 0; +} +#endif //SWELL_LICE_GDI + +bool SWELL_ChooseColor(HWND h, COLORREF *val, int ncustom, COLORREF *custom) +{ +#ifdef SWELL_LICE_GDI + ChooseColor_State state = { ncustom, custom }; + COLORREF c = val ? *val : 0; + _RGB2HSV(GetRValue(c),GetGValue(c),GetBValue(c),&state.h,&state.s,&state.v); + bool rv = DialogBoxParam(NULL,NULL,h,swellColorSelectProc,(LPARAM)&state)!=0; + delete state.bm; + if (rv && val) + { + *val = _HSV2RGBV(state.h,state.s,state.v); + } + return rv; +#else + return false; +#endif +} + +#if defined(SWELL_FREETYPE) && defined(SWELL_LICE_GDI) + +struct FontChooser_State +{ + FontChooser_State() + { + hFont = 0; + } + ~FontChooser_State() + { + DeleteObject(hFont); + } + + LOGFONT font; + HFONT hFont; + WDL_FastString lastfn; +}; + +extern const char *swell_last_font_filename; + +const char *swell_enumFontFiles(int x); +int swell_getLineLength(const char *buf, int *post_skip, int wrap_maxwid, HDC hdc); + +static LRESULT WINAPI swellFontChooserProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + enum { IDC_LIST=0x100, IDC_FACE, IDC_SIZE, IDC_WEIGHT, IDC_ITALIC }; + enum { preview_h = 90, _border = 4, _buth = 24 }; + + switch (uMsg) + { + case WM_CREATE: + if (lParam) // swell-specific + { + SetWindowLong(hwnd,GWL_WNDPROC,(LPARAM)SwellDialogDefaultWindowProc); + SetWindowLong(hwnd,DWL_DLGPROC,(LPARAM)swellFontChooserProc); + SetWindowLongPtr(hwnd,GWLP_USERDATA,lParam); + + SetWindowText(hwnd,"Choose Font"); + + SWELL_MakeSetCurParms(1,1,0,0,hwnd,false,false); + + SWELL_MakeButton(0, "OK", IDOK,0,0,0,0, 0); + SWELL_MakeButton(0, "Cancel", IDCANCEL,0,0,0,0, 0); + SWELL_MakeListBox(IDC_LIST,0,0,0,0, LBS_OWNERDRAWFIXED); + SWELL_MakeEditField(IDC_FACE, 0,0,0,0, 0); + SWELL_MakeEditField(IDC_SIZE, 0,0,0,0, 0); + SWELL_MakeCombo(IDC_WEIGHT, 0,0,0,0, CBS_DROPDOWNLIST); + SWELL_MakeCheckBox("Italic",IDC_ITALIC,0,0,0,0, 0); + + SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_ADDSTRING,0,(LPARAM)"Normal"); + SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_ADDSTRING,0,(LPARAM)"Bold"); + SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_ADDSTRING,0,(LPARAM)"Light"); + + SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false); + + SetWindowPos(hwnd,NULL,0,0, 550,380, SWP_NOZORDER|SWP_NOMOVE); + + WDL_StringKeyedArray list; + const char *fontfile; + int x; + for (x=0; (fontfile=swell_enumFontFiles(x)); x ++) + { + char buf[512]; + lstrcpyn_safe(buf,WDL_get_filepart(fontfile),sizeof(buf)); + char *tmp = buf; + while (*tmp && *tmp != '-' && *tmp != '.') tmp++; + *tmp=0; + if (*buf) list.AddUnsorted(buf,true); + } + swell_enumFontFiles(-1); // clear cache + list.Resort(); + FontChooser_State *cs = (FontChooser_State*)lParam; + bool italics = cs->font.lfItalic!=0; + int wt = cs->font.lfWeight; + const char *lp=NULL; + int cnt=0; + for (x=0;xfont.lfFaceName)) + SendDlgItemMessage(hwnd,IDC_LIST,LB_SETCURSEL,cnt,0); + + size_t ll; + if (lp && !strncmp(p,lp,ll=strlen(lp))) + { + // if this is an extension of the last one, skip + const char *trail = p+ll; + if (strlen(trail)<=2) + { + for (int y=0;y<2;y++) + { + char c = *trail; + if (c>0) c=toupper(c); + if (c == 'B' || c == 'I' || c == 'L') trail++; + } + } + else while (*trail) + { + if (!strnicmp(trail,"Bold",4)) trail+=4; + else if (!strnicmp(trail,"Light",5)) trail+=5; + else if (!strnicmp(trail,"Italic",6)) trail+=6; + else if (!strnicmp(trail,"Oblique",7)) trail+=7; + else break; + } + if (!*trail) continue; + } + cnt++; + SendDlgItemMessage(hwnd,IDC_LIST,LB_ADDSTRING,0,(LPARAM)p); + lp=p; + } + } + SetDlgItemText(hwnd,IDC_FACE,cs->font.lfFaceName); + SetDlgItemInt(hwnd,IDC_SIZE,cs->font.lfHeight < 0 ? -cs->font.lfHeight : cs->font.lfHeight,TRUE); + SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_SETCURSEL,wt<=FW_LIGHT ? 2 : wt < FW_BOLD ? 0 : 1,0); + if (italics) + CheckDlgButton(hwnd,IDC_ITALIC,BST_CHECKED); + } + break; + case WM_DRAWITEM: + { + DRAWITEMSTRUCT *di=(DRAWITEMSTRUCT *)lParam; + FontChooser_State *cs = (FontChooser_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (cs && di->CtlID == IDC_LIST) + { + char buf[512]; + buf[0]=0; + SendDlgItemMessage(hwnd,IDC_LIST,LB_GETTEXT,di->itemID,(WPARAM)buf); + if (buf[0]) + { + HFONT font = CreateFont(g_swell_ctheme.default_font_size, 0, 0, 0, cs->font.lfWeight, cs->font.lfItalic, + FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, buf); + + HGDIOBJ oldFont = SelectObject(di->hDC,font); + DrawText(di->hDC,buf,-1,&di->rcItem,DT_VCENTER|DT_LEFT|DT_NOPREFIX); + wchar_t tmp[] = {'a','A','z','Z'}; + unsigned short ind[4]; + GetGlyphIndicesW(di->hDC,tmp,4,ind,0); + SelectObject(di->hDC,oldFont); + + int x; + for (x=0;x<4 && ind[x]==0xffff;x++); + if (x==4) + { + RECT r = di->rcItem; + r.right-=4; + DrawText(di->hDC,buf,-1,&r,DT_VCENTER|DT_RIGHT|DT_NOPREFIX); + } + DeleteObject(font); + + } + } + } + return 0; + case WM_PAINT: + { + PAINTSTRUCT ps; + FontChooser_State *cs = (FontChooser_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (cs && BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + + const int border = SWELL_UI_SCALE(_border); + const int buth = SWELL_UI_SCALE(_buth); + const int ph = SWELL_UI_SCALE(preview_h); + r.left += border; + r.right -= border; + r.bottom -= border*2 + buth; + r.top = r.bottom - ph; + + HFONT f = CreateFontIndirect(&cs->font); + + HBRUSH br = CreateSolidBrush(RGB(255,255,255)); + FillRect(ps.hdc,&r,br); + DeleteObject(br); + SetTextColor(ps.hdc,RGB(0,0,0)); + SetBkMode(ps.hdc,TRANSPARENT); + r.right-=4; + r.left+=4; + if (swell_last_font_filename) + { + r.bottom -= DrawText(ps.hdc,swell_last_font_filename,-1,&r,DT_BOTTOM|DT_NOPREFIX|DT_SINGLELINE|DT_RIGHT); + } + + HGDIOBJ oldFont = SelectObject(ps.hdc,f); + + extern const char *g_swell_fontpangram; + const char *str = g_swell_fontpangram; + // + // thanks, http://dailypangram.tumblr.com/ :) + if (!str) str = "Strangely, aerobic exercise doesn’t quite work with improvised free jazz."; + + while (*str) + { + int sk=0, lb=swell_getLineLength(str, &sk, r.right-r.left, ps.hdc); + if (!lb&&!sk) break; + if (lb>0) r.top += DrawText(ps.hdc,str,lb,&r,DT_TOP|DT_LEFT|DT_NOPREFIX|DT_SINGLELINE); + str+=lb+sk; + } + + + SelectObject(ps.hdc,oldFont); + DeleteObject(f); + + + EndPaint(hwnd,&ps); + } + } + + break; + case WM_GETMINMAXINFO: + { + LPMINMAXINFO p=(LPMINMAXINFO)lParam; + p->ptMinTrackSize.x = 400; + p->ptMinTrackSize.y = 300; + } + break; + case WM_SIZE: + { + RECT r; + GetClientRect(hwnd,&r); + const int border = SWELL_UI_SCALE(_border); + const int buth = SWELL_UI_SCALE(_buth); + const int butw = SWELL_UI_SCALE(50); + const int edh = SWELL_UI_SCALE(20); + const int size_w = SWELL_UI_SCALE(50); + const int wt_w = SWELL_UI_SCALE(80); + const int italic_w = SWELL_UI_SCALE(60); + + r.left += border; + r.right -= border; + + r.bottom -= border + buth; + SetWindowPos(GetDlgItem(hwnd,IDCANCEL), NULL, r.right - butw, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hwnd,IDOK), NULL, r.right - border - butw*2, r.bottom, butw, buth, SWP_NOZORDER|SWP_NOACTIVATE); + r.bottom -= SWELL_UI_SCALE(preview_h) + border; + int psz=wdl_max(g_swell_ctheme.combo_height,edh); + r.bottom -= psz + border; + SetWindowPos(GetDlgItem(hwnd,IDC_FACE),NULL,r.left,r.bottom + (psz-edh)/2, + r.right-r.left - size_w-wt_w-italic_w - border*3, edh, SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hwnd,IDC_SIZE),NULL,r.right-size_w-wt_w-italic_w-border*2,r.bottom + (psz-edh)/2, + size_w, edh, SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hwnd,IDC_WEIGHT),NULL,r.right-wt_w-italic_w-border,r.bottom + (psz-g_swell_ctheme.combo_height)/2, + wt_w, g_swell_ctheme.combo_height, SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hwnd,IDC_ITALIC),NULL,r.right-italic_w,r.bottom + (psz-edh)/2, + italic_w, edh, SWP_NOZORDER|SWP_NOACTIVATE); + + SetWindowPos(GetDlgItem(hwnd,IDC_LIST), NULL, border, border, r.right, r.bottom - border*2, SWP_NOZORDER|SWP_NOACTIVATE); + + + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_LIST: + if (HIWORD(wParam) == LBN_SELCHANGE) + { + int idx = (int) SendDlgItemMessage(hwnd,IDC_LIST,LB_GETCURSEL,0,0); + if (idx>=0) + { + char buf[512]; + buf[0]=0; + SendDlgItemMessage(hwnd,IDC_LIST,LB_GETTEXT,idx,(WPARAM)buf); + if (buf[0]) SetDlgItemText(hwnd,IDC_FACE,buf); + } + } + break; + case IDC_SIZE: + case IDC_FACE: + case IDC_ITALIC: + case IDC_WEIGHT: + { + FontChooser_State *cs = (FontChooser_State*)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (cs) + { + if (LOWORD(wParam) == IDC_FACE) + GetDlgItemText(hwnd,IDC_FACE,cs->font.lfFaceName,sizeof(cs->font.lfFaceName)); + else if (LOWORD(wParam) == IDC_SIZE) + { + BOOL t; + int a = GetDlgItemInt(hwnd,IDC_SIZE,&t,FALSE); + if (t) + { + if (cs->font.lfHeight < 0) cs->font.lfHeight = -a; + else cs->font.lfHeight = a; + } + } + else if (LOWORD(wParam) == IDC_ITALIC) cs->font.lfItalic = IsDlgButtonChecked(hwnd,IDC_ITALIC) ? 1:0; + else if (LOWORD(wParam) == IDC_WEIGHT && HIWORD(wParam) == CBN_SELCHANGE) + { + int idx = (int) SendDlgItemMessage(hwnd,IDC_WEIGHT,CB_GETCURSEL,0,0); + if (idx==0) cs->font.lfWeight = FW_NORMAL; + else if (idx==1) cs->font.lfWeight = FW_BOLD; + else if (idx==2) cs->font.lfWeight = FW_LIGHT; + } + InvalidateRect(hwnd,NULL,FALSE); + } + } + break; + case IDCANCEL: + EndDialog(hwnd,0); + break; + case IDOK: + EndDialog(hwnd,1); + break; + } + break; + + } + return 0; +} + +void *swell_MatchFont(const char *lfFaceName, int weight, int italic, const char **fnOut); + +#endif + +bool SWELL_ChooseFont(HWND h, LOGFONT *lf) +{ +#if defined(SWELL_FREETYPE) && defined(SWELL_LICE_GDI) + FontChooser_State state; + state.font = *lf; + + bool rv = DialogBoxParam(NULL,NULL,h,swellFontChooserProc,(LPARAM)&state)!=0; + if (rv) + { + *lf = state.font; + } + return rv; +#else + return false; +#endif +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-miscdlg.mm b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-miscdlg.mm new file mode 100644 index 000000000..e63114fff --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-miscdlg.mm @@ -0,0 +1,756 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic APIs for browsing for files, directories, and messageboxes. + + These APIs don't all match the Windows equivelents, but are close enough to make it not too much trouble. + + */ + + +#ifndef SWELL_PROVIDED_BY_APP + +#include "swell.h" +#include "swell-dlggen.h" +#include "../wdlcstring.h" +#import +#include "swell-internal.h" + +struct swell_autoarp { + swell_autoarp() { pool = [[NSAutoreleasePool alloc] init]; } + ~swell_autoarp() { [pool release]; } + NSAutoreleasePool *pool; +}; + +static NSMutableArray *extensionsFromList(const char *extlist, const char *def_ext=NULL) +{ + NSMutableArray *fileTypes = [[NSMutableArray alloc] initWithCapacity:30]; + while (*extlist) + { + extlist += strlen(extlist)+1; + if (!*extlist) break; + while (*extlist) + { + while (*extlist && *extlist != '.') extlist++; + if (!*extlist) break; + extlist++; + char tmp[32]; + lstrcpyn_safe(tmp,extlist,sizeof(tmp)); + if (strstr(tmp,";")) strstr(tmp,";")[0]=0; + if (tmp[0] && tmp[0]!='*') + { + NSString *s=(NSString *)SWELL_CStringToCFString(tmp); + const size_t tmp_len = strlen(tmp); + if (def_ext && *def_ext && + !strnicmp(def_ext,tmp,tmp_len) && + (!def_ext[tmp_len] || def_ext[tmp_len] == ';')) + [fileTypes insertObject:s atIndex:0]; + else + [fileTypes addObject:s]; + [s release]; + } + while (*extlist && *extlist != ';') extlist++; + if (*extlist == ';') extlist++; + } + extlist++; + } + + return fileTypes; +} + +static const char *BFSF_Templ_dlgid; +static DLGPROC BFSF_Templ_dlgproc; +static struct SWELL_DialogResourceIndex *BFSF_Templ_reshead; +void BrowseFile_SetTemplate(const char *dlgid, DLGPROC dlgProc, struct SWELL_DialogResourceIndex *reshead) +{ + BFSF_Templ_reshead=reshead; + BFSF_Templ_dlgid=dlgid; + BFSF_Templ_dlgproc=dlgProc; +} +static const char *s_browse_extsel; + +static LRESULT fileTypeChooseProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + const int wndh = 22, lblw = 80, combow = 250, def_wid = lblw + 4 + combow + 4; + switch (uMsg) + { + case WM_CREATE: + SetOpaque(hwnd,FALSE); + SWELL_MakeSetCurParms(1,1,0,0,hwnd,true,false); + SWELL_MakeLabel(1,"File type:",1001,0,2,lblw,wndh,0); + SWELL_MakeCombo(1000, lblw + 4,0, combow, wndh,3/*CBS_DROPDOWNLIST*/); + SWELL_MakeSetCurParms(1,1,0,0,NULL,false,false); + SetWindowPos(hwnd,NULL,0,0,def_wid,wndh,SWP_NOMOVE|SWP_NOZORDER); + { + const char *extlist = ((const char **)lParam)[0]; + SetWindowLongPtr(hwnd,GWLP_USERDATA,(LPARAM)extlist); + const char *initial_file = ((const char **)lParam)[1]; + + if (initial_file) initial_file=WDL_get_fileext(initial_file); + const size_t initial_file_len = initial_file && *initial_file ? strlen(++initial_file) : 0; + + int def_sel = -1; + + HWND combo = GetDlgItem(hwnd,1000); + while (*extlist) + { + const char *next = extlist + strlen(extlist)+1; + if (!*next) break; + + if (strcmp(next,"*.*")) + { + int a = (int)SendMessage(combo,CB_ADDSTRING,0,(LPARAM)extlist); + + // userdata for each item is pointer to un-terminated extension. + const char *p = next; + while (*p && *p != '.') p++; + if (*p) p++; + const char *bestp = p; + + // scan extension list for matching initial file, use that (and set default) + if (def_sel < 0 && initial_file) while (*p) + { + if (!strnicmp(p,initial_file,initial_file_len) && (p[initial_file_len] == ';' || !p[initial_file_len])) + { + s_browse_extsel = p; + bestp = p; + def_sel = a; + break; + } + else + { + while (*p && *p != '.') p++; + if (*p) p++; + } + } + SendMessage(combo,CB_SETITEMDATA,a,(LPARAM)bestp); + } + + extlist = next + strlen(next)+1; + if (!*extlist) break; + } + SendMessage(combo,CB_SETCURSEL,def_sel>=0?def_sel:0,0); + } + return 0; + case WM_SIZE: + { + RECT r; + GetClientRect(hwnd,&r); + const int xpos = r.right / 2 - def_wid/2; + SetWindowPos(GetDlgItem(hwnd,1001),NULL, xpos,2,0,0,SWP_NOZORDER|SWP_NOSIZE); + SetWindowPos(GetDlgItem(hwnd,1000),NULL, xpos + lblw + 4,0,0,0,SWP_NOZORDER|SWP_NOSIZE); + + } + return 0; + case WM_COMMAND: + if (LOWORD(wParam) == 1000 && HIWORD(wParam) == CBN_SELCHANGE) + { + int a = (int)SendDlgItemMessage(hwnd,1000,CB_GETCURSEL,0,0); + if (a>=0) + { + const char *extlist = (const char *)GetWindowLongPtr(hwnd,GWLP_USERDATA); + if (extlist) + { + NSArray *fileTypes = extensionsFromList(extlist, + s_browse_extsel = (const char *)SendDlgItemMessage(hwnd,1000,CB_GETITEMDATA,a,0) + ); + if ([fileTypes count]>0) + { + NSSavePanel *par = (NSSavePanel*)[(NSView *)hwnd window]; + if ([par isKindOfClass:[NSSavePanel class]]) [(NSSavePanel *)par setAllowedFileTypes:fileTypes]; + } + [fileTypes release]; + } + } + } + return 0; + } + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + +static void restoreMenuForFocus() +{ + HWND h = GetFocus(); + HMENU menu = h ? GetMenu(h) : NULL; + if (!menu) + menu = SWELL_GetDefaultWindowMenu(); + if (menu) + SWELL_SetCurrentMenu(menu); +} + +// return true +bool BrowseForSaveFile(const char *text, const char *initialdir, const char *initialfile, const char *extlist, + char *fn, int fnsize) +{ + swell_autoarp auto_arp; + + NSSavePanel *panel = [NSSavePanel savePanel]; + NSMutableArray *fileTypes = extensionsFromList(extlist); + NSString *title=(NSString *)SWELL_CStringToCFString(text); + NSString *ifn=NULL, *idir = NULL; + + [panel setTitle:title]; + [panel setAccessoryView:nil]; + HWND av_parent = (HWND)panel; + + s_browse_extsel = NULL; + + if ([fileTypes count]>1) + { + const char *ar[2]={extlist,initialfile}; + av_parent = SWELL_CreateDialog(NULL,0,NULL,fileTypeChooseProc,(LPARAM)ar); + if (!av_parent) av_parent = (HWND)panel; + } + + HWND oh=NULL; + if (BFSF_Templ_dlgproc && BFSF_Templ_dlgid) // create a child dialog and set it to the panel + { + oh=SWELL_CreateDialog(BFSF_Templ_reshead, BFSF_Templ_dlgid, av_parent, BFSF_Templ_dlgproc, 0); + BFSF_Templ_dlgproc=0; + BFSF_Templ_dlgid=0; + } + if (av_parent != (HWND)panel) + { + if (oh) + { + RECT r1,r2; + GetClientRect(oh,&r1); + GetClientRect(av_parent,&r2); + + SetWindowPos(oh,NULL,0,r2.bottom,0,0,SWP_NOZORDER|SWP_NOSIZE); + if (r2.right < r1.right) r2.right=r1.right; + SetWindowPos(av_parent,NULL,0,0,r2.right,r2.bottom+r1.bottom,SWP_NOZORDER|SWP_NOMOVE); + ShowWindow(oh,SW_SHOWNA); + } + oh = av_parent; + + NSWindow *oldw = [(NSView *)av_parent window]; + [panel setAccessoryView:(NSView *)av_parent]; // we resized our accessory view + SendMessage(av_parent,WM_COMMAND,(CBN_SELCHANGE<<16) | 1000,0); + [(NSView *)av_parent setHidden:NO]; + [oldw release]; + } + else if ([fileTypes count]>0) + { + [panel setAllowedFileTypes:fileTypes]; + } + + if (initialfile && *initialfile && *initialfile != '.') + { + char s[2048]; + lstrcpyn_safe(s,initialfile,sizeof(s)); + + if (ext_valid_for_extlist(WDL_get_fileext(initialfile),extlist)>=0) + WDL_remove_fileext(s); + + char *p=s; + while (*p) p++; + while (p >= s && *p != '/') p--; + if (p>=s) + { + *p=0; + ifn=(NSString *)SWELL_CStringToCFString(p+1); + idir=(NSString *)SWELL_CStringToCFString(s[0]?s:"/"); + } + else + ifn=(NSString *)SWELL_CStringToCFString(s); + } + if (!idir && initialdir && *initialdir) + { + idir=(NSString *)SWELL_CStringToCFString(initialdir); + } + + HMENU hm=SWELL_GetDefaultModalWindowMenu(); + if (hm) hm=SWELL_DuplicateMenu(hm); + SWELL_SetCurrentMenu(hm); + + NSInteger result = [panel runModalForDirectory:idir file:ifn]; + restoreMenuForFocus(); + if (hm) DestroyMenu(hm); + + if (oh) SendMessage(oh,WM_DESTROY,0,0); + [panel setAccessoryView:nil]; + + bool rv = false; + + NSString *str; + if (result == NSOKButton && fn && fnsize > 0 && (str = [panel filename])) + { + SWELL_CFStringToCString(str,fn,fnsize); + if (fn[0]) + { + // this nonsense only seems to be necessary on 10.15 (and possibly future macOS versions?) + char tmp[256]; + + const NSUInteger nft = [fileTypes count]; + NSUInteger x = nft; + + const char *ext = WDL_get_fileext(fn); + if (*ext) + { + // see if extension is in list + ext++; + for (x = 0; x < nft; x ++) + { + NSString *s = [fileTypes objectAtIndex:x]; + if (s) + { + SWELL_CFStringToCString(s,tmp,sizeof(tmp)); + if (!stricmp(tmp,ext)) break; + } + } + } + + if (x == nft) + { + // not in list, apply default extension if specified, or first extension from list + if (initialfile && *initialfile == '.') + { + lstrcatn(fn,initialfile,fnsize); + } + else if (s_browse_extsel && *s_browse_extsel) + { + lstrcatn(fn,".",fnsize); + lstrcatn(fn,s_browse_extsel,fnsize); + } + else if (nft > 0) + { + NSString *s = [fileTypes objectAtIndex:0]; + if (s) + { + tmp[0] = '.'; + SWELL_CFStringToCString(s,tmp+1,sizeof(tmp)-1); + lstrcatn(fn,tmp,fnsize); + } + } + } + + rv = true; + } + } + + [title release]; + [fileTypes release]; + [idir release]; + [ifn release]; + + return rv; +} + +bool BrowseForDirectory(const char *text, const char *initialdir, char *fn, int fnsize) +{ + swell_autoarp auto_arp; + + NSOpenPanel *panel = [NSOpenPanel openPanel]; + NSString *title=(NSString *)SWELL_CStringToCFString(text); + NSString *idir=NULL; + + [panel setTitle:title]; + [panel setAllowsMultipleSelection:NO]; + [panel setCanChooseFiles:NO]; + [panel setCanCreateDirectories:YES]; + [panel setCanChooseDirectories:YES]; + [panel setResolvesAliases:YES]; + + HWND oh=NULL; + if (BFSF_Templ_dlgproc && BFSF_Templ_dlgid) // create a child dialog and set it to the panel + { + oh=SWELL_CreateDialog(BFSF_Templ_reshead, BFSF_Templ_dlgid, (HWND)panel, BFSF_Templ_dlgproc, 0); + BFSF_Templ_dlgproc=0; + BFSF_Templ_dlgid=0; + } + + if (initialdir && *initialdir) + { + idir=(NSString *)SWELL_CStringToCFString(initialdir); + } + + HMENU hm=SWELL_GetDefaultModalWindowMenu(); + if (hm) hm=SWELL_DuplicateMenu(hm); + SWELL_SetCurrentMenu(hm); + NSInteger result = [panel runModalForDirectory:idir file:nil types:nil]; + restoreMenuForFocus(); + if (hm) DestroyMenu(hm); + + if (oh) SendMessage(oh,WM_DESTROY,0,0); + [panel setAccessoryView:nil]; + + [idir release]; + [title release]; + + if (result != NSOKButton) return 0; + + NSArray *filesToOpen = [panel filenames]; + NSInteger count = [filesToOpen count]; + + if (!count) return 0; + + NSString *aFile = [filesToOpen objectAtIndex:0]; + if (!aFile) return 0; + if (fn && fnsize>0) + { + SWELL_CFStringToCString(aFile,fn,fnsize); + fn[fnsize-1]=0; + } + return 1; +} + + +char *BrowseForFiles(const char *text, const char *initialdir, + const char *initialfile, bool allowmul, const char *extlist) +{ + swell_autoarp auto_arp; + + NSOpenPanel *panel = [NSOpenPanel openPanel]; + NSString *title=(NSString *)SWELL_CStringToCFString(text); + NSString *ifn=NULL, *idir=NULL; + NSMutableArray *fileTypes = extensionsFromList(extlist); + + HWND oh=NULL; + if (BFSF_Templ_dlgproc && BFSF_Templ_dlgid) // create a child dialog and set it to the panel + { + oh=SWELL_CreateDialog(BFSF_Templ_reshead, BFSF_Templ_dlgid, (HWND)panel, BFSF_Templ_dlgproc, 0); + BFSF_Templ_dlgproc=0; + BFSF_Templ_dlgid=0; + } + + [panel setTitle:title]; + [panel setAllowsMultipleSelection:(allowmul?YES:NO)]; + [panel setCanChooseFiles:YES]; + [panel setCanChooseDirectories:NO]; + [panel setResolvesAliases:YES]; + + if (initialfile && *initialfile) + { + char s[2048]; + lstrcpyn_safe(s,initialfile,sizeof(s)); + char *p=s; + while (*p) p++; + while (p >= s && *p != '/') p--; + if (p>=s) + { + *p=0; + ifn=(NSString *)SWELL_CStringToCFString(p+1); + idir=(NSString *)SWELL_CStringToCFString(s[0]?s:"/"); + } + else + ifn=(NSString *)SWELL_CStringToCFString(s); + } + if (!idir && initialdir && *initialdir) + { + idir=(NSString *)SWELL_CStringToCFString(initialdir); + } + + HMENU hm=SWELL_GetDefaultModalWindowMenu(); + if (hm) hm=SWELL_DuplicateMenu(hm); + SWELL_SetCurrentMenu(hm); + + NSInteger result = [panel runModalForDirectory:idir file:ifn types:([fileTypes count]>0 ? fileTypes : nil)]; + + restoreMenuForFocus(); + if (hm) DestroyMenu(hm); + + if (oh) SendMessage(oh,WM_DESTROY,0,0); + [panel setAccessoryView:nil]; + + [ifn release]; + [idir release]; + + [fileTypes release]; + [title release]; + + if (result != NSOKButton) return 0; + + NSArray *filesToOpen = [panel filenames]; + const NSInteger count = [filesToOpen count]; + + if (!count) return 0; + + char fn[2048]; + if (count==1||!allowmul) + { + NSString *aFile = [filesToOpen objectAtIndex:0]; + if (!aFile) return 0; + SWELL_CFStringToCString(aFile,fn,sizeof(fn)); + fn[sizeof(fn)-1]=0; + char *ret=(char *)malloc(strlen(fn)+2); + memcpy(ret,fn,strlen(fn)); + ret[strlen(fn)]=0; + ret[strlen(fn)+1]=0; + return ret; + } + + size_t rsize=1; + char *ret=0; + for (NSInteger i=0; i 0 ? b1 : ret < 0 ? b3 : b2; +} + +static WDL_DLGRET color_okCancelProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + SetWindowLongPtr(hwndDlg,0,0); + return 0; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + SetWindowLongPtr(hwndDlg,0,1); + break; + case IDCANCEL: + SetWindowLongPtr(hwndDlg,0,2); + break; + } + break; + } + return 0; +} +static void okcancel_create(HWND hwnd, int f) +{ + SWELL_MakeSetCurParms(1.7,1.7,0,0,hwnd,false,false); + SWELL_MakeButton(1,"OK",IDOK,48,2,44,14,0); + SWELL_MakeButton(1,"Cancel",IDCANCEL,2,2,44,14,0); + SWELL_MakeSetCurParms(1.7,1.7,0,0,NULL,false,false); +} +static HWND makeOKcancel(HWND par) +{ + const char *dlgid = ""; + SWELL_DialogResourceIndex tmph = { + dlgid, "", + SWELL_DLG_WS_FLIPPED|SWELL_DLG_WS_NOAUTOSIZE|SWELL_DLG_WS_CHILD, + okcancel_create, + (int)(98*1.7),(int)(18*1.7), + NULL + }; + return SWELL_CreateDialog(&tmph,dlgid,par,color_okCancelProc,0); +} + +static bool ColorFromNSColor(NSColor *color, COLORREF *colout) +{ + if (!color) return false; + + CGFloat r,g,b; + NSColor *color2=[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + if (!color2) return false; + + [color2 getRed:&r green:&g blue:&b alpha:NULL]; + int rv=(int)(r*255.0 + 0.5); + int gv=(int)(g*255.0 + 0.5); + int bv=(int)(b*255.0 + 0.5); + if (rv<0)rv=0; else if (rv>255)rv=255; + if (gv<0)gv=0; else if (gv>255)gv=255; + if (bv<0)bv=0; else if (bv>255)bv=255; + + *colout = RGB(rv,gv,bv); + return true; +} + +bool SWELL_ChooseColor(HWND unused, COLORREF *a, int ncustom, COLORREF *custom) +{ + NSColorPanel *pan=[NSColorPanel sharedColorPanel]; + if (!pan||!a) return false; + + NSColor *bkcol=[NSColor colorWithCalibratedRed:GetRValue(*a)/255.0f green:GetGValue(*a)/255.0f blue:GetBValue(*a)/255.0f alpha:1.0f]; + [pan setColor:bkcol]; + [pan setShowsAlpha:NO]; + [pan setAccessoryView:nil]; + + HWND h = makeOKcancel((HWND)pan); + bool hadOK=!h; + + NSModalSession ctx=[NSApp beginModalSessionForWindow:pan]; + while ([NSApp runModalSession:ctx]==NSRunContinuesResponse && [pan isVisible]) + { + const LONG_PTR res = h ? GetWindowLongPtr(h,0) : 0; + if (res) { hadOK=res==1; break; } + Sleep(3); + } + + [NSApp endModalSession:ctx]; + + if (h) + { + SendMessage(h,WM_DESTROY,0,0); + [pan setAccessoryView:nil]; // will destroy h anyway + [pan close]; + } + + return hadOK && ColorFromNSColor([pan color],a); +} + +bool SWELL_ChooseFont(HWND unused, LOGFONT *lf) +{ + if (!lf) return false; + + NSFontPanel *pan = [NSFontPanel sharedFontPanel]; + int sz=lf->lfHeight > 0 ? lf->lfHeight : lf->lfHeight < 0 ? -lf->lfHeight : lf->lfWidth; + + char buf[1024]; + lstrcpyn_safe(buf,lf->lfFaceName,sizeof(buf)); + if (lf->lfWeight >= FW_BOLD) lstrcatn(buf," Bold",sizeof(buf)); + if (lf->lfItalic) lstrcatn(buf," Italic",sizeof(buf)); + + NSString *lbl=(NSString *)SWELL_CStringToCFString(buf); + NSFont *tmpfont = [NSFont fontWithName:lbl size:sz]; + if (!tmpfont) tmpfont = [NSFont systemFontOfSize:sz]; + [lbl release]; + + [pan setPanelFont:tmpfont isMultiple:NO]; + + [pan setAccessoryView:nil]; + HWND h = makeOKcancel((HWND)pan); + + bool hadOK=!h; + NSModalSession ctx=[NSApp beginModalSessionForWindow:pan]; + while ([NSApp runModalSession:ctx]==NSRunContinuesResponse && [pan isVisible]) + { + const LONG_PTR a = h ? GetWindowLongPtr(h,0) : 0; + if (a) { hadOK=a==1; break; } + Sleep(3); + } + + [NSApp endModalSession:ctx]; + + if (h) + { + SendMessage(h,WM_DESTROY,0,0); + [pan setAccessoryView:nil]; // will destroy h + [pan close]; + } + + if (!hadOK) return false; + + NSFont *newfont = [pan panelConvertFont:tmpfont]; + int newsz = (int) ([newfont pointSize]+0.5); + LOGFONT oldf=*lf; + if (newsz != sz) + { + lf->lfWidth=0; + lf->lfHeight = newsz; + } + + SWELL_CFStringToCString([newfont familyName],lf->lfFaceName, sizeof(lf->lfFaceName)); + SWELL_CFStringToCString([newfont displayName],buf,sizeof(buf)); + + lf->lfItalic = !! strstr(buf,"Italic"); + lf->lfWeight = strstr(buf,"Bold") ? FW_BOLD : FW_NORMAL; + + return memcmp(lf,&oldf,sizeof(LOGFONT)) != 0; +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-modstub-generic.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-modstub-generic.cpp new file mode 100644 index 000000000..c059cb710 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-modstub-generic.cpp @@ -0,0 +1,150 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. +*/ + +#ifdef SWELL_PROVIDED_BY_APP + +#define SWELL_API_DEFPARM(x) +#define SWELL_API_DEFINE(ret,func,parms) ret (*func) parms ; +extern "C" { +#include "swell.h" +}; + +// only include this file in projects that are linked to libSwell.so + +struct SWELL_CursorResourceIndex *SWELL_curmodule_cursorresource_head; +struct SWELL_DialogResourceIndex *SWELL_curmodule_dialogresource_head; +struct SWELL_MenuResourceIndex *SWELL_curmodule_menuresource_head; + +// define the functions + +static struct +{ + const char *name; + void **func; +} api_tab[]={ + +#undef _WDL_SWELL_H_API_DEFINED_ +#undef SWELL_API_DEFINE +#define SWELL_API_DEFINE(ret, func, parms) {#func, (void **)&func }, + +#include "swell.h" + +}; + +static int dummyFunc() { return 0; } + +static int doinit(void *(*GetFunc)(const char *name)) +{ + int errcnt=0; + for (int x = 0; x < (int)(sizeof(api_tab)/sizeof(api_tab[0])); x ++) + { + *api_tab[x].func=GetFunc(api_tab[x].name); + if (!*api_tab[x].func) + { + printf("SWELL API not found: %s\n",api_tab[x].name); + errcnt++; + *api_tab[x].func = (void*)&dummyFunc; + } + } + return errcnt; +} + +#ifdef SWELL_LOAD_SWELL_DYLIB + +static void *(*__loaded_getfunc)(const char *name)=NULL; + +class SwellAPPInitializer +{ +public: + SwellAPPInitializer() + { + void *(*SWELLAPI_GetFunc)(const char *name)=NULL; + char fn[4096]; + const int nSize=sizeof(fn)-64; + int sz=readlink("/proc/self/exe",fn,nSize); + if (sz<1) + { + Dl_info inf={0,}; + if (dladdr(&api_tab,&inf) && inf.dli_fname) + sz = (int) strlen(inf.dli_fname); + else + sz = 0; + } + if (sz>=nSize)sz=nSize-1; + fn[sz]=0; + char *p=fn; + while (*p) p++; + while (p >= fn && *p != '/') p--; + p++; + strcpy(p,p==fn ? "./libSwell.so" : "libSwell.so"); + + void *tmp = dlopen(fn,RTLD_LAZY); + if (!tmp) + { + printf("Error loading '%s': %s\n",fn,dlerror()); + exit(2); + } + const char *preload_fn = (const char *)dlsym(tmp,"SWELL_WANT_LOAD_LIBRARY"); + if (preload_fn && *preload_fn) + dlopen(preload_fn,RTLD_LAZY|RTLD_GLOBAL); + + *(void **)&SWELLAPI_GetFunc = dlsym(tmp,"SWELLAPI_GetFunc"); + + if (SWELLAPI_GetFunc && SWELLAPI_GetFunc(NULL)==(void*)0x100) + { + void (*set_app_main)(INT_PTR (*AppMain)(int msg, INT_PTR parm1, INT_PTR parm2)); + *(void **)&set_app_main = dlsym(tmp,"SWELL_set_app_main"); + if (set_app_main) set_app_main(SWELLAppMain); + + __loaded_getfunc = SWELLAPI_GetFunc; + if (doinit(SWELLAPI_GetFunc)) exit(1); + } + } + ~SwellAPPInitializer() + { + } +}; + +SwellAPPInitializer m_swell_appAPIinit; + +extern "C" __attribute__ ((visibility ("default"))) void *SWELLAPI_GetFunc(const char *name) +{ + if (__loaded_getfunc) return __loaded_getfunc(name); + return NULL; +} + +#else + +extern "C" __attribute__ ((visibility ("default"))) int SWELL_dllMain(HINSTANCE hInst, DWORD callMode, LPVOID _GetFunc) +{ + if (callMode == DLL_PROCESS_ATTACH) + { + if (!_GetFunc) return 0; + doinit((void *(*)(const char *)) _GetFunc); + } + + // this returning 1 allows DllMain to be called, if available + return 1; +} + +#endif + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-modstub.mm b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-modstub.mm new file mode 100644 index 000000000..2a7a5b58c --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-modstub.mm @@ -0,0 +1,95 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. +*/ + +#ifdef SWELL_PROVIDED_BY_APP + +#import +#import +#define SWELL_API_DEFPARM(x) +#define SWELL_API_DEFINE(ret,func,parms) ret (*func) parms ; +#include "swell.h" + +// only include this file in projects that are linked to swell.dylib + +struct SWELL_DialogResourceIndex *SWELL_curmodule_dialogresource_head; +struct SWELL_MenuResourceIndex *SWELL_curmodule_menuresource_head; + +// define the functions + +static struct +{ + const char *name; + void **func; +} api_tab[]={ + +#undef _WDL_SWELL_H_API_DEFINED_ +#undef SWELL_API_DEFINE +#define SWELL_API_DEFINE(ret, func, parms) {#func, (void **)&func }, + +#include "swell-functions.h" + +}; + +static int dummyFunc() { return 0; } + +class SwellAPPInitializer +{ +public: + SwellAPPInitializer() + { + void *(*SWELLAPI_GetFunc)(const char *name)=NULL; + void *(*send_msg)(id, SEL) = (void *(*)(id, SEL))objc_msgSend; + + id del = [NSApp delegate]; + if (del && [del respondsToSelector:@selector(swellGetAPPAPIFunc)]) + *(void **)&SWELLAPI_GetFunc = send_msg(del,@selector(swellGetAPPAPIFunc)); + + if (!SWELLAPI_GetFunc) NSLog(@"SWELL API provider not found\n"); + else if (SWELLAPI_GetFunc(NULL)!=(void*)0x100) + { + NSLog(@"SWELL API provider returned incorrect version\n"); + SWELLAPI_GetFunc=0; + } + + int x; + for (x = 0; x < sizeof(api_tab)/sizeof(api_tab[0]); x ++) + { + *api_tab[x].func=SWELLAPI_GetFunc?SWELLAPI_GetFunc(api_tab[x].name):0; + if (!*api_tab[x].func) + { + if (SWELLAPI_GetFunc) NSLog(@"SWELL API not found: %s\n",api_tab[x].name); + *api_tab[x].func = (void*)&dummyFunc; + } + } + } + ~SwellAPPInitializer() + { + } +}; + +SwellAPPInitializer m_swell_appAPIinit; + +extern "C" __attribute__ ((visibility ("default"))) int SWELL_dllMain(HINSTANCE hInst, DWORD callMode, LPVOID _GetFunc) +{ + // this returning 1 allows DllMain to be called, if available + return 1; +} + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-types.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-types.h new file mode 100644 index 000000000..311693c57 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-types.h @@ -0,0 +1,1459 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + SWELL provides _EXTREMELY BASIC_ win32 wrapping for OS X and maybe other platforms. + + */ + +#ifndef _WDL_SWELL_H_TYPES_DEFINED_ +#define _WDL_SWELL_H_TYPES_DEFINED_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +#include +#endif + +#include +typedef intptr_t INT_PTR, *PINT_PTR, LONG_PTR, *PLONG_PTR; +typedef uintptr_t UINT_PTR, *PUINT_PTR, ULONG_PTR, *PULONG_PTR, DWORD_PTR, *PDWORD_PTR; + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef S_OK +#define S_OK 0 +#endif +#ifndef E_FAIL +#define E_FAIL (-1) +#endif + + +// the byte ordering of RGB() etc is different than on win32 +#define RGB(r,g,b) (((r)<<16)|((g)<<8)|(b)) +#define GetRValue(x) (((x)>>16)&0xff) +#define GetGValue(x) (((x)>>8)&0xff) +#define GetBValue(x) ((x)&0xff) + +// basic platform compat defines +#ifndef stricmp +#define stricmp(x,y) strcasecmp(x,y) +#endif +#ifndef strnicmp +#define strnicmp(x,y,z) strncasecmp(x,y,z) +#endif + +#define DeleteFile(x) (!unlink(x)) +#define MoveFile(x,y) (!rename(x,y)) +#define GetCurrentDirectory(sz,buf) (!getcwd(buf,sz)) +#define SetCurrentDirectory(buf) (!chdir(buf)) +#define CreateDirectory(x,y) (!mkdir((x),0755)) + +#ifndef wsprintf +#define wsprintf sprintf +#endif + +#ifndef LOWORD +#define MAKEWORD(a, b) ((unsigned short)(((BYTE)(a)) | ((WORD)((BYTE)(b))) << 8)) +#define MAKELONG(a, b) ((int)(((unsigned short)(a)) | ((DWORD)((unsigned short)(b))) << 16)) +#define MAKEWPARAM(l, h) (WPARAM)MAKELONG(l, h) +#define MAKELPARAM(l, h) (LPARAM)MAKELONG(l, h) +#define MAKELRESULT(l, h) (LRESULT)MAKELONG(l, h) +#define LOWORD(l) ((unsigned short)(l)) +#define HIWORD(l) ((unsigned short)(((unsigned int)(l) >> 16) & 0xFFFF)) +#define LOBYTE(w) ((BYTE)(w)) +#define HIBYTE(w) ((BYTE)(((unsigned short)(w) >> 8) & 0xFF)) +#endif + +#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) +#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) + +#define UNREFERENCED_PARAMETER(P) (P) +#define _T(T) T + +#define CallWindowProc(A,B,C,D,E) ((WNDPROC)A)(B,C,D,E) +#define OffsetRect WinOffsetRect //to avoid OSX's OffsetRect function +#define SetRect WinSetRect //to avoid OSX's SetRect function +#define UnionRect WinUnionRect +#define IntersectRect WinIntersectRect + + +#define MAX_PATH 1024 + + +#if !defined(max) && !defined(WDL_NO_DEFINE_MINMAX) && !defined(NOMINMAX) +#define max(x,y) ((x)<(y)?(y):(x)) +#define min(x,y) ((x)<(y)?(x):(y)) +#endif + +// SWELLAPP stuff (swellappmain.mm) +#ifdef __cplusplus +extern "C" { +#endif +INT_PTR SWELLAppMain(int msg, INT_PTR parm1, INT_PTR parm2); // to be implemented by app (if using swellappmain.mm) +#ifdef __cplusplus +}; +#endif + +#define SWELLAPP_ONLOAD 0x0001 // initialization of app vars etc +#define SWELLAPP_LOADED 0x0002 // create dialogs etc +#define SWELLAPP_DESTROY 0x0003 // about to destroy (cleanup etc) +#define SWELLAPP_SHOULDDESTROY 0x0004 // return 0 to allow app to terminate, >0 to prevent + +#define SWELLAPP_OPENFILE 0x0050 // parm1= (const char *)string, return >0 if allowed +#define SWELLAPP_NEWFILE 0x0051 // new file, return >0 if allowed +#define SWELLAPP_SHOULDOPENNEWFILE 0x0052 // allow opening new file? >0 if allowed + +#define SWELLAPP_ONCOMMAND 0x0099 // parm1 = (int) command ID, parm2 = (id) sender +#define SWELLAPP_PROCESSMESSAGE 0x0100 // parm1=(MSG *)msg (loosely), parm2= (NSEvent *) the event . return >0 to eat + +#define SWELLAPP_ACTIVATE 0x1000 // parm1 = (bool) isactive. return nonzero to prevent WM_ACTIVATEAPP from being broadcasted +// + + +#if defined(__APPLE__) && !defined(SWELL_USE_OBJC_BOOL) + #include + // this may be safe to always use, but for now only use when using a very very modern SDK + #ifdef MAC_OS_X_VERSION_10_16 + #define SWELL_USE_OBJC_BOOL + #endif +#endif + +// basic types +#ifdef SWELL_USE_OBJC_BOOL + #include + #ifndef __OBJC__ + #undef NO + #undef YES + #undef Nil + #undef nil + #endif +#else + typedef signed char BOOL; +#endif +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned int DWORD; +typedef DWORD COLORREF; +typedef unsigned int UINT; +typedef int INT; + +typedef ULONG_PTR WPARAM; +typedef LONG_PTR LPARAM; +typedef LONG_PTR LRESULT; + + +typedef void *LPVOID, *PVOID; + +#if defined(__APPLE__) && !defined(__LP64__) +typedef signed long HRESULT; +typedef signed long LONG; +typedef unsigned long ULONG; +#else +typedef signed int HRESULT; +typedef signed int LONG; +typedef unsigned int ULONG; +#endif + +typedef short SHORT; +typedef int *LPINT; +typedef char CHAR; +typedef char *LPSTR, *LPTSTR; +typedef const char *LPCSTR, *LPCTSTR; + +#define __int64 long long // define rather than typedef, for unsigned __int64 support + +typedef unsigned __int64 ULONGLONG; + +typedef union { + unsigned long long QuadPart; + struct { + #ifdef __ppc__ + DWORD HighPart; + DWORD LowPart; + #else + DWORD LowPart; + DWORD HighPart; + #endif + }; +} ULARGE_INTEGER; + + +typedef struct HWND__ *HWND; +typedef struct HMENU__ *HMENU; +typedef void *HANDLE, *HINSTANCE, *HDROP; +typedef void *HGLOBAL; + +typedef void (*TIMERPROC)(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); + +typedef struct +{ + LONG x,y; +} POINT, *LPPOINT; + + +typedef struct +{ + SHORT x; + SHORT y; +} POINTS; + + +typedef struct +{ + LONG left,top, right, bottom; +} RECT, *LPRECT; + + +typedef struct { + unsigned char fVirt; + unsigned short key,cmd; +} ACCEL, *LPACCEL; + + +typedef struct { + DWORD dwLowDateTime; + DWORD dwHighDateTime; +} FILETIME; + +typedef struct _GUID { + unsigned int Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +} GUID; + +typedef struct { + HWND hwnd; + UINT message; + WPARAM wParam; + LPARAM lParam; + DWORD time; + POINT pt; +} MSG, *LPMSG; + +typedef struct HDC__ *HDC; +typedef struct HCURSOR__ *HCURSOR; +typedef struct HRGN__ *HRGN; + +typedef struct HGDIOBJ__ *HBITMAP; +typedef struct HGDIOBJ__ *HICON; +typedef struct HGDIOBJ__ *HGDIOBJ; +typedef struct HGDIOBJ__ *HBRUSH; +typedef struct HGDIOBJ__ *HPEN; +typedef struct HGDIOBJ__ *HFONT; + + +typedef struct +{ + HWND hwndFrom; + UINT_PTR idFrom; + UINT code; +} NMHDR, *LPNMHDR; + + +typedef struct { + NMHDR hdr; + DWORD_PTR dwItemSpec; + DWORD_PTR dwItemData; + POINT pt; + DWORD dwHitInfo; +} NMMOUSE, *LPNMMOUSE; +typedef NMMOUSE NMCLICK; +typedef LPNMMOUSE LPNMCLICK; + +typedef struct +{ + int mask, fmt,cx; + char *pszText; + int cchTextMax, iSubItem; +} LVCOLUMN; +typedef struct +{ + int mask, iItem, iSubItem, state, stateMask; + char *pszText; + int cchTextMax, iImage; + LPARAM lParam; +} LVITEM; + +typedef int (*PFNLVCOMPARE)(LPARAM, LPARAM, LPARAM); + +typedef struct HIMAGELIST__ *HIMAGELIST; + +typedef struct +{ + POINT pt; + UINT flags; + int iItem; + int iSubItem; // this is was NOT in win95. valid only for LVM_SUBITEMHITTEST +} LVHITTESTINFO, *LPLVHITTESTINFO; + + +typedef struct +{ + NMHDR hdr; + int iItem; + int iSubItem; + UINT uNewState; + UINT uOldState; + UINT uChanged; + POINT ptAction; + LPARAM lParam; +} NMLISTVIEW, *LPNMLISTVIEW; + +typedef struct +{ + NMHDR hdr; + LVITEM item; +} NMLVDISPINFO, *LPNMLVDISPINFO; + +typedef struct +{ + UINT mask; + int cxy; + char* pszText; + HBITMAP hbm; + int cchTextMax; + int fmt; + LPARAM lParam; + int iImage; + int iOrder; + UINT type; + void *pvFilter; + UINT state; +} HDITEM, *LPHDITEM; + +typedef struct TCITEM +{ + UINT mask; + DWORD dwState; + DWORD dwStateMask; + char *pszText; + int cchTextMax; + int iImage; + + LPARAM lParam; +} TCITEM, *LPTCITEM; + +typedef struct tagDRAWITEMSTRUCT { + UINT CtlType; + UINT CtlID; + UINT itemID; + UINT itemAction; + UINT itemState; + HWND hwndItem; + HDC hDC; + RECT rcItem; + DWORD_PTR itemData; +} DRAWITEMSTRUCT, *PDRAWITEMSTRUCT, *LPDRAWITEMSTRUCT; + +typedef struct tagBITMAP { + LONG bmWidth; + LONG bmHeight; + LONG bmWidthBytes; + WORD bmPlanes; + WORD bmBitsPixel; + LPVOID bmBits; +} BITMAP, *PBITMAP, *LPBITMAP; +#define ODT_MENU 1 +#define ODT_LISTBOX 2 +#define ODT_COMBOBOX 3 +#define ODT_BUTTON 4 + +#define ODS_SELECTED 0x0001 + + + + +typedef struct +{ + DWORD cbSize; + HWND hWnd; + UINT uID; + UINT uFlags; + UINT uCallbackMessage; + HICON hIcon; + CHAR szTip[64]; +} NOTIFYICONDATA,*PNOTIFYICONDATA, *LPNOTIFYICONDATA; + + +#define NIM_ADD 0x00000000 +#define NIM_MODIFY 0x00000001 +#define NIM_DELETE 0x00000002 + +#define NIF_MESSAGE 0x00000001 +#define NIF_ICON 0x00000002 +#define NIF_TIP 0x00000004 + + + +typedef struct HTREEITEM__ *HTREEITEM; + +#define TVIF_TEXT 0x0001 +#define TVIF_IMAGE 0x0002 +#define TVIF_PARAM 0x0004 +#define TVIF_STATE 0x0008 +#define TVIF_HANDLE 0x0010 +#define TVIF_SELECTEDIMAGE 0x0020 +#define TVIF_CHILDREN 0x0040 + +#define TVIS_SELECTED 0x0002 +#define TVIS_DROPHILITED 0x0008 +#define TVIS_BOLD 0x0010 +#define TVIS_EXPANDED 0x0020 + +#define TVE_COLLAPSE 0x0001 +#define TVE_EXPAND 0x0002 +#define TVE_TOGGLE 0x0003 + +#define TVN_FIRST (0U-400U) // treeview +#define TVN_SELCHANGED (TVN_FIRST-2) +#define TVN_ITEMEXPANDING (TVN_FIRST-5) + +// swell-extension: WM_MOUSEMOVE set via capture in TVN_BEGINDRAG can return: +// -1 = drag not possible +// -2 = destination at end of list +// (HTREEITEM) = will end up before this item +#define TVN_BEGINDRAG (TVN_FIRST-7) + +#define TVI_ROOT ((HTREEITEM)0xFFFF0000) +#define TVI_FIRST ((HTREEITEM)0xFFFF0001) +#define TVI_LAST ((HTREEITEM)0xFFFF0002) +#define TVI_SORT ((HTREEITEM)0xFFFF0003) + +#define TVHT_NOWHERE 0x0001 +#define TVHT_ONITEMICON 0x0002 +#define TVHT_ONITEMLABEL 0x0004 +#define TVHT_ONITEM (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON) +#define TVHT_ONITEMINDENT 0x0008 +#define TVHT_ONITEMBUTTON 0x0010 +#define TVHT_ONITEMRIGHT 0x0020 +#define TVHT_ONITEMSTATEICON 0x0040 + +#define TVHT_ABOVE 0x0100 +#define TVHT_BELOW 0x0200 +#define TVHT_TORIGHT 0x0400 +#define TVHT_TOLEFT 0x0800 + +typedef struct { + UINT mask; + HTREEITEM hItem; + UINT state; + UINT stateMask; + char *pszText; + int cchTextMax; + int iImage; + int iSelectedImage; + int cChildren; + LPARAM lParam; +} TVITEM, TV_ITEM, *LPTVITEM, *LPTV_ITEM; + +typedef struct { + HTREEITEM hParent; + HTREEITEM hInsertAfter; + TVITEM item; +} TVINSERTSTRUCT, *LPTVINSERTSTRUCT, TV_INSERTSTRUCT, *LPTV_INSERTSTRUCT; + +typedef struct { + POINT pt; + UINT flags; + HTREEITEM hItem; +} TVHITTESTINFO, *LPTVHITTESTINFO; + +typedef struct { + NMHDR hdr; + UINT action; + TVITEM itemOld; + TVITEM itemNew; + POINT ptDrag; +} NMTREEVIEW, *LPNMTREEVIEW; + + +typedef struct +{ + unsigned int cbSize, fMask, fType, fState, wID; + HMENU hSubMenu; + HICON hbmpChecked,hbmpUnchecked; + DWORD_PTR dwItemData; + char *dwTypeData; + int cch; + HBITMAP hbmpItem; +} MENUITEMINFO; + +#define SetMenuDefaultItem(a,b,c) do { if ((a)||(b)||(c)) { } } while(0) + +typedef struct { + POINT ptReserved, ptMaxSize, ptMaxPosition, ptMinTrackSize, ptMaxTrackSize; +} MINMAXINFO, *LPMINMAXINFO; + + +typedef struct +{ + int lfHeight, lfWidth, lfEscapement,lfOrientation, lfWeight; + char lfItalic, lfUnderline, lfStrikeOut, lfCharSet, lfOutPrecision, lfClipPrecision, + lfQuality, lfPitchAndFamily; + char lfFaceName[32]; +} LOGFONT; +typedef struct +{ + LONG tmHeight; + LONG tmAscent; + LONG tmDescent; + LONG tmInternalLeading; + LONG tmAveCharWidth; + // todo: implement rest +} TEXTMETRIC; + +typedef struct { + HDC hdc; + BOOL fErase; + RECT rcPaint; +} PAINTSTRUCT; + +typedef struct +{ + UINT cbSize; + UINT fMask; + int nMin; + int nMax; + UINT nPage; + int nPos; + int nTrackPos; +} SCROLLINFO, *LPSCROLLINFO; + +typedef struct +{ + DWORD styleOld; + DWORD styleNew; +} STYLESTRUCT, *LPSTYLESTRUCT; + +typedef struct _DROPFILES { + DWORD pFiles; // offset of file list + POINT pt; // drop point (client coords) + BOOL fNC; // is it on NonClient area + // and pt is in screen coords + BOOL fWide; // WIDE character switch +} DROPFILES, *LPDROPFILES; + + +typedef struct +{ + HWND hwnd; + HWND hwndInsertAfter; + int x; + int y; + int cx; + int cy; + UINT flags; +} WINDOWPOS, *LPWINDOWPOS, *PWINDOWPOS; + +typedef struct +{ + RECT rgrc[3]; + PWINDOWPOS lppos; +} NCCALCSIZE_PARAMS, *LPNCCALCSIZE_PARAMS; + + + +typedef INT_PTR (*DLGPROC)(HWND, UINT, WPARAM, LPARAM); +typedef LRESULT (*WNDPROC)(HWND, UINT, WPARAM, LPARAM); + + + +#define GF_BEGIN 1 +#define GF_INERTIA 2 +#define GF_END 4 + +#define GID_BEGIN 1 +#define GID_END 2 +#define GID_ZOOM 3 +#define GID_PAN 4 +#define GID_ROTATE 5 +#define GID_TWOFINGERTAP 6 +#define GID_ROLLOVER 7 + +typedef struct tagGESTUREINFO +{ + UINT cbSize; + DWORD dwFlags; + DWORD dwID; + HWND hwndTarget; + POINTS ptsLocation; + DWORD dwInstanceID; + DWORD dwSequenceID; + ULONGLONG ullArguments; + UINT cbExtraArgs; +} GESTUREINFO; + +// not using this stuff yet +#define GC_PAN 1 +#define GC_PAN_WITH_SINGLE_FINGER_VERTICALLY 2 +#define GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY 4 + +typedef struct tagGESTURECONFIG +{ + DWORD dwID; + DWORD dwWant; + DWORD dwBlock; +} GESTURECONFIG; + + + +#ifndef WINAPI +#define WINAPI +#endif + +#ifndef CALLBACK +#define CALLBACK +#endif + + +typedef BOOL (*PROPENUMPROCEX)(HWND hwnd, const char *lpszString, HANDLE hData, LPARAM lParam); + +// swell specific type +typedef HWND (*SWELL_ControlCreatorProc)(HWND parent, const char *cname, int idx, const char *classname, int style, int x, int y, int w, int h); + +#define DLL_PROCESS_DETACH 0 +#define DLL_PROCESS_ATTACH 1 + +// if the user implements this (and links with swell-modstub[-generic], this will get called for DLL_PROCESS_[AT|DE]TACH +#ifdef __cplusplus +extern "C" { +#endif +__attribute__ ((visibility ("default"))) BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved); +#ifdef __cplusplus +}; +#endif + +/* + ** win32 specific constants + */ +#define MB_OK 0 +#define MB_OKCANCEL 1 +#define MB_ABORTRETRYIGNORE 2 +#define MB_YESNOCANCEL 3 +#define MB_YESNO 4 +#define MB_RETRYCANCEL 5 + +#define MB_DEFBUTTON1 0 +#define MB_DEFBUTTON2 0x00000100 +#define MB_DEFBUTTON3 0x00000200 + +#define MB_ICONERROR 0 +#define MB_ICONSTOP 0 +#define MB_ICONINFORMATION 0 +#define MB_ICONWARNING 0 +#define MB_ICONQUESTION 0 +#define MB_TOPMOST 0 +#define MB_ICONEXCLAMATION 0 + +#define IDOK 1 +#define IDCANCEL 2 +#define IDABORT 3 +#define IDRETRY 4 +#define IDIGNORE 5 +#define IDYES 6 +#define IDNO 7 + +#define GW_HWNDFIRST 0 +#define GW_HWNDLAST 1 +#define GW_HWNDNEXT 2 +#define GW_HWNDPREV 3 +#define GW_OWNER 4 +#define GW_CHILD 5 + +#define GWL_HWNDPARENT (-25) +#define GWL_USERDATA (-21) +#define GWL_ID (-12) +#define GWL_STYLE (-16) // only supported for BS_ for now I think +#define GWL_EXSTYLE (-20) +#define GWL_WNDPROC (-4) +#define DWL_DLGPROC (-8) + +#define SWELL_NOT_WS_VISIBLE ((int)0x80000000) +// oops these don't match real windows +#define WS_CHILDWINDOW (WS_CHILD) +#define WS_CHILD 0x40000000L +#define WS_DISABLED 0x08000000L +#define WS_CLIPSIBLINGS 0x04000000L +#define WS_VISIBLE 0x02000000L // only used by GetWindowLong(GWL_STYLE) -- not settable +#define WS_CAPTION 0x00C00000L +#define WS_VSCROLL 0x00200000L +#define WS_HSCROLL 0x00100000L +#define WS_SYSMENU 0x00080000L +#define WS_THICKFRAME 0x00040000L +#define WS_GROUP 0x00020000L +#define WS_TABSTOP 0x00010000L + +#define TVS_DISABLEDRAGDROP 0x10 + +#define WS_BORDER 0 // ignored for now + +#define WM_CTLCOLORMSGBOX 0x0132 +#define WM_CTLCOLOREDIT 0x0133 +#define WM_CTLCOLORLISTBOX 0x0134 +#define WM_CTLCOLORBTN 0x0135 +#define WM_CTLCOLORDLG 0x0136 +#define WM_CTLCOLORSCROLLBAR 0x0137 +#define WM_CTLCOLORSTATIC 0x0138 + +#define CB_ADDSTRING 0x0143 +#define CB_DELETESTRING 0x0144 +#define CB_GETCOUNT 0x0146 +#define CB_GETCURSEL 0x0147 +#define CB_GETLBTEXT 0x0148 +#define CB_GETLBTEXTLEN 0x0149 +#define CB_INSERTSTRING 0x014A +#define CB_RESETCONTENT 0x014B +#define CB_FINDSTRING 0x014C +#define CB_SETCURSEL 0x014E +#define CB_GETITEMDATA 0x0150 +#define CB_SETITEMDATA 0x0151 +#define CB_FINDSTRINGEXACT 0x0158 +#define CB_INITSTORAGE 0x0161 + +#define LB_ADDSTRING 0x0180 // oops these don't all match real windows, todo fix (maybe) +#define LB_INSERTSTRING 0x0181 +#define LB_DELETESTRING 0x0182 +#define LB_GETTEXT 0x0183 +#define LB_RESETCONTENT 0x0184 +#define LB_SETSEL 0x0185 +#define LB_SETCURSEL 0x0186 +#define LB_GETSEL 0x0187 +#define LB_GETCURSEL 0x0188 +#define LB_GETTEXTLEN 0x018A +#define LB_GETCOUNT 0x018B +#define LB_GETSELCOUNT 0x0190 +#define LB_GETITEMDATA 0x0199 +#define LB_SETITEMDATA 0x019A +#define LB_FINDSTRINGEXACT 0x01A2 + +#define TBM_GETPOS (WM_USER) +#define TBM_SETTIC (WM_USER+4) +#define TBM_SETPOS (WM_USER+5) +#define TBM_SETRANGE (WM_USER+6) +#define TBM_SETSEL (WM_USER+10) + +#define PBM_SETRANGE (WM_USER+1) +#define PBM_SETPOS (WM_USER+2) +#define PBM_DELTAPOS (WM_USER+3) + +#define BM_GETCHECK 0x00F0 +#define BM_SETCHECK 0x00F1 +#define BM_GETIMAGE 0x00F6 +#define BM_SETIMAGE 0x00F7 +#define IMAGE_BITMAP 0 +#define IMAGE_ICON 1 + +#define NM_FIRST (0U- 0U) // generic to all controls +#define NM_LAST (0U- 99U) +#define NM_CLICK (NM_FIRST-2) // uses NMCLICK struct +#define NM_DBLCLK (NM_FIRST-3) +#define NM_RCLICK (NM_FIRST-5) // uses NMCLICK struct +#define NM_CUSTOMDRAW (NM_FIRST-12) + + +#define LVSIL_STATE 1 +#define LVSIL_SMALL 2 + +#define LVIR_BOUNDS 0 +#define LVIR_ICON 1 +#define LVIR_LABEL 2 +#define LVIR_SELECTBOUNDS 3 + + +#define LVHT_NOWHERE 0x0001 +#define LVHT_ONITEMICON 0x0002 +#define LVHT_ONITEMLABEL 0x0004 +#define LVHT_ONITEMSTATEICON 0x0008 +#define LVHT_ONITEM (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON) + +#define LVHT_ABOVE 0x0010 +#define LVHT_BELOW 0x0020 +#define LVHT_TORIGHT 0x0040 +#define LVHT_TOLEFT 0x0080 + +#define LVCF_FMT 1 +#define LVCF_WIDTH 2 +#define LVCF_TEXT 4 + +#define LVCFMT_LEFT 0 +#define LVCFMT_RIGHT 1 +#define LVCFMT_CENTER 2 + +#define LVIF_TEXT 1 +#define LVIF_IMAGE 2 +#define LVIF_PARAM 4 +#define LVIF_STATE 8 + +#define LVIS_SELECTED 1 +#define LVIS_FOCUSED 2 +#define LVNI_SELECTED 1 +#define LVNI_FOCUSED 2 +#define INDEXTOSTATEIMAGEMASK(x) ((x)<<16) +#define LVIS_STATEIMAGEMASK (255<<16) + +#define LVN_FIRST (0U-100U) // listview +#define LVN_LAST (0U-199U) +#define LVN_BEGINDRAG (LVN_FIRST-9) +#define LVN_COLUMNCLICK (LVN_FIRST-8) +#define LVN_ITEMCHANGED (LVN_FIRST-1) +#define LVN_ODFINDITEM (LVN_FIRST-52) +#define LVN_GETDISPINFO (LVN_FIRST-50) + +#define LVS_EX_GRIDLINES 0x01 +#define LVS_EX_HEADERDRAGDROP 0x10 +#define LVS_EX_FULLROWSELECT 0x20 // ignored for now (enabled by default on OSX) + +#define HDI_FORMAT 0x4 +#define HDF_SORTUP 0x0400 +#define HDF_SORTDOWN 0x0200 + +#define TCIF_TEXT 0x0001 +#define TCIF_IMAGE 0x0002 +#define TCIF_PARAM 0x0008 +//#define TCIF_STATE 0x0010 + + + +#define TCN_FIRST (0U-550U) // tab control +#define TCN_LAST (0U-580U) +#define TCN_SELCHANGE (TCN_FIRST - 1) + + +#define BS_AUTOCHECKBOX 0x00000003L +#define BS_AUTO3STATE 0x00000006L +#define BS_AUTORADIOBUTTON 0x00000009L +#define BS_OWNERDRAW 0x0000000BL +#define BS_BITMAP 0x00000080L + + + +#define BST_CHECKED 1 +#define BST_UNCHECKED 0 +#define BST_INDETERMINATE 2 + +// note: these differ in values from their win32 counterparts, because we got them +// wrong to begin with, and we'd like to keep backwards compatability for things compiled +// against an old swell.h (and using the SWELL API via an exported mechanism, i.e. third party +// plug-ins). +#define SW_HIDE 0 +#define SW_SHOWNA 1 // 8 on win32 +#define SW_SHOW 2 // 1 on win32 +#define SW_SHOWMINIMIZED 3 // 2 on win32 + +// aliases (todo implement these as needed) +#define SW_SHOWNOACTIVATE SW_SHOWNA +#define SW_NORMAL SW_SHOW +#define SW_SHOWNORMAL SW_SHOW +#define SW_SHOWMAXIMIZED SW_SHOW +#define SW_SHOWDEFAULT SW_SHOWNORMAL +#define SW_RESTORE SW_SHOWNA + +#define SWP_NOMOVE 1 +#define SWP_NOSIZE 2 +#define SWP_NOZORDER 4 +#define SWP_NOACTIVATE 8 +#define SWP_SHOWWINDOW 16 +#define SWP_FRAMECHANGED 32 +#define SWP_NOCOPYBITS 0 +#define HWND_TOP ((HWND)0) +#define HWND_BOTTOM ((HWND)1) +#define HWND_TOPMOST ((HWND)-1) +#define HWND_NOTOPMOST ((HWND)-2) + +// most of these are ignored, actually, but TPM_NONOTIFY and TPM_RETURNCMD are now used +#define TPM_LEFTBUTTON 0x0000L +#define TPM_RIGHTBUTTON 0x0002L +#define TPM_LEFTALIGN 0x0000L +#define TPM_CENTERALIGN 0x0004L +#define TPM_RIGHTALIGN 0x0008L +#define TPM_TOPALIGN 0x0000L +#define TPM_VCENTERALIGN 0x0010L +#define TPM_BOTTOMALIGN 0x0020L +#define TPM_HORIZONTAL 0x0000L /* Horz alignment matters more */ +#define TPM_VERTICAL 0x0040L /* Vert alignment matters more */ +#define TPM_NONOTIFY 0x0080L /* Don't send any notification msgs */ +#define TPM_RETURNCMD 0x0100L + +#define MIIM_ID 1 +#define MIIM_STATE 2 +#define MIIM_TYPE 4 +#define MIIM_SUBMENU 8 +#define MIIM_DATA 16 +#define MIIM_BITMAP 0x80 + +#define MF_ENABLED 0 +#define MF_GRAYED 1 +#define MF_DISABLED 2 +#define MF_STRING 0 +#define MF_BITMAP 4 +#define MF_UNCHECKED 0 +#define MF_CHECKED 8 +#define MF_POPUP 0x10 +#define MF_BYCOMMAND 0 +#define MF_BYPOSITION 0x400 +#define MF_SEPARATOR 0x800 + +#define MFT_STRING MF_STRING +#define MFT_BITMAP MF_BITMAP +#define MFT_SEPARATOR MF_SEPARATOR +#define MFT_RADIOCHECK 0x200 + +#define MFS_GRAYED (MF_GRAYED|MF_DISABLED) +#define MFS_DISABLED MFS_GRAYED +#define MFS_CHECKED MF_CHECKED +#define MFS_ENABLED MF_ENABLED +#define MFS_UNCHECKED MF_UNCHECKED + +#define EN_SETFOCUS 0x0100 +#define EN_KILLFOCUS 0x0200 +#define EN_CHANGE 0x0300 +#define STN_CLICKED 0 +#define STN_DBLCLK 1 +#define WM_CREATE 0x0001 +#define WM_DESTROY 0x0002 +#define WM_MOVE 0x0003 +#define WM_SIZE 0x0005 +#define WM_ACTIVATE 0x0006 +#define WM_SETREDRAW 0x000B // implemented on macOS NSTableViews, maybe elsewhere? +#define WM_SETTEXT 0x000C // not implemented on OSX, used internally on Linux +#define WM_PAINT 0x000F +#define WM_CLOSE 0x0010 +#define WM_ERASEBKGND 0x0014 +#define WM_SHOWWINDOW 0x0018 +#define WM_ACTIVATEAPP 0x001C +#define WM_SETCURSOR 0x0020 +#define WM_MOUSEACTIVATE 0x0021 +#define WM_GETMINMAXINFO 0x0024 +#define WM_DRAWITEM 0x002B +#define WM_SETFONT 0x0030 +#define WM_GETFONT 0x0031 +#define WM_GETOBJECT 0x003D // implemented differently than win32 -- see virtwnd/virtwnd-nsaccessibility.mm +#define WM_COPYDATA 0x004A +#define WM_NOTIFY 0x004E +#define WM_CONTEXTMENU 0x007B +#define WM_STYLECHANGED 0x007D +#define WM_DISPLAYCHANGE 0x007E +#define WM_NCDESTROY 0x0082 +#define WM_NCCALCSIZE 0x0083 +#define WM_NCHITTEST 0x0084 +#define WM_NCPAINT 0x0085 +#define WM_NCMOUSEMOVE 0x00A0 +#define WM_NCLBUTTONDOWN 0x00A1 +#define WM_NCLBUTTONUP 0x00A2 +#define WM_NCLBUTTONDBLCLK 0x00A3 +#define WM_NCRBUTTONDOWN 0x00A4 +#define WM_NCRBUTTONUP 0x00A5 +#define WM_NCRBUTTONDBLCLK 0x00A6 +#define WM_NCMBUTTONDOWN 0x00A7 +#define WM_NCMBUTTONUP 0x00A8 +#define WM_NCMBUTTONDBLCLK 0x00A9 +#define WM_KEYFIRST 0x0100 +#define WM_KEYDOWN 0x0100 +#define WM_KEYUP 0x0101 +#define WM_CHAR 0x0102 +#define WM_DEADCHAR 0x0103 +#define WM_SYSKEYDOWN 0x0104 +#define WM_SYSKEYUP 0x0105 +#define WM_SYSCHAR 0x0106 +#define WM_SYSDEADCHAR 0x0107 +#define WM_KEYLAST 0x0108 +#define WM_INITDIALOG 0x0110 +#define WM_COMMAND 0x0111 +#define WM_SYSCOMMAND 0x0112 +#define WM_TIMER 0x0113 +#define WM_HSCROLL 0x0114 +#define WM_VSCROLL 0x0115 +#define WM_INITMENUPOPUP 0x0117 +#define WM_GESTURE 0x0119 +#define WM_MOUSEFIRST 0x0200 +#define WM_MOUSEMOVE 0x0200 +#define WM_LBUTTONDOWN 0x0201 +#define WM_LBUTTONUP 0x0202 +#define WM_LBUTTONDBLCLK 0x0203 +#define WM_RBUTTONDOWN 0x0204 +#define WM_RBUTTONUP 0x0205 +#define WM_RBUTTONDBLCLK 0x0206 +#define WM_MBUTTONDOWN 0x0207 +#define WM_MBUTTONUP 0x0208 +#define WM_MBUTTONDBLCLK 0x0209 +#define WM_MOUSEWHEEL 0x020A +#define WM_MOUSEHWHEEL 0x020E +#define WM_MOUSELAST 0x020A +#define WM_CAPTURECHANGED 0x0215 +#define WM_DROPFILES 0x0233 +#define WM_SWELL_EXTENDED 0x0399 /* wParam = message specific type */ +#define WM_USER 0x0400 + +#define SC_CLOSE 0xF060 + +#define HTCAPTION 2 +#define HTBOTTOMRIGHT 17 + +#define WA_INACTIVE 0 +#define WA_ACTIVE 1 +#define WA_CLICKACTIVE 2 + +#define BN_CLICKED 0 + +#define LBN_SELCHANGE 1 +#define LBN_DBLCLK 2 +#define LB_ERR (-1) + +#define CBN_SELCHANGE 1 +#define CBN_EDITCHANGE 5 +#define CBN_DROPDOWN 7 +#define CBN_CLOSEUP 8 +#define CB_ERR (-1) + +#define EM_GETSEL 0xF0B0 +#define EM_SETSEL 0xF0B1 +#define EM_SCROLL 0xF0B5 +#define EM_REPLACESEL 0xF0C2 +#define EM_SETPASSWORDCHAR 0xF0CC + +#define SB_HORZ 0 +#define SB_VERT 1 +#define SB_CTL 2 +#define SB_BOTH 3 + +#define SB_LINEUP 0 +#define SB_LINELEFT 0 +#define SB_LINEDOWN 1 +#define SB_LINERIGHT 1 +#define SB_PAGEUP 2 +#define SB_PAGELEFT 2 +#define SB_PAGEDOWN 3 +#define SB_PAGERIGHT 3 +#define SB_THUMBPOSITION 4 +#define SB_THUMBTRACK 5 +#define SB_TOP 6 +#define SB_LEFT 6 +#define SB_BOTTOM 7 +#define SB_RIGHT 7 +#define SB_ENDSCROLL 8 + +#define DFCS_SCROLLUP 0x0000 +#define DFCS_SCROLLDOWN 0x0001 +#define DFCS_SCROLLLEFT 0x0002 +#define DFCS_SCROLLRIGHT 0x0003 +#define DFCS_SCROLLCOMBOBOX 0x0005 +#define DFCS_SCROLLSIZEGRIP 0x0008 +#define DFCS_SCROLLSIZEGRIPRIGHT 0x0010 + +#define DFCS_INACTIVE 0x0100 +#define DFCS_PUSHED 0x0200 +#define DFCS_CHECKED 0x0400 +#define DFCS_FLAT 0x4000 + +#define DFCS_BUTTONPUSH 0x0010 + +#define DFC_SCROLL 3 +#define DFC_BUTTON 4 + +#define ESB_ENABLE_BOTH 0x0000 +#define ESB_DISABLE_BOTH 0x0003 + +#define ESB_DISABLE_LEFT 0x0001 +#define ESB_DISABLE_RIGHT 0x0002 + +#define ESB_DISABLE_UP 0x0001 +#define ESB_DISABLE_DOWN 0x0002 + +#define BDR_RAISEDOUTER 0x0001 +#define BDR_SUNKENOUTER 0x0002 +#define BDR_RAISEDINNER 0x0004 +#define BDR_SUNKENINNER 0x0008 + +#define BDR_OUTER 0x0003 +#define BDR_INNER 0x000c + +#define EDGE_RAISED (BDR_RAISEDOUTER | BDR_RAISEDINNER) +#define EDGE_SUNKEN (BDR_SUNKENOUTER | BDR_SUNKENINNER) +#define EDGE_ETCHED (BDR_SUNKENOUTER | BDR_RAISEDINNER) +#define EDGE_BUMP (BDR_RAISEDOUTER | BDR_SUNKENINNER) + +#define BF_ADJUST 0x2000 +#define BF_FLAT 0x4000 +#define BF_LEFT 0x0001 +#define BF_TOP 0x0002 +#define BF_RIGHT 0x0004 +#define BF_BOTTOM 0x0008 +#define BF_RECT (BF_LEFT | BF_TOP | BF_RIGHT | BF_BOTTOM) + +#define PATCOPY (DWORD)0x00F00021 + +#define HTHSCROLL 6 +#define HTVSCROLL 7 + +#define WS_EX_LEFTSCROLLBAR 0x00004000L +#define WS_EX_ACCEPTFILES 0x00000010L + +#define SIF_RANGE 0x0001 +#define SIF_PAGE 0x0002 +#define SIF_POS 0x0004 +#define SIF_DISABLENOSCROLL 0x0008 +#define SIF_TRACKPOS 0x0010 +#define SIF_ALL (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS) + +#define SIZE_RESTORED 0 +#define SIZE_MINIMIZED 1 +#define SIZE_MAXIMIZED 2 +#define SIZE_MAXSHOW 3 +#define SIZE_MAXHIDE 4 + +typedef struct tagNMLVCUSTOMDRAW +{ + struct { + NMHDR hdr; + DWORD dwDrawStage; + HDC hdc; // not implemented + RECT rc; // not implemented + DWORD dwItemSpec; + UINT uItemState; // not implemented + LPARAM lItemlParam; // not implemented + } nmcd; + + COLORREF clrText, clrTextBk; + int iSubItem; +} NMLVCUSTOMDRAW, *LPNMLVCUSTOMDRAW; +// only currently used by listviews for color override +#define CDDS_PREPAINT (0x00001) +#define CDDS_ITEM (0x10000) +#define CDDS_ITEMPREPAINT (CDDS_ITEM | CDDS_PREPAINT) + +#ifndef MAKEINTRESOURCE +#define MAKEINTRESOURCE(x) ((const char *)(UINT_PTR)(x)) +#endif + +#ifdef FSHIFT +#undef FSHIFT +#endif + +#define FVIRTKEY 1 +#define FSHIFT 0x04 +#define FCONTROL 0x08 +#define FALT 0x10 +#define FLWIN 0x20 + + +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_MBUTTON 0x04 + +#define VK_BACK 0x08 +#define VK_TAB 0x09 + +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D + +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 + +#define VK_ESCAPE 0x1B + +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F + +#define VK_LWIN 0x5B + +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 + +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 + +// these should probably not be used (wParam is not set in WM_LBUTTONDOWN/WM_MOUSEMOVE etc) +#define MK_LBUTTON 0x01 +#define MK_RBUTTON 0x02 +#define MK_MBUTTON 0x10 + +#define IDC_SIZENESW MAKEINTRESOURCE(32643) +#define IDC_SIZENWSE MAKEINTRESOURCE(32642) +#define IDC_IBEAM MAKEINTRESOURCE(32513) +#define IDC_UPARROW MAKEINTRESOURCE(32516) +#define IDC_NO MAKEINTRESOURCE(32648) +#define IDC_SIZEALL MAKEINTRESOURCE(32646) +#define IDC_SIZENS MAKEINTRESOURCE(32645) +#define IDC_SIZEWE MAKEINTRESOURCE(32644) +#define IDC_ARROW MAKEINTRESOURCE(32512) +#define IDC_HAND MAKEINTRESOURCE(32649) + + + +#define COLOR_3DSHADOW 0 +#define COLOR_3DHILIGHT 1 +#define COLOR_3DFACE 2 +#define COLOR_BTNTEXT 3 +#define COLOR_WINDOW 4 +#define COLOR_SCROLLBAR 5 +#define COLOR_3DDKSHADOW 6 +#define COLOR_BTNFACE 7 +#define COLOR_INFOBK 8 +#define COLOR_INFOTEXT 9 + +#define SRCCOPY 0 +#define SRCCOPY_USEALPHACHAN 0xdeadbeef +#define PS_SOLID 0 + +#define DT_TOP 0 +#define DT_LEFT 0 +#define DT_CENTER 1 +#define DT_RIGHT 2 +#define DT_VCENTER 4 +#define DT_BOTTOM 8 +#define DT_WORDBREAK 0x10 +#define DT_SINGLELINE 0x20 +#define DT_NOCLIP 0x100 +#define DT_CALCRECT 0x400 +#define DT_NOPREFIX 0x800 +#define DT_END_ELLIPSIS 0x8000 + +#define FW_DONTCARE 0 +#define FW_THIN 100 +#define FW_EXTRALIGHT 200 +#define FW_LIGHT 300 +#define FW_NORMAL 400 +#define FW_MEDIUM 500 +#define FW_SEMIBOLD 600 +#define FW_BOLD 700 +#define FW_EXTRABOLD 800 +#define FW_HEAVY 900 + +#define FW_ULTRALIGHT FW_EXTRALIGHT +#define FW_REGULAR FW_NORMAL +#define FW_DEMIBOLD FW_SEMIBOLD +#define FW_ULTRABOLD FW_EXTRABOLD +#define FW_BLACK FW_HEAVY + +#define OUT_DEFAULT_PRECIS 0 +#define CLIP_DEFAULT_PRECIS 0 +#define DEFAULT_QUALITY 0 +#define DRAFT_QUALITY 1 +#define PROOF_QUALITY 2 +#define NONANTIALIASED_QUALITY 3 +#define ANTIALIASED_QUALITY 4 +#define DEFAULT_PITCH 0 +#define DEFAULT_CHARSET 0 +#define ANSI_CHARSET 0 +#define TRANSPARENT 0 +#define OPAQUE 1 + +#define NULL_PEN 1 +#define NULL_BRUSH 2 + +#define GGI_MARK_NONEXISTING_GLYPHS 1 + +#define GMEM_ZEROINIT 1 +#define GMEM_FIXED 0 +#define GMEM_MOVEABLE 0 +#define GMEM_DDESHARE 0 +#define GMEM_DISCARDABLE 0 +#define GMEM_SHARE 0 +#define GMEM_LOWER 0 +#define GHND (GMEM_MOVEABLE|GM_ZEROINIT) +#define GPTR (GMEM_FIXED|GMEM_ZEROINIT) + +#define CF_TEXT (1) +#define CF_HDROP (2) + +#define _MCW_RC 0x00000300 /* Rounding Control */ +#define _RC_NEAR 0x00000000 /* near */ +#define _RC_DOWN 0x00000100 /* down */ +#define _RC_UP 0x00000200 /* up */ +#define _RC_CHOP 0x00000300 /* chop */ + + +extern struct SWELL_DialogResourceIndex *SWELL_curmodule_dialogresource_head; +extern struct SWELL_MenuResourceIndex *SWELL_curmodule_menuresource_head; + +#define HTNOWHERE 0 +#define HTCLIENT 1 +#define HTMENU 5 +#define HTHSCROLL 6 +#define HTVSCROLL 7 + +#define SM_CXSCREEN 0 +#define SM_CYSCREEN 1 +#define SM_CXVSCROLL 2 +#define SM_CYHSCROLL 3 +#define SM_CYMENU 15 +#define SM_CYVSCROLL 20 +#define SM_CXHSCROLL 21 + + +#if 0 // these are disabled until implemented + +#define SM_CYCAPTION 4 +#define SM_CXBORDER 5 +#define SM_CYBORDER 6 +#define SM_CXDLGFRAME 7 +#define SM_CYDLGFRAME 8 +#define SM_CYVTHUMB 9 +#define SM_CXHTHUMB 10 +#define SM_CXICON 11 +#define SM_CYICON 12 +#define SM_CXCURSOR 13 +#define SM_CYCURSOR 14 +#define SM_CXFULLSCREEN 16 +#define SM_CYFULLSCREEN 17 +#define SM_CYKANJIWINDOW 18 +#define SM_MOUSEPRESENT 19 +#define SM_DEBUG 22 +#define SM_SWAPBUTTON 23 +#define SM_CXMIN 28 +#define SM_CYMIN 29 +#define SM_CXSIZE 30 +#define SM_CYSIZE 31 +#define SM_CXFRAME 32 +#define SM_CYFRAME 33 +#define SM_CXMINTRACK 34 +#define SM_CYMINTRACK 35 +#define SM_CXDOUBLECLK 36 +#define SM_CYDOUBLECLK 37 +#define SM_CXICONSPACING 38 +#define SM_CYICONSPACING 39 + +#endif // unimplemented system metrics + + +#define THREAD_BASE_PRIORITY_LOWRT 15 +#define THREAD_BASE_PRIORITY_MAX 2 +#define THREAD_BASE_PRIORITY_MIN -2 +#define THREAD_BASE_PRIORITY_IDLE -15 +#define THREAD_PRIORITY_LOWEST THREAD_BASE_PRIORITY_MIN +#define THREAD_PRIORITY_BELOW_NORMAL (THREAD_PRIORITY_LOWEST+1) +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_HIGHEST THREAD_BASE_PRIORITY_MAX +#define THREAD_PRIORITY_ABOVE_NORMAL (THREAD_PRIORITY_HIGHEST-1) +#define THREAD_PRIORITY_TIME_CRITICAL THREAD_BASE_PRIORITY_LOWRT +#define THREAD_PRIORITY_IDLE THREAD_BASE_PRIORITY_IDLE + + + +#define WAIT_OBJECT_0 (0 ) +#define WAIT_TIMEOUT (0x00000102L) +#define WAIT_FAILED (DWORD)0xFFFFFFFF +#define INFINITE 0xFFFFFFFF + + +#define FR_PRIVATE 1 // AddFontResourceEx() + +typedef struct _ICONINFO +{ + BOOL fIcon; + DWORD xHotspot; + DWORD yHotspot; + HBITMAP hbmMask; + HBITMAP hbmColor; +} ICONINFO, *PICONINFO; + +typedef struct _COPYDATASTRUCT +{ + ULONG_PTR dwData; + DWORD cbData; + PVOID lpData; +} COPYDATASTRUCT, *PCOPYDATASTRUCT; + +typedef void *HMONITOR; + +typedef struct _MONITORINFO { + DWORD cbSize; + RECT rcMonitor, rcWork; + DWORD dwFlags; +} MONITORINFO, *LPMONITORINFO; + + +typedef struct _MONITORINFOEX { + DWORD cbSize; + RECT rcMonitor, rcWork; + DWORD dwFlags; + char szDevice[256]; +} MONITORINFOEX, *LPMONITORINFOEX; + +typedef BOOL (*MONITORENUMPROC)(HMONITOR,HDC,LPRECT,LPARAM); + +#endif //_WDL_SWELL_H_TYPES_DEFINED_ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-win32.h b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-win32.h new file mode 100644 index 000000000..222b813e6 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-win32.h @@ -0,0 +1,58 @@ +#ifndef _SWELL_WIN32_H_ +#define _SWELL_WIN32_H_ + +/************* +** helpers to give swell-like functionality on win32 +*/ + +#ifdef _WIN32 +void SWELL_DisableContextMenu(HWND h, bool dis); + + + +#ifndef SWELL_WIN32_DECLARE_ONLY + +#include +#include "../wdltypes.h" + +#define SWELL_DISABLE_CTX_OLDPROC_NAME "SWELLDisableCtxOldProc" + +static LRESULT WINAPI swellDisableCtxNewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HANDLE oldProc = uMsg != WM_CONTEXTMENU ? GetProp(hwnd,SWELL_DISABLE_CTX_OLDPROC_NAME) : NULL; + if (uMsg == WM_DESTROY) + { + RemoveProp(hwnd,SWELL_DISABLE_CTX_OLDPROC_NAME); + if (oldProc) SetWindowLongPtr(hwnd,GWLP_WNDPROC,(LPARAM)oldProc); + } + return oldProc ? CallWindowProc((WNDPROC)oldProc,hwnd,uMsg,wParam,lParam) : DefWindowProc(hwnd,uMsg,wParam,lParam); +} + +void SWELL_DisableContextMenu(HWND h, bool dis) +{ + char classname[512]; + if (WDL_NOT_NORMALLY(!h)) return; + if (GetClassName(h,classname,sizeof(classname)) && !strcmp(classname,"ComboBox")) + { + HWND h2 = FindWindowEx(h,NULL,"Edit",NULL); + if (h2) h = h2; + } + + if (dis) + { + if (!GetProp(h,SWELL_DISABLE_CTX_OLDPROC_NAME)) + SetProp(h,SWELL_DISABLE_CTX_OLDPROC_NAME,(HANDLE)SetWindowLongPtr(h,GWLP_WNDPROC,(LPARAM)swellDisableCtxNewWndProc)); + } + else + { + LPARAM op = (LPARAM)GetProp(h,SWELL_DISABLE_CTX_OLDPROC_NAME); + if (op) SetWindowLongPtr(h,GWLP_WNDPROC,op); + RemoveProp(h,SWELL_DISABLE_CTX_OLDPROC_NAME); + } +} + +#endif // !SWELL_WIN32_DECLARE_ONLY + +#endif // _WIN32 + +#endif // _SWELL_WIN32_H_ diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-wnd-generic.cpp b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-wnd-generic.cpp new file mode 100644 index 000000000..6e20057f4 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-wnd-generic.cpp @@ -0,0 +1,8418 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + */ + + +#ifndef SWELL_PROVIDED_BY_APP + +#ifndef WDL_NO_DEFINE_MINMAX +#define WDL_NO_DEFINE_MINMAX +#endif +#include "swell.h" + +#define SWELL_INTERNAL_MERGESORT_IMPL +#define SWELL_INTERNAL_HTREEITEM_IMPL +#include "swell-internal.h" + +#include +#include "../mutex.h" +#include "../ptrlist.h" +#include "../assocarray.h" +#include "../queue.h" +#include "../wdlcstring.h" +#include "../wdlutf8.h" + +#include "swell-dlggen.h" + +bool swell_is_likely_capslock; // only used when processing dit events for a-zA-Z +SWELL_OSWINDOW SWELL_focused_oswindow; // top level window which has focus (might not map to a HWND__!) +HWND swell_captured_window; + +#define STATEIMAGEMASKTOINDEX(x) (((x)>>16)&0xff) + +static bool swell_is_virtkey_char(int c) +{ + return (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); +} + +HWND__ *SWELL_topwindows; +HWND swell_oswindow_to_hwnd(SWELL_OSWINDOW w) +{ + if (!w) return NULL; + HWND a = SWELL_topwindows; + while (a && a->m_oswindow != w) a=a->m_next; + return a; +} + +void swell_on_toplevel_raise(SWELL_OSWINDOW wnd) // called by swell-generic-gdk when a window is focused +{ + HWND hwnd = swell_oswindow_to_hwnd(wnd); + if (hwnd && hwnd != SWELL_topwindows) + { + // implies hwnd->m_prev + + VALIDATE_HWND_LIST(SWELL_topwindows,NULL); + + // remove from list + hwnd->m_prev->m_next = hwnd->m_next; + if (hwnd->m_next) hwnd->m_next->m_prev = hwnd->m_prev; + + // insert at front of list + hwnd->m_prev = NULL; + hwnd->m_next = SWELL_topwindows; + if (SWELL_topwindows) SWELL_topwindows->m_prev = hwnd; + SWELL_topwindows = hwnd; + VALIDATE_HWND_LIST(SWELL_topwindows,NULL); + } +} + +HWND__::HWND__(HWND par, int wID, const RECT *wndr, const char *label, bool visible, WNDPROC wndproc, DLGPROC dlgproc, HWND ownerWindow) +{ + m_refcnt=1; + m_private_data=0; + m_israised=false; + m_has_had_position=false; + m_oswindow_private=0; + m_oswindow_fullscreen=0; + + m_classname = "unknown"; + m_wndproc=wndproc?wndproc:dlgproc?(WNDPROC)SwellDialogDefaultWindowProc:(WNDPROC)DefWindowProc; + m_dlgproc=dlgproc; + m_userdata=0; + m_style=0; + m_exstyle=0; + m_id=wID; + m_owned_list=m_owner=m_owned_next=m_owned_prev=NULL; + m_children=m_parent=m_next=m_prev=NULL; + m_focused_child=NULL; + if (wndr) m_position = *wndr; + else memset(&m_position,0,sizeof(m_position)); + memset(&m_extra,0,sizeof(m_extra)); + m_visible=visible; + m_hashaddestroy=0; + m_enabled=true; + m_wantfocus=true; + m_menu=NULL; + m_font=NULL; + m_oswindow = NULL; + +#ifdef SWELL_LICE_GDI + m_paintctx=0; + m_invalidated=true; + m_child_invalidated=true; + m_backingstore=0; +#endif + + if (label) m_title.Set(label); + + SetParent(this, par); + if (!par && ownerWindow) + { + m_owned_next = ownerWindow->m_owned_list; + ownerWindow->m_owned_list = this; + if (m_owned_next) m_owned_next->m_owned_prev = this; + m_owner = ownerWindow; + } +} + +HWND__::~HWND__() +{ + if (m_wndproc) + m_wndproc(this,WM_NCDESTROY,0,0); +} + + + +HWND GetParent(HWND hwnd) +{ + if (WDL_NORMALLY(hwnd)) + { + return hwnd->m_parent ? hwnd->m_parent : hwnd->m_owner; + } + return NULL; +} + +HWND GetDlgItem(HWND hwnd, int idx) +{ + if (!idx) return hwnd; + WDL_ASSERT(hwnd != NULL); + if (hwnd) hwnd=hwnd->m_children; + while (hwnd && hwnd->m_id != (UINT)idx) hwnd=hwnd->m_next; + return hwnd; +} + + +LONG_PTR SetWindowLong(HWND hwnd, int idx, LONG_PTR val) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + if (idx==GWL_STYLE) + { + LONG ret = hwnd->m_style; + hwnd->m_style=val & ~WS_VISIBLE; + swell_oswindow_update_style(hwnd,ret); + return ret & ~WS_VISIBLE; + } + if (idx==GWL_EXSTYLE) + { + LONG ret = hwnd->m_exstyle; + hwnd->m_exstyle=val; + return ret; + } + if (idx==GWL_USERDATA) + { + LONG_PTR ret = hwnd->m_userdata; + hwnd->m_userdata=val; + return ret; + } + if (idx==GWL_ID) + { + LONG ret = hwnd->m_id; + hwnd->m_id=val; + return ret; + } + + if (idx==GWL_WNDPROC) + { + LONG_PTR ret = (LONG_PTR)hwnd->m_wndproc; + hwnd->m_wndproc=(WNDPROC)val; + return ret; + } + if (idx==DWL_DLGPROC) + { + LONG_PTR ret = (LONG_PTR)hwnd->m_dlgproc; + hwnd->m_dlgproc=(DLGPROC)val; + return ret; + } + if (idx == GWL_HWNDPARENT) + { + const LONG_PTR ret = (LONG_PTR)hwnd->m_owner; + if (val != ret) + { + if (hwnd->m_owned_next) hwnd->m_owned_next->m_owned_prev = hwnd->m_owned_prev; + if (hwnd->m_owned_prev) hwnd->m_owned_prev->m_owned_next = hwnd->m_owned_next; + if (hwnd->m_owner && hwnd->m_owner->m_owned_list == hwnd) hwnd->m_owner->m_owned_list = hwnd->m_owned_next; + hwnd->m_owned_next = hwnd->m_owned_prev = hwnd->m_owner = NULL; + HWND ownerWindow = (HWND) val; + if (ownerWindow) + { + hwnd->m_owned_next = ownerWindow->m_owned_list; + ownerWindow->m_owned_list = hwnd; + if (hwnd->m_owned_next) hwnd->m_owned_next->m_owned_prev = hwnd; + hwnd->m_owner = ownerWindow; + hwnd->m_israised=true; + } + } + return ret; + } + + if (idx>=0 && idx < 64*(int)sizeof(INT_PTR)) + { + INT_PTR ret = hwnd->m_extra[idx/sizeof(INT_PTR)]; + hwnd->m_extra[idx/sizeof(INT_PTR)]=val; + return (LONG_PTR)ret; + } + return 0; +} + +LONG_PTR GetWindowLong(HWND hwnd, int idx) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + if (idx==GWL_STYLE) + { + LONG_PTR ret = hwnd->m_style; + if (hwnd->m_visible) ret|=WS_VISIBLE; + else ret &= ~WS_VISIBLE; + return ret; + } + if (idx==GWL_EXSTYLE) + { + return hwnd->m_exstyle; + } + if (idx==GWL_USERDATA) + { + return hwnd->m_userdata; + } + if (idx==GWL_ID) + { + return hwnd->m_id; + } + + if (idx==GWL_WNDPROC) + { + return (LONG_PTR)hwnd->m_wndproc; + } + if (idx==DWL_DLGPROC) + { + return (LONG_PTR)hwnd->m_dlgproc; + } + if (idx == GWL_HWNDPARENT) + { + return (LONG_PTR)hwnd->m_owner; + } + + if (idx>=0 && idx < 64*(int)sizeof(INT_PTR)) + { + return (LONG_PTR)hwnd->m_extra[idx/sizeof(INT_PTR)]; + } + return 0; +} + + +static bool __isWindow(HWND hc, HWND hFind) +{ + while (hc) + { + if (hc == hFind || (hc->m_children && __isWindow(hc->m_children,hFind))) return true; + hc = hc->m_next; + } + return false; +} + +bool IsWindow(HWND hwnd) +{ + if (!hwnd) return false; + HWND h = SWELL_topwindows; + while (h) + { + if (hwnd == h || (h->m_children && __isWindow(h->m_children,hwnd))) return true; + h=h->m_next; + } + + return false; +} + +bool IsWindowVisible(HWND hwnd) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return false; + while (hwnd->m_visible) + { + hwnd = hwnd->m_parent; + if (!hwnd) return true; + } + return false; +} + +bool IsModalDialogBox(HWND hwnd); + +LRESULT SendMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + WNDPROC wp = hwnd->m_wndproc; + + if (msg == WM_DESTROY) + { + if (hwnd->m_hashaddestroy) return 0; + hwnd->m_hashaddestroy=1; + + if (GetCapture()==hwnd) ReleaseCapture(); + SWELL_MessageQueue_Clear(hwnd); + } + else if (hwnd->m_hashaddestroy == 2) return 0; + else if (msg==WM_CAPTURECHANGED && hwnd->m_hashaddestroy) return 0; + + hwnd->Retain(); + + LRESULT ret = wp ? wp(hwnd,msg,wParam,lParam) : 0; + + if (msg == WM_DESTROY) + { + if (GetCapture()==hwnd) ReleaseCapture(); + + SWELL_MessageQueue_Clear(hwnd); + // send WM_DESTROY to all children + HWND tmp=hwnd->m_children; + while (tmp) + { + HWND old = tmp; + tmp=tmp->m_next; + SendMessage(old,WM_DESTROY,0,0); + } + { + tmp=hwnd->m_owned_list; + while (tmp) + { + HWND old = tmp; + tmp=tmp->m_owned_next; + if (!IsModalDialogBox(old)) SendMessage(old,WM_DESTROY,0,0); + } + } + if (SWELL_focused_oswindow && SWELL_focused_oswindow == hwnd->m_oswindow) + { + HWND h = hwnd->m_owner; + while (h && !h->m_oswindow) h = h->m_parent ? h->m_parent : h->m_owner; + swell_oswindow_focus(h && h->m_oswindow ? h : NULL); + } + hwnd->m_wndproc=NULL; + hwnd->m_hashaddestroy=2; + KillTimer(hwnd,-1); + } + hwnd->Release(); + return ret; +} + +static void swell_removeWindowFromParentOrTop(HWND__ *hwnd, bool removeFromOwner) +{ + HWND par = hwnd->m_parent; + if (hwnd->m_next) hwnd->m_next->m_prev = hwnd->m_prev; + if (hwnd->m_prev) hwnd->m_prev->m_next = hwnd->m_next; + if (par) + { + if (par->m_focused_child == hwnd) par->m_focused_child=NULL; + if (par->m_children == hwnd) par->m_children = hwnd->m_next; + } + if (hwnd == SWELL_topwindows) + { + SWELL_topwindows = hwnd->m_next; + VALIDATE_HWND_LIST(SWELL_topwindows,NULL); + } + hwnd->m_next = hwnd->m_prev = hwnd->m_parent = NULL; + if (par) VALIDATE_HWND_LIST(par->m_children,par); + + if (removeFromOwner) + { + if (hwnd->m_owned_next) hwnd->m_owned_next->m_owned_prev = hwnd->m_owned_prev; + if (hwnd->m_owned_prev) hwnd->m_owned_prev->m_owned_next = hwnd->m_owned_next; + if (hwnd->m_owner && hwnd->m_owner->m_owned_list == hwnd) hwnd->m_owner->m_owned_list = hwnd->m_owned_next; + hwnd->m_owned_next = hwnd->m_owned_prev = hwnd->m_owner = NULL; + } + + if (par && !par->m_hashaddestroy) InvalidateRect(par,NULL,FALSE); +} + +void RecurseDestroyWindow(HWND hwnd) +{ + HWND tmp=hwnd->m_children; + hwnd->m_children=NULL; + + while (tmp) + { + HWND old = tmp; + tmp=tmp->m_next; + if (tmp) tmp->m_prev = NULL; + + old->m_prev = old->m_next = NULL; + RecurseDestroyWindow(old); + } + tmp=hwnd->m_owned_list; + hwnd->m_owned_list = NULL; + + while (tmp) + { + HWND old = tmp; + tmp=tmp->m_owned_next; + if (tmp) tmp->m_owned_prev = NULL; + + old->m_owned_prev = old->m_owned_next = NULL; + old->m_owner = NULL; + if (old->m_hashaddestroy) RecurseDestroyWindow(old); + } + + if (swell_captured_window == hwnd) swell_captured_window=NULL; + + swell_oswindow_destroy(hwnd); + + if (hwnd->m_menu) DestroyMenu(hwnd->m_menu); + hwnd->m_menu=0; + +#ifdef SWELL_LICE_GDI + delete hwnd->m_backingstore; + hwnd->m_backingstore=0; +#endif + + // remove from parent/global lists + swell_removeWindowFromParentOrTop(hwnd, true); + + SWELL_MessageQueue_Clear(hwnd); + KillTimer(hwnd,-1); + hwnd->Release(); +} + + +void DestroyWindow(HWND hwnd) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + if (hwnd->m_hashaddestroy) return; + + // broadcast WM_DESTROY + SendMessage(hwnd,WM_DESTROY,0,0); + + // safe to delete this window and all children directly + RecurseDestroyWindow(hwnd); + +} + + +bool IsWindowEnabled(HWND hwnd) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return false; + while (hwnd && hwnd->m_enabled) + { + hwnd=hwnd->m_parent; + } + return !hwnd; +} + +void EnableWindow(HWND hwnd, int enable) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + if (!!hwnd->m_enabled == !!enable) return; + + hwnd->m_enabled=!!enable; + swell_oswindow_update_enable(hwnd); + + if (!enable) + { + if (hwnd->m_parent && hwnd->m_parent->m_focused_child == hwnd) + hwnd->m_parent->m_focused_child = NULL; + } + InvalidateRect(hwnd,NULL,FALSE); +} + +void SetForegroundWindow(HWND hwnd) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + + // if a child window has focus, preserve that focus + while (hwnd->m_parent && !hwnd->m_oswindow) + { + hwnd->m_parent->m_focused_child = hwnd; + hwnd = hwnd->m_parent; + } + if (hwnd) swell_oswindow_focus(hwnd); +} + +void SetFocusInternal(HWND hwnd) +{ + hwnd->m_focused_child=NULL; // make sure this window has focus, not a child + SetForegroundWindow(hwnd); +} + +void SetFocus(HWND hwnd) +{ + if (!hwnd) return; // windows allows SetFocus(NULL) to defocus... + HWND oldfoc = GetFocus(); + SetFocusInternal(hwnd); + + if (hwnd->m_classname && oldfoc != hwnd) + { + if (!strcmp(hwnd->m_classname,"Edit") || + !strcmp(hwnd->m_classname,"combobox")) + SendMessage(hwnd,EM_SETSEL,0,-1); + } +} + + +int IsChild(HWND hwndParent, HWND hwndChild) +{ + if (!hwndParent || !hwndChild || hwndParent == hwndChild) return 0; + + while (hwndChild && hwndChild != hwndParent) hwndChild = hwndChild->m_parent; + + return hwndChild == hwndParent; +} + + +HWND GetFocusIncludeMenus() +{ + HWND h = swell_is_app_inactive()>0 ? NULL : swell_oswindow_to_hwnd(SWELL_focused_oswindow); + while (h) + { + HWND fc = h->m_focused_child; + if (!fc) break; + HWND s = h->m_children; + while (s && s != fc) s = s->m_next; + if (!s) break; + h = s; // descend to focused child + } + return h; +} + +HWND GetForegroundWindow() +{ + return GetFocus(); +} + +HWND GetFocus() +{ + HWND h =GetFocusIncludeMenus(); + HWND ho; + while (h && (ho=(HWND)GetProp(h,"SWELL_MenuOwner"))) h=ho; + return h; +} + +void ScreenToClient(HWND hwnd, POINT *pt) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + + HWND tmp=hwnd; + while (tmp) + { + NCCALCSIZE_PARAMS p = {{ tmp->m_position, }, }; + if (tmp->m_wndproc) tmp->m_wndproc(tmp,WM_NCCALCSIZE,0,(LPARAM)&p); + + pt->x -= p.rgrc[0].left; + pt->y -= p.rgrc[0].top; + tmp = tmp->m_parent; + } +} + +void ClientToScreen(HWND hwnd, POINT *pt) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + + HWND tmp=hwnd; + while (tmp) + { + NCCALCSIZE_PARAMS p={{tmp->m_position, }, }; + if (tmp->m_wndproc) tmp->m_wndproc(tmp,WM_NCCALCSIZE,0,(LPARAM)&p); + pt->x += p.rgrc[0].left; + pt->y += p.rgrc[0].top; + tmp = tmp->m_parent; + } +} + +void GetWindowContentViewRect(HWND hwnd, RECT *r) +{ + if (WDL_NORMALLY(hwnd) && hwnd->m_oswindow) + { + *r = hwnd->m_position; + return; + } + GetWindowRect(hwnd,r); +} + +void GetClientRect(HWND hwnd, RECT *r) +{ + r->left=r->top=r->right=r->bottom=0; + if (WDL_NOT_NORMALLY(!hwnd)) return; + + r->right = hwnd->m_position.right - hwnd->m_position.left; + r->bottom = hwnd->m_position.bottom - hwnd->m_position.top; + + NCCALCSIZE_PARAMS tr={{*r, },}; + SendMessage(hwnd,WM_NCCALCSIZE,FALSE,(LPARAM)&tr); + r->right = r->left + (tr.rgrc[0].right-tr.rgrc[0].left); + r->bottom=r->top + (tr.rgrc[0].bottom-tr.rgrc[0].top); +} + + + +void SetWindowPos(HWND hwnd, HWND zorder, int x, int y, int cx, int cy, int flags) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + // todo: handle SWP_SHOWWINDOW + RECT f = hwnd->m_position; + int reposflag = 0; + if (!(flags&SWP_NOZORDER)) + { + if (hwnd->m_parent && zorder != hwnd) + { + HWND par = hwnd->m_parent; + HWND tmp = par->m_children; + while (tmp && tmp != hwnd) tmp=tmp->m_next; + if (tmp) // we are in the list, so we can do a reorder + { + // take hwnd out of list + if (hwnd->m_prev) hwnd->m_prev->m_next = hwnd->m_next; + else par->m_children = hwnd->m_next; + if (hwnd->m_next) hwnd->m_next->m_prev = hwnd->m_prev; + hwnd->m_next=hwnd->m_prev=NULL;// leave hwnd->m_parent valid since it wont change + + // add back in + tmp = par->m_children; + if (zorder == HWND_BOTTOM || !tmp) // insert at front of list + { + if (tmp) tmp->m_prev=hwnd; + hwnd->m_next = tmp; + par->m_children = hwnd; + } + else + { + // zorder could be HWND_TOP here + while (tmp && tmp != zorder && tmp->m_next) tmp=tmp->m_next; + + // tmp is either zorder or the last item in the list + hwnd->m_next = tmp->m_next; + tmp->m_next = hwnd; + if (hwnd->m_next) hwnd->m_next->m_prev = hwnd; + hwnd->m_prev = tmp; + } + reposflag|=4; + } + } + } + if (!(flags&SWP_NOMOVE)) + { + int oldw = f.right-f.left; + int oldh = f.bottom-f.top; + f.left=x; + f.right=x+oldw; + f.top=y; + f.bottom=y+oldh; + reposflag|=1; + hwnd->m_has_had_position=true; + } + if (!(flags&SWP_NOSIZE)) + { + f.right = f.left + cx; + f.bottom = f.top + cy; + reposflag|=2; + } + if (reposflag) + { + if (hwnd->m_oswindow && (reposflag&2)) + { + swell_oswindow_begin_resize(hwnd->m_oswindow); + } + + if (reposflag&3) + { + hwnd->m_position = f; + } + + if (hwnd->m_oswindow && !hwnd->m_oswindow_fullscreen) + { + swell_oswindow_resize(hwnd->m_oswindow,reposflag,f); + if (reposflag&2) SendMessage(hwnd,WM_SIZE,0,0); + } + else + { + if (reposflag&2) SendMessage(hwnd,WM_SIZE,0,0); + InvalidateRect(hwnd->m_parent ? hwnd->m_parent : hwnd,NULL,FALSE); + } + } + swell_oswindow_postresize(hwnd,f); +} + + +BOOL EnumWindows(BOOL (*proc)(HWND, LPARAM), LPARAM lp) +{ + HWND h = SWELL_topwindows; + if (!proc) return FALSE; + while (h) + { + if (!proc(h,lp)) return FALSE; + h = h->m_next; + } + return TRUE; +} + +HWND GetWindow(HWND hwnd, int what) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + + if (what == GW_CHILD) return hwnd->m_children; + if (what == GW_OWNER) return hwnd->m_owner; + if (what == GW_HWNDNEXT) return hwnd->m_next; + if (what == GW_HWNDPREV) return hwnd->m_prev; + if (what == GW_HWNDFIRST) + { + while (hwnd->m_prev) hwnd = hwnd->m_prev; + return hwnd; + } + if (what == GW_HWNDLAST) + { + while (hwnd->m_next) hwnd = hwnd->m_next; + return hwnd; + } + return 0; +} + +HWND SetParent(HWND hwnd, HWND newPar) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return NULL; + + HWND oldPar = hwnd->m_parent; + + swell_removeWindowFromParentOrTop(hwnd, newPar != NULL && newPar != oldPar); + + if (newPar) + { + HWND fc = newPar->m_children; + if (!fc) + { + newPar->m_children = hwnd; + } + else + { + while (fc->m_next) fc = fc->m_next; + hwnd->m_prev = fc; + fc->m_next = hwnd; + } + hwnd->m_parent = newPar; + hwnd->m_style |= WS_CHILD; + + if (newPar) VALIDATE_HWND_LIST(newPar->m_children,newPar); + } + else // add to top level windows + { + hwnd->m_next=SWELL_topwindows; + if (hwnd->m_next) hwnd->m_next->m_prev = hwnd; + SWELL_topwindows = hwnd; + VALIDATE_HWND_LIST(SWELL_topwindows,NULL); + hwnd->m_style &= ~WS_CHILD; + } + + swell_oswindow_manage(hwnd,false); + return oldPar; +} + + + + +// timer stuff +typedef struct TimerInfoRec +{ + UINT_PTR timerid; + HWND hwnd; + UINT interval; + + DWORD lastFire; + int refcnt; // 0 normally, 1 if currently processing this timer + + TIMERPROC tProc; + struct TimerInfoRec *_next; +} TimerInfoRec; + +static TimerInfoRec *m_timer_list; +static WDL_Mutex m_timermutex; + +static TimerInfoRec *spare_timers; +static void free_timer(TimerInfoRec *rec) +{ + int c = 3; // max spares + TimerInfoRec *p = spare_timers; + while (p) + { + if (--c <= 0) + { + free(rec); + return; + } + p = p->_next; + } + rec->_next=spare_timers; + spare_timers=rec; +} + + +void SWELL_RunMessageLoop() +{ + SWELL_MessageQueue_Flush(); + SWELL_RunEvents(); + + const DWORD now = GetTickCount(); + WDL_MutexLock lock(&m_timermutex); + TimerInfoRec *rec = m_timer_list; + while (rec) + { + // rec->lastFire might be after now, if the timer was set from another timer + // we should run this timer if rec->lastFire is in the interval + // [now-rec->interval-some big range,now-rec->interval] + if (WDL_TICKS_IN_RANGE_ENDING_AT(rec->lastFire, now - rec->interval, 100000)) + { + rec->lastFire = GetTickCount(); + ++rec->refcnt; + + HWND h = rec->hwnd; + TIMERPROC tProc = rec->tProc; + UINT_PTR tid = rec->timerid; + m_timermutex.Leave(); + + if (tProc) tProc(h,WM_TIMER,tid,now); + else if (h) SendMessage(h,WM_TIMER,tid,0); + + m_timermutex.Enter(); + + if (--rec->refcnt < 0) + { + free_timer(rec); + rec = m_timer_list; + continue; + } + } + rec=rec->_next; + } +} + +UINT_PTR SetTimer(HWND hwnd, UINT_PTR timerid, UINT rate, TIMERPROC tProc) +{ + if (WDL_NOT_NORMALLY(!hwnd && !tProc)) return 0; // must have either callback or hwnd + + if (WDL_NOT_NORMALLY(hwnd && !timerid)) return 0; + + if (hwnd && hwnd->m_hashaddestroy) return 0; + + WDL_MutexLock lock(&m_timermutex); + TimerInfoRec *rec=NULL; + if (hwnd||timerid) + { + rec = m_timer_list; + while (rec) + { + if (rec->timerid == timerid && rec->hwnd == hwnd) // works for both kinds + break; + rec=rec->_next; + } + } + + bool recAdd=false; + if (!rec) + { + rec = spare_timers; + if (!rec) rec = (TimerInfoRec *)malloc(sizeof(TimerInfoRec)); + else spare_timers = rec->_next; + rec->refcnt = 0; + recAdd=true; + } + + if (!hwnd) timerid = (UINT_PTR)rec; + + rec->tProc = tProc; + rec->timerid = timerid; + rec->hwnd = hwnd; + rec->interval = rate<1?1: rate; + rec->lastFire = GetTickCount(); + + if (recAdd) + { + rec->_next=m_timer_list; + m_timer_list=rec; + } + + return timerid; +} + +BOOL KillTimer(HWND hwnd, UINT_PTR timerid) +{ + if (WDL_NOT_NORMALLY(!hwnd && !timerid)) return FALSE; + + WDL_MutexLock lock(&m_timermutex); + BOOL rv=FALSE; + + // don't allow removing all global timers + if (timerid!=(UINT_PTR)-1 || hwnd) + { + TimerInfoRec *rec = m_timer_list, *lrec=NULL; + while (rec) + { + if (rec->hwnd == hwnd && (timerid==(UINT_PTR)-1 || rec->timerid == timerid)) + { + TimerInfoRec *nrec = rec->_next; + + // remove self from list + if (lrec) lrec->_next = nrec; + else m_timer_list = nrec; + + if (--rec->refcnt < 0) + free_timer(rec); + + rv=TRUE; + if (timerid!=(UINT_PTR)-1) break; + + rec=nrec; + } + else + { + lrec=rec; + rec=rec->_next; + } + } + } + return rv; +} + +BOOL SetDlgItemText(HWND hwnd, int idx, const char *text) +{ + hwnd =(idx ? GetDlgItem(hwnd,idx) : hwnd); + if (WDL_NOT_NORMALLY(!hwnd)) return false; + + if (!text) text=""; + + + if (strcmp(hwnd->m_title.Get(), text)) + { + hwnd->m_title.Set(text); + swell_oswindow_update_text(hwnd); + } + SendMessage(hwnd,WM_SETTEXT,0,(LPARAM)text); + return true; +} + +int GetWindowTextLength(HWND hwnd) +{ + return WDL_NORMALLY(hwnd) ? hwnd->m_title.GetLength() : 0; +} + +BOOL GetDlgItemText(HWND hwnd, int idx, char *text, int textlen) +{ + *text=0; + hwnd = idx?GetDlgItem(hwnd,idx) : hwnd; + if (WDL_NOT_NORMALLY(!hwnd)) return false; + + // todo: sendmessage WM_GETTEXT etc? special casing for combo boxes etc + lstrcpyn_safe(text,hwnd->m_title.Get(), textlen); + return true; +} + +void CheckDlgButton(HWND hwnd, int idx, int check) +{ + hwnd = GetDlgItem(hwnd,idx); + if (WDL_NOT_NORMALLY(!hwnd)) return; + SendMessage(hwnd,BM_SETCHECK,check,0); +} + + +int IsDlgButtonChecked(HWND hwnd, int idx) +{ + hwnd = GetDlgItem(hwnd,idx); + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + return SendMessage(hwnd,BM_GETCHECK,0,0); +} + + +BOOL SetDlgItemInt(HWND hwnd, int idx, int val, int issigned) +{ + char buf[128]; + sprintf(buf,issigned?"%d":"%u",val); + return SetDlgItemText(hwnd,idx,buf); +} + +int GetDlgItemInt(HWND hwnd, int idx, BOOL *translated, int issigned) +{ + char buf[128]; + if (!GetDlgItemText(hwnd,idx,buf,sizeof(buf))) + { + if (translated) *translated=0; + return 0; + } + char *p=buf; + while (*p == ' ' || *p == '\t') p++; + int a=atoi(p); + if ((a<0 && !issigned) || (!a && p[0] != '0')) { if (translated) *translated=0; return 0; } + if (translated) *translated=1; + return a; +} + +void ShowWindow(HWND hwnd, int cmd) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + + if (cmd==SW_SHOW||cmd==SW_SHOWNA) + { + if (hwnd->m_visible) cmd = SW_SHOWNA; // do not take focus if already visible + hwnd->m_visible=true; + } + else if (cmd==SW_HIDE) + { + if (hwnd->m_visible) + { + hwnd->m_visible=false; + if (hwnd->m_parent) + InvalidateRect(hwnd->m_parent,&hwnd->m_position,FALSE); + } + } + + swell_oswindow_manage(hwnd,cmd==SW_SHOW); + if (cmd == SW_SHOW) + { + SetForegroundWindow(hwnd); + } + + InvalidateRect(hwnd,NULL,FALSE); + +} + +void *SWELL_ModalWindowStart(HWND hwnd) +{ + return 0; +} + +bool SWELL_ModalWindowRun(void *ctx, int *ret) // returns false and puts retval in *ret when done +{ + return false; +} + +void SWELL_ModalWindowEnd(void *ctx) +{ + if (ctx) + { + } +} + +void SWELL_CloseWindow(HWND hwnd) +{ + DestroyWindow(hwnd); +} + + +static void Draw3DBox(HDC hdc, const RECT *r, int bgc, int topc, int botc, bool swap=false) +{ + RECT tr = *r; + tr.right--; + tr.bottom--; + if (bgc != -1) + { + tr.left++; + tr.top++; + HBRUSH br = CreateSolidBrush(bgc); + FillRect(hdc,&tr,br); + DeleteObject(br); + tr.left--; + tr.top--; + } + + HPEN pen = CreatePen(PS_SOLID,0,swap?botc:topc); + HPEN pen2 = CreatePen(PS_SOLID,0,swap?topc:botc); + HGDIOBJ oldpen = SelectObject(hdc,pen); + MoveToEx(hdc,tr.left,tr.bottom,NULL); + LineTo(hdc,tr.left,tr.top); + LineTo(hdc,tr.right,tr.top); + + SelectObject(hdc,pen2); + LineTo(hdc,tr.right,tr.bottom); + LineTo(hdc,tr.left,tr.bottom); + + SelectObject(hdc,oldpen); + DeleteObject(pen); + DeleteObject(pen2); +} + + +#include "swell-dlggen.h" + +static HWND m_make_owner; +static RECT m_transform; +static bool m_doautoright; +static RECT m_lastdoauto; +static bool m_sizetofits; + +#define ACTIONTARGET (m_make_owner) + +void SWELL_MakeSetCurParms(float xscale, float yscale, float xtrans, float ytrans, HWND parent, bool doauto, bool dosizetofit) +{ + if (g_swell_ui_scale != 256 && xscale != 1.0f && yscale != 1.0f) + { + const float m = g_swell_ui_scale/256.0f; + xscale *= m; + yscale *= m; + } + m_sizetofits=dosizetofit; + m_lastdoauto.left = 0; + m_lastdoauto.top = -6553600; + m_lastdoauto.right = 0; + m_doautoright=doauto; + m_transform.left=(int)(xtrans*65536.0); + m_transform.top=(int)(ytrans*65536.0); + m_transform.right=(int)(xscale*65536.0); + m_transform.bottom=(int)(yscale*65536.0); + m_make_owner=parent; +} + +static void UpdateAutoCoords(RECT r) +{ + m_lastdoauto.right=r.left + r.right - m_lastdoauto.left; +} + + +static RECT MakeCoords(int x, int y, int w, int h, bool wantauto) +{ + if (w<0&&h<0) + { + RECT r = { -x, -y, -x-w, -y-h}; + return r; + } + + float ysc=m_transform.bottom/65536.0; + int newx=(int)((x+m_transform.left/65536.0)*m_transform.right/65536.0 + 0.5); + int newy=(int)(((((double)y+(double)m_transform.top/65536.0) )*ysc) + 0.5); + + RECT ret= { newx, + newy, + (int) (newx + w*(double)m_transform.right/65536.0+0.5), + (int) (newy + h*fabs(ysc)+0.5) + }; + + + RECT oret=ret; + if (wantauto && m_doautoright) + { + float dx = ret.left - m_lastdoauto.left; + if (fabs(dx)<32 && m_lastdoauto.top >= ret.top && m_lastdoauto.top < ret.bottom) + { + ret.left += (int) m_lastdoauto.right; + } + + m_lastdoauto.left = oret.right; + m_lastdoauto.top = (ret.top + ret.bottom)*0.5; + m_lastdoauto.right=0; + } + return ret; +} + +#define TRANSFORMFONTSIZE ((m_transform.right/65536.0+1.0)*3.7) + +static void paintDialogBackground(HWND hwnd, const RECT *r, HDC hdc) +{ + HBRUSH hbrush = (HBRUSH) SendMessage(GetParent(hwnd),WM_CTLCOLORSTATIC,(WPARAM)hdc,(LPARAM)hwnd); + if (hbrush == (HBRUSH)(INT_PTR)1) return; + + if (hbrush) + { + FillRect(hdc,r,hbrush); + } + else + { + SWELL_FillDialogBackground(hdc,r,0); + } +} + +static bool fast_has_focus(HWND hwnd) +{ + if (!hwnd || !SWELL_focused_oswindow || swell_is_app_inactive()>0) return false; + HWND par; + while ((par=hwnd->m_parent)!=NULL && par->m_focused_child==hwnd) + { + if (par->m_oswindow == SWELL_focused_oswindow) return true; + hwnd=par; + } + return false; +} + +static bool draw_focus_indicator(HWND hwnd, HDC hdc, const RECT *drawr) +{ + if (!fast_has_focus(hwnd)) return false; + + RECT r,tr; + const int sz = SWELL_UI_SCALE(3); + if (drawr) r=*drawr; + else GetClientRect(hwnd,&r); + + HBRUSH br = CreateSolidBrushAlpha(g_swell_ctheme.focus_hilight,.75f); + tr=r; tr.right = tr.left+sz; FillRect(hdc,&tr,br); + tr=r; tr.left = tr.right-sz; FillRect(hdc,&tr,br); + tr=r; tr.left+=sz; tr.right-=sz; + tr.bottom = tr.top+sz; FillRect(hdc,&tr,br); + tr.bottom = r.bottom; tr.top = tr.bottom-sz; FillRect(hdc,&tr,br); + + DeleteObject(br); + return true; +} + + +struct buttonWindowState +{ + buttonWindowState() { bitmap=0; bitmap_mode=0; state=0; } + ~buttonWindowState() { /* if (bitmap) DeleteObject(bitmap); */ } + + HICON bitmap; + int bitmap_mode; + int state; +}; + +static LRESULT WINAPI buttonWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_NCDESTROY: + delete (buttonWindowState *)hwnd->m_private_data; + hwnd->m_private_data=0; + break; + case WM_LBUTTONDOWN: + SetFocusInternal(hwnd); + SetCapture(hwnd); + SendMessage(hwnd,WM_USER+100,0,0); // invalidate + return 0; + case WM_MOUSEMOVE: + return 0; + case WM_KEYDOWN: + if (wParam == VK_SPACE) goto fakeButtonClick; + if (wParam == VK_RETURN) + { + if (!(hwnd->m_style & 0xf)) + goto fakeButtonClick; + } + break; + case WM_LBUTTONUP: + if (GetCapture()==hwnd) + { +fakeButtonClick: + buttonWindowState *s = (buttonWindowState*)hwnd->m_private_data; + ReleaseCapture(); // WM_CAPTURECHANGED will take care of the invalidate + RECT r; + GetClientRect(hwnd,&r); + POINT p={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)}; + hwnd->Retain(); + if ((msg==WM_KEYDOWN||PtInRect(&r,p)) && hwnd->m_id && hwnd->m_parent) + { + int sf = (hwnd->m_style & 0xf); + if (sf == BS_AUTO3STATE) + { + int a = s->state&3; + if (a==0) a=1; + else if (a==1) a=2; + else a=0; + s->state = (a) | (s->state&~3); + } + else if (sf == BS_AUTOCHECKBOX) + { + s->state = (!(s->state&3)) | (s->state&~3); + } + else if (sf == BS_AUTORADIOBUTTON) + { + int x; + for (x=0;x<2;x++) + { + HWND nw = x ? hwnd->m_next : hwnd->m_prev; + while (nw) + { + if (nw->m_classname && !strcmp(nw->m_classname,"Button")) + { + if (x && (nw->m_style & WS_GROUP)) break; + + if ((nw->m_style & 0xf) == BS_AUTORADIOBUTTON) + { + buttonWindowState *nws = (buttonWindowState*)nw->m_private_data; + if (nws && (nws->state&3)) + { + nws->state &= ~3; + InvalidateRect(nw,NULL,FALSE); + } + } + + if (nw->m_style & WS_GROUP) break; + } + else + { + break; + } + + nw=x ? nw->m_next : nw->m_prev; + } + } + + s->state = 1 | (s->state&~3); + } + SendMessage(hwnd->m_parent,WM_COMMAND,MAKEWPARAM(hwnd->m_id,BN_CLICKED),(LPARAM)hwnd); + } + if (msg == WM_KEYDOWN) InvalidateRect(hwnd,NULL,FALSE); + hwnd->Release(); + } + return 0; + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + buttonWindowState *s = (buttonWindowState*)hwnd->m_private_data; + RECT r; + GetClientRect(hwnd,&r); + + bool pressed = GetCapture()==hwnd; + + SetBkMode(ps.hdc,TRANSPARENT); + + if (hwnd->m_enabled) + SetTextColor(ps.hdc, g_swell_ctheme.button_text); + + paintDialogBackground(hwnd,&r,ps.hdc); + + if (!hwnd->m_enabled) + SetTextColor(ps.hdc, g_swell_ctheme.button_text_disabled); + + int f=DT_VCENTER; + int sf = (hwnd->m_style & 0xf); + if (sf == BS_OWNERDRAW) + { + if (hwnd->m_parent) + { + DRAWITEMSTRUCT dis = { ODT_BUTTON, hwnd->m_id, 0, 0, (UINT)(pressed?ODS_SELECTED:0),hwnd,ps.hdc,r,(DWORD_PTR)hwnd->m_userdata }; + SendMessage(hwnd->m_parent,WM_DRAWITEM,(WPARAM)hwnd->m_id,(LPARAM)&dis); + } + EndPaint(hwnd,&ps); + return 0; + } + + const bool ischk = sf == BS_AUTO3STATE || sf == BS_AUTOCHECKBOX || sf == BS_AUTORADIOBUTTON; + if (!ischk) + { + Draw3DBox(ps.hdc,&r,g_swell_ctheme.button_bg, + g_swell_ctheme.button_hilight, + g_swell_ctheme.button_shadow,pressed); + + if ((hwnd->m_style & BS_XPOSITION_MASK) == BS_LEFT) + r.left+=2; + else + f|=DT_CENTER; + if (pressed) + { + const int pad = SWELL_UI_SCALE(2); + r.left+=pad; + r.top+=pad; + if (s->bitmap) { r.right+=pad; r.bottom+=pad; } + } + } + + if (draw_focus_indicator(hwnd,ps.hdc,NULL)) + { + KillTimer(hwnd,1); + SetTimer(hwnd,1,100,NULL); + } + + if (ischk) + { + const int chksz = SWELL_UI_SCALE(12), chki = SWELL_UI_SCALE(2); + RECT tr={r.left+chki,(r.top+r.bottom)/2-chksz/2,r.left+chki+chksz}; + tr.bottom = tr.top+chksz; + + HPEN pen=CreatePen(PS_SOLID,0,g_swell_ctheme.checkbox_fg); + HGDIOBJ oldPen = SelectObject(ps.hdc,pen); + int st = (int)(s->state&3); + if (sf == BS_AUTOCHECKBOX || sf == BS_AUTO3STATE) + { + if (st==3||(st==2 && (hwnd->m_style & 0xf) == BS_AUTOCHECKBOX)) st=1; + + Draw3DBox(ps.hdc,&tr, + st==2?g_swell_ctheme.checkbox_inter: + g_swell_ctheme.checkbox_bg, + g_swell_ctheme.button_shadow, + g_swell_ctheme.button_hilight); + + if (st == 1||pressed) + { + RECT ar=tr; + ar.left+=SWELL_UI_SCALE(2); + ar.right-=SWELL_UI_SCALE(3); + ar.top+=SWELL_UI_SCALE(2); + ar.bottom-=SWELL_UI_SCALE(3); + if (pressed) + { + const int rsz=chksz/4; + ar.left+=rsz; + ar.top+=rsz; + ar.right-=rsz; + ar.bottom-=rsz; + } + MoveToEx(ps.hdc,ar.left,ar.top,NULL); + LineTo(ps.hdc,ar.right,ar.bottom); + MoveToEx(ps.hdc,ar.right,ar.top,NULL); + LineTo(ps.hdc,ar.left,ar.bottom); + } + } + else if (sf == BS_AUTORADIOBUTTON) + { + HBRUSH br = CreateSolidBrush(g_swell_ctheme.checkbox_bg); + HGDIOBJ oldBrush = SelectObject(ps.hdc,br); + Ellipse(ps.hdc,tr.left+1,tr.top+1,tr.right-1,tr.bottom-1); + SelectObject(ps.hdc,oldBrush); + DeleteObject(br); + if (st) + { + const int amt = (tr.right-tr.left)/6 + SWELL_UI_SCALE(2); + br = CreateSolidBrush(g_swell_ctheme.checkbox_fg); + oldBrush = SelectObject(ps.hdc,br); + Ellipse(ps.hdc,tr.left+amt,tr.top+amt,tr.right-amt,tr.bottom-amt); + SelectObject(ps.hdc,oldBrush); + DeleteObject(br); + } + } + SelectObject(ps.hdc,oldPen); + DeleteObject(pen); + r.left += chksz + SWELL_UI_SCALE(5); + SetTextColor(ps.hdc, + hwnd->m_enabled ? g_swell_ctheme.checkbox_text : + g_swell_ctheme.checkbox_text_disabled); + } + + if (s->bitmap) + { + BITMAP inf={0,}; + GetObject(s->bitmap,sizeof(BITMAP),&inf); + RECT cr; + cr.left = (r.right+r.left - inf.bmWidth)/2; + cr.top = (r.bottom+r.top - inf.bmHeight)/2; + cr.right = cr.left+inf.bmWidth; + cr.bottom = cr.top+inf.bmHeight; + DrawImageInRect(ps.hdc,s->bitmap,&cr); + } + else + { + char buf[512]; + buf[0]=0; + GetWindowText(hwnd,buf,sizeof(buf)); + if (buf[0]) DrawText(ps.hdc,buf,-1,&r,f); + } + + EndPaint(hwnd,&ps); + } + } + return 0; + case WM_TIMER: + if (wParam==1) + { + if (!fast_has_focus(hwnd)) + { + KillTimer(hwnd,1); + InvalidateRect(hwnd,NULL,FALSE); + } + } + break; + case BM_GETCHECK: + if (hwnd) + { + buttonWindowState *s = (buttonWindowState*)hwnd->m_private_data; + return (s->state&3); + } + return 0; + case BM_GETIMAGE: + if (wParam != IMAGE_BITMAP && wParam != IMAGE_ICON) return 0; // ignore unknown types + { + buttonWindowState *s = (buttonWindowState*)hwnd->m_private_data; + return (LRESULT) s->bitmap; + } + return 0; + case BM_SETIMAGE: + if (wParam == IMAGE_BITMAP || wParam == IMAGE_ICON) + { + buttonWindowState *s = (buttonWindowState*)hwnd->m_private_data; + LRESULT res = (LRESULT)s->bitmap; + s->bitmap = (HICON)lParam; + s->bitmap_mode = wParam; + InvalidateRect(hwnd,NULL,FALSE); + return res; + } + return 0; + case BM_SETCHECK: + if (hwnd) + { + buttonWindowState *s = (buttonWindowState*)hwnd->m_private_data; + int check = (int)wParam; + INT_PTR op = s->state; + s->state=(check > 2 || check<0 ? 1 : (check&3)) | (s->state&~3); + if (s->state == op) break; + } + else + { + break; + } + // fall through (invalidating) + case WM_USER+100: + case WM_CAPTURECHANGED: + case WM_SETTEXT: + InvalidateRect(hwnd,NULL,FALSE); + break; + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +static HWND swell_makeButton(HWND owner, int idx, RECT *tr, const char *label, bool vis, int style) +{ + HWND hwnd = new HWND__(owner,idx,tr,label,vis,buttonWindowProc); + hwnd->m_private_data = (INT_PTR) new buttonWindowState; + hwnd->m_classname = "Button"; + hwnd->m_style = style|WS_CHILD; + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + return hwnd; +} + +static LRESULT WINAPI groupWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + + const char *buf = hwnd->m_title.Get(); + int th=SWELL_UI_SCALE(20); + int tw=0; + int xp=0; + if (buf && buf[0]) + { + RECT tr={0,}; + DrawText(ps.hdc,buf,-1,&tr,DT_CALCRECT); + th=tr.bottom-tr.top; + tw=tr.right-tr.left; + } + if (hwnd->m_style & SS_CENTER) + { + xp = r.right/2 - tw/2; + } + else if (hwnd->m_style & SS_RIGHT) + { + xp = r.right - tw; + } + const int sc8 = SWELL_UI_SCALE(8); + if (xp r.right-sc8) tw=r.right-sc8-xp; + + HPEN pen = CreatePen(PS_SOLID,0,g_swell_ctheme.group_hilight); + HPEN pen2 = CreatePen(PS_SOLID,0,g_swell_ctheme.group_shadow); + HGDIOBJ oldPen=SelectObject(ps.hdc,pen); + + MoveToEx(ps.hdc,xp - (tw?sc8/2:0) + 1,th/2+1,NULL); + LineTo(ps.hdc,1,th/2+1); + LineTo(ps.hdc,1,r.bottom-1); + LineTo(ps.hdc,r.right-1,r.bottom-1); + LineTo(ps.hdc,r.right-1,th/2+1); + LineTo(ps.hdc,xp+tw + (tw?sc8/2:0),th/2+1); + + SelectObject(ps.hdc,pen2); + + MoveToEx(ps.hdc,xp - (tw?sc8/2:0),th/2,NULL); + LineTo(ps.hdc,0,th/2); + LineTo(ps.hdc,0,r.bottom-2); + LineTo(ps.hdc,r.right-2,r.bottom-2); + LineTo(ps.hdc,r.right-2,th/2); + LineTo(ps.hdc,xp+tw + (tw?4:0),th/2); + + + SelectObject(ps.hdc,oldPen); + DeleteObject(pen); + DeleteObject(pen2); + + SetTextColor(ps.hdc,g_swell_ctheme.group_text); + SetBkMode(ps.hdc,TRANSPARENT); + r.left = xp; + r.right = xp+tw; + r.bottom = th; + if (buf && buf[0]) DrawText(ps.hdc,buf,-1,&r,DT_LEFT|DT_TOP); + EndPaint(hwnd,&ps); + } + } + return 0; + case WM_SETTEXT: + InvalidateRect(hwnd,NULL,TRUE); + break; + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MOUSEMOVE: + if (GET_Y_LPARAM(lParam) >= SWELL_UI_SCALE(20)) + { + HWND par = GetParent(hwnd); + if (par) + { + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + ClientToScreen(hwnd,&pt); + ScreenToClient(par,&pt); + return SendMessage(par,msg,wParam,MAKELPARAM(pt.x,pt.y)); + } + } + break; + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +static void calcScroll(int wh, int totalw, int scroll_x, int *thumbsz, int *thumbpos) +{ + const double isz = wh / (double) totalw; + int sz = (int) (wh * isz + 0.5); + if (sz < g_swell_ctheme.scrollbar_min_thumb_height) sz=g_swell_ctheme.scrollbar_min_thumb_height; + + *thumbpos = (int) (scroll_x * isz + 0.5); + if (*thumbpos >= wh-sz) *thumbpos = wh-sz; + + *thumbsz = sz; +} + +static void drawHorizontalScrollbar(HDC hdc, RECT cr, int vieww, int totalw, int scroll_x) +{ + if (totalw <= vieww) return; + + int thumbsz, thumbpos; + calcScroll(vieww,totalw,scroll_x,&thumbsz, &thumbpos); + + HBRUSH br = CreateSolidBrush(g_swell_ctheme.scrollbar_fg); + HBRUSH br2 = CreateSolidBrush(g_swell_ctheme.scrollbar_bg); + RECT fr = { cr.left, cr.bottom - g_swell_ctheme.scrollbar_width, cr.left + thumbpos, cr.bottom }; + if (fr.right>fr.left) FillRect(hdc,&fr,br2); + + fr.left = fr.right; + fr.right = fr.left + thumbsz; + if (fr.right>fr.left) FillRect(hdc,&fr,br); + + fr.left = fr.right; + fr.right = cr.right; + if (fr.right>fr.left) FillRect(hdc,&fr,br2); + + DeleteObject(br); + DeleteObject(br2); +} + +static void drawVerticalScrollbar(HDC hdc, RECT cr, int totalh, int scroll_y) +{ + if (totalh <= cr.bottom-cr.top) return; + + int thumbsz, thumbpos; + calcScroll(cr.bottom-cr.top,totalh,scroll_y,&thumbsz, &thumbpos); + + HBRUSH br = CreateSolidBrush(g_swell_ctheme.scrollbar_fg); + HBRUSH br2 = CreateSolidBrush(g_swell_ctheme.scrollbar_bg); + RECT fr = { cr.right - g_swell_ctheme.scrollbar_width, cr.top, cr.right,cr.top+thumbpos}; + if (fr.bottom>fr.top) FillRect(hdc,&fr,br2); + + fr.top = fr.bottom; + fr.bottom = fr.top + thumbsz; + if (fr.bottom>fr.top) FillRect(hdc,&fr,br); + + fr.top = fr.bottom; + fr.bottom = cr.bottom; + if (fr.bottom>fr.top) + { + FillRect(hdc,&fr,br2); + + fr.top=fr.bottom-1; //add a little bottom border in case there is a horizontal scrollbar too + FillRect(hdc,&fr,br2); + } + + DeleteObject(br); + DeleteObject(br2); +} + +static int editMeasureLineLength(HDC hdc, const char *str, int str_len) +{ + RECT tmp = {0,}; + DrawText(hdc,str,str_len,&tmp,DT_NOPREFIX|DT_SINGLELINE|DT_CALCRECT|DT_RIGHT); + return tmp.right; +} + + +int swell_getLineLength(const char *buf, int *post_skip, int wrap_maxwid, HDC hdc) +{ + int lb=0; + int ps = 0; + while (buf[lb] && buf[lb] != '\r' && buf[lb] != '\n') lb++; + + if (wrap_maxwid > g_swell_ctheme.scrollbar_width && hdc && lb>0) + { + wrap_maxwid -= g_swell_ctheme.scrollbar_width; + + // step through a word at a time and find the most that can fit + int x=0,best_len=0,sumw=0; + for (;;) + { + while (x < lb && buf[x] > 0 && isspace(buf[x])) x++; + while (x < lb && (buf[x]<0 || !isspace(buf[x]))) x++; + const int thisw = editMeasureLineLength(hdc,buf+best_len,x-best_len); + if (thisw+sumw > wrap_maxwid) break; + sumw+=thisw; + best_len=x; + if (x >= lb) break; + } + if (best_len == 0) + { + // todo: split individual word (ugh) + if (x>0) lb = x; + } + else + lb = best_len; + + while (buf[ps+lb] == '\t' || buf[ps+lb] == ' ') ps++; // skip any trailing whitespace + } + if (buf[ps+lb] == '\r') ps++; + if (buf[ps+lb] == '\n') ps++; + *post_skip = ps; + return lb; +} + +#define EDIT_ALLOW_MULTILINE_CACHE(wwrap,hwnd,tlen) \ + ((wwrap)>0 && ((hwnd)->m_style & (ES_READONLY|ES_MULTILINE)) == (ES_READONLY|ES_MULTILINE) && (tlen)>10000) + +struct __SWELL_editControlState +{ + __SWELL_editControlState() : cache_linelen_bytes(8192) + { + cursor_timer=0; + cursor_state=0; + sel1=sel2=-1; + cursor_pos=0; + scroll_x=scroll_y=0; + max_height=0; + max_width=0; + cache_linelen_strlen = cache_linelen_w = 0; + m_disable_contextmenu = false; + } + ~__SWELL_editControlState() {} + + int cursor_pos, sel1,sel2; // in character pos (*not* bytepos) + int cursor_state; + int cursor_timer; + int scroll_x, scroll_y; + int max_height; // only used in multiline + int max_width; + + // used for caching line lengths for multiline word-wrapping edit controls + int cache_linelen_w, cache_linelen_strlen; + WDL_TypedBuf cache_linelen_bytes; + + bool m_disable_contextmenu; + + bool deleteSelection(WDL_FastString *fs); + int getSelection(WDL_FastString *fs, const char **ptrOut) const; + void moveCursor(int cp); // extends selection if shift is held, otherwise clears + void onMouseDown(int &capmode_state, int last_cursor); + void onMouseDrag(int &capmode_state, int p); + + void autoScrollToOffset(HWND hwnd, int charpos, bool is_multiline, bool word_wrap); +}; + + +static int utf8fs_charpos_to_bytepos(const WDL_FastString *fs, int charpos) +{ + return charpos < fs->GetLength() ? WDL_utf8_charpos_to_bytepos(fs->Get(),charpos) : fs->GetLength(); +} + + + +static bool editGetCharPos(HDC hdc, const char *str, int singleline_len, int charpos, int line_h, POINT *pt, int word_wrap, + __SWELL_editControlState *es, HWND hwnd) +{ + int bytepos = WDL_utf8_charpos_to_bytepos(str,charpos); + int ypos = 0; + if (singleline_len >= 0) + { + if (bytepos > singleline_len) return false; + pt->y=0; + pt->x=editMeasureLineLength(hdc,str,bytepos); + return true; + } + + + int *use_cache = NULL, use_cache_len = 0; + int title_len = 0; + const bool allow_cache = hwnd && es && EDIT_ALLOW_MULTILINE_CACHE(word_wrap,hwnd,title_len = (int)strlen(str)); + if (allow_cache && + es->cache_linelen_w == word_wrap && + es->cache_linelen_strlen == title_len) + { + use_cache = es->cache_linelen_bytes.Get(); + use_cache_len = es->cache_linelen_bytes.GetSize(); + } + + while (*str) + { + int pskip = 0, lb; + if (!use_cache || use_cache_len < 1) + { + lb = swell_getLineLength(str,&pskip,word_wrap,hdc); + } + else + { + lb = *use_cache++; + if (WDL_NOT_NORMALLY(lb < 1)) break; + } + if (bytepos < lb+pskip) + { + pt->x=editMeasureLineLength(hdc,str,bytepos); + pt->y=ypos; + return true; + } + str += lb+pskip; + bytepos -= lb+pskip; + if (*str || (pskip>0 && str[-1] == '\n')) ypos += line_h; + } + pt->x=0; + pt->y=ypos; + return true; +} + + +static int editHitTestLine(HDC hdc, const char *str, int str_len, int xpos, int ypos) +{ + RECT mr={0,}; + DrawText(hdc,str_len == 0 ? " " : str,wdl_max(str_len,1),&mr,DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT); + + if (xpos >= mr.right) return str_len; + if (xpos < 1) return 0; + + // could bsearch, but meh + int x = 0; + while (x < str_len) + { + memset(&mr,0,sizeof(mr)); + const int clen = wdl_utf8_parsechar(str+x,NULL); + DrawText(hdc,str,x+clen,&mr,DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT|DT_RIGHT/*swell-only flag*/); + if (xpos < mr.right) break; + x += clen; + } + return x; +} + +static int editHitTest(HDC hdc, const char *str, int singleline_len, int xpos, int ypos, int word_wrap, + __SWELL_editControlState *es, HWND hwnd) +{ + if (singleline_len >= 0) return editHitTestLine(hdc,str,singleline_len,xpos,1); + + const char *buf = str; + int bytepos = 0; + RECT tmp={0}; + const int line_h = DrawText(hdc," ",1,&tmp,DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT); + + int *use_cache = NULL, use_cache_len = 0; + int title_len = 0; + const bool allow_cache = hwnd && es && EDIT_ALLOW_MULTILINE_CACHE(word_wrap,hwnd,title_len = (int)strlen(str)); + if (allow_cache && + es->cache_linelen_w == word_wrap && + es->cache_linelen_strlen == title_len) + { + use_cache = es->cache_linelen_bytes.Get(); + use_cache_len = es->cache_linelen_bytes.GetSize(); + } + + for (;;) + { + int pskip=0; + int lb; + + if (!use_cache || use_cache_len < 1) + { + lb = swell_getLineLength(buf,&pskip,word_wrap,hdc); + } + else + { + lb = *use_cache++; + if (WDL_NOT_NORMALLY(lb < 1)) return bytepos; + } + + if (ypos < line_h) return bytepos + editHitTestLine(hdc,buf,lb, xpos,ypos); + ypos -= line_h; + + if (!buf[0] || !buf[lb]) return bytepos + lb; + + bytepos += lb+pskip; + buf += lb+pskip; + } +} + + +bool __SWELL_editControlState::deleteSelection(WDL_FastString *fs) +{ + if (sel1>=0 && sel2 > sel1) + { + int pos1 = utf8fs_charpos_to_bytepos(fs,sel1); + int pos2 = utf8fs_charpos_to_bytepos(fs,sel2); + if (pos2 == pos1) return false; + + int cp = utf8fs_charpos_to_bytepos(fs,cursor_pos); + fs->DeleteSub(pos1,pos2-pos1); + if (cp >= pos2) cp -= pos2-pos1; + else if (cp >= pos1) cp=pos1; + cursor_pos = WDL_utf8_bytepos_to_charpos(fs->Get(),cp); + + sel1=sel2=-1; + return true; + } + return false; +} + +int __SWELL_editControlState::getSelection(WDL_FastString *fs, const char **ptrOut) const +{ + if (sel1>=0 && sel2>sel1) + { + int pos1 = utf8fs_charpos_to_bytepos(fs,sel1); + int pos2 = utf8fs_charpos_to_bytepos(fs,sel2); + if (ptrOut) *ptrOut = fs->Get()+pos1; + return pos2-pos1; + } + return 0; +} +void __SWELL_editControlState::moveCursor(int cp) // extends selection if shift is held, otherwise clears +{ + if (GetAsyncKeyState(VK_SHIFT)&0x8000) + { + if (sel1>=0 && sel2>sel1 && (cursor_pos==sel1 || cursor_pos==sel2)) + { + if (cursor_pos==sel1) sel1=cp; + else sel2=cp; + if (sel2 sel2) + { + sel1=sel2; + sel2=last_cursor; + capmode_state = 3; + } + } + else + { + sel1=sel2=cursor_pos; + } +} + +void __SWELL_editControlState::onMouseDrag(int &capmode_state, int p) +{ + if (sel1 == sel2) + { + if (p < sel1) + { + sel1 = p; + capmode_state = 3; + } + else if (p > sel2) + { + sel2 = p; + capmode_state = 4; + } + } + else if (capmode_state == 3) + { + if (p < sel2) sel1 = p; + else if (p > sel2) + { + sel1 = sel2; + sel2 = p; + capmode_state=4; + } + } + else + { + if (p > sel1) sel2 = p; + else if (p < sel1) + { + sel2 = sel1; + sel1 = p; + capmode_state=3; + } + } +} + +void __SWELL_editControlState::autoScrollToOffset(HWND hwnd, int charpos, bool is_multiline, bool word_wrap) +{ + if (!hwnd) return; + HDC hdc = GetDC(hwnd); + if (!hdc) return; + RECT tmp={0,}; + const int line_h = DrawText(hdc," ",1,&tmp,DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX); + + GetClientRect(hwnd,&tmp); + if (is_multiline) + { + tmp.right -= g_swell_ctheme.scrollbar_width; + if (!word_wrap) tmp.bottom -= g_swell_ctheme.scrollbar_width; + } + + int wwrap = word_wrap?tmp.right:0; + POINT pt={0,}; + if (editGetCharPos(hdc, hwnd->m_title.Get(), + is_multiline? -1:hwnd->m_title.GetLength(), charpos, line_h, &pt, + wwrap, is_multiline?this:NULL,hwnd)) + { + if (!word_wrap) + { + const int padsz = wdl_max(tmp.right - line_h,line_h); + if (pt.x > scroll_x+padsz) scroll_x = pt.x - padsz; + if (pt.x < scroll_x) scroll_x=pt.x; + } + if (is_multiline) + { + if (charpos > hwnd->m_title.GetLength()) + tmp.bottom -= line_h; + if (pt.y+line_h > scroll_y+tmp.bottom) scroll_y = pt.y - tmp.bottom + line_h; + if (pt.y < scroll_y) scroll_y=pt.y; + } + if (scroll_y < 0) scroll_y=0; + if (scroll_x < 0) scroll_x=0; + } + ReleaseDC(hwnd,hdc); +} + +static bool is_word_char(char c) +{ + return c<0/*all utf-8 chars are word chars*/ || isalnum(c) || c == '_'; +} + +static int scanWord(const char *buf, int bytepos, int dir) +{ + if (dir < 0 && !bytepos) return 0; + if (dir > 0 && !buf[bytepos]) return bytepos; + + if (!buf[bytepos] && bytepos > 0) bytepos--; + + const unsigned char *bytebuf = (const unsigned char*) buf; + if (dir < 0) + { + const bool cc = is_word_char(buf[--bytepos]); + while (bytepos > 0 && is_word_char(buf[bytepos-1]) == cc) bytepos--; + while (bytepos > 0 && bytebuf[bytepos] >= 0x80 && bytebuf[bytepos] < 0xC0) bytepos--; // skip any UTF-8 continuation bytes + } + else + { + const bool cc = is_word_char(buf[bytepos]); + while (buf[bytepos+1] && is_word_char(buf[bytepos+1]) == cc) bytepos++; + bytepos++; + while (bytebuf[bytepos] >= 0x80 && bytebuf[bytepos] < 0xC0) bytepos++; // skip any UTF-8 continuation bytes + } + + return bytepos; +} + +static LRESULT OnEditKeyDown(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, + bool wantReturn, bool isMultiLine, __SWELL_editControlState *es, bool isEditCtl) +{ + if (lParam & (FCONTROL|FALT|FLWIN)) + { + if (lParam == (FVIRTKEY | FCONTROL)) + { + if (wParam == 'C' || wParam == 'X') + { + const char *s = NULL; + int slen = es->getSelection(&hwnd->m_title,&s); + if (slen > 0 && s) + { + if (!isEditCtl || !(hwnd->m_style & ES_PASSWORD)) + { + OpenClipboard(hwnd); + HANDLE h = GlobalAlloc(0,slen+1); + if (h) + { + memcpy((char*)h,s,slen); + ((char*)h)[slen] = 0; + SetClipboardData(CF_TEXT,h); + } + CloseClipboard(); + if (h && wParam == 'X' && (!isEditCtl || !(hwnd->m_style & ES_READONLY))) + { + es->deleteSelection(&hwnd->m_title); + return 7; + } + } + } + } + else if (wParam == 'V' && !(hwnd->m_style & ES_READONLY)) + { + OpenClipboard(hwnd); + HANDLE h = GetClipboardData(CF_TEXT); + const char *s = NULL; + if (h) + { + s = (const char*)GlobalLock(h); + if (s) + { + es->deleteSelection(&hwnd->m_title); + int bytepos = utf8fs_charpos_to_bytepos(&hwnd->m_title,es->cursor_pos); + hwnd->m_title.Insert(s,bytepos); + if (!(hwnd->m_style&ES_MULTILINE)) + { + char *p = (char *)hwnd->m_title.Get() + bytepos; + char *ep = p + strlen(s); + while (*p && p < ep) { if (*p == '\r' || *p == '\n') *p=' '; p++; } + } + es->cursor_pos += WDL_utf8_get_charlen(s); + GlobalUnlock(h); + } + } + CloseClipboard(); + if (s) return 7; + } + else if (wParam == 'A') + { + es->sel1 = 0; + es->cursor_pos = es->sel2 = WDL_utf8_get_charlen(hwnd->m_title.Get()); + return 2; + } + } + if (lParam & (FALT | FLWIN)) return 0; + } + else + { + if ((lParam & FVIRTKEY) && wParam == VK_TAB) return 0; // pass through to window + + const bool is_numpad = wParam >= VK_NUMPAD0 && wParam <= VK_DIVIDE; + if (wParam >= 32 && (!(lParam & FVIRTKEY) || swell_is_virtkey_char((int)wParam) || is_numpad)) + { + if (lParam & FVIRTKEY) + { + if (wParam >= 'A' && wParam <= 'Z') + { + if ((lParam&FSHIFT) ^ (swell_is_likely_capslock?0:FSHIFT)) wParam += 'a' - 'A'; + } + else if (is_numpad) + { + if (wParam <= VK_NUMPAD9) wParam += '0' - VK_NUMPAD0; + else wParam += '*' - VK_MULTIPLY; + } + } + + if (hwnd->m_style & ES_READONLY) return 1; + + char b[8]; + WDL_MakeUTFChar(b,wParam,sizeof(b)); + es->deleteSelection(&hwnd->m_title); + int bytepos = utf8fs_charpos_to_bytepos(&hwnd->m_title,es->cursor_pos); + hwnd->m_title.Insert(b,bytepos); + es->cursor_pos++; + return 7; + } + } + + if (es && (lParam & FVIRTKEY)) switch (wParam) + { + case VK_NEXT: + case VK_PRIOR: + if (!isMultiLine) break; + case VK_UP: + case VK_DOWN: + if (isMultiLine) + { + HDC hdc=GetDC(hwnd); + if (hdc) + { + RECT tmp={0}; + const int line_h = DrawText(hdc," ",1,&tmp,DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT); + POINT pt; + GetClientRect(hwnd,&tmp); + const int wwrap = (hwnd->m_style & ES_AUTOHSCROLL) ? 0 : tmp.right - g_swell_ctheme.scrollbar_width; + if (editGetCharPos(hdc, hwnd->m_title.Get(), -1, es->cursor_pos, line_h, &pt, wwrap, isEditCtl ? es : NULL, hwnd)) + { + if (wParam == VK_UP) pt.y -= line_h/2; + else if (wParam == VK_NEXT) + { + int ey = es->scroll_y + tmp.bottom - (wwrap?0:g_swell_ctheme.scrollbar_width) - line_h; + if (pt.y < ey-line_h) pt.y = ey; + else pt.y = ey + tmp.bottom - line_h - (wwrap?0:g_swell_ctheme.scrollbar_width); + } + else if (wParam == VK_PRIOR) + { + if (pt.y > es->scroll_y) pt.y = es->scroll_y; + else pt.y = es->scroll_y - (tmp.bottom-line_h/2 - g_swell_ctheme.scrollbar_width); + } + else pt.y += line_h + line_h/2; + int nextpos = editHitTest(hdc, hwnd->m_title.Get(), -1,pt.x,pt.y,wwrap,es,hwnd); + es->moveCursor(WDL_utf8_bytepos_to_charpos(hwnd->m_title.Get(),nextpos)); + } + ReleaseDC(hwnd,hdc); + } + return 3; + } + // fall through + case VK_HOME: + case VK_END: + if (isMultiLine) + { + const char *buf = hwnd->m_title.Get(); + int lpos = 0, wwrap=0; + HDC hdc=NULL; + if (!(hwnd->m_style & ES_AUTOHSCROLL)) + { + hdc = GetDC(hwnd); + RECT r; + GetClientRect(hwnd,&r); + if (hdc) wwrap = r.right - g_swell_ctheme.scrollbar_width; + } + const int cbytepos = WDL_utf8_charpos_to_bytepos(buf,es->cursor_pos); + for (;;) + { + int ps=0, lb = swell_getLineLength(buf+lpos, &ps,wwrap,hdc); + if (!buf[lpos] || (cbytepos >= lpos && cbytepos < lpos+lb+ps + (buf[lpos+lb+ps]?0:1))) + { + if (wParam == VK_HOME) es->moveCursor(WDL_utf8_bytepos_to_charpos(buf,lpos)); + else es->moveCursor(WDL_utf8_bytepos_to_charpos(buf,lpos+lb)); + return 3; + } + lpos += lb+ps; + } + if (hdc) ReleaseDC(hwnd,hdc); + } + + if (wParam == VK_UP || wParam == VK_HOME) es->moveCursor(0); + else es->moveCursor(WDL_utf8_get_charlen(hwnd->m_title.Get())); + return 3; + case VK_LEFT: + { + int cp = es->cursor_pos; + if (cp > 0) + { + const char *buf=hwnd->m_title.Get(); + if (lParam & FCONTROL) + { + cp = WDL_utf8_bytepos_to_charpos(buf, + scanWord(buf,WDL_utf8_charpos_to_bytepos(buf,cp),-1)); + } + else + { + const int p = WDL_utf8_charpos_to_bytepos(buf,--cp); + if (cp > 0 && p > 0 && buf[p] == '\n' && buf[p-1] == '\r') cp--; + } + } + es->moveCursor(cp); + } + return 3; + case VK_RIGHT: + { + int cp = es->cursor_pos; + if (cp < WDL_utf8_get_charlen(hwnd->m_title.Get())) + { + const char *buf=hwnd->m_title.Get(); + const int p = WDL_utf8_charpos_to_bytepos(buf,cp); + + if (lParam & FCONTROL) + cp = WDL_utf8_bytepos_to_charpos(buf,scanWord(buf,p,1)); + else if (buf[p] == '\r' && buf[p+1] == '\n') + cp+=2; + else + cp++; + } + es->moveCursor(cp); + } + return 3; + case VK_DELETE: + if (hwnd->m_style & ES_READONLY) return 1; + if (hwnd->m_title.GetLength()) + { + if (es->deleteSelection(&hwnd->m_title)) return 7; + + const int bytepos = utf8fs_charpos_to_bytepos(&hwnd->m_title,es->cursor_pos); + if (bytepos < hwnd->m_title.GetLength()) + { + const char *rd = hwnd->m_title.Get()+bytepos; + hwnd->m_title.DeleteSub(bytepos, rd[0] == '\r' && rd[1] == '\n' ? 2 : wdl_utf8_parsechar(rd,NULL)); + return 7; + } + } + return 1; + + case VK_BACK: + if (hwnd->m_style & ES_READONLY) return 1; + if (hwnd->m_title.GetLength()) + { + if (es->deleteSelection(&hwnd->m_title)) return 7; + if (es->cursor_pos > 0) + { + es->cursor_pos--; + const char *buf = hwnd->m_title.Get(); + int bytepos = WDL_utf8_charpos_to_bytepos(buf,es->cursor_pos); + if (bytepos > 0 && buf[bytepos] == '\n' && buf[bytepos-1] == '\r') + { + hwnd->m_title.DeleteSub(bytepos-1, 2); + es->cursor_pos--; + } + else hwnd->m_title.DeleteSub(bytepos, wdl_utf8_parsechar(hwnd->m_title.Get()+bytepos,NULL)); + return 7; + } + } + return 1; + case VK_RETURN: + if (wantReturn) + { + if (hwnd->m_style & ES_READONLY) return 1; + if (es->deleteSelection(&hwnd->m_title)) return 7; + int bytepos = utf8fs_charpos_to_bytepos(&hwnd->m_title,es->cursor_pos); + hwnd->m_title.Insert("\r\n",bytepos); + es->cursor_pos+=2; // skip \r and \n + return 7; + } + return 0; + } + return 0; +} + +static int editControlPaintLine(HDC hdc, const char *str, int str_len, int cursor_pos, int sel1, int sel2, const RECT *r, int dtflags) +{ + // cursor_pos, sel1, sel2 are all byte positions + int rv = 0; + if (str_len>0) + { + RECT outr = *r; + if (sel2 < str_len || sel1 > 0) + { + RECT tmp={0,}; + DrawText(hdc,str,str_len,&tmp,DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX); + rv = tmp.right; + DrawText(hdc,str,str_len,&outr,dtflags|DT_SINGLELINE|DT_NOPREFIX); + } + + const int offs = wdl_max(sel1,0); + const int endptr = wdl_min(sel2,str_len); + if (endptr > offs) + { + SetBkMode(hdc,OPAQUE); + SetBkColor(hdc,g_swell_ctheme.edit_bg_sel); + const int oldc = GetTextColor(hdc); + SetTextColor(hdc,g_swell_ctheme.edit_text_sel); + + RECT tmp={0,}; + DrawText(hdc,str,offs,&tmp,DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX); + outr.left += tmp.right; + DrawText(hdc,str+offs,endptr-offs,&outr,dtflags|DT_SINGLELINE|DT_NOPREFIX); + + SetBkMode(hdc,TRANSPARENT); + SetTextColor(hdc,oldc); + } + } + + if (cursor_pos >= 0 && cursor_pos <= str_len) + { + RECT mr={0,}; + if (cursor_pos>0) DrawText(hdc,str,cursor_pos,&mr,DT_CALCRECT|DT_NOPREFIX|DT_SINGLELINE); + + int oc = GetTextColor(hdc); + SetTextColor(hdc,g_swell_ctheme.edit_cursor); + mr.left = r->left + mr.right - 1; + mr.right = mr.left+1; + mr.top = r->top; + mr.bottom = r->bottom; + DrawText(hdc,"|",1,&mr,dtflags|DT_SINGLELINE|DT_NOPREFIX|DT_NOCLIP); + SetTextColor(hdc,oc); + } + return rv; +} + +static void passwordify(WDL_FastString **s) +{ + const int l = WDL_utf8_get_charlen((*s)->Get()); + if (l>0) + { + static WDL_FastString tmp; + tmp.SetLen(l,false,'*'); + *s = &tmp; + } +} + +static LRESULT WINAPI editWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + __SWELL_editControlState *es = (__SWELL_editControlState*)hwnd->m_private_data; + static int s_capmode_state /* 1=vscroll, 2=hscroll, 3=move sel1, 4=move sel2*/, s_capmode_data1; + switch (msg) + { + case WM_NCDESTROY: + delete es; + hwnd->m_private_data=0; + break; + case WM_CONTEXTMENU: + { + if (es->m_disable_contextmenu) break; + + HMENU menu=CreatePopupMenu(); + MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_TYPE,MFT_STRING, 0, + (UINT) 100, NULL,NULL,NULL,0,(char*)"Copy"}; + + if (!(hwnd->m_style & ES_PASSWORD)) + InsertMenuItem(menu,0,TRUE,&mi); + mi.wID++; + mi.dwTypeData = (char*)"Paste"; + InsertMenuItem(menu,0,TRUE,&mi); + mi.wID++; + mi.dwTypeData = (char*)"Select all"; + InsertMenuItem(menu,0,TRUE,&mi); + POINT p; + GetCursorPos(&p); + + const int a = TrackPopupMenu(menu,TPM_NONOTIFY|TPM_RETURNCMD|TPM_LEFTALIGN,p.x,p.y,0,hwnd,0); + DestroyMenu(menu); + if (a==100) OnEditKeyDown(hwnd,WM_KEYDOWN,'C',FVIRTKEY|FCONTROL,false,false,es,true); + else if (a==101) + { + OnEditKeyDown(hwnd,WM_KEYDOWN,'V',FVIRTKEY|FCONTROL,false,false,es,true); + SendMessage(GetParent(hwnd),WM_COMMAND,(EN_CHANGE<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + } + else if (a==102) SendMessage(hwnd,EM_SETSEL,0,-1); + + if (a) InvalidateRect(hwnd,NULL,FALSE); + + } + return 1; + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + es->cursor_state=1; // next invalidate draws blinky + + // todo: if selection active and not focused, do not mouse hit test + if (msg == WM_LBUTTONDOWN) + { + WDL_FastString *title = &hwnd->m_title; + if (hwnd->m_style & ES_PASSWORD) passwordify(&title); + const bool multiline = (hwnd->m_style & ES_MULTILINE) != 0; + RECT r; + GetClientRect(hwnd,&r); + if (multiline) + { + RECT br = r; + br.right -= g_swell_ctheme.scrollbar_width; + + if (es->max_width > br.right && (hwnd->m_style & ES_AUTOHSCROLL)) + { + if (GET_Y_LPARAM(lParam) >= br.bottom - g_swell_ctheme.scrollbar_width) + { + int xp = GET_X_LPARAM(lParam), xpos = xp; + + int thumbsz, thumbpos; + calcScroll(br.right, es->max_width,es->scroll_x,&thumbsz,&thumbpos); + + if (xpos < thumbpos) xp = thumbpos; + else if (xpos > thumbpos+thumbsz) xp = thumbpos + thumbsz; + + s_capmode_state = 2; + s_capmode_data1 = xp; + SetCapture(hwnd); + if (xpos < thumbpos || xpos > thumbpos+thumbsz) goto forceMouseMove; + return 0; + } + br.bottom -= g_swell_ctheme.scrollbar_width; + } + if (GET_X_LPARAM(lParam)>=br.right && es->max_height > br.bottom) + { + int yp = GET_Y_LPARAM(lParam), ypos = yp; + + int thumbsz, thumbpos; + calcScroll(br.bottom, es->max_height, es->scroll_y,&thumbsz,&thumbpos); + + if (ypos < thumbpos) yp = thumbpos; + else if (ypos > thumbpos+thumbsz) yp = thumbpos + thumbsz; + + s_capmode_state = 1; + s_capmode_data1 = yp; + SetCapture(hwnd); + if (ypos < thumbpos || ypos > thumbpos+thumbsz) goto forceMouseMove; + return 0; + } + } + + + int xo=2; + int yo = multiline ? 2 : 0; + HDC hdc=GetDC(hwnd); + const int last_cursor = es->cursor_pos; + const int wwrap = (hwnd->m_style & (ES_MULTILINE|ES_AUTOHSCROLL)) == ES_MULTILINE ? + r.right - g_swell_ctheme.scrollbar_width : 0; + es->cursor_pos = WDL_utf8_bytepos_to_charpos(title->Get(), + editHitTest(hdc,title->Get(), + multiline?-1:title->GetLength(), + GET_X_LPARAM(lParam)-xo + es->scroll_x, + GET_Y_LPARAM(lParam)-yo + es->scroll_y, + wwrap,es,hwnd) + ); + + if (msg == WM_LBUTTONDOWN) + es->onMouseDown(s_capmode_state,last_cursor); + + ReleaseDC(hwnd,hdc); + + } + SetFocusInternal(hwnd); + if (msg == WM_LBUTTONDOWN) SetCapture(hwnd); + + InvalidateRect(hwnd,NULL,FALSE); + return 0; + case WM_MOUSEWHEEL: + if (es && (hwnd->m_style & ES_MULTILINE)) + { + if ((GetAsyncKeyState(VK_CONTROL)&0x8000) || (GetAsyncKeyState(VK_MENU)&0x8000)) break; // pass modified mousewheel to parent + + RECT r; + GetClientRect(hwnd,&r); + r.right -= g_swell_ctheme.scrollbar_width; + if (es->max_width > r.right && (hwnd->m_style & ES_AUTOHSCROLL)) + { + r.bottom -= g_swell_ctheme.scrollbar_width; + } + const int viewsz = r.bottom; + const int totalsz=es->max_height + g_swell_ctheme.scrollbar_width; + + const int amt = ((short)HIWORD(wParam))/-2; + + const int oldscroll = es->scroll_y; + es->scroll_y += amt; + if (es->scroll_y + viewsz > totalsz) es->scroll_y = totalsz-viewsz; + if (es->scroll_y < 0) es->scroll_y=0; + if (es->scroll_y != oldscroll) + { + InvalidateRect(hwnd,NULL,FALSE); + } + + return 1; + } + break; + case WM_MOUSEMOVE: +forceMouseMove: + if (es && GetCapture()==hwnd) + { + RECT r; + GetClientRect(hwnd,&r); + const bool multiline = (hwnd->m_style & ES_MULTILINE) != 0; + if (multiline) + { + r.right -= g_swell_ctheme.scrollbar_width; + if (es->max_width > r.right && (hwnd->m_style & ES_AUTOHSCROLL)) + { + r.bottom -= g_swell_ctheme.scrollbar_width; + } + } + if (s_capmode_state == 1) + { + int yv = s_capmode_data1; + int amt = GET_Y_LPARAM(lParam) - yv; + + if (amt) + { + const int viewsz = r.bottom; + const int totalsz=es->max_height + g_swell_ctheme.scrollbar_width; + amt = (int)floor(amt * (double)totalsz / (double)viewsz + 0.5); + + const int oldscroll = es->scroll_y; + es->scroll_y += amt; + if (es->scroll_y + viewsz > totalsz) es->scroll_y = totalsz-viewsz; + if (es->scroll_y < 0) es->scroll_y=0; + if (es->scroll_y != oldscroll) + { + s_capmode_data1 = GET_Y_LPARAM(lParam); + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + else if (s_capmode_state == 2) + { + int xv = s_capmode_data1; + int amt = GET_X_LPARAM(lParam) - xv; + + if (amt) + { + const int viewsz = r.right; + const int totalsz=es->max_width + g_swell_ctheme.scrollbar_width; + amt = (int)floor(amt * (double)totalsz / (double)viewsz + 0.5); + + const int oldscroll = es->scroll_x; + es->scroll_x += amt; + if (es->scroll_x + viewsz > totalsz) es->scroll_x = totalsz-viewsz; + if (es->scroll_x < 0) es->scroll_x=0; + if (es->scroll_x != oldscroll) + { + s_capmode_data1 = GET_X_LPARAM(lParam); + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + else if (s_capmode_state == 3 || s_capmode_state == 4) + { + int wwrap=0; + if ((hwnd->m_style & (ES_MULTILINE|ES_AUTOHSCROLL)) == ES_MULTILINE) + { + wwrap = r.right; // already has scrollbar size removed + } + int xo=2; + int yo = multiline ? 2 : 0; + HDC hdc=GetDC(hwnd); + WDL_FastString *title = &hwnd->m_title; + if (hwnd->m_style & ES_PASSWORD) passwordify(&title); + int p = WDL_utf8_bytepos_to_charpos(title->Get(), + editHitTest(hdc,title->Get(), + multiline?-1:title->GetLength(), + GET_X_LPARAM(lParam)-xo + es->scroll_x, + GET_Y_LPARAM(lParam)-yo + es->scroll_y, wwrap,es,hwnd) + ); + ReleaseDC(hwnd,hdc); + + es->onMouseDrag(s_capmode_state,p); + + es->autoScrollToOffset(hwnd,p, + (hwnd->m_style & ES_MULTILINE) != 0, + (hwnd->m_style & (ES_MULTILINE|ES_AUTOHSCROLL)) == ES_MULTILINE); + + + InvalidateRect(hwnd,NULL,FALSE); + } + } + return 0; + case WM_LBUTTONUP: + ReleaseCapture(); + return 0; + case WM_TIMER: + if (es && wParam == 100) + { + if (++es->cursor_state >= 8) es->cursor_state=0; + if (GetFocusIncludeMenus()!=hwnd || es->cursor_state<2) InvalidateRect(hwnd,NULL,FALSE); + } + return 0; + case WM_LBUTTONDBLCLK: + if (es) + { + // technically this should select the word rather than all + es->sel1 = 0; + es->cursor_pos = es->sel2 = WDL_utf8_get_charlen(hwnd->m_title.Get()); + InvalidateRect(hwnd,NULL,FALSE); + } + return 0; + case WM_KEYDOWN: + { + const int osel1 = es && es->sel1 >= 0 && es->sel2 > es->sel1 ? es->sel1 : -1; + + int f = OnEditKeyDown(hwnd,msg,wParam,lParam, + (hwnd->m_style&ES_WANTRETURN) && (hwnd->m_style&ES_MULTILINE), + !!(hwnd->m_style&ES_MULTILINE), + es,true); + if (f) + { + if (f&4) + { + es->cache_linelen_w=0; + SendMessage(GetParent(hwnd),WM_COMMAND,(EN_CHANGE<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + } + if (f&2) + { + if ((hwnd->m_style & (ES_MULTILINE|ES_AUTOHSCROLL)) == ES_AUTOHSCROLL && + osel1 >= 0 && es->cursor_pos > osel1 && es->sel1 < 0) + es->autoScrollToOffset(hwnd,osel1,false,false); + + es->autoScrollToOffset(hwnd,es->cursor_pos, + (hwnd->m_style & ES_MULTILINE) != 0, + (hwnd->m_style & (ES_MULTILINE|ES_AUTOHSCROLL)) == ES_MULTILINE + ); + InvalidateRect(hwnd,NULL,FALSE); + } + return 0; + } + } + break; + case WM_KEYUP: + return 0; + case WM_PAINT: + { + PAINTSTRUCT ps; + + const bool focused = GetFocusIncludeMenus()==hwnd; + if (es) + { + if (focused) + { + if (!es->cursor_timer) { SetTimer(hwnd,100,100,NULL); es->cursor_timer=1; } + } + else + { + if (es->cursor_timer) { KillTimer(hwnd,100); es->cursor_timer=0; } + } + } + + if (BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + RECT orig_r = r; + WDL_FastString *title = &hwnd->m_title; + if (hwnd->m_style & ES_PASSWORD) passwordify(&title); + + Draw3DBox(ps.hdc,&r, + hwnd->m_enabled ? + //(hwnd->m_style & ES_READONLY) ? g_swell_ctheme._3dface : + g_swell_ctheme.edit_bg : + g_swell_ctheme.edit_bg_disabled, + g_swell_ctheme.edit_shadow, + g_swell_ctheme.edit_hilight); + + SetTextColor(ps.hdc, + hwnd->m_enabled ? + //(hwnd->m_style & ES_READONLY) ? g_swell_ctheme.label_text : + g_swell_ctheme.edit_text : + g_swell_ctheme.edit_text_disabled + ); + SetBkMode(ps.hdc,TRANSPARENT); + r.left+=2 - es->scroll_x; r.right-=2; + + const bool do_cursor = es->cursor_state!=0; + const int cursor_pos = focused ? utf8fs_charpos_to_bytepos(title,es->cursor_pos) : -1; + const int sel1 = es->sel1>=0 && focused ? utf8fs_charpos_to_bytepos(title,es->sel1) : -1; + const int sel2 = es->sel2>=0 && focused ? utf8fs_charpos_to_bytepos(title,es->sel2) : -1; + + const bool multiline = (hwnd->m_style & ES_MULTILINE) != 0; + + if (multiline) + { + r.top+=2 - es->scroll_y; + const char *buf = title->Get(), *buf_end = buf + title->GetLength(); + int bytepos = 0; + RECT tmp={0,}; + const int line_h = DrawText(ps.hdc," ",1,&tmp,DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX); + const int wwrap = (hwnd->m_style & ES_AUTOHSCROLL) ? 0 : orig_r.right - g_swell_ctheme.scrollbar_width; + + int *use_cache = NULL, use_cache_len = 0; + const bool allow_cache = EDIT_ALLOW_MULTILINE_CACHE(wwrap,hwnd,title->GetLength()); + if (allow_cache && + es->cache_linelen_w == wwrap && + es->cache_linelen_strlen == title->GetLength()) + { + use_cache = es->cache_linelen_bytes.Get(); + use_cache_len = es->cache_linelen_bytes.GetSize(); + } + else + { + es->cache_linelen_w=allow_cache?wwrap:0; + es->cache_linelen_strlen=allow_cache?title->GetLength():0; + es->cache_linelen_bytes.Resize(0,false); + } + + for (;;) + { + int pskip=0, lb; + + const bool vis = r.top >= -line_h && r.top < orig_r.bottom; + + if (vis || !use_cache || use_cache_len < 1) + { + lb = swell_getLineLength(buf,&pskip,wwrap,ps.hdc); + if (!use_cache && allow_cache) + { + int s = lb+pskip; + es->cache_linelen_bytes.Add(&s,1); + } + } + else + { + lb = *use_cache; + if (WDL_NOT_NORMALLY(lb < 1)) break; + } + + if (use_cache) + { + use_cache++; + use_cache_len--; + } + + if (!*buf && cursor_pos != bytepos) break; + + if (WDL_NOT_NORMALLY(buf+lb+pskip > buf_end)) break; + + if (vis) + { + int wid = editControlPaintLine(ps.hdc,buf,lb, + (do_cursor && cursor_pos >= bytepos && cursor_pos <= bytepos + lb) ? cursor_pos - bytepos : -1, + sel1 >= 0 ? (sel1 - bytepos) : -1, + sel2 >= 0 ? (sel2 - bytepos) : -1, + &r, DT_TOP); + if (wid > es->max_width) es->max_width = wid; + } + + r.top += line_h; + + if (!*buf || !buf[lb]) break; + + bytepos += lb+pskip; + buf += lb+pskip; + } + r.top += es->scroll_y; + es->max_height = r.top; + if (es->max_width > r.right && (hwnd->m_style & ES_AUTOHSCROLL)) + { + drawHorizontalScrollbar(ps.hdc,orig_r, + orig_r.right-orig_r.left - g_swell_ctheme.scrollbar_width, + es->max_width,es->scroll_x); + orig_r.bottom -= g_swell_ctheme.scrollbar_width; + r.bottom -= g_swell_ctheme.scrollbar_width; + } + if (r.top > r.bottom) + { + drawVerticalScrollbar(ps.hdc,orig_r,es->max_height,es->scroll_y); + } + } + else + { + es->max_width = editControlPaintLine(ps.hdc, title->Get(), title->GetLength(), cursor_pos, sel1, sel2, &r, DT_VCENTER); + } + + EndPaint(hwnd,&ps); + } + } + return 0; + case WM_SETTEXT: + if (es) + { + es->cursor_pos = WDL_utf8_get_charlen(hwnd->m_title.Get()); + es->sel1=es->sel2=-1; + es->cache_linelen_w=es->cache_linelen_strlen=0; + if ((hwnd->m_style & (ES_MULTILINE|ES_AUTOHSCROLL))==ES_AUTOHSCROLL && + GetFocus() != hwnd) + es->autoScrollToOffset(hwnd,0,false,false); + } + InvalidateRect(hwnd,NULL,FALSE); + if (hwnd->m_id && hwnd->m_parent) + SendMessage(hwnd->m_parent,WM_COMMAND,(EN_CHANGE<<16)|hwnd->m_id,(LPARAM)hwnd); + break; + case EM_REPLACESEL: + if (lParam && es) + { + const char *p = (const char *)lParam; + int pos = wdl_min(es->sel1,es->sel2); + es->deleteSelection(&hwnd->m_title); + if (*p) + { + if (pos < 0) pos = es->cursor_pos; + int bytepos = utf8fs_charpos_to_bytepos(&hwnd->m_title,pos); + hwnd->m_title.Insert(p,bytepos); + } + InvalidateRect(hwnd,NULL,FALSE); + } + return 0; + case EM_GETSEL: + if (es) + { + if (wParam) * (int *)wParam = es->sel1; + if (lParam) * (int *)lParam = es->sel2; + if (es->sel1 < 0 || es->sel1 > 65535 || + es->sel2 < 0 || es->sel2 > 65535) return -1; + return MAKELPARAM(es->sel1,es->sel2); + } + return 0; + case EM_SETSEL: + if (es) + { + es->sel1 = (int)wParam; + es->sel2 = (int)lParam; + if (!es->sel1 && es->sel2 == -1) es->sel2 = WDL_utf8_get_charlen(hwnd->m_title.Get()); + InvalidateRect(hwnd,NULL,FALSE); + if (es->sel2>=0) + es->autoScrollToOffset(hwnd,es->sel2, + (hwnd->m_style & ES_MULTILINE) != 0, + (hwnd->m_style & (ES_MULTILINE|ES_AUTOHSCROLL)) == ES_MULTILINE); + + } + return 0; + case EM_SCROLL: + if (es) + { + if (wParam == SB_TOP) + { + es->scroll_x=es->scroll_y=0; + InvalidateRect(hwnd,NULL,FALSE); + } + else if (wParam == SB_BOTTOM) + { + es->autoScrollToOffset(hwnd, + hwnd->m_title.GetLength() + 1, // this should be charpos rather than bytepos, but anything longer than m_title.GetLength() ensures we go to the absolute bottom + (hwnd->m_style & ES_MULTILINE) != 0, + (hwnd->m_style & (ES_MULTILINE|ES_AUTOHSCROLL)) == ES_MULTILINE); + InvalidateRect(hwnd,NULL,FALSE); + } + } + return 0; + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +static LRESULT WINAPI progressWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case PBM_DELTAPOS: + if (hwnd->m_private_data) *(int *)hwnd->m_private_data += (int) wParam; // todo: unsigned-ness conversion? unclear + InvalidateRect(hwnd,NULL,FALSE); + break; + case PBM_SETPOS: + if (hwnd->m_private_data) *(int *)hwnd->m_private_data = (int) wParam; + InvalidateRect(hwnd,NULL,FALSE); + break; + case PBM_SETRANGE: + if (hwnd->m_private_data) ((int *)hwnd->m_private_data)[1] = (int) lParam; + InvalidateRect(hwnd,NULL,FALSE); + break; + case WM_NCDESTROY: + free((int *)hwnd->m_private_data); + hwnd->m_private_data=0; + break; + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + + paintDialogBackground(hwnd,&r,ps.hdc); + + if (hwnd->m_private_data) + { + int pos = *(int *)hwnd->m_private_data; + const int range = ((int *)hwnd->m_private_data)[1]; + const int low = LOWORD(range), high=HIWORD(range); + if (pos > low && high > low) + { + if (pos > high) pos=high; + int dx = ((pos-low)*r.right)/(high-low); + r.right = dx; + HBRUSH br = CreateSolidBrush(g_swell_ctheme.progress); + FillRect(ps.hdc,&r,br); + DeleteObject(br); + } + } + + EndPaint(hwnd,&ps); + } + } + break; + } + + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +static LRESULT WINAPI trackbarWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + const int track_h = 10; + static int s_cap_offs; + switch (msg) + { + case WM_CREATE: + { + int *p = (int *)hwnd->m_private_data; + if (p) + { + p[1] = (1000<<16); + p[2] = -1; + } + } + break; + case TBM_SETPOS: + if (hwnd->m_private_data) *(int *)hwnd->m_private_data = (int) lParam; + if (wParam) InvalidateRect(hwnd,NULL,FALSE); + break; + case TBM_GETPOS: + if (hwnd->m_private_data) return *(int *)hwnd->m_private_data; + return 0; + case TBM_SETRANGE: + if (hwnd->m_private_data) ((int *)hwnd->m_private_data)[1] = (int) lParam; + if (wParam) InvalidateRect(hwnd,NULL,FALSE); + break; + case TBM_SETTIC: + if (hwnd->m_private_data) ((int *)hwnd->m_private_data)[2] = (int) lParam; + break; + case WM_NCDESTROY: + free((int *)hwnd->m_private_data); + hwnd->m_private_data=0; + break; + case WM_LBUTTONDBLCLK: + if (hwnd->m_private_data) + { + int *state = (int *)hwnd->m_private_data; + const int range = state[1]; + const int low = LOWORD(range), high=HIWORD(range); + const int to_val = state[2] >= low && state[2] <= high ? state[2] : (low+high)/2; + if (state[0] != to_val) + { + state[0] = to_val; + InvalidateRect(hwnd,NULL,FALSE); + SendMessage(hwnd->m_parent,WM_HSCROLL,SB_ENDSCROLL,(LPARAM)hwnd); + } + } + return 1; + case WM_LBUTTONDOWN: + SetFocusInternal(hwnd); + SetCapture(hwnd); + + if (hwnd->m_private_data) + { + RECT r; + GetClientRect(hwnd,&r); + const int rad = wdl_min((r.bottom-r.top)/2-1,track_h); + int *state = (int *)hwnd->m_private_data; + const int range = state[1]; + const int low = LOWORD(range), high=HIWORD(range); + const int dx = ((state[2]-low)*(r.right-2*rad))/(high-low) + rad; + s_cap_offs=0; + + if (GET_X_LPARAM(lParam) >= dx-rad && GET_X_LPARAM(lParam)<=dx-rad) + { + s_cap_offs = GET_X_LPARAM(lParam)-dx; + return 1; + } + // missed knob, so treat as a move + } + + case WM_MOUSEMOVE: + if (GetCapture()==hwnd && hwnd->m_private_data) + { + RECT r; + GetClientRect(hwnd,&r); + const int rad = wdl_min((r.bottom-r.top)/2-1,track_h); + int *state = (int *)hwnd->m_private_data; + const int range = state[1]; + const int low = LOWORD(range), high=HIWORD(range); + int xpos = GET_X_LPARAM(lParam) - s_cap_offs; + int use_range = (r.right-2*rad); + if (use_range > 0) + { + int newval = low + (xpos - rad) * (high-low) / use_range; + if (newval < low) newval=low; + else if (newval > high) newval=high; + if (newval != state[0]) + { + state[0]=newval; + InvalidateRect(hwnd,NULL,FALSE); + SendMessage(hwnd->m_parent,WM_HSCROLL,0,(LPARAM)hwnd); + } + } + } + return 1; + case WM_LBUTTONUP: + if (GetCapture()==hwnd) + { + ReleaseCapture(); + SendMessage(hwnd->m_parent,WM_HSCROLL,SB_ENDSCROLL,(LPARAM)hwnd); + } + return 1; + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + + HBRUSH hbrush = (HBRUSH) SendMessage(GetParent(hwnd),WM_CTLCOLORSTATIC,(WPARAM)ps.hdc,(LPARAM)hwnd); + if (hbrush == (HBRUSH)(INT_PTR)1) hbrush = NULL; + else + { + if (hbrush) FillRect(ps.hdc,&r,hbrush); + else + { + SWELL_FillDialogBackground(ps.hdc,&r,3); + } + } + + HBRUSH br = CreateSolidBrush(g_swell_ctheme.trackbar_track); + const int rad = wdl_min((r.bottom-r.top)/2-1,track_h); + + RECT sr = r; + sr.left += rad; + sr.right -= rad; + sr.top = (r.top+r.bottom)/2 - rad/2; + sr.bottom = sr.top + rad; + FillRect(ps.hdc,&sr,br); + DeleteObject(br); + + sr.top = (r.top+r.bottom)/2 - rad; + sr.bottom = sr.top + rad*2; + + if (hwnd->m_private_data) + { + const int *state = (const int *)hwnd->m_private_data; + const int range = state[1]; + const int low = LOWORD(range), high=HIWORD(range); + if (high > low) + { + if (state[2] >= low && state[2] <= high) + { + const int dx = ((state[2]-low)*(r.right-2*rad))/(high-low) + rad; + HBRUSH markbr = CreateSolidBrush(g_swell_ctheme.trackbar_mark); + RECT tr = sr; + tr.left = dx; + tr.right = dx+1; + FillRect(ps.hdc,&tr,markbr); + DeleteObject(markbr); + } + int pos = state[0]; + if (pos < low) pos=low; + else if (pos > high) pos = high; + + const int dx = ((pos-low)*(r.right-2*rad))/(high-low) + rad; + + HBRUSH cbr = CreateSolidBrush(g_swell_ctheme.trackbar_knob); + HGDIOBJ oldbr = SelectObject(ps.hdc,cbr); + HGDIOBJ oldpen = SelectObject(ps.hdc,GetStockObject(NULL_PEN)); + Ellipse(ps.hdc, dx-rad, sr.top, dx+rad, sr.bottom); + SelectObject(ps.hdc,oldbr); + SelectObject(ps.hdc,oldpen); + DeleteObject(cbr); + } + } + + EndPaint(hwnd,&ps); + } + } + break; + } + + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +static LRESULT WINAPI labelWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + + SetTextColor(ps.hdc, + hwnd->m_enabled ? g_swell_ctheme.label_text : + g_swell_ctheme.label_text_disabled); + SetBkMode(ps.hdc,TRANSPARENT); + + paintDialogBackground(hwnd,&r,ps.hdc); + + const char *text = hwnd->m_title.Get(); + switch (hwnd->m_style & SS_TYPEMASK) + { + case SS_ETCHEDHORZ: + case SS_ETCHEDVERT: + case SS_ETCHEDFRAME: + { + HPEN pen = CreatePen(PS_SOLID,0,g_swell_ctheme.group_hilight); + HPEN pen2 = CreatePen(PS_SOLID,0,g_swell_ctheme.group_shadow); + HGDIOBJ oldPen=SelectObject(ps.hdc,pen); + + switch (hwnd->m_style & SS_TYPEMASK) + { + case SS_ETCHEDHORZ: + MoveToEx(ps.hdc,0,1,NULL); + LineTo(ps.hdc,r.right-1,1); + SelectObject(ps.hdc,pen2); + MoveToEx(ps.hdc,0,0,NULL); + LineTo(ps.hdc,r.right-1,0); + break; + case SS_ETCHEDVERT: + MoveToEx(ps.hdc,1,0,NULL); + LineTo(ps.hdc,1,r.bottom-1); + SelectObject(ps.hdc,pen2); + MoveToEx(ps.hdc,0,0,NULL); + LineTo(ps.hdc,0,r.bottom-1); + break; + case SS_ETCHEDFRAME: + MoveToEx(ps.hdc,1,1,NULL); + LineTo(ps.hdc,1,r.bottom-1); + LineTo(ps.hdc,r.right-1,r.bottom-1); + LineTo(ps.hdc,r.right-1,1); + LineTo(ps.hdc,1,1); + SelectObject(ps.hdc,pen2); + MoveToEx(ps.hdc,0,0,NULL); + LineTo(ps.hdc,0,r.bottom-2); + LineTo(ps.hdc,r.right-2,r.bottom-2); + LineTo(ps.hdc,r.right-2,0); + LineTo(ps.hdc,0,0); + break; + } + + SelectObject(ps.hdc,oldPen); + DeleteObject(pen); + DeleteObject(pen2); + + text = ""; + } + break; + case SS_LEFT: + if (text[0]) + { + RECT tmp={0,}; + const int line_h = DrawText(ps.hdc," ",1,&tmp,DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT); + if (r.bottom > line_h*5/3) + { + int loffs=0; + while (text[loffs] && r.top < r.bottom) + { + int post=0, lb=swell_getLineLength(text+loffs, &post, r.right, ps.hdc); + if (lb>0) + DrawText(ps.hdc,text+loffs,lb,&r,DT_TOP|DT_SINGLELINE|DT_LEFT); + r.top += line_h; + loffs+=lb+post; + } + text = ""; + } + } + break; + } + + if (text[0]) + { + DrawText(ps.hdc,text,-1,&r, + ((hwnd->m_style & SS_CENTER) ? DT_CENTER : + (hwnd->m_style & SS_RIGHT) ? DT_RIGHT : 0)| + DT_VCENTER); + } + EndPaint(hwnd,&ps); + } + } + return 0; + case WM_SETTEXT: + InvalidateRect(hwnd,NULL,FALSE); + break; + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + if (hwnd->m_style & SS_NOTIFY) + SendMessage(GetParent(hwnd),WM_COMMAND, + ((msg==WM_LBUTTONDOWN?STN_CLICKED:STN_DBLCLK)<<16)|(hwnd->m_id&0xffff),0); + return 1; + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +struct __SWELL_ComboBoxInternalState_rec +{ + __SWELL_ComboBoxInternalState_rec(const char *_desc=NULL, LPARAM _parm=0) { desc=_desc?strdup(_desc):NULL; parm=_parm; } + ~__SWELL_ComboBoxInternalState_rec() { free(desc); } + char *desc; + LPARAM parm; + static int cmp(const __SWELL_ComboBoxInternalState_rec **a, const __SWELL_ComboBoxInternalState_rec **b) { return strcmp((*a)->desc, (*b)->desc); } +}; + +class __SWELL_ComboBoxInternalState +{ + public: + __SWELL_ComboBoxInternalState() { selidx=-1; } + ~__SWELL_ComboBoxInternalState() { } + + int selidx; + WDL_PtrList_DeleteOnDestroy<__SWELL_ComboBoxInternalState_rec> items; + __SWELL_editControlState editstate; +}; + +static LRESULT WINAPI comboWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static const int buttonwid = 16; // used in edit combobox + static int s_capmode_state; + __SWELL_ComboBoxInternalState *s = (__SWELL_ComboBoxInternalState*)hwnd->m_private_data; + if (msg >= CB_ADDSTRING && msg <= CB_INITSTORAGE) + { + if (s) + { + switch (msg) + { + case CB_ADDSTRING: + + if (!(hwnd->m_style & CBS_SORT)) + { + s->items.Add(new __SWELL_ComboBoxInternalState_rec((const char *)lParam)); + return s->items.GetSize() - 1; + } + else + { + __SWELL_ComboBoxInternalState_rec *r=new __SWELL_ComboBoxInternalState_rec((const char *)lParam); + // find position of insert for wParam + bool m; + int idx = s->items.LowerBound(r,&m,__SWELL_ComboBoxInternalState_rec::cmp); + if (s->selidx >= idx) s->selidx++; + s->items.Insert(idx,r); + return idx; + } + + case CB_INSERTSTRING: + if (wParam > (WPARAM)s->items.GetSize()) wParam=(WPARAM)s->items.GetSize(); + s->items.Insert(wParam,new __SWELL_ComboBoxInternalState_rec((const char *)lParam)); + if (s->selidx >= (int)wParam) s->selidx++; + return wParam; + + case CB_DELETESTRING: + if (wParam >= (WPARAM)s->items.GetSize()) return CB_ERR; + + s->items.Delete(wParam,true); + + if (wParam == (WPARAM)s->selidx || s->selidx >= s->items.GetSize()) { s->selidx=-1; InvalidateRect(hwnd,NULL,FALSE); } + else if ((int)wParam < s->selidx) s->selidx--; + + return s->items.GetSize(); + + case CB_GETCOUNT: return s->items.GetSize(); + case CB_GETCURSEL: return s->selidx >=0 && s->selidx < s->items.GetSize() ? s->selidx : -1; + + case CB_GETLBTEXTLEN: + case CB_GETLBTEXT: + if (wParam < (WPARAM)s->items.GetSize()) + { + __SWELL_ComboBoxInternalState_rec *rec = s->items.Get(wParam); + if (!rec) return CB_ERR; + const char *ptr=rec->desc; + int l = ptr ? strlen(ptr) : 0; + if (msg == CB_GETLBTEXT && lParam) memcpy((char *)lParam,ptr?ptr:"",l+1); + return l; + } + return CB_ERR; + case CB_RESETCONTENT: + s->selidx=-1; + s->items.Empty(true); + return 0; + case CB_SETCURSEL: + if (wParam >= (WPARAM)s->items.GetSize()) + { + if (s->selidx!=-1) + { + s->selidx = -1; + SetWindowText(hwnd,""); + InvalidateRect(hwnd,NULL,FALSE); + } + return CB_ERR; + } + else + { + if (s->selidx != (int)wParam) + { + s->selidx=(int)wParam; + char *ptr=s->items.Get(wParam)->desc; + SetWindowText(hwnd,ptr); + InvalidateRect(hwnd,NULL,FALSE); + } + } + return s->selidx; + + case CB_GETITEMDATA: + if (wParam < (WPARAM)s->items.GetSize()) + { + return s->items.Get(wParam)->parm; + } + return CB_ERR; + case CB_SETITEMDATA: + if (wParam < (WPARAM)s->items.GetSize()) + { + s->items.Get(wParam)->parm=lParam; + return 0; + } + return CB_ERR; + case CB_INITSTORAGE: + return 0; + + case CB_FINDSTRINGEXACT: + case CB_FINDSTRING: + { + int x; + int a = (int)wParam; + a++; + for (x=a;xitems.GetSize();x++) + if (msg == CB_FINDSTRING ? + !stricmp(s->items.Get(x)->desc,(char *)lParam) : + !strcmp(s->items.Get(x)->desc,(char *)lParam)) return x; + + for (x=0;xitems.Get(x)->desc,(char *)lParam) : + !strcmp(s->items.Get(x)->desc,(char *)lParam)) return x; + } + return CB_ERR; + } + } + } + + switch (msg) + { + case WM_NCDESTROY: + hwnd->m_private_data=0; + delete s; + break; + case WM_TIMER: + if (wParam == 100) + { + if (++s->editstate.cursor_state >= 8) s->editstate.cursor_state=0; + if (GetFocusIncludeMenus()!=hwnd || s->editstate.cursor_state<2) InvalidateRect(hwnd,NULL,FALSE); + } + else if (wParam==1) + { + if (!fast_has_focus(hwnd)) + { + KillTimer(hwnd,1); + InvalidateRect(hwnd,NULL,FALSE); + } + } + return 0; + + case WM_LBUTTONDOWN: + { + RECT r; + GetClientRect(hwnd,&r); + if ((hwnd->m_style & CBS_DROPDOWNLIST) == CBS_DROPDOWNLIST || GET_X_LPARAM(lParam) >= r.right-SWELL_UI_SCALE(buttonwid)) + { + s_capmode_state=5; + } + else + { + int xo=3; + HDC hdc=GetDC(hwnd); + const int last_cursor = s->editstate.cursor_pos; + s->editstate.cursor_pos = + WDL_utf8_bytepos_to_charpos(hwnd->m_title.Get(), + editHitTest(hdc,hwnd->m_title.Get(), + hwnd->m_title.GetLength(), + GET_X_LPARAM(lParam)-xo + s->editstate.scroll_x, + GET_Y_LPARAM(lParam),0,NULL,NULL) + ); + + s->editstate.onMouseDown(s_capmode_state,last_cursor); + + ReleaseDC(hwnd,hdc); + + SetFocusInternal(hwnd); + } + SetCapture(hwnd); + } + InvalidateRect(hwnd,NULL,FALSE); + return 0; + case WM_MOUSEMOVE: + if (GetCapture()==hwnd) + { + if (s_capmode_state == 3 || s_capmode_state == 4) + { + const bool multiline = (hwnd->m_style & ES_MULTILINE) != 0; + int xo=3; + HDC hdc=GetDC(hwnd); + int p = WDL_utf8_bytepos_to_charpos(hwnd->m_title.Get(), + editHitTest(hdc,hwnd->m_title.Get(), + multiline?-1:hwnd->m_title.GetLength(), + GET_X_LPARAM(lParam)-xo + s->editstate.scroll_x, + GET_Y_LPARAM(lParam),0,NULL,NULL) + ); + ReleaseDC(hwnd,hdc); + + s->editstate.onMouseDrag(s_capmode_state,p); + s->editstate.autoScrollToOffset(hwnd,p,false,false); + + InvalidateRect(hwnd,NULL,FALSE); + } + } + return 0; + case WM_LBUTTONUP: + if (GetCapture()==hwnd) + { + ReleaseCapture(); +popupMenu: + if (s && s->items.GetSize() && s_capmode_state == 5) + { + int x; + HMENU menu = CreatePopupMenu(); + for (x=0;xitems.GetSize();x++) + { + MENUITEMINFO mi={sizeof(mi),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING, + (UINT) (x == s->selidx?MFS_CHECKED:0), + (UINT) (100+x), NULL,NULL,NULL,0,s->items.Get(x)->desc}; + InsertMenuItem(menu,x,TRUE,&mi); + } + + hwnd->Retain(); + RECT r; + GetWindowRect(hwnd,&r); + RECT req_r = { 1<<30, 1<<30, 0, s->selidx }; + int a = TrackPopupMenu(menu,TPM_NONOTIFY|TPM_RETURNCMD|TPM_LEFTALIGN,r.left,r.bottom,0,hwnd,&req_r); + DestroyMenu(menu); + if (hwnd->m_private_data && a>=100 && a < s->items.GetSize()+100) + { + s->selidx = a-100; + char *ptr=s->items.Get(s->selidx)->desc; + SetWindowText(hwnd,ptr); + InvalidateRect(hwnd,NULL,FALSE); + SendMessage(GetParent(hwnd),WM_COMMAND,(GetWindowLong(hwnd,GWL_ID)&0xffff) | (CBN_SELCHANGE<<16),(LPARAM)hwnd); + } + hwnd->Release(); + } + } + s_capmode_state=0; + return 0; + case WM_LBUTTONDBLCLK: + if (s) + { + // technically this should select the word rather than all + s->editstate.sel1 = 0; + s->editstate.cursor_pos = s->editstate.sel2 = WDL_utf8_get_charlen(hwnd->m_title.Get()); + InvalidateRect(hwnd,NULL,FALSE); + } + return 0; + case WM_KEYDOWN: + if ((lParam&FVIRTKEY) && wParam == VK_DOWN) { s_capmode_state=5; goto popupMenu; } + if ((hwnd->m_style & CBS_DROPDOWNLIST) != CBS_DROPDOWNLIST && + OnEditKeyDown(hwnd,msg,wParam,lParam,false,false,&s->editstate,false)) + { + if (s) s->selidx=-1; // lookup from text? + SendMessage(GetParent(hwnd),WM_COMMAND,(CBN_EDITCHANGE<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + s->editstate.autoScrollToOffset(hwnd,s->editstate.cursor_pos,false,false); + InvalidateRect(hwnd,NULL,FALSE); + return 0; + } + if (wParam == VK_SPACE) { s_capmode_state=5; goto popupMenu; } + break; + case WM_KEYUP: + return 0; + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + bool pressed = s_capmode_state == 5 && GetCapture()==hwnd; + + SetTextColor(ps.hdc, + hwnd->m_enabled ? g_swell_ctheme.combo_text : + g_swell_ctheme.combo_text_disabled); + SetBkMode(ps.hdc,TRANSPARENT); + + Draw3DBox(ps.hdc,&r,g_swell_ctheme.combo_bg, + g_swell_ctheme.combo_hilight, + g_swell_ctheme.combo_shadow,pressed); + + int cursor_pos = -1; + bool focused = false; + if ((hwnd->m_style & CBS_DROPDOWNLIST) != CBS_DROPDOWNLIST) + { + focused = GetFocusIncludeMenus()==hwnd; + if (focused) + { + if (!s->editstate.cursor_timer) { SetTimer(hwnd,100,100,NULL); s->editstate.cursor_timer=1; } + } + else + { + if (s->editstate.cursor_timer) { KillTimer(hwnd,100); s->editstate.cursor_timer=0; } + } + + HBRUSH br = CreateSolidBrush(g_swell_ctheme.combo_bg2); + RECT tr=r; + const int pad = SWELL_UI_SCALE(2); + tr.left+=pad; tr.top+=pad; tr.bottom-=pad; tr.right -= SWELL_UI_SCALE(buttonwid+2); + FillRect(ps.hdc,&tr,br); + DeleteObject(br); + + if (focused && s->editstate.cursor_state) + { + cursor_pos = utf8fs_charpos_to_bytepos(&hwnd->m_title,s->editstate.cursor_pos); + } + } + + HBRUSH br = CreateSolidBrush(pressed?g_swell_ctheme.combo_arrow_press : g_swell_ctheme.combo_arrow); + HGDIOBJ oldbr=SelectObject(ps.hdc,br); + HGDIOBJ oldpen=SelectObject(ps.hdc,GetStockObject(NULL_PEN)); + const int dw = SWELL_UI_SCALE(8); + const int dh = SWELL_UI_SCALE(4); + const int cx = r.right-dw/2-SWELL_UI_SCALE(4); + const int cy = (r.bottom+r.top)/2; + + POINT pts[3] = { + { cx-dw/2,cy-dh/2 }, + { cx,cy+dh/2 }, + { cx+dw/2,cy-dh/2 } + }; + Polygon(ps.hdc,pts,3); + + SelectObject(ps.hdc,oldpen); + SelectObject(ps.hdc,oldbr); + DeleteObject(br); + + + if (pressed) + { + r.left+=SWELL_UI_SCALE(2); + r.top+=SWELL_UI_SCALE(2); + } + + r.left+=SWELL_UI_SCALE(3); + r.right-=SWELL_UI_SCALE(3); + + if ((hwnd->m_style & CBS_DROPDOWNLIST) != CBS_DROPDOWNLIST) + { + r.right -= SWELL_UI_SCALE(buttonwid+2); + editControlPaintLine(ps.hdc, hwnd->m_title.Get(), hwnd->m_title.GetLength(), cursor_pos, + focused ? s->editstate.sel1 : -1, focused ? s->editstate.sel2 : -1, &r, DT_VCENTER); + } + else + { + char buf[512]; + buf[0]=0; + GetWindowText(hwnd,buf,sizeof(buf)); + if (buf[0]) DrawText(ps.hdc,buf,-1,&r,DT_VCENTER); + } + + if (draw_focus_indicator(hwnd,ps.hdc,NULL)) + { + KillTimer(hwnd,1); + SetTimer(hwnd,1,100,NULL); + } + EndPaint(hwnd,&ps); + } + } + return 0; + case WM_SETTEXT: + s->editstate.cursor_pos = WDL_utf8_get_charlen(hwnd->m_title.Get()); + s->editstate.sel1 = s->editstate.sel2 = -1; + case WM_CAPTURECHANGED: + InvalidateRect(hwnd,NULL,FALSE); + break; + case EM_GETSEL: + if (s && (hwnd->m_style & CBS_DROPDOWNLIST) != CBS_DROPDOWNLIST) + { + if (wParam) * (int *)wParam = s->editstate.sel1; + if (lParam) * (int *)lParam = s->editstate.sel2; + if (s->editstate.sel1 < 0 || s->editstate.sel1 > 65535 || + s->editstate.sel2 < 0 || s->editstate.sel2 > 65535) return -1; + return MAKELPARAM(s->editstate.sel1,s->editstate.sel2); + } + return 0; + case EM_SETSEL: + if (s && (hwnd->m_style & CBS_DROPDOWNLIST) != CBS_DROPDOWNLIST) + { + s->editstate.sel1 = (int)wParam; + s->editstate.sel2 = (int)lParam; + if (!s->editstate.sel1 && s->editstate.sel2 == -1) + s->editstate.sel2 = WDL_utf8_get_charlen(hwnd->m_title.Get()); + if (s->editstate.sel2>=0) + s->editstate.autoScrollToOffset(hwnd,s->editstate.sel2, false, false); + InvalidateRect(hwnd,NULL,FALSE); + } + return 0; + case WM_MOUSEWHEEL: + // pass modified mousewheel to parent (not sure if we need to do this but other controls do...) + if ((GetAsyncKeyState(VK_CONTROL)&0x8000) || (GetAsyncKeyState(VK_MENU)&0x8000)) break; + + if (s) + { + int amt = ((short)HIWORD(wParam)); + if (GetAsyncKeyState(VK_SHIFT)&0x8000) + { + if (amt<0) amt=-1; + else if (amt>0) amt=1; + } + else + amt /= 120; + + if (amt) + { + int nv = s->selidx + amt; + if (nv>=s->items.GetSize()) nv=s->items.GetSize()-1; + if (nv < 0) nv=0; + if (nv != s->selidx && s->items.Get(nv)) + { + s->selidx=nv; + SetWindowText(hwnd,s->items.Get(nv)->desc); + InvalidateRect(hwnd,NULL,FALSE); + SendMessage(GetParent(hwnd),WM_COMMAND,(GetWindowLong(hwnd,GWL_ID)&0xffff) | (CBN_SELCHANGE<<16),(LPARAM)hwnd); + } + } + } + + return 0; + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + + +/// these are for swell-dlggen.h +HWND SWELL_MakeButton(int def, const char *label, int idx, int x, int y, int w, int h, int flags) +{ + UINT_PTR a=(UINT_PTR)label; + if (a < 65536) label = "ICONTEMP"; + + RECT tr=MakeCoords(x,y,w,h,true); + HWND hwnd = swell_makeButton(m_make_owner,idx,&tr,label,!(flags&SWELL_NOT_WS_VISIBLE),(def ? BS_DEFPUSHBUTTON : 0) | (flags&BS_XPOSITION_MASK)); + + if (m_doautoright) UpdateAutoCoords(tr); + if (def) { } + return hwnd; +} + + +HWND SWELL_MakeLabel( int align, const char *label, int idx, int x, int y, int w, int h, int flags) +{ + RECT tr=MakeCoords(x,y,w,h,true); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,label, !(flags&SWELL_NOT_WS_VISIBLE),labelWindowProc); + hwnd->m_classname = "Static"; + if (align > 0) flags |= SS_RIGHT; + else if (align == 0) flags |= SS_CENTER; + hwnd->m_style = (flags & ~SWELL_NOT_WS_VISIBLE)|WS_CHILD; + hwnd->m_wantfocus = false; + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + if (m_doautoright) UpdateAutoCoords(tr); + return hwnd; +} +HWND SWELL_MakeEditField(int idx, int x, int y, int w, int h, int flags) +{ + RECT tr=MakeCoords(x,y,w,h,true); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,NULL, !(flags&SWELL_NOT_WS_VISIBLE),editWindowProc); + hwnd->m_private_data = (INT_PTR) new __SWELL_editControlState; + hwnd->m_style = WS_CHILD | (flags & ~SWELL_NOT_WS_VISIBLE); + hwnd->m_classname = "Edit"; + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + if (m_doautoright) UpdateAutoCoords(tr); + return hwnd; +} + + +HWND SWELL_MakeCheckBox(const char *name, int idx, int x, int y, int w, int h, int flags=0) +{ + return SWELL_MakeControl(name,idx,"Button",BS_AUTOCHECKBOX|flags,x,y,w,h,0); +} + +struct SWELL_ListView_Col +{ + char *name; + int xwid; + int sortindicator; + int col_index; +}; + +enum { LISTVIEW_HDR_YMARGIN = 2 }; + +enum ListViewCapMode +{ + LISTVIEW_CAP_NONE=0, + LISTVIEW_CAP_XSCROLL, // data1=xp + LISTVIEW_CAP_YSCROLL, // data1=yp + LISTVIEW_CAP_DRAG, // data1=row, data2=displaycolumnindex + LISTVIEW_CAP_COLRESIZE, // data1 = displaycolumnindex, data2=xoffset + LISTVIEW_CAP_COLCLICK, // data1 = displaycolumnindex, data2=original lparam, advances to COLREORDER with same parameters + LISTVIEW_CAP_COLREORDER, +}; + +struct listViewState +{ + listViewState(bool ownerData, bool isMultiSel, bool isListBox) + { + m_selitem=-1; + m_is_multisel = isMultiSel; + m_is_listbox = isListBox; + m_owner_data_size = ownerData ? 0 : -1; + m_last_row_height = 0; + m_scroll_x=m_scroll_y=0; + m_capmode_state=LISTVIEW_CAP_NONE; + m_capmode_data1=0; + m_capmode_data2=0; + m_status_imagelist = NULL; + m_status_imagelist_type = 0; + m_extended_style=0; + + m_color_bg = g_swell_ctheme.listview_bg; + m_color_bg_sel = g_swell_ctheme.listview_bg_sel; + m_color_text = g_swell_ctheme.listview_text; + m_color_text_sel = g_swell_ctheme.listview_text_sel; + m_color_grid = g_swell_ctheme.listview_grid; + memset(m_color_extras,0xff,sizeof(m_color_extras)); // if !=-1, overrides bg/fg for (focus?0:2) + } + ~listViewState() + { + m_data.Empty(true); + const int n=m_cols.GetSize(); + for (int x=0;x m_data; + WDL_TypedBuf m_cols; + + + int GetColumnIndex(int dispindex) const + { + if (m_is_listbox || !m_cols.GetSize()) + { + WDL_ASSERT(dispindex==0); + return 0; + } + if (WDL_NORMALLY(dispindex>=0 && dispindex < m_cols.GetSize())) + { + WDL_ASSERT(m_cols.Get()[dispindex].col_index >= 0); + WDL_ASSERT(m_cols.Get()[dispindex].col_index < m_cols.GetSize()); + return m_cols.Get()[dispindex].col_index; + } + return 0; + } + SWELL_ListView_Col *GetColumnByIndex(int idx) const + { + SWELL_ListView_Col *c = m_cols.Get(); + const int n = m_cols.GetSize(); + for (int x = 0; x < n; x ++) if (c[x].col_index == idx) return c+x; + return NULL; + } + + int GetNumItems() const { return m_owner_data_size>=0 ? m_owner_data_size : m_data.GetSize(); } + bool IsOwnerData() const { return m_owner_data_size>=0; } + bool HasColumnHeaders(HWND hwnd) const + { + if (m_is_listbox || !m_cols.GetSize()) return false; + return !(hwnd->m_style & LVS_NOCOLUMNHEADER) && (hwnd->m_style & LVS_REPORT); + } + int GetColumnHeaderHeight(HWND hwnd) const { return HasColumnHeaders(hwnd) ? m_last_row_height+LISTVIEW_HDR_YMARGIN : 0; } + + int m_owner_data_size; // -1 if m_data valid, otherwise size + int m_last_row_height; + int m_selitem; // for single sel, or used for focus for multisel + + ListViewCapMode m_capmode_state; + int m_scroll_x,m_scroll_y, m_capmode_data1,m_capmode_data2; + int m_extended_style; + + int m_color_bg, m_color_bg_sel, m_color_text, m_color_text_sel, m_color_grid; + int m_color_extras[4]; + + int getTotalWidth() const + { + int s = 0; + const SWELL_ListView_Col *col = m_cols.Get(); + const int n = m_cols.GetSize(); + for (int x=0; x < n; x ++) s += col[x].xwid; + return s; + } + + void sanitizeScroll(HWND h) + { + RECT r; + GetClientRect(h,&r); + r.right -= g_swell_ctheme.scrollbar_width; + + const int mx = getTotalWidth() - r.right; + if (m_scroll_x > mx) m_scroll_x = mx; + if (m_scroll_x < 0) m_scroll_x = 0; + + if (m_last_row_height > 0) + { + r.bottom -= GetColumnHeaderHeight(h); + if (mx>0) r.bottom -= g_swell_ctheme.scrollbar_width; + + const int vh = m_last_row_height * GetNumItems(); + if (m_scroll_y < 0 || vh <= r.bottom) m_scroll_y=0; + else if (m_scroll_y > vh - r.bottom) m_scroll_y = vh - r.bottom; + } + } + + WDL_TypedBuf m_owner_multisel_state; + + bool get_sel(int idx) + { + if (!m_is_multisel) return idx>=0 && idx == m_selitem; + if (m_owner_data_size<0) + { + SWELL_ListView_Row *p = m_data.Get(idx); + return p && (p->m_tmp&1); + } + const unsigned int mask = 1<<(idx&31); + const int szn = idx/32; + const unsigned int *p=m_owner_multisel_state.Get(); + return p && idx>=0 && szn < m_owner_multisel_state.GetSize() && (p[szn]&mask); + } + bool set_sel(int idx, bool v) // returns true if value changed + { + if (!m_is_multisel) + { + const int oldsel = m_selitem; + if (v) m_selitem = idx; + else if (oldsel == idx) m_selitem = -1; + + return oldsel != m_selitem; + } + else if (m_owner_data_size<0) + { + SWELL_ListView_Row *p = m_data.Get(idx); + if (p) + { + const int oldtmp = p->m_tmp; + return (p->m_tmp = (v ? (oldtmp|1) : (oldtmp&~1))) != oldtmp; + } + } + else + { + if (idx>=0 && idx < m_owner_data_size) + { + const int szn = idx/32; + const int oldsz=m_owner_multisel_state.GetSize(); + unsigned int *p = m_owner_multisel_state.Get(); + if (oldszm_tmp; + if (*a&1) + { + *a&=~1; + rv=true; + } + } + return rv; + } + + bool rv=false; + int n = m_owner_multisel_state.GetSize(); + if (n > m_owner_data_size) n=m_owner_data_size; + for(int x=0;x *m_status_imagelist; + int m_status_imagelist_type; + + static int compareRows(const SWELL_ListView_Row **_a, const SWELL_ListView_Row **_b) + { + const char *a, *b; + if (!_a || !(a=(*_a)->m_vals.Get(0))) a=""; + if (!_b || !(b=(*_b)->m_vals.Get(0))) b=""; + return strcmp(a,b); + } +}; + +// returns non-NULL if a searching string occurred +static const char *stateStringOnKey(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (lParam & (FCONTROL|FALT|FLWIN)) return NULL; + if (uMsg != WM_KEYDOWN) return NULL; + static WDL_FastString str; + static DWORD last_t; + DWORD now = GetTickCount(); + if ((now-last_t) > 500) str.Set(""); + last_t = now; + + const bool is_numpad = wParam >= VK_NUMPAD0 && wParam <= VK_DIVIDE; + if ((lParam & FVIRTKEY) && wParam == VK_BACK) + { + str.SetLen(utf8fs_charpos_to_bytepos(&str,WDL_utf8_get_charlen(str.Get())-1)); + } + else if (wParam >= 32 && (!(lParam & FVIRTKEY) || swell_is_virtkey_char((int)wParam) || is_numpad)) + { + if (lParam & FVIRTKEY) + { + if (wParam >= 'A' && wParam <= 'Z') + { + if ((lParam&FSHIFT) ^ (swell_is_likely_capslock?0:FSHIFT)) wParam += 'a' - 'A'; + } + else if (is_numpad) + { + if (wParam <= VK_NUMPAD9) wParam += '0' - VK_NUMPAD0; + else wParam += '*' - VK_MULTIPLY; + } + } + + char b[8]; + WDL_MakeUTFChar(b,wParam,sizeof(b)); + str.Append(b); + return str.Get(); + } + + return NULL; +} + + +static LRESULT listViewWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + enum { col_resize_sz = 3 }; + listViewState *lvs = (listViewState *)hwnd->m_private_data; + static POINT s_clickpt; + switch (msg) + { + case WM_MOUSEWHEEL: + if ((GetAsyncKeyState(VK_CONTROL)&0x8000) || (GetAsyncKeyState(VK_MENU)&0x8000)) break; // pass modified mousewheel to parent + + { + const int amt = ((short)HIWORD(wParam))/40; + if (amt && lvs) + { + if (GetAsyncKeyState(VK_SHIFT)&0x8000) + { + const int oldscroll = lvs->m_scroll_x; + lvs->m_scroll_x -= amt*4; + lvs->sanitizeScroll(hwnd); + if (lvs->m_scroll_x != oldscroll) + InvalidateRect(hwnd,NULL,FALSE); + } + else + { + const int oldscroll = lvs->m_scroll_y; + lvs->m_scroll_y -= amt*lvs->m_last_row_height; + lvs->sanitizeScroll(hwnd); + if (lvs->m_scroll_y != oldscroll) + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + return 1; + case WM_RBUTTONDOWN: + if (lvs && lvs->m_last_row_height>0 && !lvs->m_is_listbox) + { + const int hdr_size = lvs->GetColumnHeaderHeight(hwnd); + if (hdr_size > 0 && GET_Y_LPARAM(lParam) < hdr_size) break; // let it get converted to WM_CONTEXTMENU + + LVHITTESTINFO inf = { { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }, }; + + const int row = ListView_SubItemHitTest(hwnd, &inf); + const int n = ListView_GetItemCount(hwnd); + if (row>=0 && rowm_id,NM_RCLICK},row,inf.iSubItem,0,0,0, {inf.pt.x,inf.pt.y}}; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + return 1; + case WM_SETCURSOR: + if (lvs) + { + POINT p; + GetCursorPos(&p); + ScreenToClient(hwnd,&p); + const int hdr_size = lvs->GetColumnHeaderHeight(hwnd); + if (p.y >= 0 && p.y < hdr_size) + { + const SWELL_ListView_Col *col = lvs->m_cols.Get(); + p.x += lvs->m_scroll_x; + if (lvs->hasStatusImage()) p.x -= lvs->m_last_row_height; + + for (int x=0; x < lvs->m_cols.GetSize(); x ++) + { + const int minw = wdl_max(col_resize_sz+1,col[x].xwid); + if (p.x >= minw-col_resize_sz && p.x < minw) + { + SetCursor(SWELL_LoadCursor(IDC_SIZEWE)); + return 1; + } + p.x -= col[x].xwid; + } + } + } + break; + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + SetFocusInternal(hwnd); + if (msg == WM_LBUTTONDOWN) SetCapture(hwnd); + else ReleaseCapture(); + + if (lvs && lvs->m_last_row_height>0) + { + s_clickpt.x = GET_X_LPARAM(lParam); + s_clickpt.y = GET_Y_LPARAM(lParam); + RECT r; + GetClientRect(hwnd,&r); + const int hdr_size = lvs->GetColumnHeaderHeight(hwnd); + const int hdr_size_nomargin = hdr_size>0 ? hdr_size-LISTVIEW_HDR_YMARGIN : 0; + const int n=lvs->GetNumItems(); + const int row_height = lvs->m_last_row_height; + const int totalw = lvs->getTotalWidth(); + + if (hdr_size + n * row_height > r.bottom - g_swell_ctheme.scrollbar_width) + r.right -= g_swell_ctheme.scrollbar_width; + + if (GET_Y_LPARAM(lParam) >= 0 && GET_Y_LPARAM(lParam) < hdr_size) + { + const SWELL_ListView_Col *col = lvs->m_cols.Get(); + int px = GET_X_LPARAM(lParam) + lvs->m_scroll_x; + if (lvs->hasStatusImage()) px -= lvs->m_last_row_height; + for (int x=0; x < lvs->m_cols.GetSize(); x ++) + { + const int minw = wdl_max(col_resize_sz+1,col[x].xwid); + if (px >= minw-col_resize_sz && px < minw) + { + lvs->m_capmode_state = LISTVIEW_CAP_COLRESIZE; + lvs->m_capmode_data1 = x; + lvs->m_capmode_data2 = minw-px; + return 0; + } + + if (px >= 0 && px m_capmode_state = LISTVIEW_CAP_COLCLICK; + lvs->m_capmode_data1 = x; + lvs->m_capmode_data2 = (int)lParam; + } + return 0; + } + px -= col[x].xwid; + } + } + else if (totalw > r.right && + GET_Y_LPARAM(lParam) >= r.bottom - g_swell_ctheme.scrollbar_width) + { + const int xpos = GET_X_LPARAM(lParam); + int xp = xpos; + + int thumbsz, thumbpos; + calcScroll(r.right,totalw,lvs->m_scroll_x,&thumbsz,&thumbpos); + + if (xpos < thumbpos) xp = thumbpos; // jump on first mouse move + else if (xpos > thumbpos+thumbsz) xp = thumbpos + thumbsz; + + lvs->m_capmode_state = LISTVIEW_CAP_XSCROLL; + lvs->m_capmode_data1 = xp; + if (xpos < thumbpos || xpos > thumbpos+thumbsz) goto forceMouseMove; + return 0; + } + + lvs->m_capmode_state=LISTVIEW_CAP_NONE; + const int ypos = GET_Y_LPARAM(lParam) - hdr_size_nomargin; + + if (totalw > r.right) r.bottom -= g_swell_ctheme.scrollbar_width; + if (n * row_height > r.bottom - hdr_size_nomargin && + GET_X_LPARAM(lParam) >= r.right) + { + int yp = GET_Y_LPARAM(lParam); + + int thumbsz, thumbpos; + calcScroll(r.bottom - hdr_size_nomargin, n*row_height,lvs->m_scroll_y,&thumbsz,&thumbpos); + + if (ypos < thumbpos) yp = thumbpos + hdr_size_nomargin; // jump on first mouse move + else if (ypos > thumbpos+thumbsz) yp = thumbpos + hdr_size_nomargin + thumbsz; + + lvs->m_capmode_state = LISTVIEW_CAP_YSCROLL; + lvs->m_capmode_data1 = yp; + if (ypos < thumbpos || ypos > thumbpos+thumbsz) goto forceMouseMove; + return 0; + } + + const int hit = ypos >= 0 ? ((ypos+lvs->m_scroll_y) / row_height) : -1; + if (hit < 0) return 1; + + int subitem = 0; // display index + + { + const int ncol=lvs->m_cols.GetSize(); + const bool has_image = lvs->hasStatusImage(); + int xpos=0, xpt = GET_X_LPARAM(lParam) + lvs->m_scroll_x; + if (has_image) xpos += lvs->m_last_row_height; + for (int x=0;xm_cols.Get()[x].xwid; + if (xpt >= xpos && xpt < xpos+xwid) { subitem = x; break; } + xpos += xwid; + } + } + + + if (!lvs->m_is_multisel) + { + const int oldsel = lvs->m_selitem; + if (hit >= 0 && hit < n) lvs->m_selitem = hit; + else lvs->m_selitem = -1; + + if (lvs->m_is_listbox) + { + //if (oldsel != lvs->m_selitem) + SendMessage(GetParent(hwnd),WM_COMMAND,(LBN_SELCHANGE<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + if (msg == WM_LBUTTONDBLCLK) + SendMessage(GetParent(hwnd),WM_COMMAND,(LBN_DBLCLK<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + } + else + { + if (hit >= 0) + { + lvs->m_capmode_state = LISTVIEW_CAP_DRAG; + lvs->m_capmode_data1 = hit; + lvs->m_capmode_data2 = subitem; + } + + if(hit < n) + { + NMLISTVIEW nm={{hwnd,hwnd->m_id,msg == WM_LBUTTONDBLCLK ? NM_DBLCLK : NM_CLICK},hit,lvs->GetColumnIndex(subitem),0,0,0, {s_clickpt.x, s_clickpt.y }}; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + if (oldsel != lvs->m_selitem) + { + NMLISTVIEW nm={{hwnd,hwnd->m_id,LVN_ITEMCHANGED},lvs->m_selitem,1,LVIS_SELECTED,}; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + } + InvalidateRect(hwnd,NULL,FALSE); + } + else + { + bool changed = false; + if (hit >= n) + { + changed |= lvs->clear_sel(); + lvs->m_selitem = -1; + } + else + { + const bool ctrl = (GetAsyncKeyState(VK_CONTROL)&0x8000)!=0; + if (!ctrl) + { + if (!lvs->get_sel(hit)) + changed |= lvs->clear_sel(); + } + if ((GetAsyncKeyState(VK_SHIFT)&0x8000) && lvs->m_selitem >= 0) + { + int a=lvs->m_selitem; + int b = hit; + if (a>b) { b=a; a=hit; } + while (a<=b) changed |= lvs->set_sel(a++,true); + } + else + { + lvs->m_selitem = hit; + changed |= lvs->set_sel(hit,!ctrl || !lvs->get_sel(hit)); + } + } + + if (lvs->m_is_listbox) + { + if (changed) SendMessage(GetParent(hwnd),WM_COMMAND,(LBN_SELCHANGE<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + if (msg == WM_LBUTTONDBLCLK) + SendMessage(GetParent(hwnd),WM_COMMAND,(LBN_DBLCLK<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + } + else + { + if (hit >=0 && hit < n) + { + lvs->m_capmode_state = LISTVIEW_CAP_DRAG; + lvs->m_capmode_data1 = hit; + lvs->m_capmode_data2 = subitem; + NMLISTVIEW nm={{hwnd,hwnd->m_id,msg == WM_LBUTTONDBLCLK ? NM_DBLCLK : NM_CLICK},hit,lvs->GetColumnIndex(subitem),LVIS_SELECTED,}; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + if (changed) + { + NMLISTVIEW nm={{hwnd,hwnd->m_id,LVN_ITEMCHANGED},hit,0,LVIS_SELECTED,}; + if (nm.iItem < 0 || nm.iItem >= n) nm.iItem=0; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + } + + InvalidateRect(hwnd,NULL,FALSE); + } + } + return 1; + case WM_MOUSEMOVE: + if (GetCapture()==hwnd && lvs) + { +forceMouseMove: + RECT r; + GetClientRect(hwnd,&r); + r.right -= g_swell_ctheme.scrollbar_width; + switch (lvs->m_capmode_state) + { + case LISTVIEW_CAP_NONE: + break; + case LISTVIEW_CAP_COLRESIZE: + { + int x = lvs->m_capmode_data1; + int xp = GET_X_LPARAM(lParam) + lvs->m_scroll_x + lvs->m_capmode_data2; + if (lvs->hasStatusImage()) xp -= lvs->m_last_row_height; + + SWELL_ListView_Col *col = lvs->m_cols.Get(); + if (x < lvs->m_cols.GetSize()) + { + for (int i = 0; i < x; i ++) xp -= col[i].xwid; + if (xp<0) xp=0; + if (col[x].xwid != xp) + { + col[x].xwid = xp; + if (lvs->m_scroll_x > 0 && GET_X_LPARAM(lParam) < 0) + lvs->m_scroll_x--; + else if (GET_X_LPARAM(lParam) > r.right+12) + lvs->m_scroll_x+=16; // additional check might not be necessary? + + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + break; + case LISTVIEW_CAP_DRAG: + if (!lvs->m_is_listbox) + { + const int dx = GET_X_LPARAM(lParam) - s_clickpt.x, dy = GET_Y_LPARAM(lParam) - s_clickpt.y; + if (dx*dx+dy*dy > 32) + { + NMLISTVIEW nm={{hwnd,hwnd->m_id,LVN_BEGINDRAG},lvs->m_capmode_data1,lvs->GetColumnIndex(lvs->m_capmode_data2)}; + lvs->m_capmode_state=LISTVIEW_CAP_NONE; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + } + break; + case LISTVIEW_CAP_YSCROLL: + { + int yv = lvs->m_capmode_data1; + int amt = GET_Y_LPARAM(lParam) - yv; + + if (amt) + { + const int totalw = lvs->getTotalWidth(); + if (totalw > r.right) r.bottom -= lvs->m_last_row_height; + + + const int viewsz = r.bottom-lvs->GetColumnHeaderHeight(hwnd); + const double totalsz=(double)lvs->GetNumItems() * (double)lvs->m_last_row_height; + amt = (int)floor(amt * totalsz / (double)viewsz + 0.5); + + const int oldscroll = lvs->m_scroll_y; + lvs->m_scroll_y += amt; + lvs->sanitizeScroll(hwnd); + if (lvs->m_scroll_y != oldscroll) + { + lvs->m_capmode_data1 = GET_Y_LPARAM(lParam); + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + break; + case LISTVIEW_CAP_XSCROLL: + { + int xv = lvs->m_capmode_data1; + int amt = GET_X_LPARAM(lParam) - xv; + + if (amt) + { + const int viewsz = r.right; + const double totalsz=(double)lvs->getTotalWidth(); + amt = (int)floor(amt * totalsz / (double)viewsz + 0.5); + + const int oldscroll = lvs->m_scroll_x; + lvs->m_scroll_x += amt; + lvs->sanitizeScroll(hwnd); + if (lvs->m_scroll_x != oldscroll) + { + lvs->m_capmode_data1 = GET_X_LPARAM(lParam); + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + break; + case LISTVIEW_CAP_COLCLICK: + if (lvs->m_extended_style & LVS_EX_HEADERDRAGDROP) + { + const int xd = GET_X_LPARAM(lParam) - GET_X_LPARAM((LPARAM)lvs->m_capmode_data2), + yd = GET_Y_LPARAM(lParam) - GET_Y_LPARAM((LPARAM)lvs->m_capmode_data2); + if (xd*xd + yd*yd < 4*4) break; + lvs->m_capmode_state = LISTVIEW_CAP_COLREORDER; + } + case LISTVIEW_CAP_COLREORDER: + InvalidateRect(hwnd,NULL,FALSE); + break; + } + } + return 1; + case WM_LBUTTONUP: + if (GetCapture()==hwnd) + { + if (lvs->m_capmode_state == LISTVIEW_CAP_COLCLICK) + { + HWND par = hwnd->m_parent; + if (par) + { + NMLISTVIEW hdr={{hwnd,(UINT_PTR)hwnd->m_id,LVN_COLUMNCLICK},-1, lvs->GetColumnIndex(lvs->m_capmode_data1) }; + if (par->m_wndproc&&!par->m_hashaddestroy) par->m_wndproc(par,WM_NOTIFY,hwnd->m_id, (LPARAM) &hdr); + } + } + else if (lvs->m_capmode_state == LISTVIEW_CAP_COLREORDER) + { + const int oldidx = lvs->m_capmode_data1; + const int ncols=lvs->m_cols.GetSize(); + if (oldidx >= 0 && oldidx < ncols) + { + POINT p; + GetCursorPos(&p); + ScreenToClient(hwnd,&p); + int xpos=-lvs->m_scroll_x; + SWELL_ListView_Col *cols = lvs->m_cols.Get(); + int x; + for (x = 0; x < ncols; x ++) + { + int xwid = cols[x].xwid; + if (!x && lvs->hasStatusImage()) xwid += lvs->m_last_row_height; + if (p.x < xpos + xwid/2) break; + xpos += xwid; + } + + if (x != oldidx && x != oldidx+1) + { + SWELL_ListView_Col c = cols[oldidx]; + lvs->m_cols.Delete(oldidx); + + if (x > oldidx) x--; + lvs->m_cols.Insert(c,x); + + } + } + } + ReleaseCapture(); + InvalidateRect(hwnd,NULL,FALSE); + } + return 1; + case WM_KEYDOWN: + if (lvs) + { + const char *s = stateStringOnKey(msg,wParam,lParam); + if (s) + { + int col = 0; // column index (not display) + if (!lvs->m_is_listbox) + { + for (int x=0;xm_cols.GetSize();x++) + { + if (lvs->m_cols.Get()[x].sortindicator) + { + col = lvs->m_cols.Get()[x].col_index; + break; + } + } + } + + const int n = lvs->GetNumItems(); + int selitem = lvs->m_selitem; + if (selitem < 0 || selitem >= n) selitem=0; + for (int x=0;xIsOwnerData()) + { + SWELL_ListView_Row *row = lvs->m_data.Get(offs); + if (row) v = row->m_vals.Get(col); + } + else + { + buf[0]=0; + NMLVDISPINFO nm={{hwnd,hwnd->m_id,LVN_GETDISPINFO},{LVIF_TEXT, offs,col, 0,0, buf, sizeof(buf), -1 }}; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + v = buf; + } + + if (v && !strnicmp(v,s,strlen(s))) + { + if (!lvs->m_is_multisel) + { + const int oldsel = lvs->m_selitem; + lvs->m_selitem = offs; + + if (lvs->m_is_listbox) + { + SendMessage(GetParent(hwnd),WM_COMMAND,(LBN_SELCHANGE<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + } + else + { + if (oldsel != lvs->m_selitem) + { + NMLISTVIEW nm={{hwnd,hwnd->m_id,LVN_ITEMCHANGED},lvs->m_selitem,1,LVIS_SELECTED,}; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + } + } + else + { + bool changed = lvs->clear_sel() | lvs->set_sel(offs,true); + lvs->m_selitem = offs; + + if (lvs->m_is_listbox) + { + if (changed) SendMessage(GetParent(hwnd),WM_COMMAND,(LBN_SELCHANGE<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + } + else + { + if (changed) + { + NMLISTVIEW nm={{hwnd,hwnd->m_id,LVN_ITEMCHANGED},offs,0,LVIS_SELECTED,}; + if (nm.iItem < 0 || nm.iItem >= n) nm.iItem=0; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + } + } + + ListView_EnsureVisible(hwnd,offs,FALSE); + InvalidateRect(hwnd,NULL,FALSE); + break; + } + } + } + } + if (lvs && (lParam & FVIRTKEY)) + { + int flag=0; + int ni; + const int oldsel = lvs->m_selitem; + + switch (wParam) + { + case VK_NEXT: + case VK_PRIOR: + case VK_UP: + case VK_DOWN: + { + RECT r; + GetClientRect(hwnd,&r); + const int page = lvs->m_last_row_height ? + (r.bottom - g_swell_ctheme.scrollbar_width)/lvs->m_last_row_height : 4; + const int cnt = lvs->GetNumItems(); + + ni = lvs->m_selitem + (wParam == VK_UP ? -1 : + wParam == VK_PRIOR ? 2-wdl_max(page,3) : + wParam == VK_NEXT ? wdl_max(page,3)-2 : + 1); + + if (ni<0) ni=0; + if (ni>cnt-1) ni=cnt-1; + const bool shift = (GetAsyncKeyState(VK_SHIFT)&0x8000)!=0; + + if (ni>=0 && (ni!=lvs->m_selitem || !shift)) + { + if (lvs->m_is_multisel) + { + if (!shift) + { + lvs->clear_sel(); + lvs->set_sel(ni,true); + } + else + { + if (lvs->get_sel(ni)) lvs->set_sel(lvs->m_selitem,false); + else lvs->set_sel(ni,true); + } + } + lvs->m_selitem=ni; + flag|=3; + } + } + flag|=4; + break; + case VK_HOME: + case VK_END: + ni = wParam == VK_HOME ? 0 : lvs->GetNumItems()-1; + if (ni != lvs->m_selitem) + { + if (lvs->m_is_multisel) + { + if (!(GetAsyncKeyState(VK_SHIFT)&0x8000)) + { + lvs->clear_sel(); + lvs->set_sel(ni,true); + } + else + { + if (wParam == VK_HOME) + { + for (ni = lvs->m_selitem; ni >= 0; ni--) + lvs->set_sel(ni,true); + } + else + { + for (int x=wdl_max(0,lvs->m_selitem); x <= ni; x++) + lvs->set_sel(x,true); + } + } + } + lvs->m_selitem=ni; + flag|=3; + } + flag|=4; + break; + } + if (flag) + { + if (flag & 2) + { + if (lvs->m_is_listbox) + { + if (oldsel != lvs->m_selitem) + SendMessage(GetParent(hwnd),WM_COMMAND,(LBN_SELCHANGE<<16) | (hwnd->m_id&0xffff),(LPARAM)hwnd); + } + else + { + NMLISTVIEW nm={{hwnd,hwnd->m_id,LVN_ITEMCHANGED},lvs->m_selitem,0,LVIS_SELECTED,}; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + ListView_EnsureVisible(hwnd,lvs->m_selitem,FALSE); + } + if (flag&1) InvalidateRect(hwnd,NULL,FALSE); + + return 0; + } + } + break; + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT cr; + GetClientRect(hwnd,&cr); + RECT rr = cr; + HBRUSH bgbr = CreateSolidBrush(lvs->m_color_bg); + FillRect(ps.hdc,&cr,bgbr); + DeleteObject(bgbr); + const bool focused = GetFocus() == hwnd; + const int bgsel = lvs->m_color_extras[focused ? 0 : 2 ]; + bgbr=CreateSolidBrush(bgsel == -1 ? lvs->m_color_bg_sel : bgsel); + if (lvs) + { + TEXTMETRIC tm; + GetTextMetrics(ps.hdc,&tm); + const int row_height = tm.tmHeight + 4; + lvs->m_last_row_height = row_height; + lvs->sanitizeScroll(hwnd); + + const bool owner_data = lvs->IsOwnerData(); + const int nrows = owner_data ? lvs->m_owner_data_size : lvs->m_data.GetSize(); + const int hdr_size = lvs->GetColumnHeaderHeight(hwnd); + const int hdr_size_nomargin = hdr_size>0 ? hdr_size-LISTVIEW_HDR_YMARGIN : 0; + int ypos = hdr_size - lvs->m_scroll_y; + + SetBkMode(ps.hdc,TRANSPARENT); + const int ncols = lvs->m_cols.GetSize(); + const int nc = wdl_max(ncols,1); + const SWELL_ListView_Col *cols = lvs->m_cols.Get(); + + const bool has_image = lvs->hasAnyImage(); + const bool has_status_image = lvs->hasStatusImage(); + const int xo = lvs->m_scroll_x; + + const int totalw = lvs->getTotalWidth(); + + const bool vscroll_area = hdr_size + nrows * row_height > cr.bottom - g_swell_ctheme.scrollbar_width; + const bool hscroll = totalw > cr.right - (vscroll_area ? g_swell_ctheme.scrollbar_width : 0); + if (hscroll) + cr.bottom -= g_swell_ctheme.scrollbar_width; + + HPEN gridpen = NULL; + HGDIOBJ oldpen = NULL; + if (!lvs->m_is_listbox && (hwnd->m_style & LVS_REPORT) && (lvs->m_extended_style&LVS_EX_GRIDLINES)) + { + gridpen = CreatePen(PS_SOLID,0,lvs->m_color_grid); + oldpen = SelectObject(ps.hdc,gridpen); + } + + HWND par = GetParent(hwnd); + NMLVCUSTOMDRAW nmlvcd={ { {hwnd,(UINT_PTR)hwnd->m_id, NM_CUSTOMDRAW}, CDDS_ITEMPREPAINT,ps.hdc, } }; + for (int rowidx = 0; rowidx < nrows && ypos < cr.bottom; rowidx ++) + { + const char *str = NULL; + char buf[4096]; + + bool sel; + { + RECT tr={cr.left,ypos,cr.right,ypos + row_height}; + if (tr.bottom < hdr_size) + { + ypos += row_height; + continue; + } + + sel = lvs->get_sel(rowidx); + if (sel) + { + FillRect(ps.hdc,&tr,bgbr); + } + } + + COLORREF text_c; + if (sel) + { + int c = lvs->m_color_extras[focused ? 1 : 3 ]; + text_c = c == -1 ? lvs->m_color_text_sel : c; + } + else + text_c = lvs->m_color_text; + + nmlvcd.nmcd.dwItemSpec = (DWORD)rowidx; + SWELL_ListView_Row *row = lvs->m_data.Get(rowidx); + int xpos=-xo; + for (int col = 0; col < nc && xpos < cr.right; col ++) + { + const int col_idx = lvs->GetColumnIndex(col); + + nmlvcd.iSubItem = col_idx; + nmlvcd.clrText = text_c; + SendMessage(par,WM_NOTIFY,hwnd->m_id&0xffff,(LPARAM)&nmlvcd); + SetTextColor(ps.hdc, nmlvcd.clrText); + + int image_idx = 0; + if (owner_data) + { + NMLVDISPINFO nm={{hwnd,hwnd->m_id,LVN_GETDISPINFO},{LVIF_TEXT, rowidx,col_idx, 0,0, buf, sizeof(buf), -1 }}; + if (!col && has_image) + { + if (lvs->m_status_imagelist_type == LVSIL_STATE) nm.item.mask |= LVIF_STATE; + else if (lvs->m_status_imagelist_type == LVSIL_SMALL) nm.item.mask |= LVIF_IMAGE; + + } + buf[0]=0; + SendMessage(par,WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + str=buf; + if (!col && has_image) + { + if (lvs->m_status_imagelist_type == LVSIL_STATE) image_idx=STATEIMAGEMASKTOINDEX(nm.item.state); + else if (lvs->m_status_imagelist_type == LVSIL_SMALL) image_idx = nm.item.iImage + 1; + } + } + else + { + if (!col && has_image) image_idx = row->m_imageidx; + if (row) str = row->m_vals.Get(col_idx); + } + + RECT ar = { xpos,ypos, cr.right, ypos + row_height }; + if (!col && has_image) + { + if (image_idx>0) + { + HICON icon = lvs->m_status_imagelist->Get(image_idx-1); + if (icon) + { + if (has_status_image || col >= ncols) + ar.right = ar.left + row_height; + else + ar.right = ar.left + wdl_min(row_height,cols[col].xwid); + DrawImageInRect(ps.hdc,icon,&ar); + } + } + + if (has_status_image) + { + xpos += row_height; + } + ar.left += row_height; + } + + if (lvs->m_is_listbox && (hwnd->m_style & LBS_OWNERDRAWFIXED)) + { + if (hwnd->m_parent) + { + DRAWITEMSTRUCT dis = { ODT_LISTBOX, hwnd->m_id, (UINT)rowidx, 0, + (UINT)(sel?ODS_SELECTED:0),hwnd,ps.hdc,ar,row?(DWORD_PTR)row->m_param:0 }; + dis.rcItem.left++; + if (cr.bottom-cr.top < nrows*row_height) + dis.rcItem.right -= g_swell_ctheme.scrollbar_width; + SendMessage(hwnd->m_parent,WM_DRAWITEM,(WPARAM)hwnd->m_id,(LPARAM)&dis); + } + } + else + { + if (ncols > 0) + { + ar.right = xpos + cols[col].xwid - SWELL_UI_SCALE(3); + xpos += cols[col].xwid; + } + else ar.right = cr.right; + + if (ar.right > ar.left) + { + const int adj = (ar.right-ar.left)/16; + const int maxadj = SWELL_UI_SCALE(4); + ar.left += wdl_min(adj,maxadj); + + if(str) + DrawText(ps.hdc,str,-1,&ar,DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX); + } + } + } + ypos += row_height; + if (gridpen) + { + MoveToEx(ps.hdc,0,ypos-1,NULL); + LineTo(ps.hdc,cr.right,ypos-1); + } + } + if (gridpen) + { + if (row_height>0) for (;;) + { + ypos += row_height; + if (ypos >= cr.bottom) break; + MoveToEx(ps.hdc,0,ypos-1,NULL); + LineTo(ps.hdc,cr.right,ypos-1); + } + int xpos=(has_status_image ? row_height : 0) - xo; + for (int col=0; col < ncols; col ++) + { + xpos += cols[col].xwid; + if (xpos > cr.right) break; + MoveToEx(ps.hdc,xpos-1,hdr_size_nomargin,NULL); + LineTo(ps.hdc,xpos-1,cr.bottom); + } + } + if (hdr_size_nomargin>0) + { + HBRUSH br = CreateSolidBrush(g_swell_ctheme.listview_hdr_bg); + int xpos=(has_status_image ? row_height : 0) - xo; + ypos=0; + SetTextColor(ps.hdc,g_swell_ctheme.listview_hdr_text); + + if (xpos>0) + { + RECT tr={0,ypos,xpos,ypos+hdr_size_nomargin }; + FillRect(ps.hdc,&tr,br); + } + for (int col=0; col < ncols; col ++) + { + RECT tr={xpos,ypos,0,ypos + hdr_size_nomargin }; + xpos += cols[col].xwid; + tr.right = xpos; + + if (tr.right > tr.left) + { + Draw3DBox(ps.hdc,&tr, + g_swell_ctheme.listview_hdr_bg, + g_swell_ctheme.listview_hdr_hilight, + g_swell_ctheme.listview_hdr_shadow); + + if (cols[col].sortindicator != 0) + { + const int tsz = (tr.bottom-tr.top)/4; + if (tr.right > tr.left + 2*tsz) + { + const int x1 = tr.left + 2; + int y2 = (tr.bottom+tr.top)/2 - tsz/2 - tsz/4; + int y1 = y2 + tsz; + if (cols[col].sortindicator >= 0) + { + int tmp=y1; + y1=y2; + y2=tmp; + } + HBRUSH hdrbr = CreateSolidBrush(g_swell_ctheme.listview_hdr_arrow); + HGDIOBJ oldBrush = SelectObject(ps.hdc,hdrbr); + HGDIOBJ oldPen = SelectObject(ps.hdc,GetStockObject(NULL_PEN)); + + POINT pts[3] = {{x1,y1}, {x1+tsz*2,y1}, {x1+tsz,y2}}; + Polygon(ps.hdc,pts,3); + SelectObject(ps.hdc,oldBrush); + SelectObject(ps.hdc,oldPen); + DeleteObject(hdrbr); + tr.left = x1 + tsz*2; + } + } + + if (cols[col].name) + { + tr.left += wdl_min((tr.right-tr.left)/4,4); + DrawText(ps.hdc,cols[col].name,-1,&tr,DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX); + } + } + if (xpos >= cr.right) break; + } + if (xpos < cr.right) + { + RECT tr={xpos,ypos,cr.right,ypos+hdr_size_nomargin }; + FillRect(ps.hdc,&tr,br); + } + DeleteObject(br); + if (lvs->m_capmode_state == LISTVIEW_CAP_COLREORDER && GetCapture()==hwnd) + { + const int oldidx = lvs->m_capmode_data1; + if (oldidx >= 0 && oldidx < ncols) + { + POINT p; + GetCursorPos(&p); + ScreenToClient(hwnd,&p); + xpos=-lvs->m_scroll_x; + int x; + for (x = 0; x < ncols; x ++) + { + int xwid = cols[x].xwid; + if (!x && lvs->hasStatusImage()) xwid += lvs->m_last_row_height; + if (p.x < xpos + xwid/2) break; + xpos += xwid; + } + + if (x != oldidx && x != oldidx+1) + { + const char *s = cols[lvs->m_capmode_data1].name; + int sz = cols[lvs->m_capmode_data1].xwid; + if (sz < hdr_size_nomargin) sz = hdr_size_nomargin; + br = CreateSolidBrushAlpha(g_swell_ctheme.focusrect,0.75f); + RECT r = {xpos, ypos, xpos + sz, ypos + hdr_size_nomargin }; + FillRect(ps.hdc, &r, br); + if (s) DrawText(ps.hdc,s,-1,&r,DT_SINGLELINE|DT_LEFT|DT_VCENTER|DT_NOPREFIX); + DeleteObject(br); + } + } + } + } + if (gridpen) + { + SelectObject(ps.hdc,oldpen); + DeleteObject(gridpen); + } + + cr.top += hdr_size_nomargin; + drawVerticalScrollbar(ps.hdc,cr,nrows*row_height,lvs->m_scroll_y); + + if (hscroll) + { + cr.bottom += g_swell_ctheme.scrollbar_width; + drawHorizontalScrollbar(ps.hdc,cr, + cr.right-cr.left - (vscroll_area ? g_swell_ctheme.scrollbar_width : 0), + totalw,lvs->m_scroll_x); + } + } + Draw3DBox(ps.hdc,&rr,-1, + g_swell_ctheme.listview_hilight, + g_swell_ctheme.listview_shadow); + DeleteObject(bgbr); + + EndPaint(hwnd,&ps); + } + } + return 0; + case WM_NCDESTROY: + hwnd->m_private_data = 0; + delete lvs; + break; + case LB_ADDSTRING: + case LB_INSERTSTRING: + if (lvs && !lvs->IsOwnerData()) + { + SWELL_ListView_Row *row=new SWELL_ListView_Row; + row->m_vals.Add(strdup((const char *)lParam)); + int idx; + if (msg == LB_ADDSTRING && hwnd->m_style & LBS_SORT) + { + bool isMatch; + idx=lvs->m_data.LowerBound(row,&isMatch,listViewState::compareRows); + } + else if (msg == LB_INSERTSTRING) + { + idx=(int)wParam; + if (idx<0 || idx>lvs->m_data.GetSize()) idx=lvs->m_data.GetSize(); + } + else + idx=lvs->m_data.GetSize(); + lvs->m_data.Insert(idx,row); + InvalidateRect(hwnd,NULL,FALSE); + return idx; + } + return LB_ERR; + case LB_DELETESTRING: + if (lvs && !lvs->IsOwnerData()) + { + int idx = (int) wParam; + if (idx<0 || idx>=lvs->m_data.GetSize()) return LB_ERR; + lvs->m_data.Delete(idx,true); + InvalidateRect(hwnd,NULL,FALSE); + return lvs->m_data.GetSize(); + } + return LB_ERR; + case LB_GETTEXT: + if (!lParam) return LB_ERR; + *(char *)lParam = 0; + if (lvs && !lvs->IsOwnerData()) + { + SWELL_ListView_Row *row = lvs->m_data.Get(wParam); + if (row && row->m_vals.Get(0)) + { + strcpy((char *)lParam, row->m_vals.Get(0)); + return (LRESULT)strlen(row->m_vals.Get(0)); + } + } + return LB_ERR; + case LB_GETTEXTLEN: + { + SWELL_ListView_Row *row=lvs->m_data.Get(wParam); + if (row) + { + const char *p=row->m_vals.Get(0); + return p?strlen(p):0; + } + } + return LB_ERR; + case LB_FINDSTRINGEXACT: + if (lParam) + { + int x = (int) wParam + 1; + if (x < 0) x=0; + const int n = lvs->m_data.GetSize(); + for (int i = 0; i < n; i ++) + { + SWELL_ListView_Row *row=lvs->m_data.Get(x); + if (row) + { + const char *p = row->m_vals.Get(0); + if (p && !stricmp(p,(const char *)lParam)) return x; + } + if (++x >= n) x=0; + } + } + return LB_ERR; + case LB_RESETCONTENT: + if (lvs && !lvs->IsOwnerData()) + { + lvs->m_data.Empty(true,free); + } + InvalidateRect(hwnd,NULL,FALSE); + return 0; + case LB_SETSEL: + if (lvs && lvs->m_is_multisel) + { + if (lvs->IsOwnerData()) + { + } + else + { + if ((int)lParam == -1) + { + int x; + const int n=lvs->m_data.GetSize(); + for(x=0;xm_data.Get(x); + if (row) row->m_tmp = (row->m_tmp&~1) | (wParam?1:0); + } + InvalidateRect(hwnd,NULL,FALSE); + } + else + { + SWELL_ListView_Row *row=lvs->m_data.Get((int)lParam); + if (!row) return LB_ERR; + const int otmp = row->m_tmp; + row->m_tmp = (row->m_tmp&~1) | (wParam?1:0); + if (row->m_tmp != otmp) InvalidateRect(hwnd,NULL,FALSE); + return 0; + } + } + } + return LB_ERR; + case LB_SETCURSEL: + if (lvs && !lvs->IsOwnerData() && !lvs->m_is_multisel) + { + lvs->m_selitem = (int)wParam; + InvalidateRect(hwnd,NULL,FALSE); + } + return LB_ERR; + case LB_GETSEL: + if (lvs && lvs->m_is_multisel) + { + if (lvs->IsOwnerData()) + { + } + else + { + SWELL_ListView_Row *row=lvs->m_data.Get((int)wParam); + if (!row) return LB_ERR; + return row->m_tmp&1; + } + } + return LB_ERR; + case LB_GETCURSEL: + if (lvs) + { + return (LRESULT)lvs->m_selitem; + } + return LB_ERR; + case LB_GETCOUNT: + if (lvs) return lvs->GetNumItems(); + return LB_ERR; + case LB_GETSELCOUNT: + if (lvs && lvs->m_is_multisel) + { + int cnt=0; + if (lvs->IsOwnerData()) + { + } + else + { + int x; + const int n=lvs->m_data.GetSize(); + for(x=0;xm_data.Get(x); + if (row && (row->m_tmp&1)) cnt++; + } + } + return cnt; + } + return LB_ERR; + case LB_GETITEMDATA: + if (lvs && !lvs->IsOwnerData()) + { + SWELL_ListView_Row *row = lvs->m_data.Get(wParam); + return row ? row->m_param : LB_ERR; + } + return LB_ERR; + case LB_SETITEMDATA: + if (lvs && !lvs->IsOwnerData()) + { + SWELL_ListView_Row *row = lvs->m_data.Get(wParam); + if (row) row->m_param = lParam; + return row ? 0 : LB_ERR; + } + return LB_ERR; + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +struct treeViewState +{ + treeViewState() + { + m_sel=NULL; + m_last_row_height=0; + m_scroll_x=m_scroll_y=m_capmode=0; + m_root.m_state = TVIS_EXPANDED; + m_root.m_haschildren=true; + } + ~treeViewState() + { + } + bool findItem(HTREEITEM item, HTREEITEM *parOut, int *idxOut) + { + if (!m_root.FindItem(item,parOut,idxOut)) return false; + if (parOut && *parOut == &m_root) *parOut = NULL; + return true; + } + + int navigateSel(HWND hwnd, int key, int pagesize) // returns 2 force invalidate, 1 if ate key + { + HTREEITEM par=NULL; + int idx=0,tmp=1; + + switch (key) + { + case VK_LEFT: + if (m_sel && findItem(m_sel,&par,NULL)) + { + if (m_sel->m_haschildren && (m_sel->m_state & TVIS_EXPANDED)) + { + NMTREEVIEW nmhdr={{hwnd,(UINT_PTR)hwnd->m_id,TVN_ITEMEXPANDING},}; + nmhdr.action = TVE_COLLAPSE; + nmhdr.itemNew.hItem=m_sel; + nmhdr.itemNew.lParam=m_sel->m_param; + if (!SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nmhdr)) + { + m_sel->m_state &= ~TVIS_EXPANDED; + return 2; + } + else + { + return 1; + } + } + if (par) m_sel=par; + } + return 1; + case VK_HOME: + m_sel=m_root.m_children.Get(0); + return 1; + case VK_END: + par = &m_root; + while (par && par->m_haschildren && + par->m_children.GetSize() && (par->m_state & TVIS_EXPANDED)) + par = par->m_children.Get(par->m_children.GetSize()-1); + if (par && par != &m_root) m_sel=par; + return 1; + case VK_RIGHT: + if (m_sel && findItem(m_sel,NULL,NULL) && m_sel->m_haschildren) + { + if (!(m_sel->m_state&TVIS_EXPANDED)) + { + NMTREEVIEW nmhdr={{hwnd,(UINT_PTR)hwnd->m_id,TVN_ITEMEXPANDING},}; + nmhdr.action = TVE_EXPAND; + nmhdr.itemNew.hItem=m_sel; + nmhdr.itemNew.lParam=m_sel->m_param; + if (!SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nmhdr)) + { + m_sel->m_state |= TVIS_EXPANDED; + return 2; + } + else + { + return 1; + } + } + par = m_sel->m_children.Get(0); + if (par) m_sel=par; + } + return 1; + case VK_PRIOR: + tmp = wdl_max(pagesize,2) - 1; + case VK_UP: + while (tmp-- > 0) + { + if (m_sel && findItem(m_sel,&par,&idx)) + { + if (idx>0) + { + par = (par?par:&m_root)->m_children.Get(idx-1); + while (par && (par->m_state & TVIS_EXPANDED) && + par->m_haschildren && par->m_children.GetSize()) + par = par->m_children.Get(par->m_children.GetSize()-1); + } + if (par) m_sel=par; + } + } + return 1; + case VK_NEXT: + tmp = wdl_max(pagesize,2) - 1; + case VK_DOWN: + while (tmp-- > 0) + { + if (m_sel && findItem(m_sel,&par,&idx)) + { + if (m_sel->m_haschildren && + m_sel->m_children.GetSize() && + (m_sel->m_state & TVIS_EXPANDED)) + { + par = m_sel->m_children.Get(0); + if (par) m_sel=par; + continue; + } + +next_item_in_parent: + if (par) + { + if (idx+1 < par->m_children.GetSize()) + { + par = par->m_children.Get(idx+1); + if (par) m_sel=par; + } + else if (findItem(par,&par,&idx)) goto next_item_in_parent; + } + else + { + par = m_root.m_children.Get(idx+1); + if (par) m_sel=par; + } + } + } + return 1; + } + return 0; + } + + void doDrawItem(HTREEITEM item, HDC hdc, RECT *rect) // draws any subitems too, updates rect->top + { +#ifdef SWELL_LICE_GDI + if (!item) return; + + if (item != &m_root) + { + const int ob = rect->bottom; + rect->bottom = rect->top + m_last_row_height; + if (rect->right > rect->left) + { + int oc=0; + if (item == m_sel) + { + SetBkMode(hdc,OPAQUE); + SetBkColor(hdc,g_swell_ctheme.treeview_bg_sel); + oc = GetTextColor(hdc); + SetTextColor(hdc,g_swell_ctheme.treeview_text_sel); + } + + RECT dr = *rect; + const int sz = m_last_row_height/4; + if (item->m_haschildren) + { + bool exp = (item->m_state&TVIS_EXPANDED); + POINT pts[3]; + if (exp) + { + const int yo = dr.top + sz+sz/2,xo=dr.left+1; + pts[0].x=xo; pts[0].y=yo; + pts[1].x=xo+sz*2; pts[1].y=yo; + pts[2].x=xo+sz; pts[2].y=yo+sz; + } + else + { + const int yo = dr.top + sz, xo = dr.left+sz*3/4+1; + pts[0].x=xo; pts[0].y=yo; + pts[1].x=xo+sz; pts[1].y=yo+sz; + pts[2].x=xo; pts[2].y=yo+sz*2; + } + Polygon(hdc,pts,3); + } + dr.left += sz*2+3; + + DrawText(hdc,item->m_value ? item->m_value : "",-1,&dr,DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX); + if (item == m_sel) + { + SetBkMode(hdc,TRANSPARENT); + SetTextColor(hdc,oc); + } + } + rect->top = rect->bottom; + rect->bottom = ob; + } + + if ((item->m_state & TVIS_EXPANDED) && item->m_haschildren && item->m_children.GetSize()) + { + const int n = item->m_children.GetSize(); + rect->left += m_last_row_height; + for (int x=0;xtop < rect->bottom;x++) + { + doDrawItem(item->m_children.Get(x),hdc,rect); + } + rect->left -= m_last_row_height; + } +#endif + } + HTREEITEM hitTestItem(HTREEITEM item, int *y, int *xo) + { + *y -= m_last_row_height; + if (*y < 0) return item; + if ((item->m_state & TVIS_EXPANDED) && item->m_haschildren && item->m_children.GetSize()) + { + int x; + const int n = item->m_children.GetSize(); + for (x=0;xm_children.Get(x),y,xo); + if (t) + { + if (xo) *xo += m_last_row_height; + return t; + } + } + } + return NULL; + } + int CalculateItemHeight(HTREEITEM__ *item, HTREEITEM stopAt, bool *done) + { + int h = m_last_row_height; + if (item == stopAt) { *done=true; return 0; } + + if ((item->m_state & TVIS_EXPANDED) && + item->m_haschildren && + item->m_children.GetSize()) + { + const int n = item->m_children.GetSize(); + for (int x=0;xm_children.Get(x),stopAt,done); + if (*done) break; + } + } + return h; + } + + int calculateContentsHeight(HTREEITEM item=NULL) + { + bool done=false; + const int rv = CalculateItemHeight(&m_root,item,&done); + if (item && !done) return 0; + return rv - m_last_row_height; + } + + int sanitizeScroll(HWND h) + { + RECT r; + GetClientRect(h,&r); + if (m_last_row_height > 0) + { + const int vh = calculateContentsHeight(); + if (m_scroll_y < 0 || vh <= r.bottom) m_scroll_y=0; + else if (m_scroll_y > vh - r.bottom) m_scroll_y = vh - r.bottom; + return vh; + } + return 0; + } + + void ensureItemVisible(HWND hwnd, HTREEITEM item) + { + if (m_last_row_height<1) return; + const int x = item ? calculateContentsHeight(item) : 0; + RECT r; + GetClientRect(hwnd,&r); + if (x < m_scroll_y) m_scroll_y = x; + else if (x+m_last_row_height > m_scroll_y+r.bottom) + m_scroll_y = x+m_last_row_height - r.bottom; + } + + HTREEITEM__ m_root; + HTREEITEM m_sel; + int m_last_row_height; + + int m_scroll_x,m_scroll_y; + + int m_capmode; // HIWORD is 0 for normal (then LOWORD gets 1 set if drag began), 1 for scroll (LOWORD has ypos) +}; + +static LRESULT treeViewWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static POINT s_clickpt; + treeViewState *tvs = (treeViewState *)hwnd->m_private_data; + switch (msg) + { + case WM_MOUSEWHEEL: + if ((GetAsyncKeyState(VK_CONTROL)&0x8000) || (GetAsyncKeyState(VK_MENU)&0x8000)) break; // pass modified mousewheel to parent + + { + const int amt = ((short)HIWORD(wParam))/40; + if (amt && tvs) + { + const int oldscroll = tvs->m_scroll_y; + tvs->m_scroll_y -= amt*tvs->m_last_row_height; + tvs->sanitizeScroll(hwnd); + if (tvs->m_scroll_y != oldscroll) + InvalidateRect(hwnd,NULL,FALSE); + + } + } + return 1; + case WM_KEYDOWN: + if (tvs && (lParam & FVIRTKEY)) + { + HTREEITEM oldSel = tvs->m_sel; + RECT r; + GetClientRect(hwnd,&r); + int flag = tvs->navigateSel(hwnd,(int)wParam,tvs->m_last_row_height ? r.bottom / tvs->m_last_row_height : 4); + if (oldSel != tvs->m_sel) + { + if (tvs->m_sel) tvs->ensureItemVisible(hwnd,tvs->m_sel); + InvalidateRect(hwnd,NULL,FALSE); + NMTREEVIEW nm={{(HWND)hwnd,(UINT_PTR)hwnd->m_id,TVN_SELCHANGED},}; + nm.itemNew.hItem = tvs->m_sel; + nm.itemNew.lParam = tvs->m_sel ? tvs->m_sel->m_param : 0; + SendMessage(GetParent(hwnd),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm); + } + else if (flag&2) InvalidateRect(hwnd,NULL,FALSE); + + if (flag) return 0; + } + break; + case WM_LBUTTONDOWN: + SetFocusInternal(hwnd); + SetCapture(hwnd); + if (tvs) + { + tvs->m_capmode=0; + s_clickpt.x = GET_X_LPARAM(lParam); + s_clickpt.y = GET_Y_LPARAM(lParam); + RECT cr; + GetClientRect(hwnd,&cr); + int total_h; + if (GET_X_LPARAM(lParam) >= cr.right-g_swell_ctheme.scrollbar_width && + (total_h=tvs->sanitizeScroll(hwnd)) > cr.bottom) + { + int ypos = GET_Y_LPARAM(lParam); + int yp = ypos; + + int thumbsz, thumbpos; + calcScroll(cr.bottom, total_h,tvs->m_scroll_y,&thumbsz,&thumbpos); + + if (ypos < thumbpos) yp = thumbpos; // jump on first mouse move + else if (ypos > thumbpos+thumbsz) yp = thumbpos + thumbsz; + + tvs->m_capmode = (1<<16) | (yp&0xffff); + if (ypos < thumbpos || ypos > thumbpos+thumbsz) goto forceMouseMove; + return 0; + } + + if (tvs->m_last_row_height) + { + int y = GET_Y_LPARAM(lParam) + tvs->m_scroll_y + tvs->m_last_row_height; + int xo=-tvs->m_last_row_height; + HTREEITEM hit = tvs->hitTestItem(&tvs->m_root,&y,&xo); + if (hit && GET_X_LPARAM(lParam) >= xo) + { + if (hit->m_haschildren) + { + if (GET_X_LPARAM(lParam) < xo + (tvs->m_last_row_height/4)*2+3) + { + NMTREEVIEW nmhdr={{hwnd,(UINT_PTR)hwnd->m_id,TVN_ITEMEXPANDING},}; + nmhdr.action = (hit->m_state&TVIS_EXPANDED)!=TVIS_EXPANDED ? TVE_EXPAND : TVE_COLLAPSE; + nmhdr.itemNew.hItem=hit; + nmhdr.itemNew.lParam=hit->m_param; + if (!SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nmhdr)) + { + hit->m_state ^= TVIS_EXPANDED; + InvalidateRect(hwnd,NULL,FALSE); + } + return 0; + } + } + if (tvs->m_sel != hit) + { + tvs->m_sel = hit; + InvalidateRect(hwnd,NULL,FALSE); + NMTREEVIEW nm={{(HWND)hwnd,(UINT_PTR)hwnd->m_id,TVN_SELCHANGED},}; + nm.itemNew.hItem = hit; + nm.itemNew.lParam = hit ? hit->m_param : 0; + SendMessage(GetParent(hwnd),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm); + } + } + } + } + return 0; + case WM_MOUSEMOVE: + if (GetCapture()==hwnd && tvs) + { +forceMouseMove: + switch (HIWORD(tvs->m_capmode)) + { + case 0: + if (!(tvs->m_capmode & 1)) + { + const int dx = GET_X_LPARAM(lParam) - s_clickpt.x, dy = GET_Y_LPARAM(lParam) - s_clickpt.y; + if (dx*dx+dy*dy > 32) + { + tvs->m_capmode|=1; + HTREEITEM item = TreeView_GetSelection(hwnd); + NMTREEVIEW nm={{(HWND)hwnd,(UINT_PTR)hwnd->m_id,TVN_BEGINDRAG},}; + nm.itemNew.hItem = item; + nm.itemNew.lParam = item ? item->m_param : 0; + SendMessage(GetParent(hwnd),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm); + } + } + break; + case 1: + { + int yv = (short)LOWORD(tvs->m_capmode); + int amt = GET_Y_LPARAM(lParam) - yv; + + if (amt) + { + RECT r; + GetClientRect(hwnd,&r); + + const int viewsz = r.bottom; + const double totalsz=tvs->calculateContentsHeight(); + amt = (int)floor(amt * totalsz / (double)viewsz + 0.5); + + const int oldscroll = tvs->m_scroll_y; + tvs->m_scroll_y += amt; + tvs->sanitizeScroll(hwnd); + if (tvs->m_scroll_y != oldscroll) + { + tvs->m_capmode = (GET_Y_LPARAM(lParam)&0xffff) | (1<<16); + InvalidateRect(hwnd,NULL,FALSE); + } + } + } + break; + } + } + return 1; + case WM_LBUTTONUP: + if (GetCapture() == hwnd) + { + ReleaseCapture(); + } + return 1; + case WM_RBUTTONDOWN: + if (tvs && tvs->m_last_row_height>0) + { + NMLISTVIEW nm={{hwnd,hwnd->m_id,NM_RCLICK},0,0,0,}; + SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nm); + } + return 1; + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + { + HBRUSH br = CreateSolidBrush(g_swell_ctheme.treeview_bg); + FillRect(ps.hdc,&r,br); + DeleteObject(br); + } + if (tvs) + { + RECT cr=r; + SetTextColor(ps.hdc,g_swell_ctheme.treeview_text); + + const int lrh = tvs->m_last_row_height; + TEXTMETRIC tm; + GetTextMetrics(ps.hdc,&tm); + const int row_height = tm.tmHeight; + tvs->m_last_row_height = row_height; + const int total_h = tvs->sanitizeScroll(hwnd); + if (!lrh && tvs->m_sel) + tvs->ensureItemVisible(hwnd,tvs->m_sel); + + SetBkMode(ps.hdc,TRANSPARENT); + + r.top -= tvs->m_scroll_y; + + HBRUSH br = CreateSolidBrush(g_swell_ctheme.treeview_arrow); + HGDIOBJ oldpen = SelectObject(ps.hdc,GetStockObject(NULL_PEN)); + HGDIOBJ oldbr = SelectObject(ps.hdc,br); + + r.left -= tvs->m_last_row_height; + tvs->doDrawItem(&tvs->m_root,ps.hdc,&r); + + SelectObject(ps.hdc,oldbr); + SelectObject(ps.hdc,oldpen); + DeleteObject(br); + + drawVerticalScrollbar(ps.hdc,cr,total_h,tvs->m_scroll_y); + Draw3DBox(ps.hdc,&cr,-1, + g_swell_ctheme.treeview_hilight, + g_swell_ctheme.treeview_shadow); + } + + EndPaint(hwnd,&ps); + } + } + return 0; + case WM_NCDESTROY: + hwnd->m_private_data = 0; + delete tvs; + break; + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + +struct tabControlState +{ + tabControlState() { m_curtab=0; } + ~tabControlState() { m_tabs.Empty(true,free); } + int m_curtab; + WDL_PtrList m_tabs; +}; + +#define TABCONTROL_HEIGHT SWELL_UI_SCALE(20) + +static LRESULT tabControlWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + const int xdiv = 6,xpad=4; + + tabControlState *s = (tabControlState *)hwnd->m_private_data; + switch (msg) + { + case WM_NCDESTROY: + hwnd->m_private_data = 0; + delete s; + break; + case WM_TIMER: + if (wParam==1) + { + if (!fast_has_focus(hwnd)) + { + KillTimer(hwnd,1); + InvalidateRect(hwnd,NULL,FALSE); + } + } + break; + case WM_LBUTTONUP: + if (GET_Y_LPARAM(lParam) < TABCONTROL_HEIGHT) + { + return 1; + } + break; + case WM_KEYDOWN: + if ((lParam&0xff)==FVIRTKEY && + (wParam==VK_LEFT || + wParam==VK_RIGHT || + wParam==VK_HOME || + wParam == VK_END)) + { + int ct = s->m_curtab; + if (wParam==VK_LEFT)ct--; + else ct++; + if (ct>=s->m_tabs.GetSize()||wParam == VK_END) ct=s->m_tabs.GetSize()-1; + if (ct<0||wParam==VK_HOME) ct=0; + if (ct != s->m_curtab) + { + s->m_curtab = ct; + NMHDR nm={hwnd,(UINT_PTR)hwnd->m_id,TCN_SELCHANGE}; + InvalidateRect(hwnd,NULL,FALSE); + SendMessage(GetParent(hwnd),WM_NOTIFY,nm.idFrom,(LPARAM)&nm); + } + + return 0; + } + break; + case WM_LBUTTONDOWN: + if (GET_Y_LPARAM(lParam) < TABCONTROL_HEIGHT) + { + SetFocusInternal(hwnd); + int xp=GET_X_LPARAM(lParam),tab; + HDC dc = GetDC(hwnd); + int tabchg = -1; + for (tab = 0; tab < s->m_tabs.GetSize(); tab ++) + { + const char *buf = s->m_tabs.Get(tab); + RECT tr={0,}; + DrawText(dc,buf,-1,&tr,DT_CALCRECT|DT_NOPREFIX|DT_SINGLELINE); + xp -= tr.right - tr.left + 2*SWELL_UI_SCALE(xpad) + SWELL_UI_SCALE(xdiv); + if (xp < 0) + { + if (s->m_curtab != tab) + { + tabchg = tab; + } + break; + } + } + if (tabchg >=0) + { + s->m_curtab = tabchg; + NMHDR nm={hwnd,(UINT_PTR)hwnd->m_id,TCN_SELCHANGE}; + InvalidateRect(hwnd,NULL,FALSE); + SendMessage(GetParent(hwnd),WM_NOTIFY,nm.idFrom,(LPARAM)&nm); + } + else + InvalidateRect(hwnd,NULL,FALSE); + + ReleaseDC(hwnd,dc); + return 1; + } + break; + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + + int tab; + int xp=0; + HPEN pen = CreatePen(PS_SOLID,0,g_swell_ctheme.tab_hilight); + HPEN pen2 = CreatePen(PS_SOLID,0,g_swell_ctheme.tab_shadow); + + SetBkMode(ps.hdc,TRANSPARENT); + SetTextColor(ps.hdc,g_swell_ctheme.tab_text); + HGDIOBJ oldPen=SelectObject(ps.hdc,pen); + const int th = TABCONTROL_HEIGHT; + + { + RECT bgr={0,0,r.right,th}; + IntersectRect(&bgr,&bgr,&ps.rcPaint); + HBRUSH hbrush = (HBRUSH) SendMessage(hwnd,WM_CTLCOLORDLG,(WPARAM)ps.hdc,(LPARAM)hwnd); + if (hbrush && hbrush != (HBRUSH)1) FillRect(ps.hdc,&bgr,hbrush); + else SWELL_FillDialogBackground(ps.hdc,&bgr,0); + } + + int lx=0; + RECT fr={0,}; + for (tab = 0; tab < s->m_tabs.GetSize() && xp < r.right; tab ++) + { + const char *buf = s->m_tabs.Get(tab); + RECT tr={0,}; + DrawText(ps.hdc,buf,-1,&tr,DT_CALCRECT|DT_NOPREFIX|DT_SINGLELINE); + int tw=tr.right-tr.left + 2*SWELL_UI_SCALE(xpad); + + const int olx=lx; + lx=xp + tw+SWELL_UI_SCALE(xdiv); + + MoveToEx(ps.hdc,xp,th-1,NULL); + LineTo(ps.hdc,xp,0); + LineTo(ps.hdc,xp+tw,0); + SelectObject(ps.hdc,pen2); + LineTo(ps.hdc,xp+tw,th-1); + + if (tab==s->m_curtab) + { + fr.left=xp; + fr.right=xp+tw; + fr.top=0; + fr.bottom=th-2; + } + + MoveToEx(ps.hdc, tab == s->m_curtab ? lx-SWELL_UI_SCALE(xdiv) : olx,th-1,NULL); + LineTo(ps.hdc,lx,th-1); + + SelectObject(ps.hdc,pen); + + tr.left = xp+SWELL_UI_SCALE(xpad); + tr.top=0; + tr.right = xp+tw-SWELL_UI_SCALE(xpad); + tr.bottom = th; + + DrawText(ps.hdc,buf,-1,&tr,DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX); + xp = lx; + } + if (draw_focus_indicator(hwnd,ps.hdc,&fr)) + { + KillTimer(hwnd,1); + SetTimer(hwnd,1,100,NULL); + } + SelectObject(ps.hdc,pen2); + MoveToEx(ps.hdc,lx,th-1,NULL); + LineTo(ps.hdc,r.right,th-1); + + SelectObject(ps.hdc,oldPen); + + EndPaint(hwnd,&ps); + DeleteObject(pen); + DeleteObject(pen2); + } + } + return 0; + } + return DefWindowProc(hwnd,msg,wParam,lParam); +} + + + +HWND SWELL_MakeListBox(int idx, int x, int y, int w, int h, int styles) +{ + RECT tr=MakeCoords(x,y,w,h,true); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,NULL, !(styles&SWELL_NOT_WS_VISIBLE), listViewWindowProc); + hwnd->m_style = WS_CHILD | (styles & ~SWELL_NOT_WS_VISIBLE); + hwnd->m_classname = "ListBox"; + hwnd->m_private_data = (INT_PTR) new listViewState(false, !!(styles & LBS_EXTENDEDSEL), true); + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + if (m_doautoright) UpdateAutoCoords(tr); + return hwnd; +} + +typedef struct ccprocrec +{ + SWELL_ControlCreatorProc proc; + int cnt; + struct ccprocrec *next; +} ccprocrec; + +static ccprocrec *m_ccprocs; + +void SWELL_RegisterCustomControlCreator(SWELL_ControlCreatorProc proc) +{ + if (WDL_NOT_NORMALLY(!proc)) return; + + ccprocrec *p=m_ccprocs; + if (p) for (;;) + { + if (p->proc == proc) + { + p->cnt++; + return; + } + if (!p->next) break; + p=p->next; + } + ccprocrec *ent = (ccprocrec*)malloc(sizeof(ccprocrec)); + ent->proc=proc; + ent->cnt=1; + ent->next=NULL; + + if (p) p->next=ent; + else m_ccprocs=ent; +} + +void SWELL_UnregisterCustomControlCreator(SWELL_ControlCreatorProc proc) +{ + if (WDL_NOT_NORMALLY(!proc)) return; + + ccprocrec *lp=NULL; + ccprocrec *p=m_ccprocs; + while (p) + { + if (p->proc == proc) + { + if (--p->cnt <= 0) + { + if (lp) lp->next=p->next; + else m_ccprocs=p->next; + free(p); + } + return; + } + lp=p; + p=p->next; + } + WDL_ASSERT(false); +} + + + +HWND SWELL_MakeControl(const char *cname, int idx, const char *classname, int style, int x, int y, int w, int h, int exstyle) +{ + if (m_ccprocs) + { + RECT poo=MakeCoords(x,y,w,h,false); + ccprocrec *p=m_ccprocs; + while (p) + { + HWND hhh=p->proc((HWND)m_make_owner,cname,idx,classname,style,poo.left,poo.top,poo.right-poo.left,poo.bottom-poo.top); + if (hhh) + { + if (exstyle) SetWindowLong(hhh,GWL_EXSTYLE,exstyle); + return hhh; + } + p=p->next; + } + } + if (!stricmp(classname,"SysTabControl32")) + { + RECT tr=MakeCoords(x,y,w,h,false); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,NULL, !(style&SWELL_NOT_WS_VISIBLE), tabControlWindowProc); + hwnd->m_style = WS_CHILD | (style & ~SWELL_NOT_WS_VISIBLE); + hwnd->m_classname = "SysTabControl32"; + hwnd->m_private_data = (INT_PTR) new tabControlState; + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + SetWindowPos(hwnd,HWND_BOTTOM,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); + return hwnd; + } + else if (!stricmp(classname, "SysListView32")||!stricmp(classname, "SysListView32_LB")) + { + RECT tr=MakeCoords(x,y,w,h,false); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,NULL, !(style&SWELL_NOT_WS_VISIBLE), listViewWindowProc); + hwnd->m_style = WS_CHILD | (style & ~SWELL_NOT_WS_VISIBLE); + hwnd->m_classname = "SysListView32"; + if (!stricmp(classname, "SysListView32")) + hwnd->m_private_data = (INT_PTR) new listViewState(!!(style & LVS_OWNERDATA), !(style & LVS_SINGLESEL), false); + else + hwnd->m_private_data = (INT_PTR) new listViewState(false,false, true); + + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + return hwnd; + } + else if (!stricmp(classname, "SysTreeView32")) + { + RECT tr=MakeCoords(x,y,w,h,false); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,NULL, !(style&SWELL_NOT_WS_VISIBLE), treeViewWindowProc); + hwnd->m_style = WS_CHILD | (style & ~SWELL_NOT_WS_VISIBLE); + hwnd->m_classname = "SysTreeView32"; + hwnd->m_private_data = (INT_PTR) new treeViewState; + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + return hwnd; + } + else if (!stricmp(classname, "msctls_progress32")) + { + RECT tr=MakeCoords(x,y,w,h,false); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,NULL, !(style&SWELL_NOT_WS_VISIBLE), progressWindowProc); + hwnd->m_wantfocus = false; + hwnd->m_style = WS_CHILD | (style & ~SWELL_NOT_WS_VISIBLE); + hwnd->m_classname = "msctls_progress32"; + int *state = (int *)calloc(2,sizeof(int)); // pos, range + if (state) state[1] = 100<<16; + hwnd->m_private_data = (INT_PTR) state; + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + return hwnd; + } + else if (!stricmp(classname,"Edit")) + { + return SWELL_MakeEditField(idx,x,y,w,h,style); + } + else if (!stricmp(classname, "Static")) + { + RECT tr=MakeCoords(x,y,w,h,false); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,cname, !(style&SWELL_NOT_WS_VISIBLE),labelWindowProc); + hwnd->m_wantfocus = false; + hwnd->m_style = WS_CHILD | (style & ~SWELL_NOT_WS_VISIBLE); + hwnd->m_classname = "Static"; + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + if (m_doautoright) UpdateAutoCoords(tr); + return hwnd; + } + else if (!stricmp(classname,"Button")) + { + RECT tr=MakeCoords(x,y,w,h,true); + HWND hwnd = swell_makeButton(m_make_owner,idx,&tr,cname,!(style&SWELL_NOT_WS_VISIBLE),(style&~SWELL_NOT_WS_VISIBLE)|WS_CHILD); + if (m_doautoright) UpdateAutoCoords(tr); + return hwnd; + } + else if (!stricmp(classname,"REAPERhfader")||!stricmp(classname,"msctls_trackbar32")) + { + RECT tr=MakeCoords(x,y,w,h,true); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,cname, !(style&SWELL_NOT_WS_VISIBLE),trackbarWindowProc); + hwnd->m_style = WS_CHILD | (style & ~SWELL_NOT_WS_VISIBLE); + hwnd->m_classname = !stricmp(classname,"REAPERhfader") ? "REAPERhfader" : "msctls_trackbar32"; + hwnd->m_private_data = (INT_PTR) calloc(3,sizeof(int)); // pos, range, tic + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + return hwnd; + } + else if (!stricmp(classname,"COMBOBOX")) + { + return SWELL_MakeCombo(idx, x, y, w, h, style); + } + return 0; +} + +HWND SWELL_MakeCombo(int idx, int x, int y, int w, int h, int flags) +{ + RECT tr=MakeCoords(x,y,w,h,true); + const int maxh = g_swell_ctheme.combo_height; + if (tr.bottom > tr.top + maxh) tr.bottom=tr.top+maxh; + HWND hwnd = new HWND__(m_make_owner,idx,&tr,NULL, !(flags&SWELL_NOT_WS_VISIBLE),comboWindowProc); + hwnd->m_private_data = (INT_PTR) new __SWELL_ComboBoxInternalState; + hwnd->m_style = (flags & ~SWELL_NOT_WS_VISIBLE)|WS_CHILD; + hwnd->m_classname = "combobox"; + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + if (m_doautoright) UpdateAutoCoords(tr); + return hwnd; +} + +HWND SWELL_MakeGroupBox(const char *name, int idx, int x, int y, int w, int h, int style) +{ + RECT tr=MakeCoords(x,y,w,h,false); + HWND hwnd = new HWND__(m_make_owner,idx,&tr,name, !(style&SWELL_NOT_WS_VISIBLE),groupWindowProc); + hwnd->m_wantfocus = false; + hwnd->m_style = WS_CHILD | (style & ~SWELL_NOT_WS_VISIBLE) | BS_GROUPBOX; + hwnd->m_classname = "Button"; + hwnd->m_wndproc(hwnd,WM_CREATE,0,0); + SetWindowPos(hwnd,HWND_BOTTOM,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE); + return hwnd; +} + + +int TabCtrl_GetItemCount(HWND hwnd) +{ + tabControlState *s = hwnd ? (tabControlState*) hwnd->m_private_data : NULL; + return WDL_NORMALLY(s) ? s->m_tabs.GetSize() : 0; +} + +BOOL TabCtrl_AdjustRect(HWND hwnd, BOOL fLarger, RECT *r) +{ + if (WDL_NOT_NORMALLY(!r || !hwnd)) return FALSE; + + r->top += TABCONTROL_HEIGHT; + + return TRUE; +} + + +BOOL TabCtrl_DeleteItem(HWND hwnd, int idx) +{ + tabControlState *s = hwnd ? (tabControlState*) hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!s || !s->m_tabs.Get(idx))) return FALSE; + + s->m_tabs.Delete(idx,true); + if (s->m_curtab>0) s->m_curtab--; + InvalidateRect(hwnd,NULL,FALSE); + // todo: send notification? + + return TRUE; +} + +int TabCtrl_InsertItem(HWND hwnd, int idx, TCITEM *item) +{ + tabControlState *s = WDL_NORMALLY(hwnd) ? (tabControlState*) hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!item || !s)) return -1; + if (WDL_NOT_NORMALLY(!(item->mask & TCIF_TEXT) || !item->pszText)) return -1; + + s->m_tabs.Insert(idx, strdup(item->pszText)); + + InvalidateRect(hwnd,NULL,FALSE); + // todo: send notification if s->m_tabs.GetSize()==1 ? + + return TRUE; +} + +int TabCtrl_SetCurSel(HWND hwnd, int idx) +{ + tabControlState *s = WDL_NORMALLY(hwnd) ? (tabControlState*) hwnd->m_private_data : NULL; + if (!s || !s->m_tabs.Get(idx)) return -1; + const int lt =s->m_curtab; + s->m_curtab = idx; + InvalidateRect(hwnd,NULL,FALSE); + + return lt; +} + +int TabCtrl_GetCurSel(HWND hwnd) +{ + tabControlState *s = WDL_NORMALLY(hwnd) ? (tabControlState*) hwnd->m_private_data : NULL; + return s ? s->m_curtab : -1; +} + +void ListView_SetExtendedListViewStyleEx(HWND h, int flag, int mask) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return; + lvs->m_extended_style = (lvs->m_extended_style & ~mask) | (flag&mask); +} + +void SWELL_SetListViewFastClickMask(HWND hList, int mask) +{ +} + +void ListView_SetImageList(HWND h, HIMAGELIST imagelist, int which) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return; + lvs->m_status_imagelist= (WDL_PtrList *)imagelist; + lvs->m_status_imagelist_type = which; +} + +int ListView_GetColumnWidth(HWND h, int pos) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return 0; + SWELL_ListView_Col *c = lvs->GetColumnByIndex(pos); + return WDL_NORMALLY(c) ? c->xwid : 0; +} + +void ListView_InsertColumn(HWND h, int pos, const LVCOLUMN *lvc) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || !lvc)) return; + SWELL_ListView_Col col = { 0, 100 }; + if (lvc->mask & LVCF_WIDTH) col.xwid = lvc->cx; + if (lvc->mask & LVCF_TEXT) col.name = lvc->pszText ? strdup(lvc->pszText) : NULL; + + for (int x = 0; x < lvs->m_cols.GetSize(); x++) + if (lvs->m_cols.Get()[x].col_index>=pos) + lvs->m_cols.Get()[x].col_index++; + + if (pos<0)pos=0; + else if (pos>lvs->m_cols.GetSize()) pos=lvs->m_cols.GetSize(); + col.col_index = pos; + + lvs->m_cols.Insert(col,pos); +} + +void ListView_SetColumn(HWND h, int pos, const LVCOLUMN *lvc) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || !lvc)) return; + SWELL_ListView_Col *col = lvs->GetColumnByIndex(pos); + if (WDL_NOT_NORMALLY(!col)) return; + if (lvc->mask & LVCF_WIDTH) col->xwid = lvc->cx; + if (lvc->mask & LVCF_TEXT) + { + free(col->name); + col->name = lvc->pszText ? strdup(lvc->pszText) : NULL; + } +} + +void ListView_GetItemText(HWND hwnd, int item, int subitem, char *text, int textmax) +{ + LVITEM it={LVIF_TEXT,item,subitem,0,0,text,textmax,}; + ListView_GetItem(hwnd,&it); +} + +int ListView_InsertItem(HWND h, const LVITEM *item) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || lvs->IsOwnerData() || !item || item->iSubItem)) return 0; + + int idx = (int) item->iItem; + if (idx<0 || idx>lvs->m_data.GetSize()) idx=lvs->m_data.GetSize(); + SWELL_ListView_Row *row=new SWELL_ListView_Row; + row->m_vals.Add((item->mask&LVIF_TEXT) && item->pszText ? strdup(item->pszText) : NULL); + row->m_param = (item->mask&LVIF_PARAM) ? item->lParam : 0; + row->m_tmp = ((item->mask & LVIF_STATE) && (item->state & LVIS_SELECTED)) ? 1:0; + lvs->m_data.Insert(idx,row); + if (item->mask&LVIF_STATE) + { + if (item->stateMask & LVIS_STATEIMAGEMASK) row->m_imageidx=STATEIMAGEMASKTOINDEX(item->state); + if (item->stateMask & LVIS_SELECTED) lvs->set_sel(idx,!!(item->state&LVIS_SELECTED)); + } + InvalidateRect(h,NULL,FALSE); + return idx; +} + +void ListView_SetItemText(HWND h, int ipos, int cpos, const char *txt) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || lvs->IsOwnerData() || cpos < 0)) return; + const int ncol = wdl_max(lvs->m_cols.GetSize(),1); + if (WDL_NOT_NORMALLY(cpos >= ncol)) return; + + SWELL_ListView_Row *row=lvs->m_data.Get(ipos); + if (!row) return; + while (row->m_vals.GetSize()<=cpos) row->m_vals.Add(NULL); + free(row->m_vals.Get(cpos)); + row->m_vals.Set(cpos,txt?strdup(txt):NULL); + InvalidateRect(h,NULL,FALSE); +} + +int ListView_GetNextItem(HWND h, int istart, int flags) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return -1; + const int n = lvs->GetNumItems(); + for (int x = wdl_max(0,istart+1); x < n; x ++) + { + if (flags&LVNI_SELECTED) if (lvs->get_sel(x)) return x; + if (flags&LVNI_FOCUSED) if (lvs->m_selitem==x) return x; + } + return -1; +} + +bool ListView_SetItem(HWND h, LVITEM *item) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || !item)) return false; + + const bool ownerData = lvs->IsOwnerData(); + if (!ownerData) + { + SWELL_ListView_Row *row=lvs->m_data.Get(item->iItem); + if (!row) return false; + + const int ncol = wdl_max(lvs->m_cols.GetSize(),1); + if (item->iSubItem >= 0 && item->iSubItem < ncol) + { + while (row->m_vals.GetSize()<=item->iSubItem) row->m_vals.Add(NULL); + if (item->mask&LVIF_TEXT) + { + free(row->m_vals.Get(item->iSubItem)); + row->m_vals.Set(item->iSubItem,item->pszText?strdup(item->pszText):NULL); + } + } + if (item->mask & LVIF_PARAM) + { + row->m_param = item->lParam; + } + if (item->mask&LVIF_IMAGE) + { + row->m_imageidx=item->iImage+1; + } + } + else + { + if (item->iItem < 0 || item->iItem >= lvs->GetNumItems()) return false; + } + if (item->mask & LVIF_STATE) + { + ListView_SetItemState(h,item->iItem,item->state,item->stateMask); + } + + InvalidateRect(h,NULL,FALSE); + + return true; +} + +bool ListView_GetItem(HWND h, LVITEM *item) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || !item)) return false; + if (!lvs->IsOwnerData()) + { + SWELL_ListView_Row *row=lvs->m_data.Get(item->iItem); + if (!row) return false; + if ((item->mask&LVIF_TEXT)&&item->pszText && item->cchTextMax > 0) + { + const char *v=row->m_vals.Get(item->iSubItem); + lstrcpyn_safe(item->pszText, v?v:"",item->cchTextMax); + } + if (item->mask & LVIF_PARAM) item->lParam = row->m_param; + } + else + { + if (item->iItem < 0 || item->iItem >= lvs->GetNumItems()) return false; + + int mask = item->mask & (LVIF_PARAM|LVIF_TEXT); + if (mask & LVIF_TEXT) + { + if (!item->pszText || item->cchTextMax < 1) mask &= ~LVIF_TEXT; + else *item->pszText = 0; + } + if (mask) + { + NMLVDISPINFO nm={{h, (UINT_PTR)GetWindowLong(h,GWL_ID), LVN_GETDISPINFO}}; + nm.item.mask = mask; + nm.item.iItem = item->iItem; + nm.item.iSubItem = item->iSubItem; + nm.item.pszText = item->pszText; + nm.item.cchTextMax = item->cchTextMax; + SendMessage(GetParent(h),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm); + if (mask & LVIF_PARAM) item->lParam = nm.item.lParam; + } + } + + if (item->mask & LVIF_STATE) + { + item->state = 0; + if ((item->stateMask & LVIS_SELECTED) && lvs->get_sel(item->iItem)) item->state |= LVIS_SELECTED; + if ((item->stateMask & LVIS_FOCUSED) && lvs->m_selitem == item->iItem) item->state |= LVIS_FOCUSED; + if (item->stateMask & 0xff0000) + { + SWELL_ListView_Row *row = lvs->m_data.Get(item->iItem); + if (row) + item->state |= INDEXTOSTATEIMAGEMASK(row->m_imageidx); + } + } + + return true; +} +int ListView_GetItemState(HWND h, int ipos, UINT mask) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return 0; + int ret = 0; + if (mask & LVIS_SELECTED) ret |= (lvs->get_sel(ipos) ? LVIS_SELECTED : 0 ); + if ((mask & LVIS_FOCUSED) && lvs->m_selitem == ipos) ret |= LVIS_FOCUSED; + if ((mask & LVIS_STATEIMAGEMASK) && lvs->m_status_imagelist_type == LVSIL_STATE) + { + SWELL_ListView_Row *row = lvs->m_data.Get(ipos); + if (row) + ret |= INDEXTOSTATEIMAGEMASK(row->m_imageidx); + } + return ret; +} + +bool ListView_SetItemState(HWND h, int ipos, UINT state, UINT statemask) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return false; + + static int _is_doing_all; + + if (ipos == -1) + { + int x; + int n=ListView_GetItemCount(h); + _is_doing_all++; + if ((statemask & LVIS_SELECTED) && (state & LVIS_SELECTED) && !lvs->m_is_multisel) + statemask &= ~LVIS_SELECTED; + for (x = 0; x < n; x ++) + ListView_SetItemState(h,x,state,statemask); + _is_doing_all--; + ListView_RedrawItems(h,0,n-1); + return true; + } + bool changed=false; + + if (statemask & LVIS_SELECTED) changed |= lvs->set_sel(ipos,!!(state&LVIS_SELECTED)); + if (statemask & LVIS_FOCUSED) + { + if (state&LVIS_FOCUSED) + { + if (lvs->m_selitem != ipos) + { + changed=true; + lvs->m_selitem = ipos; + } + } + } + if ((statemask & LVIS_STATEIMAGEMASK) && lvs->m_status_imagelist_type == LVSIL_STATE) + { + SWELL_ListView_Row *row = lvs->m_data.Get(ipos); + if (row) + { + const int idx= row->m_imageidx; + row->m_imageidx=STATEIMAGEMASKTOINDEX(state); + if (!changed && idx != row->m_imageidx) ListView_RedrawItems(h,ipos,ipos); + } + } + + if (changed) + { + static int __rent; + if (!__rent) + { + __rent++; + NMLISTVIEW nm={{(HWND)h,(unsigned short)h->m_id,LVN_ITEMCHANGED},ipos,0,state,}; + SendMessage(GetParent(h),WM_NOTIFY,h->m_id,(LPARAM)&nm); + __rent--; + } + if (!_is_doing_all) ListView_RedrawItems(h,ipos,ipos); + } + return true; +} +void ListView_RedrawItems(HWND h, int startitem, int enditem) +{ + if (WDL_NOT_NORMALLY(!h)) return; + InvalidateRect(h,NULL,FALSE); +} + +void ListView_DeleteItem(HWND h, int ipos) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || lvs->IsOwnerData())) return; + lvs->m_data.Delete(ipos,true); + InvalidateRect(h,NULL,FALSE); +} + +void ListView_DeleteAllItems(HWND h) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || lvs->IsOwnerData())) return; + lvs->m_data.Empty(true); + InvalidateRect(h,NULL,FALSE); +} + +int ListView_GetSelectedCount(HWND h) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return 0; + const int n = lvs->GetNumItems(); + int sum=0,x; + for (x=0;xget_sel(x)) sum++; + return sum; +} + +int ListView_GetItemCount(HWND h) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return 0; + return lvs->GetNumItems(); +} + +int ListView_GetSelectionMark(HWND h) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return 0; + const int n = lvs->GetNumItems(); + int x; + for (x=0;xget_sel(x)) return x; + return -1; +} +int SWELL_GetListViewHeaderHeight(HWND h) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + return WDL_NORMALLY(lvs) ? lvs->GetColumnHeaderHeight(h) : 0; +} + +void ListView_SetColumnWidth(HWND h, int pos, int wid) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return; + SWELL_ListView_Col *col = lvs->GetColumnByIndex(pos); + WDL_ASSERT(col || !pos); + if (col) + { + col->xwid = wid; + InvalidateRect(h,NULL,FALSE); + } +} + +int ListView_HitTest(HWND h, LVHITTESTINFO *pinf) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || !pinf)) return -1; + + pinf->flags=0; + pinf->iItem=-1; + + int x=pinf->pt.x; + int y=pinf->pt.y; + + RECT r; + GetClientRect(h,&r); + + if (x < 0) pinf->flags |= LVHT_TOLEFT; + if (x >= r.right) pinf->flags |= LVHT_TORIGHT; + if (y < 0) pinf->flags |= LVHT_ABOVE; + if (y >= r.bottom) pinf->flags |= LVHT_BELOW; + + if (!pinf->flags && lvs->m_last_row_height) + { + const int ypos = y - lvs->GetColumnHeaderHeight(h); + const int hit = ypos >= 0 ? ((ypos + lvs->m_scroll_y) / lvs->m_last_row_height) : -1; + if (hit < 0) pinf->flags |= LVHT_ABOVE; + pinf->iItem=hit < 0 || hit >= lvs->GetNumItems() ? -1 : hit; + if (pinf->iItem >= 0) + { + if (lvs->m_status_imagelist && x < lvs->m_last_row_height) + { + pinf->flags=LVHT_ONITEMSTATEICON; + } + else + { + pinf->flags=LVHT_ONITEMLABEL; + } + } + else + { + pinf->flags=LVHT_NOWHERE; + } + } + + return pinf->iItem; +} +int ListView_SubItemHitTest(HWND h, LVHITTESTINFO *pinf) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || !pinf)) return -1; + + const int row = ListView_HitTest(h, pinf); + int x,xpos=-lvs->m_scroll_x,idx=0; + const int n=lvs->m_cols.GetSize(); + const bool has_image = lvs->hasStatusImage(); + if (has_image) xpos += lvs->m_last_row_height; + for (x=0;xm_cols.Get()[x].xwid; + if (pinf->pt.x >= xpos && pinf->pt.x < xpos+xwid) { idx = lvs->m_cols.Get()[x].col_index; break; } + xpos += xwid; + } + pinf->iSubItem = idx; + return row; +} + +void ListView_SetItemCount(HWND h, int cnt) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs) || !lvs->IsOwnerData()) return; + lvs->m_owner_data_size = cnt > 0 ? cnt : 0; + if (lvs->m_owner_multisel_state.GetSize() > lvs->m_owner_data_size) lvs->m_owner_multisel_state.Resize(lvs->m_owner_data_size); + if (lvs->m_selitem >= lvs->m_owner_data_size) lvs->m_selitem = -1; +} + +void ListView_EnsureVisible(HWND h, int i, BOOL pok) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs) || !lvs->m_last_row_height) return; + const int n = lvs->GetNumItems(); + if (i>=0 && i < n) + { + const int row_height = lvs->m_last_row_height; + RECT r; + GetClientRect(h,&r); + r.bottom -= lvs->GetColumnHeaderHeight(h); + if (lvs->getTotalWidth() > r.right) r.bottom -= row_height; + + const int oldy = lvs->m_scroll_y; + if (i*row_height < lvs->m_scroll_y) lvs->m_scroll_y = i*row_height; + else if ((i+1)*row_height > lvs->m_scroll_y + r.bottom) lvs->m_scroll_y = (i+1)*row_height-r.bottom; + lvs->sanitizeScroll(h); + if (oldy != lvs->m_scroll_y) + { + InvalidateRect(h,NULL,FALSE); + } + } + +} +bool ListView_GetSubItemRect(HWND h, int item, int subitem, int code, RECT *r) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || !r)) return false; + + r->top = lvs->m_last_row_height * item - lvs->m_scroll_y; + r->top += lvs->GetColumnHeaderHeight(h); + RECT cr; + GetClientRect(h,&cr); + r->left=cr.left; + r->right=cr.right; + + if (subitem>0) + { + int x,xpos=-lvs->m_scroll_x; + const int n=lvs->m_cols.GetSize(); + for (x = 0; x < n; x ++) + { + int xwid = lvs->m_cols.Get()[x].xwid; + if (!x && lvs->hasStatusImage()) xwid += lvs->m_last_row_height; + if (lvs->m_cols.Get()[x].col_index == subitem) + { + r->left=xpos; + r->right=xpos+xwid; + break; + } + xpos += xwid; + } + } + + if (r->top < -64-lvs->m_last_row_height) r->top = -64 - lvs->m_last_row_height; + if (r->top > cr.bottom+64) r->top = cr.bottom+64; + + r->bottom = r->top + lvs->m_last_row_height; + + return true; +} + +bool ListView_GetItemRect(HWND h, int item, RECT *r, int code) +{ + return ListView_GetSubItemRect(h, item, -1, code, r); +} + +bool ListView_Scroll(HWND h, int xscroll, int yscroll) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs) || !lvs->m_last_row_height) return false; + const int oldy = lvs->m_scroll_y, oldx = lvs->m_scroll_x; + lvs->m_scroll_x += xscroll; + lvs->m_scroll_y += yscroll; + lvs->sanitizeScroll(h); + if (oldy != lvs->m_scroll_y || oldx != lvs->m_scroll_x) + InvalidateRect(h,NULL,FALSE); + return true; +} + +void ListView_SortItems(HWND hwnd, PFNLVCOMPARE compf, LPARAM parm) +{ + listViewState *lvs = hwnd ? (listViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || + lvs->m_is_listbox || + lvs->m_owner_data_size >= 0 || !compf)) return; + + WDL_HeapBuf tmp; + char *b = (char*)tmp.ResizeOK(lvs->m_data.GetSize()*sizeof(void *)); + if (b) + __listview_mergesort_internal(lvs->m_data.GetList(),lvs->m_data.GetSize(), + sizeof(void *),compf,parm,(char*)b); + InvalidateRect(hwnd,NULL,FALSE); +} + +bool ListView_DeleteColumn(HWND h, int pos) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return false; + SWELL_ListView_Col *c = lvs->GetColumnByIndex(pos); + if (!c) return false; + + const int cidx = c->col_index; + free(c->name); + lvs->m_cols.Delete((int) (c - lvs->m_cols.Get())); + + for (int x = 0; x < lvs->m_cols.GetSize(); x ++) + { + c = lvs->m_cols.Get()+x; + WDL_ASSERT(c->col_index != cidx); + if (c->col_index > cidx) c->col_index--; + } + InvalidateRect(h,NULL,FALSE); + return true; +} + +int ListView_GetCountPerPage(HWND h) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs) || + !lvs->m_last_row_height) return 0; + + RECT cr; + GetClientRect(h,&cr); + cr.bottom -= lvs->GetColumnHeaderHeight(h); + return (cr.bottom-cr.top) / lvs->m_last_row_height; +} + +HWND ChildWindowFromPoint(HWND h, POINT p) +{ + if (WDL_NOT_NORMALLY(!h)) return 0; + + RECT r={0,}; + + for(;;) + { + HWND h2=h->m_children; + RECT sr; + + NCCALCSIZE_PARAMS tr={{h->m_position,},}; + if (h->m_wndproc) h->m_wndproc(h,WM_NCCALCSIZE,0,(LPARAM)&tr); + r.left += tr.rgrc[0].left - h->m_position.left; + r.top += tr.rgrc[0].top - h->m_position.top; + + HWND best=NULL; + RECT bestsr = { 0, }; + while (h2) + { + sr = h2->m_position; + sr.left += r.left; + sr.right += r.left; + sr.top += r.top; + sr.bottom += r.top; + + if (h2->m_visible && PtInRect(&sr,p)) + { + bestsr = sr; + best=h2; + } + + h2 = h2->m_next; + } + if (!best) break; // h is the window we care about + + h=best; // descend to best + r=bestsr; + } + + return h; +} + +static HWND recurseOwnedWindowHitTest(HWND h, POINT p, int maxdepth) +{ + RECT r; + GetWindowContentViewRect(h,&r); + if (!PtInRect(&r,p)) return NULL; + + // check any owned windows first, as they are always above our window + if (h->m_owned_list && maxdepth > 0) + { + HWND owned = h->m_owned_list; + while (owned) + { + if (owned->m_visible) + { + HWND hit = recurseOwnedWindowHitTest(owned,p,maxdepth-1); + if (hit) return hit; + } + owned = owned->m_owned_next; + } + } + p.x -= r.left; + p.y -= r.top; + return ChildWindowFromPoint(h,p); +} + +HWND WindowFromPoint(POINT p) +{ + HWND h = SWELL_topwindows; + while (h) + { + if (h->m_visible) + { + HWND hit = recurseOwnedWindowHitTest(h,p,20); + if (hit) return hit; + } + h = h->m_next; + } + return NULL; +} + +#ifdef _DEBUG +int g_swell_in_paint; +#endif + +BOOL InvalidateRect(HWND hwnd, const RECT *r, int eraseBk) +{ +#ifdef _DEBUG + if (g_swell_in_paint) + { + printf("swell-generic: calling InvalidateRect() from within paint, this is allowed but bad form.\n"); + // WDL_ASSERT(false); + } +#endif + + if (WDL_NOT_NORMALLY(!hwnd) || hwnd->m_hashaddestroy) return FALSE; + +#ifdef SWELL_LICE_GDI + RECT rect; + if (r) + { + rect = *r; + } + else + { + rect = hwnd->m_position; + WinOffsetRect(&rect, -rect.left, -rect.top); + } + + // rect is in client coordinates of h + HWND h = hwnd; + for (;;) + { + if (!h->m_visible || h->m_hashaddestroy) return FALSE; + + RECT ncrect = h->m_position; + if (h->m_oswindow) WinOffsetRect(&ncrect, -ncrect.left, -ncrect.top); + + NCCALCSIZE_PARAMS tr; + memset(&tr,0,sizeof(tr)); + tr.rgrc[0] = ncrect; + if (h->m_wndproc) h->m_wndproc(h,WM_NCCALCSIZE,0,(LPARAM)&tr); + + WinOffsetRect(&rect,tr.rgrc[0].left, tr.rgrc[0].top); + + if (!IntersectRect(&rect,&rect,&ncrect)) return FALSE; + + if (h->m_oswindow) break; + + h=h->m_parent; + if (!h) return FALSE; + } + + { + hwnd->m_invalidated=true; + HWND t=hwnd->m_parent; + if (t && (t->m_style & WS_CLIPSIBLINGS)) + { + // child window, invalidate any later children that intersect us (redraw them on top of us) + HWND nw = hwnd->m_next; + while (nw) + { + RECT tmp; + if (nw->m_visible && !nw->m_invalidated && WinIntersectRect(&tmp,&hwnd->m_position,&nw->m_position)) + nw->m_invalidated=true; + nw=nw->m_next; + } + } + + while (t) + { + if (eraseBk) + { + t->m_invalidated=true; + eraseBk--; + } + t->m_child_invalidated=true; + t=t->m_parent; + } + } + swell_oswindow_invalidate(h, (hwnd!=h || r) ? &rect : NULL); +#endif + return TRUE; +} + + +HWND GetCapture() +{ + return swell_captured_window; +} + +HWND SetCapture(HWND hwnd) +{ + WDL_ASSERT(hwnd != NULL); + HWND oc = swell_captured_window; + if (oc != hwnd) + { + swell_captured_window=hwnd; + if (oc) SendMessage(oc,WM_CAPTURECHANGED,0,(LPARAM)hwnd); + } + return oc; +} + +void ReleaseCapture() +{ + if (swell_captured_window) + { + SendMessage(swell_captured_window,WM_CAPTURECHANGED,0,0); + swell_captured_window=0; + } +} + +static HWND getNextFocusWindow(HWND hwnd, bool rev, HWND foc_child) +{ + HWND ch = NULL; + if (foc_child) + { + ch = hwnd->m_children; + while (ch && ch != foc_child) ch = ch->m_next; + } + + int pass=0; + if (ch) + { + ch = rev ? ch->m_prev : ch->m_next; + } + else + { + ch = hwnd->m_children; + if (ch && rev) while (ch->m_next) ch=ch->m_next; + pass++; + } + + for (;;) + { + while (ch) + { + // scan to find next matching control + if (ch->m_wantfocus && ch->m_visible && ch->m_enabled) break; + ch = rev ? ch->m_prev : ch->m_next; + } + if (ch || ++pass>1 || hwnd->m_parent) break; + + // continue searching + ch = hwnd->m_children; + if (ch && rev) while (ch->m_next) ch=ch->m_next; + } + if (ch && ch->m_children) + { + HWND sub = getNextFocusWindow(ch,rev,NULL); + if (sub) return sub; + } + return ch; +} + + +LRESULT SwellDialogDefaultWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + DLGPROC d=(DLGPROC)GetWindowLong(hwnd,DWL_DLGPROC); + if (d) + { + if (uMsg == WM_PAINT) + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + HBRUSH hbrush = (HBRUSH) d(hwnd,WM_CTLCOLORDLG,(WPARAM)ps.hdc,(LPARAM)hwnd); + if (hbrush && hbrush != (HBRUSH)1) + { + FillRect(ps.hdc,&ps.rcPaint,hbrush); + } + else if (1) + { + SWELL_FillDialogBackground(ps.hdc,&ps.rcPaint,0); + } + + EndPaint(hwnd,&ps); + } + } + + LRESULT r=(LRESULT) d(hwnd,uMsg,wParam,lParam); + + + if (r) return r; + + if (uMsg == WM_KEYDOWN) + { + if (!hwnd->m_parent) + { + if (wParam == VK_ESCAPE) + { + if (IsWindowEnabled(hwnd) && !SendMessage(hwnd,WM_CLOSE,0,0)) + SendMessage(hwnd,WM_COMMAND,IDCANCEL,0); + return 0; + } + else if (wParam == VK_RETURN) + { + HWND c = GetWindow(hwnd,GW_CHILD); + while (c) + { + if (c->m_id && (c->m_style&BS_DEFPUSHBUTTON) && + c->m_classname && !strcmp(c->m_classname,"Button")) + { + SendMessage(hwnd,WM_COMMAND,c->m_id,0); + return 0; + } + c = GetWindow(c,GW_HWNDNEXT); + } + c = GetDlgItem(hwnd,IDOK); + if (c) SendMessage(hwnd,WM_COMMAND,IDOK,0); + return 0; + } + } + int navdir = 0; + + if (wParam == VK_TAB && (lParam&~FSHIFT) == FVIRTKEY) navdir = (lParam & FSHIFT) ? -1 : 1; + else if ((lParam&0xff) == FVIRTKEY) + { + if (wParam == VK_LEFT || wParam == VK_UP) navdir = -1; + else if (wParam == VK_RIGHT || wParam == VK_DOWN) navdir = 1; + } + + if (navdir) + { + HWND ch = getNextFocusWindow(hwnd,navdir<0,hwnd->m_focused_child); + if (ch) + { + SetFocus(ch); + + InvalidateRect(ch,NULL,FALSE); + return 0; + } + } + } + } + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + +BOOL EndPaint(HWND hwnd, PAINTSTRUCT *ps) +{ + WDL_ASSERT(hwnd != NULL); + return TRUE; +} + + +static HFONT menubar_font; + +static bool wantRightAlignedMenuBarItem(const char *p) +{ + char c = *p; + return c > 0 && c != '&' && !isalnum(c); +} + +#define MENUBAR_SELECTED_ITEM_XPAD \ + wdl_min(g_swell_ctheme.menubar_spacing_width,g_swell_ctheme.menubar_margin_width) + +static int menuBarHitTest(HWND hwnd, int mousex, int mousey, RECT *rOut, int forceItem, int expandedItem) +{ + int rv=-1; + RECT r; + GetWindowContentViewRect(hwnd,&r); + if (forceItem >= 0 || (mousey>=r.top && mousey < r.top+g_swell_ctheme.menubar_height)) + { + HDC dc = GetWindowDC(hwnd); + + const int xselpad = MENUBAR_SELECTED_ITEM_XPAD, xpad=g_swell_ctheme.menubar_spacing_width; + int x,xpos=r.left + g_swell_ctheme.menubar_margin_width; + HMENU__ *menu = (HMENU__*)hwnd->m_menu; + HGDIOBJ oldfont = dc ? SelectObject(dc,menubar_font) : NULL; + const int n=menu->items.GetSize(); + for(x=0;xitems.Get(x); + if (inf->fType == MFT_STRING && inf->dwTypeData) + { + bool dis = !!(inf->fState & MF_GRAYED); + RECT cr={0,}; + DrawText(dc,inf->dwTypeData,-1,&cr,DT_CALCRECT); + if (x == n-1 && wantRightAlignedMenuBarItem(inf->dwTypeData)) + { + xpos = wdl_max(xpos,r.right - g_swell_ctheme.menubar_margin_width - cr.right); + cr.right = r.right - g_swell_ctheme.menubar_margin_width - xpos; + } + + if (forceItem >=0 ? forceItem == x : + expandedItem < 0 ? (mousex >= xpos - xpad && mousex < xpos + cr.right + (xpad+1)*3/4) : + x == expandedItem ? (mousex >= xpos - xselpad && mousex < xpos + cr.right + xselpad) : + mousex >= xpos && mousex < xpos + cr.right + xpad - (x == expandedItem-1 ? xselpad : 0) + ) + { + if (!dis) + { + rOut->left = xpos - xselpad; + rOut->right = xpos + cr.right; + rOut->top = r.top; + rOut->bottom = r.top + g_swell_ctheme.menubar_height; + rv=x; + } + break; + } + + xpos+=cr.right+xpad; + } + } + + if (dc) + { + SelectObject(dc,oldfont); + ReleaseDC(hwnd,dc); + } + } + return rv; +} + +static RECT g_menubar_lastrect; +static HWND g_menubar_active; +static POINT g_menubar_startpt; +static bool g_menubar_active_drag; + +HWND swell_window_wants_all_input() +{ + return g_menubar_active_drag ? g_menubar_active : NULL; +} + +int menuBarNavigate(int dir) // -1 if no menu bar active, 0 if did nothing, 1 if navigated +{ + if (!g_menubar_active || !g_menubar_active->m_menu) return -1; + HMENU__ *menu = (HMENU__*)g_menubar_active->m_menu; + RECT r; + const int x = menuBarHitTest(g_menubar_active,0,0,&r,menu->sel_vis + dir, -1); + if (x>=0) + { + MENUITEMINFO *inf = menu->items.Get(x); + if (inf && inf->hSubMenu) + { + menu->sel_vis = x; + g_menubar_lastrect = r; + + DestroyPopupMenus(); + return 1; + } + } + return 0; +} + +RECT g_trackpopup_yroot; +static void runMenuBar(HWND hwnd, HMENU__ *menu, int x, const RECT *use_r, int flag) +{ + menu->Retain(); + MENUITEMINFO *inf = menu->items.Get(x); + RECT r = *use_r; + g_trackpopup_yroot = r; + + RECT mbr; + GetWindowContentViewRect(hwnd,&mbr); + mbr.right -= mbr.left; + mbr.left=0; + mbr.bottom = 0; + mbr.top = -g_swell_ctheme.menubar_height; + menu->sel_vis = x; + GetCursorPos(&g_menubar_startpt); + g_menubar_active = hwnd; + g_menubar_active_drag=true; + for (;;) + { + InvalidateRect(hwnd,&mbr,FALSE); + if (TrackPopupMenu(inf->hSubMenu,0,r.left,r.bottom,flag,hwnd,NULL) || menu->sel_vis == x) break; + + x = menu->sel_vis; + inf = menu->items.Get(x); + if (!inf || !inf->hSubMenu) break; + + r = g_menubar_lastrect; + } + menu->sel_vis=-1; + InvalidateRect(hwnd,&mbr,FALSE); + g_menubar_active = NULL; + g_trackpopup_yroot.top = g_trackpopup_yroot.bottom = 0; + menu->Release(); +} + +LRESULT DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_DESTROY: + if (g_menubar_active == hwnd) g_menubar_active=NULL; + break; + case WM_NCMOUSEMOVE: + if (g_menubar_active == hwnd && hwnd->m_menu) + { + swell_delegate_menu_message(hwnd,lParam,WM_MOUSEMOVE,true); + + HMENU__ *menu = (HMENU__*)hwnd->m_menu; + RECT r; + const int x = menuBarHitTest(hwnd,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),&r,-1, menu->sel_vis); + if (x>=0 && x != menu->sel_vis) + { + MENUITEMINFO *inf = menu->items.Get(x); + if (inf && inf->hSubMenu) + { + menu->sel_vis = x; + g_menubar_lastrect = r; + + DestroyPopupMenus(); // cause new menu to be popped up + } + } + } + break; + + case WM_NCCALCSIZE: + if (!hwnd->m_parent && hwnd->m_menu) + { + RECT *r = (RECT*)lParam; + r->top += g_swell_ctheme.menubar_height; + } + break; + case WM_NCPAINT: + if (!hwnd->m_parent && hwnd->m_menu) + { + HDC dc = GetWindowDC(hwnd); + if (dc) + { + if (!menubar_font) + menubar_font = CreateFont(g_swell_ctheme.menubar_font_size,0,0,0,FW_NORMAL,0,0,0,0,0,0,0,0,g_swell_deffont_face); + + RECT r; + GetWindowContentViewRect(hwnd,&r); + r.right -= r.left; r.left=0; + r.bottom -= r.top; r.top=0; + if (r.bottom>g_swell_ctheme.menubar_height) r.bottom=g_swell_ctheme.menubar_height; + + bool isactive = false; + if (swell_is_app_inactive() == 0) // draw as inactive if maybe inactive + { + HWND foc = GetFocus(); + if (foc && (foc == hwnd || IsChild(hwnd,foc))) isactive = true; + } + + { + HBRUSH br=CreateSolidBrush(isactive ? g_swell_ctheme.menubar_bg : g_swell_ctheme.menubar_bg_inactive); + FillRect(dc,&r,br); + DeleteObject(br); + } + + HGDIOBJ oldfont = SelectObject(dc,menubar_font); + SetBkMode(dc,TRANSPARENT); + + int x,xpos=g_swell_ctheme.menubar_margin_width; + HMENU__ *menu = (HMENU__*)hwnd->m_menu; + const int n = menu->items.GetSize(); + for(x=0;xitems.Get(x); + if (inf->fType == MFT_STRING && inf->dwTypeData) + { + bool dis = !!(inf->fState & MF_GRAYED); + RECT cr={0}; + DrawText(dc,inf->dwTypeData,-1,&cr,DT_CALCRECT); + + if (x == n-1 && wantRightAlignedMenuBarItem(inf->dwTypeData)) + { + cr.left = wdl_max(xpos,r.right - g_swell_ctheme.menubar_margin_width - cr.right); + cr.right = r.right - g_swell_ctheme.menubar_margin_width; + } + else + { + cr.left = xpos; + cr.right += xpos; + } + cr.top = r.top; + cr.bottom = r.bottom; + if (!dis && menu->sel_vis == x) + { + RECT crb = cr; + crb.left -= MENUBAR_SELECTED_ITEM_XPAD; + crb.right += MENUBAR_SELECTED_ITEM_XPAD; + HBRUSH br = CreateSolidBrush(g_swell_ctheme.menubar_bg_sel); + FillRect(dc,&crb,br); + DeleteObject(br); + SetTextColor(dc,g_swell_ctheme.menubar_text_sel); + } + else SetTextColor(dc, + !isactive ? g_swell_ctheme.menubar_text_inactive : + dis ? g_swell_ctheme.menubar_text_disabled : + g_swell_ctheme.menubar_text); + + DrawText(dc,inf->dwTypeData,-1,&cr,DT_VCENTER|DT_LEFT); + xpos=cr.right+g_swell_ctheme.menubar_spacing_width; + } + } + + SelectObject(dc,oldfont); + ReleaseDC(hwnd,dc); + } + } + break; + case WM_RBUTTONUP: + case WM_NCRBUTTONUP: + { + POINT p={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)}; + HWND hwndDest=hwnd; + if (msg==WM_RBUTTONUP) + { + ClientToScreen(hwnd,&p); + HWND h=WindowFromPoint(p); + if (h && IsChild(hwnd,h)) hwndDest=h; + } + SendMessage(hwnd,WM_CONTEXTMENU,(WPARAM)hwndDest,(p.x&0xffff)|(p.y<<16)); + } + return 1; + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + if (!hwnd->m_parent && hwnd->m_menu) + { + if (msg == WM_NCLBUTTONUP && g_menubar_active_drag) + { + g_menubar_active_drag=false; + if (swell_delegate_menu_message(hwnd,lParam,WM_LBUTTONUP,true)) return 0; + + POINT pt; + GetCursorPos(&pt); + pt.x -= g_menubar_startpt.x; + pt.y -= g_menubar_startpt.y; + if (pt.x*pt.x+ pt.y*pt.y > 4*4) + { + DestroyPopupMenus(); + return 0; + } + } + RECT r; + const int x = menuBarHitTest(hwnd,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),&r,-1,-1); + if (x>=0) + { + HMENU__ *menu = (HMENU__*)hwnd->m_menu; + MENUITEMINFO *inf = menu->items.Get(x); + if (inf) + { + if (inf->hSubMenu) + { + if (msg == WM_NCLBUTTONDOWN) + { + runMenuBar(hwnd,menu,x,&r,0xbeef); + } + } + else if (msg == WM_NCLBUTTONUP) + { + if (inf->wID) SendMessage(hwnd,WM_COMMAND,inf->wID,0); + } + } + } + } + break; + case WM_NCHITTEST: + if (!hwnd->m_parent && hwnd->m_menu) + { + RECT r; + GetWindowContentViewRect(hwnd,&r); + if (GET_Y_LPARAM(lParam)>=r.top && GET_Y_LPARAM(lParam) < r.top+g_swell_ctheme.menubar_height) return HTMENU; + } + // todo: WM_NCCALCSIZE etc + return HTCLIENT; + case WM_KEYDOWN: + case WM_KEYUP: + if (hwnd->m_parent) return SendMessage(hwnd->m_parent,msg,wParam,lParam); + + if (msg == WM_KEYDOWN && hwnd->m_menu && + lParam == (FVIRTKEY | FALT) && ( + (wParam >= 'A' && wParam <= 'Z') || + (wParam >= '0' && wParam <= '9') + ) + ) + { + HMENU__ *menu = (HMENU__*)hwnd->m_menu; + const int n=menu->items.GetSize(); + for(int x=0;xitems.Get(x); + if (inf->fType == MFT_STRING && + !(inf->fState & MF_GRAYED) && + inf->dwTypeData) + { + const char *p = inf->dwTypeData; + while (*p) + { + if (*p++ == '&') + { + if (*p != '&') break; + p++; + } + } + if (*p > 0 && (WPARAM)toupper(*p) == wParam) + { + if (inf->hSubMenu) + { + RECT r; + if (menuBarHitTest(hwnd,0,0,&r,x,-1)>=0) + { + runMenuBar(hwnd,menu,x,&r,0xbeee); + } + } + else + { + if (inf->wID) SendMessage(hwnd,WM_COMMAND,inf->wID,0); + } + + return 1; + } + } + } + } + return 69; + + case WM_CONTEXTMENU: + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + case WM_SETCURSOR: + return hwnd->m_parent ? SendMessage(hwnd->m_parent,msg,wParam,lParam) : 0; + + case WM_SETFONT: + hwnd->m_font = (HFONT)wParam; + return 0; + + case WM_GETFONT: + return (LRESULT)(hwnd->m_font ? hwnd->m_font : SWELL_GetDefaultFont()); + + case WM_DROPFILES: + if (hwnd->m_parent && wParam && !(GetWindowLong(hwnd,GWL_EXSTYLE)&WS_EX_ACCEPTFILES)) + { + DROPFILES *df=(DROPFILES*)wParam; + ClientToScreen(hwnd,&df->pt); + ScreenToClient(hwnd->m_parent,&df->pt); + + return SendMessage(hwnd->m_parent,msg,wParam,lParam); + } + return 0; + + } + return 0; +} + + + + + + + + + + + + + + + + + + +///////////////// clipboard compatability (NOT THREAD SAFE CURRENTLY) + + +BOOL DragQueryPoint(HDROP hDrop,LPPOINT pt) +{ + if (!hDrop) return 0; + DROPFILES *df=(DROPFILES*)GlobalLock(hDrop); + BOOL rv=!df->fNC; + *pt=df->pt; + GlobalUnlock(hDrop); + return rv; +} + +void DragFinish(HDROP hDrop) +{ +//do nothing for now (caller will free hdrops) +} + +UINT DragQueryFile(HDROP hDrop, UINT wf, char *buf, UINT bufsz) +{ + if (!hDrop) return 0; + DROPFILES *df=(DROPFILES*)GlobalLock(hDrop); + + UINT rv=0; + char *p=(char*)df + df->pFiles; + if (wf == 0xFFFFFFFF) + { + while (*p) + { + rv++; + p+=strlen(p)+1; + } + } + else + { + while (*p) + { + if (!wf--) + { + if (buf) + { + lstrcpyn_safe(buf,p,bufsz); + rv=strlen(buf); + } + else rv=strlen(p); + + break; + } + p+=strlen(p)+1; + } + } + GlobalUnlock(hDrop); + return rv; +} + + +///////// PostMessage emulation + +BOOL PostMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + WDL_ASSERT(hwnd != NULL); + return SWELL_Internal_PostMessage(hwnd,message,wParam,lParam); +} + +void SWELL_MessageQueue_Clear(HWND h) +{ + SWELL_Internal_PMQ_ClearAllMessages(h); +} + + + +// implementation of postmessage stuff + + + + +typedef struct PMQ_rec +{ + HWND hwnd; + UINT msg; + WPARAM wParam; + LPARAM lParam; + + struct PMQ_rec *next; +} PMQ_rec; + +static WDL_Mutex m_pmq_mutex; +static PMQ_rec *m_pmq, *m_pmq_empty, *m_pmq_tail; +static int m_pmq_size; + +#define MAX_POSTMESSAGE_SIZE 1024 + + +void SWELL_Internal_PostMessage_Init() +{ +} + +void SWELL_MessageQueue_Flush() +{ + m_pmq_mutex.Enter(); + int max_amt = m_pmq_size; + PMQ_rec *p=m_pmq; + if (p) + { + m_pmq = p->next; + if (m_pmq_tail == p) m_pmq_tail=NULL; + m_pmq_size--; + } + m_pmq_mutex.Leave(); + + // process out up to max_amt of queue + while (p) + { + SendMessage(p->hwnd,p->msg,p->wParam,p->lParam); + + m_pmq_mutex.Enter(); + // move this message to empty list + p->next=m_pmq_empty; + m_pmq_empty = p; + + // get next queued message (if within limits) + p = (--max_amt > 0) ? m_pmq : NULL; + if (p) + { + m_pmq = p->next; + if (m_pmq_tail == p) m_pmq_tail=NULL; + m_pmq_size--; + } + m_pmq_mutex.Leave(); + } +} + +void SWELL_Internal_PMQ_ClearAllMessages(HWND hwnd) +{ + m_pmq_mutex.Enter(); + PMQ_rec *p=m_pmq; + PMQ_rec *lastrec=NULL; + while (p) + { + if (hwnd && p->hwnd != hwnd) { lastrec=p; p=p->next; } + else + { + PMQ_rec *next=p->next; + + p->next=m_pmq_empty; // add p to empty list + m_pmq_empty=p; + m_pmq_size--; + + + if (p==m_pmq_tail) m_pmq_tail=lastrec; // update tail + + if (lastrec) p = lastrec->next = next; + else p = m_pmq = next; + } + } + m_pmq_mutex.Leave(); +} + +BOOL SWELL_Internal_PostMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (WDL_NOT_NORMALLY(!hwnd) || hwnd->m_hashaddestroy) return FALSE; + + BOOL ret=FALSE; + m_pmq_mutex.Enter(); + + if (m_pmq_empty||m_pmq_sizenext; + else rec=(PMQ_rec*)malloc(sizeof(PMQ_rec)); + rec->next=0; + rec->hwnd=hwnd; + rec->msg=msg; + rec->wParam=wParam; + rec->lParam=lParam; + + if (m_pmq_tail) m_pmq_tail->next=rec; + else + { + PMQ_rec *p=m_pmq; + while (p && p->next) p=p->next; // shouldnt happen unless m_pmq is NULL As well but why not for safety + if (p) p->next=rec; + else m_pmq=rec; + } + m_pmq_tail=rec; + m_pmq_size++; + + ret=TRUE; + } + + m_pmq_mutex.Leave(); + + return ret; +} + + +int EnumPropsEx(HWND hwnd, PROPENUMPROCEX proc, LPARAM lParam) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return -1; + int x; + for (x =0 ; x < hwnd->m_props.GetSize(); x ++) + { + const char *k=""; + void *p = hwnd->m_props.Enumerate(x,&k); + if (!proc(hwnd,k,p,lParam)) return 0; + } + return 1; +} + +HANDLE GetProp(HWND hwnd, const char *name) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return NULL; + return hwnd->m_props.Get(name); +} + +BOOL SetProp(HWND hwnd, const char *name, HANDLE val) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return false; + hwnd->m_props.Insert(name,(void *)val); + return TRUE; +} + +HANDLE RemoveProp(HWND hwnd, const char *name) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return NULL; + HANDLE h =GetProp(hwnd,name); + hwnd->m_props.Delete(name); + return h; +} + + +int GetSystemMetrics(int p) +{ + switch (p) + { + case SM_CXSCREEN: + case SM_CYSCREEN: + { + RECT r; + SWELL_GetViewPort(&r, NULL, false); + return p==SM_CXSCREEN ? r.right-r.left : r.bottom-r.top; + } + case SM_CXHSCROLL: + case SM_CYHSCROLL: + case SM_CXVSCROLL: + case SM_CYVSCROLL: return g_swell_ctheme.smscrollbar_width; + case SM_CYMENU: return g_swell_ctheme.menubar_height; + } + return 0; +} + +BOOL ScrollWindow(HWND hwnd, int xamt, int yamt, const RECT *lpRect, const RECT *lpClipRect) +{ + if (WDL_NOT_NORMALLY(!hwnd) || (!xamt && !yamt)) return FALSE; + InvalidateRect(hwnd,NULL,FALSE); + + // move child windows only + hwnd = hwnd->m_children; + while (hwnd) + { + hwnd->m_position.left += xamt; + hwnd->m_position.right += xamt; + hwnd->m_position.top += yamt; + hwnd->m_position.bottom += yamt; + + hwnd=hwnd->m_next; + } + return TRUE; +} + +HWND FindWindowEx(HWND par, HWND lastw, const char *classname, const char *title) +{ + HWND h=lastw?GetWindow(lastw,GW_HWNDNEXT):par?GetWindow(par,GW_CHILD):SWELL_topwindows; + while (h) + { + bool isOk=true; + if (title && strcmp(title,h->m_title.Get())) isOk=false; + else if (classname) + { + if (!h->m_classname || strcmp(classname,h->m_classname)) isOk=false; + } + + if (isOk) return h; + h=GetWindow(h,GW_HWNDNEXT); + } + return NULL; +} + + +HTREEITEM TreeView_InsertItem(HWND hwnd, TV_INSERTSTRUCT *ins) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs || !ins)) return NULL; + + HTREEITEM__ *par=NULL; + int inspos=0; + + if (ins->hParent && ins->hParent != TVI_ROOT && ins->hParent != TVI_FIRST && ins->hParent != TVI_LAST && ins->hParent != TVI_SORT) + { + if (tvs->findItem(ins->hParent,&par,&inspos)) + { + par = ins->hParent; + } + else return 0; + } + + if (ins->hInsertAfter == TVI_FIRST) inspos=0; + else if (ins->hInsertAfter == TVI_LAST || ins->hInsertAfter == TVI_SORT || !ins->hInsertAfter) + inspos=(par ? par : &tvs->m_root)->m_children.GetSize(); + else inspos = (par ? par : &tvs->m_root)->m_children.Find(ins->hInsertAfter)+1; + + HTREEITEM__ *item=new HTREEITEM__; + if (ins->item.mask & TVIF_CHILDREN) item->m_haschildren = !!ins->item.cChildren; + if (ins->item.mask & TVIF_PARAM) item->m_param = ins->item.lParam; + if (ins->item.mask & TVIF_TEXT) item->m_value = strdup(ins->item.pszText); + + (par ? par : &tvs->m_root)->m_children.Insert(inspos,item); + + InvalidateRect(hwnd,NULL,FALSE); + return item; +} + +BOOL TreeView_Expand(HWND hwnd, HTREEITEM item, UINT flag) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs || !tvs->findItem(item,NULL,NULL))) return FALSE; + + if (flag == TVE_EXPAND && (item->m_state & TVIS_EXPANDED)) return TRUE; + if (flag == TVE_COLLAPSE && !(item->m_state & TVIS_EXPANDED)) return TRUE; + + NMTREEVIEW nmhdr={{hwnd,(UINT_PTR)hwnd->m_id,TVN_ITEMEXPANDING},}; + nmhdr.action = flag; + nmhdr.itemNew.hItem=item; + nmhdr.itemNew.lParam=item->m_param; + if (SendMessage(GetParent(hwnd),WM_NOTIFY,hwnd->m_id,(LPARAM)&nmhdr)) return TRUE; + + if (flag == TVE_EXPAND) item->m_state |= TVIS_EXPANDED; + else if (flag == TVE_COLLAPSE) item->m_state &= ~TVIS_EXPANDED; + else if (flag == TVE_TOGGLE) item->m_state ^= TVIS_EXPANDED; + + InvalidateRect(hwnd,NULL,FALSE); + return TRUE; +} + +HTREEITEM TreeView_GetSelection(HWND hwnd) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs) || + !tvs->m_sel || !tvs->findItem(tvs->m_sel,NULL,NULL)) return NULL; + return tvs->m_sel; +} + +void TreeView_DeleteItem(HWND hwnd, HTREEITEM item) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs)) return; + HTREEITEM par=NULL; + int idx=0; + if (!tvs->findItem(item,&par,&idx)) return; + + if (tvs->m_sel && (item == tvs->m_sel || item->FindItem(tvs->m_sel,NULL,NULL))) tvs->m_sel=par; + + (par ? par : &tvs->m_root)->m_children.Delete(idx,true); + InvalidateRect(hwnd,NULL,FALSE); +} + +void TreeView_DeleteAllItems(HWND hwnd) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs)) return; + tvs->m_root.m_children.Empty(true); + tvs->m_sel=NULL; + InvalidateRect(hwnd,NULL,FALSE); +} + +void TreeView_EnsureVisible(HWND hwnd, HTREEITEM item) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs)) return; + if (!item || !tvs->findItem(item,NULL,NULL)) return; + tvs->ensureItemVisible(hwnd,item); + InvalidateRect(hwnd,NULL,FALSE); +} + +void TreeView_SelectItem(HWND hwnd, HTREEITEM item) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs)) return; + + if (tvs->m_sel == item || (item && !tvs->findItem(item,NULL,NULL))) return; + + tvs->m_sel = item; + + static int __rent; + if (!__rent) + { + __rent++; + NMTREEVIEW nm={{(HWND)hwnd,(UINT_PTR)hwnd->m_id,TVN_SELCHANGED},}; + nm.itemNew.hItem = item; + nm.itemNew.lParam = item ? item->m_param : 0; + SendMessage(GetParent(hwnd),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm); + __rent--; + } + tvs->ensureItemVisible(hwnd,tvs->m_sel); + InvalidateRect(hwnd,NULL,FALSE); +} + +BOOL TreeView_GetItem(HWND hwnd, LPTVITEM pitem) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs || !pitem) || + !(pitem->mask & TVIF_HANDLE) || !(pitem->hItem)) return FALSE; + + HTREEITEM ti = pitem->hItem; + pitem->cChildren = ti->m_haschildren ? 1:0; + pitem->lParam = ti->m_param; + if ((pitem->mask&TVIF_TEXT)&&pitem->pszText&&pitem->cchTextMax>0) + { + lstrcpyn_safe(pitem->pszText,ti->m_value?ti->m_value:"",pitem->cchTextMax); + } + pitem->state=(ti == tvs->m_sel ? TVIS_SELECTED : 0) | (ti->m_state & TVIS_EXPANDED); + + return TRUE; +} + +BOOL TreeView_SetItem(HWND hwnd, LPTVITEM pitem) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs || !pitem) || + !(pitem->mask & TVIF_HANDLE) || !(pitem->hItem)) return FALSE; + + if (!tvs->findItem(pitem->hItem,NULL,NULL)) return FALSE; + + HTREEITEM__ *ti = (HTREEITEM__*)pitem->hItem; + + if (pitem->mask & TVIF_CHILDREN) ti->m_haschildren = pitem->cChildren?1:0; + if (pitem->mask & TVIF_PARAM) ti->m_param = pitem->lParam; + + if ((pitem->mask&TVIF_TEXT)&&pitem->pszText) + { + free(ti->m_value); + ti->m_value=strdup(pitem->pszText); + InvalidateRect(hwnd, 0, FALSE); + } + + ti->m_state = (ti->m_state & ~pitem->stateMask) | (pitem->state & pitem->stateMask &~ TVIS_SELECTED); + + if (pitem->stateMask & pitem->state & TVIS_SELECTED) + { + tvs->m_sel = ti; + static int __rent; + if (!__rent) + { + __rent++; + NMTREEVIEW nm={{hwnd,(UINT_PTR)hwnd->m_id,TVN_SELCHANGED},}; + nm.itemNew.hItem = ti; + nm.itemNew.lParam = ti ? ti->m_param : 0; + SendMessage(GetParent(hwnd),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm); + __rent--; + } + } + + InvalidateRect(hwnd,NULL,FALSE); + + return TRUE; +} + +HTREEITEM TreeView_HitTest(HWND hwnd, TVHITTESTINFO *hti) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs || !hti) || + !tvs->m_last_row_height) return NULL; + + RECT r; + GetClientRect(hwnd,&r); + if (!PtInRect(&r,hti->pt)) return NULL; + + int y = hti->pt.y + tvs->m_scroll_y + tvs->m_last_row_height; + HTREEITEM item = tvs->hitTestItem(&tvs->m_root,&y,NULL); + if (!item) + { + hti->flags |= TVHT_BELOW; + } + return item; +} + +HTREEITEM TreeView_GetRoot(HWND hwnd) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs)) return NULL; + return tvs->m_root.m_children.Get(0); +} + +HTREEITEM TreeView_GetParent(HWND hwnd, HTREEITEM item) +{ + if (!item) return TreeView_GetRoot(hwnd); + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + + HTREEITEM par=NULL; + int idx=0; + if (WDL_NOT_NORMALLY(!tvs || !tvs->findItem(item,&par,&idx))) return NULL; + return par; +} + +HTREEITEM TreeView_GetChild(HWND hwnd, HTREEITEM item) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!tvs)) return NULL; + return (item && item != TVI_ROOT ? item : &tvs->m_root)->m_children.Get(0); +} + +HTREEITEM TreeView_GetNextSibling(HWND hwnd, HTREEITEM item) +{ + treeViewState *tvs = hwnd ? (treeViewState *)hwnd->m_private_data : NULL; + + HTREEITEM par=NULL; + int idx=0; + if (WDL_NOT_NORMALLY(!tvs || !tvs->findItem(item,&par,&idx))) return NULL; + + return (par ? par : &tvs->m_root)->m_children.Get(idx+1); +} +BOOL TreeView_SetIndent(HWND hwnd, int indent) +{ + return FALSE; +} + +void TreeView_SetBkColor(HWND hwnd, int color) +{ + // todo implement treeview colors +} +void TreeView_SetTextColor(HWND hwnd, int color) +{ + // todo implement treeview colors +} + +#define IS_CLASSNAME_LISTVIEW_LISTBOX(x) ((x) && (!strcmp(x,"SysListView32") || !strcmp(x,"ListBox"))) + +void ListView_SetBkColor(HWND h, int color) +{ + if (WDL_NORMALLY(h && h->m_private_data && IS_CLASSNAME_LISTVIEW_LISTBOX(h->m_classname))) + { + listViewState *lvs = (listViewState *)h->m_private_data; + if (lvs) lvs->m_color_bg = color; + } +} +void ListView_SetTextBkColor(HWND h, int color) +{ +} +void ListView_SetTextColor(HWND h, int color) +{ + if (WDL_NORMALLY(h && h->m_private_data && IS_CLASSNAME_LISTVIEW_LISTBOX(h->m_classname))) + { + listViewState *lvs = (listViewState *)h->m_private_data; + lvs->m_color_text = color; + } +} +void ListView_SetGridColor(HWND h, int color) +{ + if (WDL_NORMALLY(h && h->m_private_data && IS_CLASSNAME_LISTVIEW_LISTBOX(h->m_classname))) + { + listViewState *lvs = (listViewState *)h->m_private_data; + lvs->m_color_grid = color; + } +} +void ListView_SetSelColors(HWND h, int *colors, int ncolors) +{ + if (WDL_NORMALLY(h && h->m_private_data && h->m_classname)) + { + if (IS_CLASSNAME_LISTVIEW_LISTBOX(h->m_classname)) + { + listViewState *lvs = (listViewState *)h->m_private_data; + if (colors && ncolors > 0) + memcpy(lvs->m_color_extras,colors,wdl_min(ncolors*sizeof(int),sizeof(lvs->m_color_extras))); + } + else if (!strcmp(h->m_classname,"SysTreeView32")) + { + // todo implement treeview colors + } + else + { + WDL_ASSERT(false); + } + } +} +int ListView_GetTopIndex(HWND h) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs) || !lvs->m_last_row_height) return 0; + return lvs->m_scroll_y / lvs->m_last_row_height; +} +BOOL ListView_GetColumnOrderArray(HWND h, int cnt, int* arr) +{ + if (WDL_NOT_NORMALLY(!arr)) return FALSE; + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || !lvs->HasColumnHeaders(h))) return FALSE; + + for (int x=0;xm_cols.GetSize() ? lvs->m_cols.Get()[x].col_index : x; + return TRUE; +} +BOOL ListView_SetColumnOrderArray(HWND h, int cnt, int* arr) +{ + if (!arr) return FALSE; + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs || !lvs->HasColumnHeaders(h))) return FALSE; + + WDL_TypedBuf tmp; + + int x; + // O(N^2) but even with 1000 columns who cares? + for (x = 0; x < cnt; x ++) + { + SWELL_ListView_Col *c = lvs->GetColumnByIndex(arr[x]); + if (WDL_NORMALLY(c != NULL)) + { + tmp.Add(*c); + lvs->m_cols.Delete((int) (c - lvs->m_cols.Get())); + } + } + WDL_ASSERT(lvs->m_cols.GetSize()==0); + for (x = 0; x < tmp.GetSize(); x ++) + { + lvs->m_cols.Add(tmp.Get()+x,1); + } + + return TRUE; +} +HWND ListView_GetHeader(HWND h) +{ + return h; +} + +int Header_GetItemCount(HWND h) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + return WDL_NORMALLY(lvs) ? lvs->m_cols.GetSize() : 0; +} + +BOOL Header_GetItem(HWND h, int col, HDITEM* hi) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return FALSE; + const SWELL_ListView_Col *c = lvs->GetColumnByIndex(col); + if (!c) return FALSE; + + if (hi->mask&HDI_FORMAT) + { + if (c->sortindicator<0) hi->fmt = HDF_SORTUP; + else if (c->sortindicator>0) hi->fmt = HDF_SORTDOWN; + else hi->fmt=0; + } + + return TRUE; +} + +BOOL Header_SetItem(HWND h, int col, HDITEM* hi) +{ + listViewState *lvs = h ? (listViewState *)h->m_private_data : NULL; + if (WDL_NOT_NORMALLY(!lvs)) return FALSE; + SWELL_ListView_Col *c = lvs->GetColumnByIndex(col); + if (!c) return FALSE; + + if (hi->mask&HDI_FORMAT) + { + if (hi->fmt & HDF_SORTUP) c->sortindicator=-1; + else if (hi->fmt & HDF_SORTDOWN) c->sortindicator=1; + else c->sortindicator=0; + } + + return TRUE; +} + + +BOOL EnumChildWindows(HWND hwnd, BOOL (*cwEnumFunc)(HWND,LPARAM),LPARAM lParam) +{ + if (WDL_NORMALLY(hwnd) && hwnd->m_children) + { + HWND n=hwnd->m_children; + while (n) + { + if (!cwEnumFunc(n,lParam) || !EnumChildWindows(n,cwEnumFunc,lParam)) return FALSE; + n = n->m_next; + } + } + return TRUE; +} +void SWELL_GetDesiredControlSize(HWND hwnd, RECT *r) +{ +} + +BOOL SWELL_IsGroupBox(HWND hwnd) +{ + return WDL_NORMALLY(hwnd) && hwnd->m_classname && !stricmp(hwnd->m_classname,"Button") && (hwnd->m_style & BS_GROUPBOX); +} +BOOL SWELL_IsButton(HWND hwnd) +{ + return WDL_NORMALLY(hwnd) && hwnd->m_classname && !stricmp(hwnd->m_classname,"Button") && !(hwnd->m_style & BS_GROUPBOX); +} +BOOL SWELL_IsStaticText(HWND hwnd) +{ + return WDL_NORMALLY(hwnd) && hwnd->m_classname && !stricmp(hwnd->m_classname,"Static"); +} + + +BOOL ShellExecute(HWND hwndDlg, const char *action, const char *content1, const char *content2, const char *content3, int blah) +{ + const char *xdg = "/usr/bin/xdg-open"; + const char *argv[3] = { NULL }; + char *tmp=NULL; + + if (!content1 || !*content1) return FALSE; + + if (!strnicmp(content1,"http://",7) || !strnicmp(content1,"https://",8)) + { + argv[0] = xdg; + argv[1] = content1; + } + else if (!stricmp(content1,"explorer.exe")) + { + const char *fn = content2; + if (fn && !strnicmp(fn, "/select,\"", 9)) + { + tmp = strdup(fn+9); + if (*tmp && tmp[strlen(tmp)-1]=='\"') tmp[strlen(tmp)-1]='\0'; + WDL_remove_filepart(tmp); + fn = tmp; + } + if (!fn || !*fn) return FALSE; + + argv[0] = xdg; + argv[1] = fn; + } + else if (!stricmp(content1,"notepad.exe")||!stricmp(content1,"notepad")) + { + if (!content2 || !*content2) return FALSE; + argv[0] = xdg; + argv[1] = content2; + } + else + { + if (content2 && *content2) + { + argv[0] = content1; + argv[1] = content2; + } + else + { + argv[0] = xdg; + argv[1] = content1; // default to xdg-open for whatever else + } + } + + const pid_t pid = fork(); + if (pid == 0) + { + for (int x=0;argv[x];x++) argv[x] = strdup(argv[x]); + execv(argv[0],(char *const*)argv); + exit(0); // if execv fails for some reason + } + free(tmp); + return pid>0; +} + + + +static LRESULT WINAPI focusRectWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + if (BeginPaint(hwnd,&ps)) + { + RECT r; + GetClientRect(hwnd,&r); + HBRUSH br = CreateSolidBrushAlpha(g_swell_ctheme.focusrect,0.5f); + HPEN pen = CreatePen(0,PS_SOLID,g_swell_ctheme.focusrect); + HGDIOBJ oldbr = SelectObject(ps.hdc,br); + HGDIOBJ oldpen = SelectObject(ps.hdc,pen); + Rectangle(ps.hdc,0,0,r.right,r.bottom); + SelectObject(ps.hdc,oldbr); + SelectObject(ps.hdc,oldpen); + DeleteObject(br); + DeleteObject(pen); + EndPaint(hwnd,&ps); + } + } + return 0; + + } + return DefWindowProc(hwnd,uMsg,wParam,lParam); +} + +// r=NULL to "free" handle +// otherwise r is in hwndPar coordinates +void SWELL_DrawFocusRect(HWND hwndPar, RECT *rct, void **handle) +{ + if (WDL_NOT_NORMALLY(!handle)) return; + + HWND h = (HWND) *handle; + if (h && (!rct || h->m_parent != hwndPar)) + { + DestroyWindow(h); + h->Release(); + *handle = NULL; + h = NULL; + } + + if (rct) + { + if (!h) + { + h = new HWND__(hwndPar,0,rct,"",false,focusRectWndProc); + h->m_style = WS_CHILD; // using this for top-level will also keep it out of the window list + h->Retain(); + *handle = h; + ShowWindow(h,SW_SHOWNA); + } + SetWindowPos(h,HWND_TOP,rct->left,rct->top,rct->right-rct->left,rct->bottom-rct->top,SWP_NOACTIVATE); + InvalidateRect(h,NULL,FALSE); + } + + if (hwndPar) + { + InvalidateRect(hwndPar,NULL,FALSE); + UpdateWindow(hwndPar); + } +} + +void SWELL_BroadcastMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HWND h = SWELL_topwindows; + while (h) + { + SendMessage(h,uMsg,wParam,lParam); + if (uMsg == WM_DISPLAYCHANGE) + InvalidateRect(h,NULL,FALSE); + h = h->m_next; + } +} + +void SetOpaque(HWND h, bool opaque) +{ +} +void SetAllowNoMiddleManRendering(HWND h, bool allow) +{ +} +int SWELL_GetDefaultButtonID(HWND hwndDlg, bool onlyIfEnabled) +{ + return 0; +} + +void SWELL_HideApp() +{ +} + +BOOL SWELL_GetGestureInfo(LPARAM lParam, GESTUREINFO* gi) +{ + return FALSE; +} + +void SWELL_SetWindowWantRaiseAmt(HWND h, int amt) +{ +} +int SWELL_GetWindowWantRaiseAmt(HWND h) +{ + return 0; +} + +// copied from swell-wnd.mm, can maybe have a common impl instead +void SWELL_GenerateDialogFromList(const void *_list, int listsz) +{ +#define SIXFROMLIST list->p1,list->p2,list->p3, list->p4, list->p5, list->p6 + SWELL_DlgResourceEntry *list = (SWELL_DlgResourceEntry*)_list; + while (listsz>0) + { + if (!strcmp(list->str1,"__SWELL_BUTTON")) + { + SWELL_MakeButton(list->flag1,list->str2, SIXFROMLIST); + } + else if (!strcmp(list->str1,"__SWELL_EDIT")) + { + SWELL_MakeEditField(SIXFROMLIST); + } + else if (!strcmp(list->str1,"__SWELL_COMBO")) + { + SWELL_MakeCombo(SIXFROMLIST); + } + else if (!strcmp(list->str1,"__SWELL_LISTBOX")) + { + SWELL_MakeListBox(SIXFROMLIST); + } + else if (!strcmp(list->str1,"__SWELL_GROUP")) + { + SWELL_MakeGroupBox(list->str2,SIXFROMLIST); + } + else if (!strcmp(list->str1,"__SWELL_CHECKBOX")) + { + SWELL_MakeCheckBox(list->str2,SIXFROMLIST); + } + else if (!strcmp(list->str1,"__SWELL_LABEL")) + { + SWELL_MakeLabel(list->flag1, list->str2, SIXFROMLIST); + } + else if (!strcmp(list->str1,"__SWELL_ICON")) + { + // todo (str2 is likely a (const char *)(INT_PTR)resid + } + else if (*list->str2) + { + SWELL_MakeControl(list->str1, list->flag1, list->str2, SIXFROMLIST); + } + listsz--; + list++; + } +} + +void SWELL_SetClassName(HWND hwnd, const char *p) +{ + if (WDL_NORMALLY(hwnd)) + hwnd->m_classname=p; +} + +int GetClassName(HWND hwnd, char *buf, int bufsz) +{ + if (WDL_NOT_NORMALLY(!hwnd || !hwnd->m_classname || !buf || bufsz<1)) return 0; + lstrcpyn_safe(buf,hwnd->m_classname,bufsz); + return (int)strlen(buf); +} + +void SWELL_DisableContextMenu(HWND hwnd, bool dis) +{ + if (WDL_NORMALLY(hwnd)) + { + if (!strcmp(hwnd->m_classname,"Edit")) + { + __SWELL_editControlState *es = (__SWELL_editControlState*)hwnd->m_private_data; + if (es) es->m_disable_contextmenu = dis; + } + } +} + +#ifdef _DEBUG +void VALIDATE_HWND_LIST(HWND listHead, HWND par) +{ + if (!listHead) return; + WDL_ASSERT(!listHead->m_prev); + WDL_ASSERT(listHead->m_parent == par); + WDL_ASSERT(listHead->m_next != listHead); + HWND last = listHead; + HWND list = listHead->m_next; + while (list) + { + WDL_ASSERT(list->m_prev == last); + WDL_ASSERT(list->m_parent == par); + last = list; + list = list->m_next; + } +} +#endif + + + +#endif diff --git a/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-wnd.mm b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-wnd.mm new file mode 100644 index 000000000..fecd53b16 --- /dev/null +++ b/source/modules/ysfx/thirdparty/WDL/source/WDL/swell/swell-wnd.mm @@ -0,0 +1,7146 @@ +/* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX) + Copyright (C) 2006 and later, Cockos, Inc. + + 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. + + + This file provides basic windows APIs for handling windows, as well as the stubs to enable swell-dlggen to work. + + */ + + +#ifndef SWELL_PROVIDED_BY_APP + +#import +#import +#include "swell.h" +#include "../mutex.h" +#include "../ptrlist.h" +#include "../queue.h" +#include "../wdlcstring.h" + +#include "swell-dlggen.h" + +#define SWELL_INTERNAL_MERGESORT_IMPL +#define SWELL_INTERNAL_HTREEITEM_IMPL +#include "swell-internal.h" + +static bool SWELL_NeedModernListViewHacks() +{ +#ifdef __LP64__ + return false; +#else + // only needed on 32 bit yosemite as of 10.10.3, but who knows when it will be necessary elsewhere + return SWELL_GetOSXVersion() >= 0x10a0; +#endif +} + +static LRESULT sendSwellMessage(id obj, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (obj && [obj respondsToSelector:@selector(onSwellMessage:p1:p2:)]) + return [(SWELL_hwndChild *)obj onSwellMessage:uMsg p1:wParam p2:lParam]; + return 0; +} +static void InvalidateSuperViews(NSView *view); +#define STANDARD_CONTROL_NEEDSDISPLAY_IMPL(classname) \ + - (const char *)swellGetClass { return ( classname ); } \ + - (void)setNeedsDisplay:(BOOL)flag \ + { \ + [super setNeedsDisplay:flag]; \ + if (flag) InvalidateSuperViews(self); \ + } \ + - (void)setNeedsDisplayInRect:(NSRect)rect \ + { \ + [super setNeedsDisplayInRect:rect]; \ + InvalidateSuperViews(self); \ + } + + +int g_swell_osx_readonlytext_wndbg = 0; +int g_swell_osx_style = 0; // &1 = rounded buttons, &2=big sur styled lists +static void *SWELL_CStringToCFString_FilterPrefix(const char *str) +{ + int c=0; + while (str[c] && str[c] != '&' && c++<1024); + if (!str[c] || c>=1024 || strlen(str)>=1024) return SWELL_CStringToCFString(str); + char buf[1500]; + const char *p=str; + char *op=buf; + while (*p) + { + if (*p == '&') p++; + if (!*p) break; + *op++=*p++; + } + *op=0; + return SWELL_CStringToCFString(buf); + +} + +static int _nsStringSearchProc(const void *_a, const void *_b) +{ + NSString *a=(NSString *)_a; + NSString *b = (NSString *)_b; + return (int)[a compare:b]; +} +static int _nsMenuSearchProc(const void *_a, const void *_b) +{ + NSString *a=(NSString *)_a; + NSMenuItem *b = (NSMenuItem *)_b; + return (int)[a compare:[b title]]; +} +static int _listviewrowSearchFunc(const void *_a, const void *_b, const void *ctx) +{ + const char *a = (const char *)_a; + SWELL_ListView_Row *row = (SWELL_ListView_Row *)_b; + const char *b=""; + if (!row || !(b=row->m_vals.Get(0))) b=""; + return strcmp(a,b); +} +static int _listviewrowSearchFunc2(const void *_a, const void *_b, const void *ctx) +{ + const char *a = (const char *)_a; + SWELL_ListView_Row *row = (SWELL_ListView_Row *)_b; + const char *b=""; + if (!row || !(b=row->m_vals.Get(0))) b=""; + return strcmp(b,a); +} + +// modified bsearch: returns place item SHOULD be in if it's not found +static NSInteger arr_bsearch_mod(void *key, NSArray *arr, int (*compar)(const void *, const void *)) +{ + const NSInteger nmemb = [arr count]; + NSInteger p,lim,base=0; + + for (lim = nmemb; lim != 0; lim >>= 1) { + p = base + (lim >> 1); + int cmp = compar(key, [arr objectAtIndex:p]); + if (cmp == 0) return (p); + if (cmp > 0) { /* key > p: move right */ + // check to see if key is less than p+1, if it is, we're done + base = p + 1; + if (base >= nmemb || compar(key,[arr objectAtIndex:base])<=0) return base; + lim--; + } /* else move left */ + } + return 0; +} + + +template static int ptrlist_bsearch_mod(void *key, WDL_PtrList *arr, int (*compar)(const void *, const void *, const void *ctx), void *ctx) +{ + const int nmemb = arr->GetSize(); + int base=0, lim, p; + + for (lim = nmemb; lim != 0; lim >>= 1) { + p = base + (lim >> 1); + int cmp = compar(key, arr->Get(p),ctx); + if (cmp == 0) return (p); + if (cmp > 0) { /* key > p: move right */ + // check to see if key is less than p+1, if it is, we're done + base = p + 1; + if (base >= nmemb || compar(key,arr->Get(base),ctx)<=0) return base; + lim--; + } /* else move left */ + } + return 0; +} + + +@implementation SWELL_TabView +STANDARD_CONTROL_NEEDSDISPLAY_IMPL("SysTabControl32") + +-(void)setNotificationWindow:(id)dest +{ + m_dest=dest; +} +-(id)getNotificationWindow +{ + return m_dest; +} +-(NSInteger) tag +{ + return m_tag; +} +-(void) setTag:(NSInteger)tag +{ + m_tag=tag; +} +- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem +{ + if (m_dest) + { + NMHDR nm={(HWND)self,(UINT_PTR)[self tag],TCN_SELCHANGE}; + SendMessage((HWND)m_dest,WM_NOTIFY,nm.idFrom,(LPARAM)&nm); + } +} +@end + + +@implementation SWELL_ProgressView +STANDARD_CONTROL_NEEDSDISPLAY_IMPL("msctls_progress32") + +-(NSInteger) tag +{ + return m_tag; +} +-(void) setTag:(NSInteger)tag +{ + m_tag=tag; +} +-(LRESULT)onSwellMessage:(UINT)msg p1:(WPARAM)wParam p2:(LPARAM)lParam +{ + if (msg == PBM_SETRANGE) + { + [self setMinValue:LOWORD(lParam)]; + [self setMaxValue:HIWORD(lParam)]; + } + else if (msg==PBM_SETPOS) + { + [self setDoubleValue:(double)wParam]; + [self stopAnimation:self]; + } + else if (msg==PBM_DELTAPOS) + { + [self incrementBy:(double)wParam]; + } + return 0; +} + +@end + +@implementation SWELL_ListViewCell +-(NSColor *)highlightColorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + if ([controlView isKindOfClass:[SWELL_ListView class]]) + { + if (((SWELL_ListView *)controlView)->m_selColors) return nil; + } + else if ([controlView isKindOfClass:[SWELL_TreeView class]]) + { + if (((SWELL_TreeView *)controlView)->m_selColors) return nil; + } + + return [super highlightColorWithFrame:cellFrame inView:controlView]; +} +- (NSRect)drawingRectForBounds:(NSRect)rect +{ + const NSSize sz = [self cellSizeForBounds:rect]; + rect = [super drawingRectForBounds:rect]; + const int offs = (int) floor((rect.size.height - sz.height) * .5); + if (offs>0) + { + rect.origin.y += offs; + rect.size.height -= offs*2; + } + return rect; +} +@end + +@implementation SWELL_StatusCell +-(id)initNewCell +{ + if ((self=[super initTextCell:@""])) + { + status=0; + } + return self; +} +-(void)setStatusImage:(NSImage *)img +{ + status=img; +} +- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + if (status) + { + const double fr_h = cellFrame.size.height; + const NSSize image_sz = [status size]; + const double use_h = wdl_min(image_sz.height, fr_h); + const double yo = (fr_h-use_h)*.5; + double use_w,xo; + if (use_h > cellFrame.size.width) + { + use_w = cellFrame.size.width; + xo = 0.0; + } + else + { + use_w = use_h; + xo = yo; + } + + [status drawInRect:NSMakeRect(cellFrame.origin.x + xo,cellFrame.origin.y + yo,use_w,use_h) + fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + } + cellFrame.origin.x += cellFrame.size.height + 2.0; + cellFrame.size.width -= cellFrame.size.height + 2.0; + [super drawWithFrame:cellFrame inView:controlView]; +} + +@end + +@implementation SWELL_TreeView +STANDARD_CONTROL_NEEDSDISPLAY_IMPL("SysTreeView32") + +-(id) init +{ + if ((self = [super init])) + { + m_fakerightmouse=false; + m_items=new WDL_PtrList; + m_fgColor=0; + m_selColors=0; + } + return self; +} +-(void) dealloc +{ + if (m_items) m_items->Empty(true); + delete m_items; + m_items=0; + [m_fgColor release]; + [m_selColors release]; + [super dealloc]; +} + +-(bool) findItem:(HTREEITEM)item parOut:(HTREEITEM__ **)par idxOut:(int *)idx +{ + if (!m_items||!item) return false; + int x=m_items->Find((HTREEITEM__*)item); + if (x>=0) + { + *par=NULL; + *idx=x; + return true; + } + for (x = 0; x < m_items->GetSize(); x++) + { + if (m_items->Get(x)->FindItem(item,par,idx)) return true; + } + + return false; +} + +-(NSInteger) outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item +{ + if (item == nil) return m_items ? m_items->GetSize() : 0; + return ((HTREEITEM__*)[item getValue])->m_children.GetSize(); +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item +{ + if (item==nil) return YES; + HTREEITEM__ *it=(HTREEITEM__ *)[item getValue]; + + return it && it->m_haschildren; +} + +- (id)outlineView:(NSOutlineView *)outlineView + child:(NSInteger)index + ofItem:(id)item +{ + HTREEITEM__ *row=item ? ((HTREEITEM__*)[item getValue])->m_children.Get(index) : m_items ? m_items->Get(index) : 0; + + return (id)(row ? row->m_dh : NULL); +} + +- (id)outlineView:(NSOutlineView *)outlineView + objectValueForTableColumn:(NSTableColumn *)tableColumn + byItem:(id)item +{ + if (!item) return @""; + HTREEITEM__ *it=(HTREEITEM__ *)[item getValue]; + + if (!it || !it->m_value) return @""; + + NSString *str=(NSString *)SWELL_CStringToCFString(it->m_value); + + return [str autorelease]; +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView + writeItems:(NSArray *)items + toPasteboard:(NSPasteboard *)pasteboard +{ + if (self->style & TVS_DISABLEDRAGDROP) return NO; + [pasteboard declareTypes:[NSArray arrayWithObject:@"swell_treeview"] owner:nil]; + [pasteboard setString:@"" forType:@"swell_treeview"]; + return YES; +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView + acceptDrop:(id)info + item:(id)item + childIndex:(NSInteger)index +{ + HWND par = GetParent((HWND)self); + if (par && GetCapture() == par) + { + POINT p; + GetCursorPos(&p); + ScreenToClient(par,&p); + SendMessage(par,WM_LBUTTONUP,0,MAKELPARAM(p.x,p.y)); + } + return YES; +} + +/* +- (BOOL)outlineView:(NSOutlineView *)outlineView + shouldExpandItem:(id)item +{ + return NO; // optionally while dragging? +} +*/ + +- (void)outlineView:(NSOutlineView *)outlineView + draggingSession:(NSDraggingSession *)session + endedAtPoint:(NSPoint)screenPoint + operation:(NSDragOperation)operation +{ + [self unregisterDraggedTypes]; + HWND par = GetParent((HWND)self); + if (par && GetCapture() == par) + { + // usually acceptDrop above will be the one that is called, but if the user ended up elsewhere + // this might, let the caller clean up capture + POINT p; + GetCursorPos(&p); + ScreenToClient(par,&p); + SendMessage(par,WM_LBUTTONUP,0,MAKELPARAM(p.x,p.y)); + } +} + +- (void)outlineView:(NSOutlineView *)outlineView + draggingSession:(NSDraggingSession *)session + willBeginAtPoint:(NSPoint)screenPoint + forItems:(NSArray *)draggedItems +{ + if (self->style & TVS_DISABLEDRAGDROP) return; + HWND hwnd = (HWND)self, par = GetParent(hwnd); + if (par) + { + TVHITTESTINFO tht; + memset(&tht,0,sizeof(tht)); + GetCursorPos(&tht.pt); + ScreenToClient(hwnd, &tht.pt); + HTREEITEM sel = TreeView_GetSelection(hwnd), hit = TreeView_HitTest(hwnd, &tht); + if (hit && hit != sel) + { + TreeView_SelectItem(hwnd,hit); + sel = hit; + } + + NMTREEVIEW nm={{hwnd,(UINT_PTR)[self tag],TVN_BEGINDRAG},}; + nm.itemNew.hItem = sel; + nm.itemNew.lParam = sel ? sel->m_param : 0; + SendMessage(par,WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm); + if (GetCapture() == par) + [self registerForDraggedTypes:[NSArray arrayWithObject: @"swell_treeview"]]; + } +} + +- (NSDragOperation)outlineView:(NSOutlineView *)outlineView + validateDrop:(id)info + proposedItem:(id)item + proposedChildIndex:(NSInteger)index +{ + HWND hwnd=(HWND)self, par = GetParent(hwnd); + if (par && GetCapture()==par) + { + POINT p; + GetCursorPos(&p); + TVHITTESTINFO tht; + memset(&tht,0,sizeof(tht)); + tht.pt = p; + + ScreenToClient(par,&p); + LRESULT move_res = SendMessage(par,WM_MOUSEMOVE,0,MAKELPARAM(p.x,p.y)); + if (move_res == (LRESULT)-1) return NSDragOperationNone; + if (move_res == (LRESULT)-2) // move to end + { + HTREEITEM par_item = NULL; + HTREEITEM li = self->m_items ? self->m_items->Get(self->m_items->GetSize()-1) : NULL; + while (li && li->m_children.GetSize()) + { + par_item = li; + li = li->m_children.Get(li->m_children.GetSize()-1); + } + if (par_item && par_item->m_children.GetSize()) [self setDropItem:par_item->m_dh dropChildIndex:par_item->m_children.GetSize()]; + } + else if (move_res >= 65536) + { + HTREEITEM paritem = NULL; + int idx=0; + // it is safe (but time consuming!) to call findItem: on a possibly-junk pointer + if ([self findItem:(HTREEITEM)(INT_PTR)move_res parOut:&paritem idxOut:&idx] && paritem) + [self setDropItem:paritem->m_dh dropChildIndex:idx]; + } + return NSDragOperationPrivate; + } + return NSDragOperationNone; + +} + +-(void)mouseDown:(NSEvent *)theEvent +{ + if (([theEvent modifierFlags] & NSControlKeyMask) && IsRightClickEmulateEnabled()) + { + m_fakerightmouse=1; + } + else + { + + NMCLICK nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_CLICK},}; + SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv); + + m_fakerightmouse=0; + [super mouseDown:theEvent]; + } +} + +-(void)mouseDragged:(NSEvent *)theEvent +{ +} + +-(void)mouseUp:(NSEvent *)theEvent +{ + if (m_fakerightmouse||([theEvent modifierFlags] & NSControlKeyMask)) [self rightMouseUp:theEvent]; + else [super mouseUp:theEvent]; +} +- (void)rightMouseUp:(NSEvent *)theEvent +{ + bool wantContext=true; + + NMCLICK nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_RCLICK},}; + if (SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv)) wantContext=false; + + if (wantContext) + { + POINT p; + GetCursorPos(&p); + SendMessage((HWND)[self target],WM_CONTEXTMENU,(WPARAM)self,(p.x&0xffff)|(p.y<<16)); + } + + m_fakerightmouse=0; +} + +- (void)highlightSelectionInClipRect:(NSRect)theClipRect +{ + if (m_selColors) + { + int a = GetFocus() == (HWND)self ? 0 : 2; + if ([m_selColors count] >= a) + { + NSColor *c=[m_selColors objectAtIndex:a]; + if (c) + { + // calculate rect of selected items, combine with theClipRect, and fill these areas with our background (phew!) + + NSInteger x = [self selectedRow]; + if (x>=0) + { + NSRect r = [self rectOfRow:x]; + r = NSIntersectionRect(r,theClipRect); + if (r.size.height>0 && r.size.width>0) + { + [c setFill]; + NSRectFill(r); + } + } + return ; + } + } + } + return [super highlightSelectionInClipRect:theClipRect]; +} + + + + +@end + + + + + +@implementation SWELL_ListView +STANDARD_CONTROL_NEEDSDISPLAY_IMPL( m_lbMode ? "SysListView32_LB" : "SysListView32" ) + +-(BOOL)accessibilityPerformShowMenu +{ + HWND par = (HWND)[self target]; + if (par) + { + int row = (int)ListView_GetNextItem((HWND)self,-1,LVNI_FOCUSED); + int col = 0; // todo + NMLISTVIEW nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_RCLICK}, row, col, 0, 0, 0, }; + SendMessage(par,WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv); + return YES; + } + return NO; +} + +-(LONG)getSwellStyle { return style; } + +-(void)setSwellStyle:(LONG)st +{ + bool hdrchg= ((style&LVS_NOCOLUMNHEADER) != (st&LVS_NOCOLUMNHEADER)); + style=st; + if ((style&LVS_REPORT) && hdrchg) + { + // todo some crap with NSTableView::setHeaderView, but it's complicated + } +} + +-(id) init +{ + if ((self = [super init])) + { + m_selColors=0; + m_fgColor = 0; + ownermode_cnt=0; + m_status_imagelist_type=-1; + m_status_imagelist=0; + m_leftmousemovecnt=0; + m_fakerightmouse=false; + m_lbMode=0; + m_fastClickMask=0; + m_last_shift_clicked_item = m_last_plainly_clicked_item=-1; + m_start_item=-1; + m_start_subitem=-1; + m_start_item_clickmode=0; // 0=clicked item, 1=clicked image, &2=sent drag message, &4=quickclick mode + m_cols = new WDL_PtrList; + m_items=new WDL_PtrList; + } + return self; +} +-(void) dealloc +{ + if (m_items) m_items->Empty(true); + delete m_items; + delete m_cols; + m_cols=0; + m_items=0; + [m_fgColor release]; + [m_selColors release]; + [super dealloc]; +} + +-(int)getColumnPos:(int)idx // get current position of column that was originally at idx +{ + int pos=idx; + if (m_cols) + { + NSTableColumn* col=m_cols->Get(idx); + if (col) + { + NSArray* arr=[self tableColumns]; + if (arr) + { + pos=(int)[arr indexOfObject:col]; + } + } + } + return pos; +} + +- (void)highlightSelectionInClipRect:(NSRect)theClipRect +{ + if (m_selColors) + { + int a = GetFocus() == (HWND)self ? 0 : 2; + if ([m_selColors count] >= a) + { + NSColor *c=[m_selColors objectAtIndex:a]; + if (c) + { + // calculate rect of selected items, combine with theClipRect, and fill these areas with our background (phew!) + bool needfillset=true; + NSInteger x = [self rowAtPoint:NSMakePoint(0,theClipRect.origin.y)]; + if (x<0)x=0; + const NSInteger n = [self numberOfRows]; + for (;x = theClipRect.origin.y + theClipRect.size.height) break; + + if ([self isRowSelected:x]) + { + r = NSIntersectionRect(r,theClipRect); + if (r.size.height>0 && r.size.width>0) + { + if (needfillset) { needfillset=false; [c setFill]; } + NSRectFill(r); + } + } + } + return ; + } + } + } + return [super highlightSelectionInClipRect:theClipRect]; +} +-(int)getColumnIdx:(int)pos // get original index of column that is currently at position +{ + int idx=pos; + NSArray* arr=[self tableColumns]; + if (arr && pos>=0 && pos < [arr count]) + { + NSTableColumn* col=[arr objectAtIndex:pos]; + if (col && m_cols) + { + idx=m_cols->Find(col); + } + } + return idx; +} + +-(NSInteger)columnAtPoint:(NSPoint)pt +{ + int pos=(int)[super columnAtPoint:pt]; + return (NSInteger) [self getColumnIdx:pos]; +} + + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView +{ + return (!m_lbMode && (style & LVS_OWNERDATA)) ? ownermode_cnt : (m_items ? m_items->GetSize():0); +} + +- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + NSString *str=NULL; + int image_idx=0; + + if (!m_lbMode && (style & LVS_OWNERDATA)) + { + HWND tgt=(HWND)[self target]; + + char buf[1024]; + NMLVDISPINFO nm={{(HWND)self, (UINT_PTR)[self tag], LVN_GETDISPINFO}}; + nm.item.mask=LVIF_TEXT; + if (m_status_imagelist_type==LVSIL_STATE) nm.item.mask |= LVIF_STATE; + else if (m_status_imagelist_type == LVSIL_SMALL) nm.item.mask |= LVIF_IMAGE; + nm.item.iImage = -1; + nm.item.iItem=(int)rowIndex; + nm.item.iSubItem=m_cols->Find(aTableColumn); + nm.item.pszText=buf; + nm.item.cchTextMax=sizeof(buf)-1; + buf[0]=0; + SendMessage(tgt,WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm); + + if (m_status_imagelist_type == LVSIL_STATE) image_idx=(nm.item.state>>16)&0xff; + else if (m_status_imagelist_type == LVSIL_SMALL) image_idx = nm.item.iImage + 1; + str=(NSString *)SWELL_CStringToCFString(nm.item.pszText); + } + else + { + char *p=NULL; + SWELL_ListView_Row *r=0; + if (m_items && m_cols && (r=m_items->Get(rowIndex))) + { + p=r->m_vals.Get(m_cols->Find(aTableColumn)); + if (m_status_imagelist_type == LVSIL_STATE || m_status_imagelist_type == LVSIL_SMALL) + { + image_idx=r->m_imageidx; + } + } + + str=(NSString *)SWELL_CStringToCFString(p); + + if (style & LBS_OWNERDRAWFIXED) + { + SWELL_ODListViewCell *cell=[aTableColumn dataCell]; + if ([cell isKindOfClass:[SWELL_ODListViewCell class]]) [cell setItemIdx:(int)rowIndex]; + } + } + + if (!m_lbMode && m_status_imagelist) + { + SWELL_StatusCell *cell=(SWELL_StatusCell*)[aTableColumn dataCell]; + if ([cell isKindOfClass:[SWELL_StatusCell class]]) + { + HICON icon=m_status_imagelist->Get(image_idx-1); + NSImage *img=NULL; + if (icon) img=(NSImage *)GetNSImageFromHICON(icon); + [cell setStatusImage:img]; + } + } + + return [str autorelease]; + +} + + +-(void)mouseDown:(NSEvent *)theEvent +{ + if (([theEvent modifierFlags] & NSControlKeyMask) && IsRightClickEmulateEnabled()) + { + m_fakerightmouse=1; + m_start_item=-1; + m_start_subitem=-1; + } + else + { + if ([theEvent clickCount]>1 && SWELL_NeedModernListViewHacks()) + { + [super mouseDown:theEvent]; + return; + } + m_leftmousemovecnt=0; + m_fakerightmouse=0; + + NSPoint pt=[theEvent locationInWindow]; + pt=[self convertPoint:pt fromView:nil]; + m_start_item=(int)[self rowAtPoint:pt]; + m_start_subitem=(int)[self columnAtPoint:pt]; + + + + m_start_item_clickmode=0; + if (m_start_item >=0 && (m_fastClickMask&(1<Get(nmlv.iItem); + if (row) + nmlv.lParam = row->m_param; + SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv); + m_start_item_clickmode=4; + } + else + { + if (m_start_item>=0 && m_status_imagelist && LVSIL_STATE == m_status_imagelist_type && pt.x <= [self rowHeight]) // in left area + { + m_start_item_clickmode=1; + } + } + } +} + +-(void)mouseDragged:(NSEvent *)theEvent +{ + if (++m_leftmousemovecnt==4) + { + if (m_start_item>=0 && !(m_start_item_clickmode&3)) + { + if (!m_lbMode) + { + // if m_start_item isnt selected, change selection to it now + if (!(m_start_item_clickmode&4) && ![self isRowSelected:m_start_item]) + { + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:m_start_item] byExtendingSelection:!!(GetAsyncKeyState(VK_CONTROL)&0x8000)]; + } + NMLISTVIEW hdr={{(HWND)self,(UINT_PTR)[self tag],LVN_BEGINDRAG},m_start_item,m_start_subitem,0,}; + SendMessage((HWND)[self target],WM_NOTIFY,hdr.hdr.idFrom, (LPARAM) &hdr); + m_start_item_clickmode |= 2; + } + } + } + else if (m_leftmousemovecnt > 4 && !(m_start_item_clickmode&1)) + { + HWND tgt=(HWND)[self target]; + POINT p; + GetCursorPos(&p); + ScreenToClient(tgt,&p); + + SendMessage(tgt,WM_MOUSEMOVE,0,(p.x&0xffff) + (((int)p.y)<<16)); + } +} + +-(void)mouseUp:(NSEvent *)theEvent +{ + if ((m_fakerightmouse||([theEvent modifierFlags] & NSControlKeyMask)) && IsRightClickEmulateEnabled()) + { + [self rightMouseUp:theEvent]; + } + else + { + if ([theEvent clickCount]>1 && SWELL_NeedModernListViewHacks()) + { + [super mouseUp:theEvent]; + return; + } + if (!(m_start_item_clickmode&1)) + { + if (m_leftmousemovecnt>=0 && m_leftmousemovecnt<4 && !(m_start_item_clickmode&4)) + { + const bool msel = [self allowsMultipleSelection]; + if (m_lbMode && !msel) // listboxes --- allow clicking to reset the selection + { + [self deselectAll:self]; + } + + if (SWELL_NeedModernListViewHacks()) + { + if (m_start_item>=0) + { + NSMutableIndexSet *m = [[NSMutableIndexSet alloc] init]; + if (GetAsyncKeyState(VK_CONTROL)&0x8000) + { + [m addIndexes:[self selectedRowIndexes]]; + if ([m containsIndex:m_start_item]) [m removeIndex:m_start_item]; + else + { + if (!msel) [m removeAllIndexes]; + [m addIndex:m_start_item]; + } + m_last_plainly_clicked_item = m_start_item; + } + else if (msel && (GetAsyncKeyState(VK_SHIFT)&0x8000)) + { + [m addIndexes:[self selectedRowIndexes]]; + const int n = ListView_GetItemCount((HWND)self); + if (m_last_plainly_clicked_item<0 || m_last_plainly_clicked_item>=n) + m_last_plainly_clicked_item=m_start_item; + + if (m_last_shift_clicked_item>=0 && + m_last_shift_clicked_item=4) + { + HWND tgt=(HWND)[self target]; + POINT p; + GetCursorPos(&p); + ScreenToClient(tgt,&p); + SendMessage(tgt,WM_LBUTTONUP,0,(p.x&0xffff) + (((int)p.y)<<16)); + } + } + } + + if (!m_lbMode && !(m_start_item_clickmode&(2|4))) + { + NSPoint pt=[theEvent locationInWindow]; + pt=[self convertPoint:pt fromView:nil]; + int col = (int)[self columnAtPoint:pt]; + NMLISTVIEW nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_CLICK}, (int)[self rowAtPoint:pt], col, 0, 0, 0, {NSPOINT_TO_INTS(pt)}, }; + SWELL_ListView_Row *row=m_items->Get(nmlv.iItem); + if (row) nmlv.lParam = row->m_param; + SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv); + } +} + +- (void)rightMouseUp:(NSEvent *)theEvent +{ + bool wantContext=true; + + if (!m_lbMode) + { + NSPoint pt=[theEvent locationInWindow]; + pt=[self convertPoint:pt fromView:nil]; + + // note, windows selects on right mousedown + NSInteger row =[self rowAtPoint:pt]; + if (row >= 0 && ![self isRowSelected:row]) + { + NSIndexSet* rows=[NSIndexSet indexSetWithIndex:row]; + [self deselectAll:self]; + [self selectRowIndexes:rows byExtendingSelection:NO]; + } + + NMLISTVIEW nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_RCLICK}, (int)row, (int)[self columnAtPoint:pt], 0, 0, 0, {NSPOINT_TO_INTS(pt)}, }; + if (SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv)) wantContext=false; + } + if (wantContext) + { + POINT p; + GetCursorPos(&p); + SendMessage((HWND)[self target],WM_CONTEXTMENU,(WPARAM)self,(p.x&0xffff)|(p.y<<16)); + } + m_fakerightmouse=0; +} + +-(LRESULT)onSwellMessage:(UINT)msg p1:(WPARAM)wParam p2:(LPARAM)lParam +{ + HWND hwnd=(HWND)self; + switch (msg) + { + case LB_RESETCONTENT: + ownermode_cnt=0; + if (m_items) + { + m_items->Empty(true); + [self reloadData]; + } + return 0; + case LB_ADDSTRING: + case LB_INSERTSTRING: + { + int cnt=ListView_GetItemCount(hwnd); + if (msg == LB_ADDSTRING && (style & LBS_SORT)) + { + SWELL_ListView *tv=(SWELL_ListView*)hwnd; + if (tv->m_lbMode && tv->m_items) + { + cnt=ptrlist_bsearch_mod((char *)lParam,tv->m_items,_listviewrowSearchFunc,NULL); + } + } + if (msg==LB_ADDSTRING) wParam=cnt; + else if (wParam > cnt) wParam=cnt; + LVITEM lvi={LVIF_TEXT,(int)wParam,0,0,0,(char *)lParam}; + ListView_InsertItem(hwnd,&lvi); + } + return wParam; + case LB_GETCOUNT: return ListView_GetItemCount(hwnd); + case LB_SETSEL: + ListView_SetItemState(hwnd, (int)lParam,wParam ? LVIS_SELECTED : 0,LVIS_SELECTED); + return 0; + case LB_GETTEXT: + if (lParam) + { + SWELL_ListView_Row *row=self->m_items ? self->m_items->Get(wParam) : NULL; + *(char *)lParam = 0; + if (row && row->m_vals.Get(0)) + { + strcpy((char *)lParam, row->m_vals.Get(0)); + return (LRESULT)strlen(row->m_vals.Get(0)); + } + } + return LB_ERR; + case LB_GETTEXTLEN: + { + SWELL_ListView_Row *row=self->m_items ? self->m_items->Get(wParam) : NULL; + if (row) + { + const char *p=row->m_vals.Get(0); + return p?strlen(p):0; + } + } + return LB_ERR; + case LB_FINDSTRINGEXACT: + if (lParam) + { + int x = (int) wParam + 1; + if (x < 0) x=0; + const int n = self->m_items ? self->m_items->GetSize() : 0; + for (int i = 0; i < n; i ++) + { + SWELL_ListView_Row *row=self->m_items->Get(x); + if (row) + { + const char *p = row->m_vals.Get(0); + if (p && !stricmp(p,(const char *)lParam)) return x; + } + if (++x >= n) x=0; + } + } + return LB_ERR; + case LB_GETSEL: + return !!(ListView_GetItemState(hwnd,(int)wParam,LVIS_SELECTED)&LVIS_SELECTED); + case LB_GETCURSEL: + return [self selectedRow]; + case LB_SETCURSEL: + { + if (wParamGet(wParam); + if (row) return row->m_param; + } + } + return 0; + case LB_SETITEMDATA: + { + if (m_items) + { + SWELL_ListView_Row *row=m_items->Get(wParam); + if (row) row->m_param=lParam; + } + } + return 0; + case LB_GETSELCOUNT: + return [[self selectedRowIndexes] count]; + case LB_DELETESTRING: + ListView_DeleteItem((HWND)self, (int)wParam); + return 0; + case WM_SETREDRAW: + if (wParam) + { + if (SWELL_GetOSXVersion() >= 0x1070 && [self respondsToSelector:@selector(endUpdates)]) + { + [self endUpdates]; + // workaround for a weird 10.14.6 bug + // if the caller calls this, then invalidaterect()s the parent window right away, + // appkit will (sometimes) throw an exception unlocking focus on the NSScrollView. + // invalidating our window directly seems to prevent this. + [self setNeedsDisplay:YES]; + } + } + else + { + if (SWELL_GetOSXVersion() >= 0x1070 && [self respondsToSelector:@selector(beginUpdates)]) + [self beginUpdates]; + } + + return 0; + } + return 0; +} + +-(int)getSwellNotificationMode +{ + return m_lbMode; +} +-(void)setSwellNotificationMode:(int)lbMode +{ + m_lbMode=lbMode; +} + +-(void)onSwellCommand:(int)cmd +{ + // ignore commands +} + +@end + +@implementation SWELL_ImageButtonCell +- (NSRect)drawTitle:(NSAttributedString *)title withFrame:(NSRect)frame inView:(NSView *)controlView +{ + return NSMakeRect(0,0,0,0); +} +@end + +@implementation SWELL_ODButtonCell +- (BOOL)isTransparent +{ + return YES; +} +- (BOOL)isOpaque +{ + return NO; +} + +- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + NSView *ctl=[self controlView]; + if (!ctl) { [super drawWithFrame:cellFrame inView:controlView]; return; } + + HDC hdc=SWELL_CreateGfxContext([NSGraphicsContext currentContext]); + if (hdc) + { + HWND notWnd = GetParent((HWND)ctl); + DRAWITEMSTRUCT dis={ODT_BUTTON,(UINT)[ctl tag],0,0,0,(HWND)ctl,hdc,{0,},0}; + NSRECT_TO_RECT(&dis.rcItem,cellFrame); + SendMessage(notWnd,WM_DRAWITEM,dis.CtlID,(LPARAM)&dis); + + SWELL_DeleteGfxContext(hdc); + } + +} +@end + +@implementation SWELL_ODListViewCell +-(void)setOwnerControl:(SWELL_ListView *)t { m_ownctl=t; m_lastidx=0; } +-(void)setItemIdx:(int)idx +{ + m_lastidx=idx; +} +- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + if (!m_ownctl) { [super drawInteriorWithFrame:cellFrame inView:controlView]; return; } + + int itemidx=m_lastidx; + LPARAM itemData=0; + SWELL_ListView_Row *row=m_ownctl->m_items->Get(itemidx); + if (row) itemData=row->m_param; + + HDC hdc=SWELL_CreateGfxContext([NSGraphicsContext currentContext]); + if (hdc) + { + HWND notWnd = GetParent((HWND)m_ownctl); + DRAWITEMSTRUCT dis={ODT_LISTBOX,(UINT)[m_ownctl tag],(UINT)itemidx,0,0,(HWND)m_ownctl,hdc,{0,},(DWORD_PTR)itemData}; + NSRECT_TO_RECT(&dis.rcItem,cellFrame); + SendMessage(notWnd,WM_DRAWITEM,dis.CtlID,(LPARAM)&dis); + + SWELL_DeleteGfxContext(hdc); + } +} + + +@end + + + + + +HWND GetDlgItem(HWND hwnd, int idx) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + + NSView *v=0; + id pid=(id)hwnd; + if ([pid isKindOfClass:[NSWindow class]]) v=[((NSWindow *)pid) contentView]; + else if ([pid isKindOfClass:[NSView class]]) v=(NSView *)pid; + + if (!idx || !v) return (HWND)v; + + SWELL_BEGIN_TRY + + NSArray *ar = [v subviews]; + const NSInteger n=[ar count]; + for (NSInteger x=0;xhwnd && [rec->hwnd respondsToSelector:@selector(swellSetOwner:)]) + [(SWELL_ModelessWindow *)rec->hwnd swellSetOwner:(id)bla]; + rec=rec->_next; + } + } + } + } + // move all child and owned windows over to new window + NSArray *ar=[oldw childWindows]; + if (ar) + { + int x; + for (x = 0; x < [ar count]; x ++) + { + NSWindow *cw=[ar objectAtIndex:x]; + if (cw) + { + [cw retain]; + [oldw removeChildWindow:cw]; + [(NSWindow *)bla addChildWindow:cw ordered:NSWindowAbove]; + [cw release]; + + + } + } + } + + if (oldpar) [oldpar addChildWindow:(NSWindow *)bla ordered:NSWindowAbove]; + if (oldtitle[0]) SetWindowText(hwnd,oldtitle); + + [(NSWindow *)bla setFrame:fr display:dovis]; + [(NSWindow *)bla setLevel:oldlevel]; + if (dovis) ShowWindow(bla,SW_SHOW); + + DestroyWindow((HWND)oldw); + } + else + { + [oldw setContentView:tv]; + [tv release]; + } + + } + } + } + return 0; + } + + if (idx == GWL_HWNDPARENT) + { + NSWindow *window = [pid window]; + if (![window respondsToSelector:@selector(swellGetOwner)]) return 0; + + NSWindow *new_owner = val && [(id)(INT_PTR)val isKindOfClass:[NSView class]] ? [(NSView *)(INT_PTR)val window] : NULL; + if (new_owner && ![new_owner respondsToSelector:@selector(swellAddOwnedWindow:)]) new_owner=NULL; + + NSWindow *old_owner = [(SWELL_ModelessWindow *)window swellGetOwner]; + if (old_owner != new_owner) + { + if (old_owner) [(SWELL_ModelessWindow*)old_owner swellRemoveOwnedWindow:window]; + [(SWELL_ModelessWindow *)window swellSetOwner:nil]; + if (new_owner) [(SWELL_ModelessWindow *)new_owner swellAddOwnedWindow:window]; + } + return (old_owner ? (LONG_PTR)[old_owner contentView] : 0); + } + + if ([pid respondsToSelector:@selector(setSwellExtraData:value:)]) + { + LONG_PTR ov=0; + if ([pid respondsToSelector:@selector(getSwellExtraData:)]) ov=(LONG_PTR)[pid getSwellExtraData:idx]; + + [pid setSwellExtraData:idx value:val]; + + return ov; + } + + SWELL_END_TRY(;) + return 0; +} + +LONG_PTR GetWindowLong(HWND hwnd, int idx) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + id pid=(id)hwnd; + + SWELL_BEGIN_TRY + + if (idx==GWL_EXSTYLE && [pid respondsToSelector:@selector(swellGetExtendedStyle)]) + { + return (LONG_PTR)[pid swellGetExtendedStyle]; + } + + if (idx==GWL_USERDATA && [pid respondsToSelector:@selector(getSwellUserData)]) + { + return (LONG_PTR)[pid getSwellUserData]; + } + if (idx==GWL_USERDATA && [pid isKindOfClass:[NSText class]]) + { + return 0xdeadf00b; + } + + if (idx==GWL_ID && [pid respondsToSelector:@selector(tag)]) + return [pid tag]; + + + if (idx==GWL_WNDPROC && [pid respondsToSelector:@selector(getSwellWindowProc)]) + { + return (LONG_PTR)[pid getSwellWindowProc]; + } + if (idx==DWL_DLGPROC && [pid respondsToSelector:@selector(getSwellDialogProc)]) + { + return (LONG_PTR)[pid getSwellDialogProc]; + } + if (idx==GWL_STYLE) + { + int ret=0; + if (![pid isHidden]) ret |= WS_VISIBLE; + if ([pid respondsToSelector:@selector(getSwellStyle)]) + { + return (LONG_PTR)(([pid getSwellStyle]&~WS_VISIBLE) | ret); + } + + if ([pid isKindOfClass:[NSButton class]]) + { + int tmp; + if ([pid allowsMixedState]) ret |= BS_AUTO3STATE; + else if ([pid isKindOfClass:[SWELL_Button class]] && (tmp = (int)[pid swellGetRadioFlags])) + { + ret |= BS_AUTORADIOBUTTON; + if (tmp&2) ret|=WS_GROUP; + } + else ret |= BS_AUTOCHECKBOX; + } + + if ([pid isKindOfClass:[NSView class]]) + { + if ([[pid window] contentView] != pid) ret |= WS_CHILDWINDOW; + else + { + NSUInteger smask =[[pid window] styleMask]; + if (smask & NSTitledWindowMask) + { + ret|=WS_CAPTION; + if (smask & NSResizableWindowMask) ret|=WS_THICKFRAME; + } + } + } + + return ret; + } + if (idx == GWL_HWNDPARENT) + { + NSWindow *window = [pid window]; + if (![window respondsToSelector:@selector(swellGetOwner)]) return 0; + NSWindow *old_owner = [(SWELL_ModelessWindow *)window swellGetOwner]; + return (old_owner ? (LONG_PTR)[old_owner contentView] : 0); + } + + if ([pid respondsToSelector:@selector(getSwellExtraData:)]) + { + return (LONG_PTR)[pid getSwellExtraData:idx]; + } + + SWELL_END_TRY(;) + return 0; +} + +static bool IsWindowImpl(NSView *ch, NSView *par) +{ + if (!par || ![par isKindOfClass:[NSView class]]) return false; + + NSArray *ar = [par subviews]; + if (!ar) return false; + [ar retain]; + NSInteger x,n=[ar count]; + for (x=0;xtype != TYPE_BITMAP) return 0; + return i->bitmapptr; +} + + +@implementation SWELL_Button : NSButton + +STANDARD_CONTROL_NEEDSDISPLAY_IMPL("Button") + +-(id) init { + self = [super init]; + if (self != nil) { + m_userdata=0; + m_swellGDIimage=0; + m_radioflags=0; + } + return self; +} +-(int)swellGetRadioFlags { return m_radioflags; } +-(void)swellSetRadioFlags:(int)f { m_radioflags=f; } +-(LONG_PTR)getSwellUserData { return m_userdata; } +-(void)setSwellUserData:(LONG_PTR)val { m_userdata=val; } + +-(void)setSwellGDIImage:(void *)par +{ + m_swellGDIimage=par; +} +-(void *)getSwellGDIImage +{ + return m_swellGDIimage; +} + +@end + + +NSFont *SWELL_GetNSFont(HGDIOBJ__ *obj); + +LRESULT SendMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return 0; + + SWELL_BEGIN_TRY + id obj=(id)hwnd; + if ([obj respondsToSelector:@selector(onSwellMessage:p1:p2:)]) + { + return (LRESULT) [obj onSwellMessage:msg p1:wParam p2:lParam]; + } + else + { + if (msg == BM_GETCHECK && [obj isKindOfClass:[NSButton class]]) + { + NSInteger a=[(NSButton*)obj state]; + if (a==NSMixedState) return BST_INDETERMINATE; + return a!=NSOffState; + } + if (msg == BM_SETCHECK && [obj isKindOfClass:[NSButton class]]) + { + [(NSButton*)obj setState:(wParam&BST_INDETERMINATE)?NSMixedState:((wParam&BST_CHECKED)?NSOnState:NSOffState)]; + return 0; + } + if ((msg==BM_GETIMAGE || msg == BM_SETIMAGE) && [obj isKindOfClass:[SWELL_Button class]]) + { + if (wParam != IMAGE_BITMAP && wParam != IMAGE_ICON) return 0; // ignore unknown types + LONG_PTR ret=(LONG_PTR) (void *)[obj getSwellGDIImage]; + if (msg==BM_SETIMAGE) + { + NSImage *img=NULL; + if (lParam) img=(NSImage *)__GetNSImageFromHICON((HICON)lParam); + [obj setImage:img]; + [obj setSwellGDIImage:(void *)(img?lParam:0)]; + } + return ret; + } + else if (msg >= CB_ADDSTRING && msg <= CB_INITSTORAGE && ([obj isKindOfClass:[NSPopUpButton class]] || [obj isKindOfClass:[NSComboBox class]])) + { + switch (msg) + { + case CB_ADDSTRING: return SWELL_CB_AddString(hwnd,0,(char*)lParam); + case CB_DELETESTRING: SWELL_CB_DeleteString(hwnd,0,(int)wParam); return 1; + case CB_GETCOUNT: return SWELL_CB_GetNumItems(hwnd,0); + case CB_GETCURSEL: return SWELL_CB_GetCurSel(hwnd,0); + case CB_GETLBTEXT: return SWELL_CB_GetItemText(hwnd,0,(int)wParam,(char *)lParam, 1<<20); + case CB_GETLBTEXTLEN: return SWELL_CB_GetItemText(hwnd,0,(int)wParam,NULL,0); + case CB_INSERTSTRING: return SWELL_CB_InsertString(hwnd,0,(int)wParam,(char *)lParam); + case CB_RESETCONTENT: SWELL_CB_Empty(hwnd,0); return 0; + case CB_SETCURSEL: SWELL_CB_SetCurSel(hwnd,0,(int)wParam); return 0; + case CB_GETITEMDATA: return SWELL_CB_GetItemData(hwnd,0,(int)wParam); + case CB_SETITEMDATA: SWELL_CB_SetItemData(hwnd,0,(int)wParam,lParam); return 0; + case CB_FINDSTRING: + case CB_FINDSTRINGEXACT: + if (lParam) return SWELL_CB_FindString(hwnd,0,(int)wParam,(const char *)lParam,msg==CB_FINDSTRINGEXACT); + return CB_ERR; + case CB_INITSTORAGE: return 0; + } + return 0; + } + else if (msg >= TBM_GETPOS && msg <= TBM_SETRANGE && ([obj isKindOfClass:[NSSlider class]])) + { + switch (msg) + { + case TBM_GETPOS: return SWELL_TB_GetPos(hwnd,0); + case TBM_SETTIC: SWELL_TB_SetTic(hwnd,0,(int)lParam); return 1; + case TBM_SETPOS: SWELL_TB_SetPos(hwnd,0,(int)lParam); return 1; + case TBM_SETRANGE: SWELL_TB_SetRange(hwnd,0,LOWORD(lParam),HIWORD(lParam)); return 1; + } + return 0; + } + else if ((msg == EM_SETSEL || msg == EM_GETSEL || msg == EM_SETPASSWORDCHAR) && ([obj isKindOfClass:[NSTextField class]])) + { + if (msg == EM_GETSEL) + { + NSRange range={0,}; + NSResponder *rs = [[obj window] firstResponder]; + if ([rs isKindOfClass:[NSView class]] && [(NSView *)rs isDescendantOf:obj]) + { + NSText* text=[[obj window] fieldEditor:YES forObject:(NSTextField*)obj]; + if (text) range=[text selectedRange]; + } + if (wParam) *(int*)wParam=(int)range.location; + if (lParam) *(int*)lParam=(int)(range.location+range.length); + } + else if (msg == EM_SETSEL) + { + // [(NSTextField*)obj selectText:obj]; // Force the window's text field editor onto this control + // don't force it, just ignore EM_GETSEL/EM_SETSEL if not in focus + NSResponder *rs = [[obj window] firstResponder]; + if ([rs isKindOfClass:[NSView class]] && [(NSView *)rs isDescendantOf:obj]) + { + NSText* text = [[obj window] fieldEditor:YES forObject:(NSTextField*)obj]; // then get it from the window + NSUInteger sl = [[text string] length]; + if (wParam == -1) lParam = wParam = 0; + else if (lParam == -1) lParam = sl; + if (wParam>sl) wParam=sl; + if (lParam>sl) lParam=sl; + if (text) [text setSelectedRange:NSMakeRange(wParam, wdl_max(lParam-wParam,0))]; // and set the range + } + } + else if (msg == EM_SETPASSWORDCHAR) + { + // not implemented, because it requires replacing obj within its parent window + // instead caller explicitly destroy the edit control and create a new one with ES_PASSWORD + } + return 0; + } + else if (msg == WM_SETFONT && ([obj isKindOfClass:[NSTextField class]])) + { + NSFont *font = SWELL_GetNSFont((HGDIOBJ__*)wParam); + if (font) [obj setFont:font]; + return 0; + } + else + { + NSWindow *w; + NSView *v; + // if content view gets unhandled message send to window + if ([obj isKindOfClass:[NSView class]] && (w=[obj window]) && [w contentView] == obj && [w respondsToSelector:@selector(onSwellMessage:p1:p2:)]) + { + return (LRESULT) [(SWELL_hwndChild *)w onSwellMessage:msg p1:wParam p2:lParam]; + } + // if window gets unhandled message send to content view + else if ([obj isKindOfClass:[NSWindow class]] && (v=[obj contentView]) && [v respondsToSelector:@selector(onSwellMessage:p1:p2:)]) + { + return (LRESULT) [(SWELL_hwndChild *)v onSwellMessage:msg p1:wParam p2:lParam]; + } + } + } + SWELL_END_TRY(;) + return 0; +} + +void DestroyWindow(HWND hwnd) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + SWELL_BEGIN_TRY + id pid=(id)hwnd; + if ([pid isKindOfClass:[NSView class]]) + { + KillTimer(hwnd,~(UINT_PTR)0); + sendSwellMessage((id)pid,WM_DESTROY,0,0); + + NSWindow *pw = [(NSView *)pid window]; + if (pw && [pw contentView] == pid) // destroying contentview should destroy top level window + { + DestroyWindow((HWND)pw); + } + else + { + if (pw) + { + id foc=[pw firstResponder]; + if (foc && (foc == pid || IsChild((HWND)pid,(HWND)foc))) + { + [pw makeFirstResponder:nil]; + } + } + [(NSView *)pid removeFromSuperview]; + } + } + else if ([pid isKindOfClass:[NSWindow class]]) + { + KillTimer(hwnd,~(UINT_PTR)0); + sendSwellMessage([(id)pid contentView],WM_DESTROY,0,0); + sendSwellMessage((id)pid,WM_DESTROY,0,0); + + if ([(id)pid respondsToSelector:@selector(swellDoDestroyStuff)]) + [(id)pid swellDoDestroyStuff]; + + NSWindow *par=[(NSWindow*)pid parentWindow]; + if (par) + { + [par removeChildWindow:(NSWindow*)pid]; + } + [(NSWindow *)pid close]; // this is probably bad, but close takes too long to close! + } + SWELL_END_TRY(;) +} + +void EnableWindow(HWND hwnd, int enable) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + SWELL_BEGIN_TRY + id bla=(id)hwnd; + if ([bla isKindOfClass:[NSWindow class]]) bla = [bla contentView]; + + if (bla && [bla respondsToSelector:@selector(setEnabled:)]) + { + if (enable == -1000 && [bla respondsToSelector:@selector(setEnabledSwellNoFocus)]) + [(SWELL_hwndChild *)bla setEnabledSwellNoFocus]; + else + [bla setEnabled:(enable?YES:NO)]; + if ([bla isKindOfClass:[SWELL_TextField class]]) + [(SWELL_TextField*)bla initColors:-1]; + } + SWELL_END_TRY(;) +} + +void SetForegroundWindow(HWND hwnd) +{ + WDL_ASSERT(hwnd != NULL); + SetFocus(hwnd); +} + +void SetFocus(HWND hwnd) // these take NSWindow/NSView, and return NSView * +{ + id r=(id) hwnd; + if (!r) return; // on win32 SetFocus(NULL) is allowed, removes focus (maybe we should implement) + + SWELL_BEGIN_TRY + if ([r isKindOfClass:[NSWindow class]]) + { + [(NSWindow *)r makeFirstResponder:[(NSWindow *)r contentView]]; + if ([(NSWindow *)r isVisible]) [(NSWindow *)r makeKeyAndOrderFront:nil]; + } + else if (WDL_NORMALLY([r isKindOfClass:[NSView class]])) + { + NSWindow *wnd=[(NSView *)r window]; + if (wnd) + { + if ([wnd isVisible]) + [wnd makeKeyAndOrderFront:nil]; + if ([r acceptsFirstResponder]) + [wnd makeFirstResponder:r]; + } + } + SWELL_END_TRY(;) +} + +void SWELL_GetViewPort(RECT *r, const RECT *sourcerect, bool wantWork) +{ + SWELL_BEGIN_TRY + + NSArray *ar=[NSScreen screens]; + + const NSInteger cnt=[ar count]; + int cx=0; + int cy=0; + if (sourcerect) + { + cx=(sourcerect->left+sourcerect->right)/2; + cy=(sourcerect->top+sourcerect->bottom)/2; + } + for (NSInteger x = 0; x < cnt; x ++) + { + NSScreen *sc=[ar objectAtIndex:x]; + if (sc) + { + NSRect tr=wantWork ? [sc visibleFrame] : [sc frame]; + if (!x || (cx >= tr.origin.x && cx < tr.origin.x+tr.size.width && + cy >= tr.origin.y && cy < tr.origin.y+tr.size.height)) + { + NSRECT_TO_RECT(r,tr); + } + } + } + if (!cnt) + { + r->left=r->top=0; + r->right=1600; + r->bottom=1200; + } + SWELL_END_TRY(;) +} + +void ScreenToClient(HWND hwnd, POINT *p) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + // no need to try/catch, this should never have an issue *wince* + + id ch=(id)hwnd; + if ([ch isKindOfClass:[NSWindow class]]) ch=[((NSWindow *)ch) contentView]; + if (WDL_NOT_NORMALLY(!ch || ![ch isKindOfClass:[NSView class]])) return; + + NSWindow *window=[ch window]; + + NSPoint wndpt = [window convertScreenToBase:NSMakePoint(p->x,p->y)]; + + // todo : WM_NCCALCSIZE + NSPOINT_TO_POINT(p, [ch convertPoint:wndpt fromView:nil]); +} + +void ClientToScreen(HWND hwnd, POINT *p) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + + id ch=(id)hwnd; + if ([ch isKindOfClass:[NSWindow class]]) ch=[((NSWindow *)ch) contentView]; + if (!ch || ![ch isKindOfClass:[NSView class]]) return; + + NSWindow *window=[ch window]; + + NSPoint wndpt = [ch convertPoint:NSMakePoint(p->x,p->y) toView:nil]; + + // todo : WM_NCCALCSIZE + + NSPOINT_TO_POINT(p,[window convertBaseToScreen:wndpt]); +} + +static NSView *NavigateUpScrollClipViews(NSView *ch) +{ + NSView *par=[ch superview]; + if (par && [par isKindOfClass:[NSClipView class]]) + { + par=[par superview]; + if (par && [par isKindOfClass:[NSScrollView class]]) + { + ch=par; + } + } + return ch; +} + +HWND SWELL_NavigateUpScrollClipViews(HWND h) +{ + NSView *v = 0; + if (h && [(id)h isKindOfClass:[NSView class]]) v = (NSView *)h; + else if (h && [(id)h isKindOfClass:[NSWindow class]]) v = [(NSWindow *)h contentView]; + if (v) + return (HWND)NavigateUpScrollClipViews(v); + return 0; +} + +bool GetWindowRect(HWND hwnd, RECT *r) +{ + r->left=r->top=r->right=r->bottom=0; + if (WDL_NOT_NORMALLY(!hwnd)) return false; + + SWELL_BEGIN_TRY + + id ch=(id)hwnd; + NSWindow *nswnd; + if ([ch isKindOfClass:[NSView class]] && (nswnd=[(NSView *)ch window]) && [nswnd contentView]==ch) + ch=nswnd; + + if ([ch isKindOfClass:[NSWindow class]]) + { + NSRECT_TO_RECT(r,[ch frame]); + return true; + } + if (![ch isKindOfClass:[NSView class]]) return false; + ch=NavigateUpScrollClipViews(ch); + NSRECT_TO_RECT(r,[ch bounds]); + ClientToScreen((HWND)ch,(POINT *)r); + ClientToScreen((HWND)ch,((POINT *)r)+1); + SWELL_END_TRY(return false;) + + return true; +} + +void GetWindowContentViewRect(HWND hwnd, RECT *r) +{ + SWELL_BEGIN_TRY + NSWindow *nswnd; + if (hwnd && [(id)hwnd isKindOfClass:[NSView class]] && (nswnd=[(NSView *)hwnd window]) && [nswnd contentView]==(id)hwnd) + hwnd=(HWND)nswnd; + + if (hwnd && [(id)hwnd isKindOfClass:[NSWindow class]]) + { + NSView *ch=[(id)hwnd contentView]; + NSRECT_TO_RECT(r,[ch bounds]); + ClientToScreen(hwnd,(POINT *)r); + ClientToScreen(hwnd,((POINT *)r)+1); + } + else GetWindowRect(hwnd,r); + SWELL_END_TRY(;) +} + + +void GetClientRect(HWND hwnd, RECT *r) +{ + r->left=r->top=r->right=r->bottom=0; + if (WDL_NOT_NORMALLY(!hwnd)) return; + + SWELL_BEGIN_TRY + id ch=(id)hwnd; + if ([ch isKindOfClass:[NSWindow class]]) ch=[((NSWindow *)ch) contentView]; + if (!ch || ![ch isKindOfClass:[NSView class]]) return; + ch=NavigateUpScrollClipViews(ch); + + NSRECT_TO_RECT(r,[ch bounds]); + + // todo this may need more attention + NCCALCSIZE_PARAMS tr={{*r,},}; + SendMessage(hwnd,WM_NCCALCSIZE,FALSE,(LPARAM)&tr); + r->right = r->left + (tr.rgrc[0].right-tr.rgrc[0].left); + r->bottom = r->top + (tr.rgrc[0].bottom-tr.rgrc[0].top); + SWELL_END_TRY(;) +} + + + +void SetWindowPos(HWND hwnd, HWND hwndAfter, int x, int y, int cx, int cy, int flags) +{ + if (WDL_NOT_NORMALLY(!hwnd)) return; + + SWELL_BEGIN_TRY + NSWindow *nswnd; // content views = move window + if (hwnd && [(id)hwnd isKindOfClass:[NSView class]] && (nswnd=[(NSView *)hwnd window]) && [nswnd contentView]==(id)hwnd) + hwnd=(HWND)nswnd; + + // todo: handle SWP_SHOWWINDOW + id ch=(id)hwnd; + bool isview=false; + if ([ch isKindOfClass:[NSWindow class]] || (isview=[ch isKindOfClass:[NSView class]])) + { + if (isview) + { + ch=NavigateUpScrollClipViews(ch); + if (isview && !(flags&SWP_NOZORDER)) + { + NSView *v = (NSView *)ch; + NSView *par = [v superview]; + NSArray *subs = [par subviews]; + NSInteger idx = [subs indexOfObjectIdenticalTo:v], cnt=[subs count]; + + NSView *viewafter = NULL; + NSWindowOrderingMode omode = NSWindowAbove; + + if (cnt>1 && hwndAfter != (HWND)ch) + { + if (hwndAfter==HWND_TOPMOST||hwndAfter==HWND_NOTOPMOST) + { + } + else if (hwndAfter == HWND_TOP) + { + if (idx0) viewafter = [subs objectAtIndex:0]; + omode = NSWindowBelow; + } + else + { + NSInteger a=[subs indexOfObjectIdenticalTo:(NSView *)hwndAfter]; + if (a != NSNotFound && a != (idx-1)) viewafter = (NSView *)hwndAfter; + } + } + + if (viewafter) + { + HWND h = GetCapture(); + if (!h || (h!=(HWND)v && !IsChild((HWND)v,h))) // if this window is captured don't reorder! + { + NSView *oldfoc = (NSView*)[[v window] firstResponder]; + if (!oldfoc || ![oldfoc isKindOfClass:[NSView class]] || + (oldfoc != v && ![oldfoc isDescendantOf:v])) oldfoc=NULL; + + // better way to do this? maybe :/ + [v retain]; + [v removeFromSuperviewWithoutNeedingDisplay]; + [par addSubview:v positioned:omode relativeTo:viewafter]; + [v release]; + + if (oldfoc) [[v window] makeFirstResponder:oldfoc]; + } + } + } + } + NSRect f=[ch frame]; + bool repos=false; + if (!(flags&SWP_NOMOVE)) + { + f.origin.x=(float)x; + f.origin.y=(float)y; + repos=true; + } + if (!(flags&SWP_NOSIZE)) + { + f.size.width=(float)cx; + f.size.height=(float)cy; + if (f.size.height<0)f.size.height=-f.size.height; + repos=true; + } + if (repos) + { + if (!isview) + { + NSSize mins=[ch minSize]; + NSSize maxs=[ch maxSize]; + if (f.size.width < mins.width) f.size.width=mins.width; + else if (f.size.width > maxs.width) f.size.width=maxs.width; + if (f.size.height < mins.height) f.size.height=mins.height; + else if (f.size.height> maxs.height) f.size.height=maxs.height; + [ch setFrame:f display:NO]; + [ch display]; + } + else + { + // this doesnt seem to actually be a good idea anymore + // if ([[ch window] contentView] != ch && ![[ch superview] isFlipped]) +// f.origin.y -= f.size.height; + [ch setFrame:f]; + if ([ch isKindOfClass:[NSScrollView class]]) + { + NSView *cv=[ch documentView]; + if (cv && [cv isKindOfClass:[NSTextView class]]) + { + NSRect fr=[cv frame]; + NSSize sz=[ch contentSize]; + int a=0; + if (![ch hasHorizontalScroller]) {a ++; fr.size.width=sz.width; } + if (![ch hasVerticalScroller]) { a++; fr.size.height=sz.height; } + if (a) [cv setFrame:fr]; + } + } + } + } + return; + } + SWELL_END_TRY(;) +} + +BOOL EnumWindows(BOOL (*proc)(HWND, LPARAM), LPARAM lp) +{ + NSArray *ch=[NSApp windows]; + [ch retain]; + const NSInteger n=[ch count]; + for(NSInteger x=0;x0) + { + return (HWND)[ar objectAtIndex:0]; + } + return 0; + } + if (what == GW_OWNER) + { + v=NavigateUpScrollClipViews(v); + if ([[v window] contentView] == v) + { + if ([[v window] respondsToSelector:@selector(swellGetOwner)]) + { + return (HWND)[(SWELL_ModelessWindow*)[v window] swellGetOwner]; + } + return 0; + } + return (HWND)[v superview]; + } + + if (what >= GW_HWNDFIRST && what <= GW_HWNDPREV) + { + v=NavigateUpScrollClipViews(v); + if ([[v window] contentView] == v) + { + if (what <= GW_HWNDLAST) return (HWND)hwnd; // content view is only window + + return 0; // we're the content view so cant do next/prev + } + NSView *par=[v superview]; + if (par) + { + NSArray *ar=[par subviews]; + NSInteger cnt; + if (ar && (cnt=[ar count]) > 0) + { + if (what == GW_HWNDFIRST) + return (HWND)[ar objectAtIndex:0]; + if (what == GW_HWNDLAST) + return (HWND)[ar objectAtIndex:(cnt-1)]; + + NSInteger idx=[ar indexOfObjectIdenticalTo:v]; + if (idx == NSNotFound) return 0; + + if (what==GW_HWNDNEXT) idx++; + else if (what==GW_HWNDPREV) idx--; + + if (idx<0 || idx>=cnt) return 0; + + return (HWND)[ar objectAtIndex:idx]; + } + } + return 0; + } + SWELL_END_TRY(;) + return 0; +} + + +HWND GetParent(HWND hwnd) +{ + SWELL_BEGIN_TRY + if (WDL_NORMALLY(hwnd) && [(id)hwnd isKindOfClass:[NSView class]]) + { + hwnd=(HWND)NavigateUpScrollClipViews((NSView *)hwnd); + + NSView *cv=[[(NSView *)hwnd window] contentView]; + if (cv == (NSView *)hwnd) hwnd=(HWND)[(NSView *)hwnd window]; // passthrough to get window parent + else + { + HWND h=(HWND)[(NSView *)hwnd superview]; + return h; + } + } + + if (hwnd && [(id)hwnd isKindOfClass:[NSWindow class]]) + { + HWND h= (HWND)[(NSWindow *)hwnd parentWindow]; + if (h) h=(HWND)[(NSWindow *)h contentView]; + if (h) return h; + } + + if (hwnd && [(id)hwnd respondsToSelector:@selector(swellGetOwner)]) + { + HWND h= (HWND)[(SWELL_ModelessWindow *)hwnd swellGetOwner]; + if (h && [(id)h isKindOfClass:[NSWindow class]]) h=(HWND)[(NSWindow *)h contentView]; + return h; + } + + SWELL_END_TRY(;) + return 0; +} + +HWND SetParent(HWND hwnd, HWND newPar) +{ + SWELL_BEGIN_TRY + NSView *v=(NSView *)hwnd; + WDL_ASSERT(hwnd != NULL); + if (!v || ![(id)v isKindOfClass:[NSView class]]) return 0; + v=NavigateUpScrollClipViews(v); + + if ([(id)hwnd isKindOfClass:[NSView class]]) + { + NSView *tv=(NSView *)hwnd; + if ([[tv window] contentView] == tv) // if we're reparenting a contentview (aka top level window) + { + if (!newPar) return NULL; + + NSView *npv = (NSView *)newPar; + if ([npv isKindOfClass:[NSWindow class]]) npv=[(NSWindow *)npv contentView]; + if (!npv || ![npv isKindOfClass:[NSView class]]) + return NULL; + + char oldtitle[2048]; + oldtitle[0]=0; + GetWindowText(hwnd,oldtitle,sizeof(oldtitle)); + + NSWindow *oldwnd = [tv window]; + id oldown = NULL; + if ([oldwnd respondsToSelector:@selector(swellGetOwner)]) oldown=[(SWELL_ModelessWindow*)oldwnd swellGetOwner]; + + if ([tv isKindOfClass:[SWELL_hwndChild class]]) ((SWELL_hwndChild*)tv)->m_lastTopLevelOwner = oldown; + + [tv retain]; + SWELL_hwndChild *tmpview = [[SWELL_hwndChild alloc] initChild:nil Parent:(NSView *)oldwnd dlgProc:nil Param:0]; + [tmpview release]; + + [npv addSubview:tv]; + [tv release]; + + DestroyWindow((HWND)oldwnd); // close old window since its no longer used + if (oldtitle[0]) SetWindowText(hwnd,oldtitle); + return (HWND)npv; + } + else if (!newPar) // not content view, not parent (so making it a top level modeless dialog) + { + char oldtitle[2048]; + oldtitle[0]=0; + GetWindowText(hwnd,oldtitle,sizeof(oldtitle)); + + [tv retain]; + [tv removeFromSuperview]; + + + unsigned int wf=(NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask|NSResizableWindowMask); + if ([tv respondsToSelector:@selector(swellCreateWindowFlags)]) + wf=(unsigned int)[(SWELL_hwndChild *)tv swellCreateWindowFlags]; + + HWND newOwner=NULL; + if ([tv isKindOfClass:[SWELL_hwndChild class]]) + { + id oldown = ((SWELL_hwndChild*)tv)->m_lastTopLevelOwner; + if (oldown) + { + NSArray *ch=[NSApp windows]; + const NSInteger n = [ch count]; + for(NSInteger x=0;x