├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── ci.yml │ └── wasm.yml ├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── cmake ├── Findglfw3.cmake └── LinkGLFW.cmake ├── docs ├── 42.md ├── Basics.md ├── Colors.md ├── Functions.md ├── Hooks.md ├── Images.md ├── Input.md ├── Shaders.md ├── Textures.md ├── XPM42.md ├── assets │ ├── demo.gif │ └── logo.png └── index.md ├── include ├── KHR │ └── khrplatform.h ├── MLX42 │ ├── MLX42.h │ └── MLX42_Int.h ├── glad │ └── glad.h └── lodepng │ └── lodepng.h ├── lib ├── glad │ └── glad.c └── png │ └── lodepng.c ├── shaders ├── default.frag └── default.vert ├── src ├── font │ ├── font.h │ └── mlx_font.c ├── mlx_cursor.c ├── mlx_exit.c ├── mlx_images.c ├── mlx_init.c ├── mlx_keys.c ├── mlx_loop.c ├── mlx_monitor.c ├── mlx_mouse.c ├── mlx_put_pixel.c ├── mlx_window.c ├── textures │ ├── mlx_png.c │ ├── mlx_texture.c │ └── mlx_xpm42.c └── utils │ ├── mlx_compare.c │ ├── mlx_error.c │ ├── mlx_list.c │ └── mlx_utils.c ├── tests ├── CMakeLists.txt ├── WindowFixture.hpp └── tests.cpp ├── tools ├── compile_shader.bat ├── compile_shader.sh └── xpm3_conv.py └── web ├── README.md ├── coi-serviceworker.js ├── demo.js ├── demo.wasm └── index.html /.gitattributes: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings 2 | 3 | # Set the default behavior, in case people don't have core.autocrlf set 4 | * text=auto 5 | 6 | # Declare files that will always have a certain EOL 7 | *.sh text eol=lf 8 | shaders/** text eol=lf 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **NOTE** 11 | Before creating a bug report! Make sure you git pull from master and check if the bug still exists! 12 | 13 | **Describe the bug** 14 | A clear and concise description of what the bug is. 15 | 16 | **To Reproduce** 17 | Steps to reproduce the behavior: 18 | 1. Go to '...' 19 | 2. Click on '...' 20 | 3. Scroll down to '...' 21 | 4. See error 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | If applicable, add screenshots to help explain your problem. 28 | 29 | **Desktop (please complete the following information):** 30 | - OS: [e.g. MacOS] 31 | - Version: [e.g. BigSur] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[REQUEST]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Codam Coding College, Amsterdam @ 2022-2023-2023 by W2Wizard. 3 | # See README in the root project for more information. 4 | # ----------------------------------------------------------------------------- 5 | 6 | name: Build 7 | 8 | #=============================================================================# 9 | 10 | on: 11 | push: 12 | branches: [ master ] 13 | pull_request: 14 | branches: [ master ] 15 | 16 | #=============================================================================# 17 | 18 | jobs: 19 | 20 | # Tests 21 | #=============================================================================# 22 | 23 | unit-test: 24 | timeout-minutes: 10 25 | runs-on: ubuntu-latest 26 | needs: build 27 | env: 28 | DISPLAY: ":99" 29 | 30 | steps: 31 | - name: Clone repository 32 | uses: actions/checkout@v3 33 | 34 | - name: Install Dependencies 35 | run: | 36 | sudo apt-get update -qq 37 | sudo apt-get install -y -qq xorg-dev xvfb 38 | 39 | - name: Setup virtual screen 40 | run: Xvfb $DISPLAY -screen 0 1280x1024x24 & 41 | 42 | - name: Build MLX42 & tests 43 | run: cmake -DBUILD_TESTS=YES -B ${{github.workspace}}/build && cmake --build ${{github.workspace}}/build --parallel 44 | 45 | - name: Run tests 46 | run: ctest --output-on-failure --test-dir ${{github.workspace}}/build 47 | # Unix 48 | #=============================================================================# 49 | 50 | build: 51 | timeout-minutes: 10 52 | runs-on: ${{ matrix.os }} 53 | strategy: 54 | matrix: 55 | os: [ubuntu-latest, macos-latest, windows-latest] 56 | 57 | steps: 58 | - name: Clone repository 59 | uses: actions/checkout@v3 60 | 61 | # Windows will just fetch glfw with cmake automatically. 62 | # This avoids doing extra work like installing a package manager. 63 | - name: Install Dependencies 64 | if: matrix.os != 'windows-latest' 65 | run: | 66 | set -x 67 | if [ "$RUNNER_OS" == "Linux" ]; then 68 | sudo apt-get update -qq 69 | sudo apt-get install -y -qq xorg-dev 70 | elif [ "$RUNNER_OS" == "macOS" ]; then 71 | brew update 72 | brew install glfw 73 | fi 74 | 75 | - name: Build 76 | run: cmake -B build && cmake --build build --parallel 77 | 78 | #=============================================================================# 79 | -------------------------------------------------------------------------------- /.github/workflows/wasm.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | workflow_dispatch: 6 | 7 | permissions: 8 | contents: read 9 | pages: write 10 | id-token: write 11 | 12 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 13 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: false 17 | 18 | # Single deploy job since we're just deploying 19 | jobs: 20 | deploy: 21 | environment: 22 | name: github-pages 23 | url: ${{ steps.deployment.outputs.page_url }} 24 | runs-on: ubuntu-latest 25 | #TODO: add a build step to get the wasm file instead of commiting it. 26 | #Doesn't really matter atm since the git history is polluted anyway 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | - name: Setup Pages 31 | uses: actions/configure-pages@v5 32 | - name: Upload artifact 33 | uses: actions/upload-pages-artifact@v3 34 | with: 35 | path: './web' 36 | - name: Deploy to GitHub Pages 37 | id: deployment 38 | uses: actions/deploy-pages@v4 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # Misc 55 | main.c 56 | temp/ 57 | .vscode/ 58 | lib/glfw/ 59 | .DS_Store 60 | 61 | # Special shader files 62 | mlx_*_shader.c 63 | build/ 64 | main.c 65 | test 66 | 67 | # Automatic downloaded deps 68 | _deps/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Codam Coding College, Amsterdam @ 2022-2023 by W2Wizard. 3 | # See README in the root project for more information. 4 | # ----------------------------------------------------------------------------- 5 | 6 | # CMake specifications 7 | # ----------------------------------------------------------------------------- 8 | cmake_minimum_required (VERSION 3.16.0) 9 | project(mlx42 VERSION 2.4.1) 10 | message(STATUS "MLX42 @ ${CMAKE_PROJECT_VERSION}") 11 | 12 | # Variables 13 | # ----------------------------------------------------------------------------- 14 | set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 15 | set(TOOLS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools) 16 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 17 | set(CMAKE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 18 | set(CMAKE_C_STANDARD 11) 19 | set(CMAKE_C_EXTENSIONS OFF) 20 | set(CMAKE_C_STANDARD_REQUIRED ON) 21 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 22 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 23 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") 24 | 25 | # Options 26 | set(DEBUG OFF CACHE BOOL "Build MLX42 in debug mode, enabling assertions") 27 | set(GLFW_FETCH ON CACHE BOOL "Clone and install GLFW") 28 | set(BUILD_TESTS OFF CACHE BOOL "Build the tests to verify the integrity of the lib") 29 | 30 | # Compile Options 31 | # ----------------------------------------------------------------------------- 32 | 33 | # Reduce the size of LodePNG, we don't need these things. 34 | add_definitions(-D LODEPNG_NO_COMPILE_ENCODER) 35 | add_definitions(-D LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS) 36 | 37 | if(UNIX AND NOT EMSCRIPTEN) 38 | add_compile_options( 39 | -Wextra 40 | -Wall 41 | -Werror 42 | -Wunreachable-code 43 | 44 | # Some low priority warnings that are annoying. 45 | -Wno-char-subscripts 46 | -Wno-sign-compare 47 | -Wno-unused-parameter 48 | -Wno-missing-field-initializers 49 | ) 50 | if(DEBUG) 51 | message(STATUS "Building in DEBUG mode") 52 | add_compile_options(-g) 53 | else() 54 | message(STATUS "Building in RELEASE mode") 55 | add_definitions(-D NDEBUG) 56 | add_compile_options(-O3) 57 | endif(DEBUG) 58 | else() 59 | # TODO: Figure out what we need for windows. 60 | endif() 61 | 62 | # Build specific files 63 | # @see https://cmake.org/cmake/help/latest/command/add_custom_command.html 64 | # ----------------------------------------------------------------------------- 65 | 66 | if (UNIX) 67 | set(CCSHADER ${TOOLS_DIR}/compile_shader.sh) 68 | else() 69 | set(CCSHADER ${TOOLS_DIR}/compile_shader.bat) 70 | endif() 71 | 72 | if(EMSCRIPTEN) 73 | set(EMSCRIPTEN_VALUE 1) 74 | else() 75 | set(EMSCRIPTEN_VALUE 0) 76 | endif() 77 | 78 | # Add custom command for fragment shader 79 | add_custom_command( 80 | COMMENT "Building fragment shader" 81 | DEPENDS ${PROJECT_SOURCE_DIR}/shaders/default.frag 82 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mlx_frag_shader.c 83 | COMMAND ${CCSHADER} ${PROJECT_SOURCE_DIR}/shaders/default.frag ${EMSCRIPTEN_VALUE} > ${CMAKE_CURRENT_BINARY_DIR}/mlx_frag_shader.c 84 | VERBATIM 85 | PRE_BUILD 86 | USES_TERMINAL 87 | ) 88 | 89 | # Add custom command for vertex shader 90 | add_custom_command( 91 | COMMENT "Building vertex shader" 92 | DEPENDS ${PROJECT_SOURCE_DIR}/shaders/default.vert 93 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mlx_vert_shader.c 94 | COMMAND ${CCSHADER} ${PROJECT_SOURCE_DIR}/shaders/default.vert ${EMSCRIPTEN_VALUE} > ${CMAKE_CURRENT_BINARY_DIR}/mlx_vert_shader.c 95 | VERBATIM 96 | PRE_BUILD 97 | USES_TERMINAL 98 | ) 99 | 100 | # Sources 101 | # ----------------------------------------------------------------------------- 102 | add_library(mlx42 STATIC 103 | 104 | # Root 105 | ${SOURCE_DIR}/mlx_cursor.c 106 | ${SOURCE_DIR}/mlx_exit.c 107 | ${SOURCE_DIR}/mlx_images.c 108 | ${SOURCE_DIR}/mlx_init.c 109 | ${SOURCE_DIR}/mlx_keys.c 110 | ${SOURCE_DIR}/mlx_loop.c 111 | ${SOURCE_DIR}/mlx_monitor.c 112 | ${SOURCE_DIR}/mlx_mouse.c 113 | ${SOURCE_DIR}/mlx_put_pixel.c 114 | ${SOURCE_DIR}/mlx_window.c 115 | 116 | # Utils 117 | ${SOURCE_DIR}/utils/mlx_error.c 118 | ${SOURCE_DIR}/utils/mlx_list.c 119 | ${SOURCE_DIR}/utils/mlx_utils.c 120 | ${SOURCE_DIR}/utils/mlx_compare.c 121 | 122 | # Textures 123 | ${SOURCE_DIR}/font/mlx_font.c 124 | ${SOURCE_DIR}/textures/mlx_png.c 125 | ${SOURCE_DIR}/textures/mlx_texture.c 126 | ${SOURCE_DIR}/textures/mlx_xpm42.c 127 | 128 | # Libs 129 | lib/png/lodepng.c 130 | lib/glad/glad.c 131 | 132 | mlx_vert_shader.c 133 | mlx_frag_shader.c 134 | ) 135 | target_include_directories(mlx42 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) 136 | 137 | # Dependencies 138 | # ----------------------------------------------------------------------------- 139 | 140 | find_package(OpenGL REQUIRED) 141 | 142 | if(EMSCRIPTEN) 143 | target_link_libraries(mlx42 "-s USE_GLFW=3" "-s FULL_ES3=1") 144 | else() 145 | target_link_libraries(mlx42 OpenGL::GL) 146 | find_package(glfw3) 147 | if (glfw3_FOUND) 148 | target_link_libraries(mlx42 ${GLFW3_LIBRARY}) 149 | endif() 150 | if (NOT glfw3_FOUND AND GLFW_FETCH) 151 | message(STATUS "Install GLFW to suppress this message") 152 | message(STATUS "Please wait, fetching GLFW ...") 153 | include(${CMAKE_DIR}/LinkGLFW.cmake) 154 | LinkGLFW(mlx42) 155 | elseif(NOT glfw3_FOUND AND NOT GLFW_FETCH) 156 | message(FATAL_ERROR "Unable to build: GLFW can't be found nor fetched.") 157 | endif() 158 | if(APPLE) 159 | target_link_libraries(mlx42 "-framework Cocoa" "-framework IOKit") 160 | endif() 161 | endif() 162 | 163 | # Testing 164 | # ----------------------------------------------------------------------------- 165 | # Only build tests if we are the main project or explicitly told to, make sure 166 | # tests are not built when mlx42 is included as a subproject, use MLX42_BUILD_TESTS to overwrite this 167 | # use cmake -DBUILD_TESTS=ON/-DMLX42_BUILD_TESTS=ON to build tests 168 | 169 | if ((PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME AND BUILD_TESTS) OR MLX42_BUILD_TESTS) 170 | add_subdirectory(tests) 171 | enable_testing() 172 | endif() 173 | 174 | # Installation 175 | # ----------------------------------------------------------------------------- 176 | # Convenience feature to install the library and headers to the system. 177 | # Use cmake -DCMAKE_INSTALL_PREFIX=/usr/local for example to install to /usr/local 178 | # or any other directory that you want to install to. 179 | # 180 | # This only really useful if you are a system administrator and want to install 181 | # the library to the system, if you are a developer you should just use the 182 | # library as a subproject as you probably don't have (nor really should) have any 183 | # ambitions to use this for anything other than your own school projects. 184 | 185 | install( 186 | DIRECTORY ./include/MLX42 DESTINATION ${CMAKE_INSTALL_PREFIX}/include 187 | FILES_MATCHING PATTERN MLX42.h 188 | ) 189 | 190 | install(TARGETS mlx42 191 | EXPORT mlx42Targets 192 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 193 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 194 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 195 | ) 196 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness towards other people 21 | * Being respectful of differing opinions, viewpoints and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit or reject 47 | comments, commits, code, wiki edits, issues and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | main@w2wizard.dev. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Warning 75 | 76 | **Community Impact**: A violation through a single incident or series 77 | of actions. 78 | 79 | **Consequence**: A warning with consequences for continued behavior. No 80 | interaction with the people involved, including unsolicited interaction with 81 | those enforcing the Code of Conduct, for a specified period of time. This 82 | includes avoiding interactions in community spaces as well as external channels 83 | like social media. Violating these terms may lead to a temporary or 84 | permanent ban. 85 | 86 | ### 2. Temporary Ban 87 | 88 | **Community Impact**: A serious violation of community standards, including 89 | sustained inappropriate behavior. 90 | 91 | **Consequence**: A temporary ban from any sort of interaction or public 92 | communication with the community for a specified period of time. No public or 93 | private interaction with the people involved, including unsolicited interaction 94 | with those enforcing the Code of Conduct, is allowed during this period. 95 | Violating these terms may lead to a permanent ban. 96 | 97 | ### 3. Permanent Ban 98 | 99 | **Community Impact**: Demonstrating a pattern of violation of community 100 | standards, including sustained inappropriate behavior, harassment of an 101 | individual or aggression towards or disparagement of classes of individuals. 102 | 103 | **Consequence**: A permanent ban from any sort of public interaction within 104 | the community. 105 | 106 | ## Attribution 107 | 108 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 109 | version 2.0, available at 110 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 111 | 112 | [homepage]: https://www.contributor-covenant.org 113 | 114 | For answers to common questions about this code of conduct, see the FAQ at 115 | https://www.contributor-covenant.org/faq. Translations are available at 116 | https://www.contributor-covenant.org/translations. 117 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute to MLX42 2 | 3 | ## Read the wiki for repo codestyle! 4 | 5 | For any questions, suggestions or help [Contact Me](mailto:lde-la-h@student.codam.nl) 6 | 7 | ## **Found a bug?** 8 | 9 | * Avoid opening any new issues without having checked if your problem has already been reported. If there are no currently open issues that fit your problem's description, feel free to [add it](https://github.com/W2Codam/MLX42/issues/new/choose). 10 | 11 | * When writing an issue make sure to include a clear title and description as well as having filled out all the necessary information: System info, OS, OS-Version, ... 12 | 13 | * If possible add pictures of the issue. 14 | 15 | * Maybe fix it yourself :D ? 16 | 17 | ## Contributing 18 | 19 | Before thinking of adding a contribution, think. Is it necessary? Will this actually be a useful/required feature? Is your implementation good? 20 | Provide clear and documented explanation as to what was changed. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | 42MLX_Logo 4 |
5 |
6 | Written by W2.Wizard for Codam 7 |
8 |
9 | License GPL2.0 10 | Build 11 | Forks 12 |
13 |
14 | 15 | MLX42 is a performant, easy to use, cross-platform, minimal windowing graphics library to create graphical applications without having to work directly with the native windowing framework of the given operating system. 16 | 17 | It provides primitive tools to draw textures onto the window as well as modifying them at runtime as they get displayed on the window. 18 | 19 | > [!IMPORTANT] 20 | > At times it may seem like no updates have taken place for a long time. This is expected, the project / lib is considered completed and requires minimal updates. Bug fixes are still guaranteed and the project is still being actively maintained. 21 | 22 | # Features ✨ 23 | 24 | MLX42 comes with a plethora of features that make using it actually a joy instead of a chore. 25 | 26 | ## Cross-Platform 🖥️ 27 | 28 | Run it on your grandma's WindowsXP, on your uncle's debian or on a hipster's MacOS! 29 | No matter what the platform, if MLX42 can build on it, it will run on its native windowing system. 30 | 31 | This allows you to work on your project no matter which machine it is on. 32 | 33 | ## Documented 📚 34 | 35 | MLX42 cares about good documentation, every function, usage and even some internal workings are documented! 36 | No more guessing and searching how something functions or is supposed to work. 37 | 38 | ## Performance 🚀 39 | 40 | It is built on OpenGL and uses batched rendering to speed up the rendering process compared to other libraries. 41 | 42 | ## Open source && Community driven 🌐 43 | This project is being actively maintained by Codam as well as students from the 42 Network. This gives students the direct opportunity to learn more about the library itself as well as fix any potential bugs instead of merely accepting them. 44 | 45 | ## Emscripten Compatibility 🚀 46 | MLX42 introduces compatibility with [Emscripten](https://emscripten.org/), allowing MLX42 to run in web browsers through WebAssembly. This modification were made possible thanks to [@PepeLevi](https://github.com/PepeLevi/MLX42_emcc), credits to him for his fork and contributions. 47 | 48 | ### Highlights 49 | - **Emscripten Support**: Compile MLX42 with Emscripten, enabling graphical applications to run in a web environment. 50 | - **WebAssembly Compatibility**: Ensures that MLX42 can be utilized in modern web browsers, expanding its usability beyond traditional desktop environments. 51 | - **Updated Documentation**: Provided guidance on how to build and run MLX42 projects using Emscripten. 52 | 53 | --- 54 | 55 | ## Installation 🏗️ 56 | ### General compilation 57 | 58 | Overall the building of this project is as follows for ALL systems. As long as CMake can make sense of it. 59 | 60 | 1. [Download and build MLX42](#download-and-build---mlx42) 61 | 62 | In case your system doesn't have [glfw](https://github.com/glfw/glfw) installed cmake will detect this and download it for you. 63 | You can then run `sudo make install` in the `_deps` directory of glfw. If you're using a 42 Computer (MacOS, Linux), ask your favourite sysadmin to install it. 64 | Same goes for CMake or any other dependencies you might need for your system. 65 | 66 | However if you can't do either CMake will still be able to fetch GLFW and build it. You can then statically link it from the `_deps` folder. 67 | 68 | > [!NOTE] 69 | > For Codam, GLFW is already installed on the Macs. 70 | 71 | > [!NOTE] 72 | > During the linking stage, the flag to link GLFW can either be: -lglfw3 or -lglfw depending on your system. 73 | 74 | 1. Compile your program with the library: 75 | - For: [MacOS](#for-macos) 76 | - For: [Linux](#for-linux) 77 | - For: [Windows](#for-windows) 78 | 79 | 2. Profit! 80 | 81 | ### Installing to the system 82 | 83 | To fully build the library and install it to your system run the following command: 84 | ```bash 85 | cmake -B build && cmake --build build --parallel --config (Debug|Release|RelWithDebInfo|MinSizeRel) --target install 86 | ``` 87 | 88 | By default windows will place the installed lib into: `C:\Program Files (x86)\mlx42` and for MacOS / Linux it will be placed into `/usr/local/lib` and `/usr/local/include` respectively. 89 | 90 | ### Unit tests 91 | MLX42 comes with some unit tests to ensure the integrity of the library, to build them run the following command: 92 | ```sh 93 | cmake -DBUILD_TESTS=ON -B build && cmake --build build --parallel 94 | ``` 95 | 96 | Then simply run them with: 97 | ```sh 98 | ctest --output-on-failure --test-dir build 99 | ``` 100 | 101 | ---- 102 | 103 | ## Download and build - MLX42 104 | 105 | ```bash 106 | git clone https://github.com/codam-coding-college/MLX42.git 107 | cd MLX42 108 | cmake -B build # build here refers to the outputfolder. 109 | cmake --build build -j4 # or do make -C build -j4 110 | ``` 111 | 112 | The output library file is called `libmlx42.a` and is located in the `build` folder that you specified. 113 | 114 | ### Available Options 115 | 116 | You can pass build [options](./docs/index.md#available-options) to cmake, e.g: `cmake -DDEBUG=1 -DGLFW_FETCH=0...`. These will for instance let you build it in DEBUG mode or alter any sort of behaviour at build-time. 117 | 118 | You can find an example makefile in the documentation [here](https://github.com/codam-coding-college/MLX42/blob/master/docs/Basics.md). 119 | 120 | ---- 121 | 122 | ## For MacOS: 123 | 124 | ### Installing the dependencies 125 | 126 | If your system has neither GLFW nor CMake its highly recommended you use brew to install those missing dependencies. 127 | 128 | For 42 Campuses you can use: [42Homebrew](https://github.com/kube/42homebrew) 129 | 130 | Otherwise with homebrew: 131 | ```bash 132 | brew install glfw 133 | brew install cmake 134 | ``` 135 | If you are using Apple Silicon (M1 chip or later), note that the Homebrew install path is different. 136 | You may want to update your shell configuration file. For Zsh users (default shell on newer macOS versions): 137 | ```bash 138 | nano ~/.zshrc 139 | export LIBRARY_PATH=/opt/homebrew/lib 140 | ``` 141 | Restart your shell session or restart your terminal for the changes to take effect. 142 | 143 | For MacOS you need to use the following flags to compile your program with the library 144 | in order to link the program with the correct frameworks: 145 | ```bash 146 | -framework Cocoa -framework OpenGL -framework IOKit 147 | ``` 148 | 149 | Normally if you simply installed / built `glfw` from source or already have it installed 150 | the compilation should be: 151 | ```bash 152 | gcc main.c ... libmlx42.a -Iinclude -lglfw 153 | ``` 154 | 155 | #### Via [Homebrew](https://brew.sh/) / [42Homebrew](https://github.com/kube/42homebrew) 156 | ```bash 157 | # Homebrew 158 | gcc main.c ... libmlx42.a -Iinclude -lglfw -L"/opt/homebrew/Cellar/glfw/3.3.8/lib/" 159 | 160 | # 42Homebrew 161 | gcc main.c ... libmlx42.a -Iinclude -lglfw -L"/Users/$(USER)/.brew/opt/glfw/lib/" 162 | ``` 163 | 164 | #### MacOS Security: 165 | 166 | When running your program in MacOS it may complain, because with Macs you just gotta think differently. 167 | In case of any security warnings or MacOS telling you it can't verify the author/developer, go to ```Settings > Security & Privacy```. 168 | 169 | There will be a pop-up at the bottom telling you that an application tried to run, click the option to let it run. 170 | 171 | ---- 172 | 173 | ## For Linux: 174 | 175 | 1. Install the necessary packages: 176 | 177 | For Debian like (Ubuntu, Mint, Pop OS...): 178 | ```bash 179 | sudo apt update 180 | sudo apt install build-essential libx11-dev libglfw3-dev libglfw3 xorg-dev 181 | ``` 182 | 183 | For Arch-linux (Manjaro, Endeavor, Garuda): 184 | ```bash 185 | sudo pacman -S glfw-x11 186 | ``` 187 | OR (if you use sway/wlroots compositor or other wayland compositor) 188 | 189 | ```bash 190 | sudo pacman -S glfw-wayland 191 | ``` 192 | 193 | 2. [Download and build MLX42](#download-and-build---mlx42) 194 | 195 | 3. Compile your program with the library: 196 | 197 | ```bash 198 | gcc main.c ... libmlx42.a -Iinclude -ldl -lglfw -pthread -lm 199 | ``` 200 | 4. Profit! 201 | 202 | ---- 203 | 204 | ## For Windows (with Windows Subsystem for Linux 2 (WSL2)) 205 | 206 | > [!IMPORTANT] 207 | > Before starting with all these steps, [read this](https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps) 208 | 209 | 1. Set these variables in your `.zshrc` or `.bashrc`: 210 | ```bash 211 | export DISPLAY=$(ip route list default | awk '{print $3}'):0 212 | export LIBGL_ALWAYS_INDIRECT=0 213 | ``` 214 | (If the DISPLAY export command is failing, see this [StackOverflow](https://stackoverflow.com/a/61110604) post for alternatives) 215 | 216 | 2. Download and install an XServer application with extended configuration (XMing does not qualify) 217 | VcXsrv works: https://sourceforge.net/projects/vcxsrv/ 218 | 219 | 3. Open Windows Defender Firewall, and follow these steps: 220 | - Go to 'Allow an app or feature through Windows Defender Firewall' 221 | - Change Settings 222 | - Find the installed XServer, for VcXsrv that's 'VcXsrv windows server' 223 | - Enable communication over Private **and** Public network 224 | Optionally you might be able to provide these settings on first launch of the XServer application, 225 | and they might not even show up in the list until the first time you start the app. 226 | 227 | 4. Start the XLaunch application (for VcXsrv) and provide these configuration settings: 228 | - Leave Display number on auto 229 | - Start no client 230 | - **UNTICK** `Native opengl` option 231 | - **TICK** `Disable access control` 232 | - Finish starting the server 233 | 234 | 5. Probably all the other steps for Linux apply, just clone, build and run. 235 | 236 | ---- 237 | 238 | ## For Windows Native: 239 | 240 | > [!IMPORTANT] 241 | > Be aware that Visual Studio (2022) is required for this. Developing on Windows can be somewhat frustrating. 242 | 243 | We highly recommend you simply use [WSL2](#for-windows-with-windows-subsystem-for-linux-2-wsl2) to make this as painless as possible. 244 | However if you insist on building for windows natively then all you need in terms of dependencies is: 245 | 246 | - [CMake](https://cmake.org/download/) 247 | - [GLFW](https://www.glfw.org/download.html) 248 | 249 | Once you have all the dependencies correctly installed `CMake` will generate 250 | the visual studio project files. Simply build it and once you have a `.lib` file 251 | move them to your actual project and install them as you would with any other library. 252 | 253 | Just in case here's a [video](https://youtu.be/or1dAmUO8k0?t=494) showing you how this can be done. Conveniently this video also covers how you can link `glfw`. 254 | 255 | Of course it's up to you to make sure that the code you write is portable. Things that exist on `Unix` don't necessarily exist on `Win32`. 256 | 257 | ## Example 258 | 259 | ![MLX42](docs/assets/demo.gif) 260 | 261 | ```c 262 | // ----------------------------------------------------------------------------- 263 | // Codam Coding College, Amsterdam @ 2022-2023 by W2Wizard. 264 | // See README in the root project for more information. 265 | // ----------------------------------------------------------------------------- 266 | 267 | #include 268 | #include 269 | #include 270 | #include 271 | 272 | #define WIDTH 512 273 | #define HEIGHT 512 274 | 275 | static mlx_image_t* image; 276 | 277 | // ----------------------------------------------------------------------------- 278 | 279 | int32_t ft_pixel(int32_t r, int32_t g, int32_t b, int32_t a) 280 | { 281 | return (r << 24 | g << 16 | b << 8 | a); 282 | } 283 | 284 | void ft_randomize(void* param) 285 | { 286 | (void)param; 287 | for (uint32_t i = 0; i < image->width; ++i) 288 | { 289 | for (uint32_t y = 0; y < image->height; ++y) 290 | { 291 | uint32_t color = ft_pixel( 292 | rand() % 0xFF, // R 293 | rand() % 0xFF, // G 294 | rand() % 0xFF, // B 295 | rand() % 0xFF // A 296 | ); 297 | mlx_put_pixel(image, i, y, color); 298 | } 299 | } 300 | } 301 | 302 | void ft_hook(void* param) 303 | { 304 | mlx_t* mlx = param; 305 | 306 | if (mlx_is_key_down(mlx, MLX_KEY_ESCAPE)) 307 | mlx_close_window(mlx); 308 | if (mlx_is_key_down(mlx, MLX_KEY_UP)) 309 | image->instances[0].y -= 5; 310 | if (mlx_is_key_down(mlx, MLX_KEY_DOWN)) 311 | image->instances[0].y += 5; 312 | if (mlx_is_key_down(mlx, MLX_KEY_LEFT)) 313 | image->instances[0].x -= 5; 314 | if (mlx_is_key_down(mlx, MLX_KEY_RIGHT)) 315 | image->instances[0].x += 5; 316 | } 317 | 318 | // ----------------------------------------------------------------------------- 319 | 320 | int32_t main(void) 321 | { 322 | mlx_t* mlx; 323 | 324 | // Gotta error check this stuff 325 | if (!(mlx = mlx_init(WIDTH, HEIGHT, "MLX42", true))) 326 | { 327 | puts(mlx_strerror(mlx_errno)); 328 | return(EXIT_FAILURE); 329 | } 330 | if (!(image = mlx_new_image(mlx, 128, 128))) 331 | { 332 | mlx_close_window(mlx); 333 | puts(mlx_strerror(mlx_errno)); 334 | return(EXIT_FAILURE); 335 | } 336 | if (mlx_image_to_window(mlx, image, 0, 0) == -1) 337 | { 338 | mlx_close_window(mlx); 339 | puts(mlx_strerror(mlx_errno)); 340 | return(EXIT_FAILURE); 341 | } 342 | 343 | mlx_loop_hook(mlx, ft_randomize, mlx); 344 | mlx_loop_hook(mlx, ft_hook, mlx); 345 | 346 | mlx_loop(mlx); 347 | mlx_terminate(mlx); 348 | return (EXIT_SUCCESS); 349 | } 350 | 351 | ``` 352 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | 2.x.x | ✅ | 8 | | 1.0.x | ❌ | 9 | 10 | ## Reporting a Vulnerability 11 | 12 | For security issues please refrain from opening an issue! 13 | Instead write an email to [main@w2wizard.dev](mailto:main@w2wizard.dev) 14 | -------------------------------------------------------------------------------- /cmake/Findglfw3.cmake: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Codam Coding College, Amsterdam @ 2022-2023 by W2Wizard. 3 | # See README in the root project for more information. 4 | # ----------------------------------------------------------------------------- 5 | 6 | # Try to find GLFW3 library and include path. 7 | # Once done this will define: 8 | # - GLFW3_FOUND 9 | # - GLFW3_INCLUDE_PATH 10 | # - GLFW3_LIBRARY 11 | 12 | # Possbile header locations 13 | set(_glfw3_HEADER_SEARCH_DIRS 14 | "/usr/include" 15 | "/usr/local/include" 16 | "C:/Program Files/GLFW/include" 17 | "C:/Program Files (x86)/GLFW/include" 18 | "$ENV{HOME}/.brew/include/" 19 | "$ENV{HOME}/homebrew/include/" 20 | ) 21 | 22 | # Possbile library locations 23 | set(_glfw3_LIB_SEARCH_DIRS 24 | "/usr/lib" 25 | "/usr/local/lib" 26 | "C:/Program Files/GLFW" 27 | "C:/Program Files (x86)/GLFW" 28 | "$ENV{HOME}/.brew/lib/" 29 | "$ENV{HOME}/homebrew/lib/" 30 | ) 31 | 32 | # Search for the header 33 | find_path(GLFW3_INCLUDE_PATH "GLFW/glfw3.h" PATHS ${_glfw3_HEADER_SEARCH_DIRS}) 34 | 35 | # Search for the library 36 | find_library(GLFW3_LIBRARY NAMES glfw3 glfw PATHS ${_glfw3_LIB_SEARCH_DIRS}) 37 | 38 | if (GLFW3_INCLUDE_PATH AND GLFW3_LIBRARY) 39 | set(glfw3_FOUND "YES") 40 | include_directories(${GLFW3_INCLUDE_PATH}) 41 | message(STATUS "Found GLFW: ${GLFW3_LIBRARY}") 42 | else() 43 | set(glfw3_FOUND "NO") 44 | message(WARNING "Unable to find dependency: GLFW\nDid you install it?") 45 | endif() 46 | -------------------------------------------------------------------------------- /cmake/LinkGLFW.cmake: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Codam Coding College, Amsterdam @ 2022-2023 by W2Wizard. 3 | # See README in the root project for more information. 4 | # ----------------------------------------------------------------------------- 5 | 6 | include(FetchContent) 7 | 8 | macro(LinkGLFW TARGET) 9 | FetchContent_Declare( 10 | glfw 11 | GIT_REPOSITORY https://github.com/glfw/glfw 12 | GIT_TAG 3.3.8 13 | ) 14 | 15 | FetchContent_GetProperties(glfw) 16 | 17 | if (NOT glfw_POPULATED) 18 | FetchContent_Populate(glfw) 19 | 20 | # Just configure GLFW only 21 | set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "Build Examples" FORCE) 22 | set(GLFW_BUILD_TESTS OFF CACHE BOOL "Build tests" FORCE) 23 | set(GLFW_BUILD_DOCS OFF CACHE BOOL "Build docs" FORCE) 24 | set(GLFW_INSTALL ON CACHE BOOL "Configure an install" FORCE) 25 | 26 | # This excludes glfw from being rebuilt when ALL_BUILD is built 27 | # it will only be built when a target is built that has a dependency on glfw 28 | add_subdirectory(${glfw_SOURCE_DIR} ${glfw_BINARY_DIR} EXCLUDE_FROM_ALL) 29 | 30 | # Set the target's folders 31 | set_target_properties(glfw PROPERTIES FOLDER ${PROJECT_NAME}/thirdparty) 32 | endif() 33 | 34 | target_include_directories(${TARGET} PRIVATE ${glfw_SOURCE_DIR}/include) 35 | target_link_libraries(${TARGET} glfw) 36 | 37 | add_dependencies(${TARGET} glfw) 38 | endmacro() 39 | -------------------------------------------------------------------------------- /docs/42.md: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 |
8 | 42MLX_Logo 9 |
10 |
11 | Written by W2.Wizard for the 42 Network 12 |
13 |
14 | 15 |
16 |

Welcome to the MLX42, 42Campus documentation

17 |
18 | 19 | # Foreword 20 | If you're considering allowing the use of MLX42 in your campus and wondering why you should do it, how it should work, and what it will take to get it working, then you're in the right place! 21 | 22 | MLX42 has been battle-tested multiple times at hackathons and other campuses and has received over 500 commits since 2021. All students share the same sentiment: they enjoy using the library, but it's not too easy to finish the projects. During that time many bugs, leaks, and segfaults have been fixed. 23 | 24 | It addresses one of the main problems at 42, which is that although it is a tech school, it does not provide its students with well-maintained tools to improve their education. All a campus wants is to give its students the best they can. 25 | 26 | | Campus | Allowed | Additional Notes | 27 | | ----------|:------------:| :------------------| 28 | | Codam | ✅ | | 29 | | Mulhouse | ✅ | | 30 | | Quebec | ✅ | | 31 | | Belo Horizonte | ❓ | | 32 | | London | ❌ | Just because ? | 33 | | São Paulo | ✅ | | 34 | | Rio | ❓ | | 35 | | Malaga | ✅ | | 36 | | Singapore | ❓ | Asked their pedago, awaiting a response.| 37 | | Barcelona | ✅ | Would like propose some modifications preferably | 38 | | Nice | ✅ | Banned `mlx_put_pixel` & `mlx_resize_image` otherwise fully authorized 🎊 | 39 | | Berlin | ✅ | Banned `mlx_put_pixel` & `mlx_resize_image` otherwise fully authorized 🎊` | 40 | | Heilbronn | ✅ | | 41 | | 1337 | ✅ | 1337 Refers to every campus part of 1337 | 42 | | Angoulême | ✅ | | 43 | | Lausanne | ❓ | No idea how to reach them | 44 | | Prague | ✅ | | 45 | | 19 | ❌ | No reason (?) You can probably blame paris again I guess | 46 | | Hive | ✅ | | 47 | | Le Havre | ✅ | | 48 | | Vienna | ❓ | | 49 | | Seoul | ❌ | I didn't really understand why but basically as long as Paris says no they will too | 50 | | Madrid | ✅ | Authorized since the Linux migration | 51 | | Porto | ❌ | Not Auth from Paris| 52 | | Lisboa | ❌ | Not Auth from Paris| 53 | | Wolfsburg | ❌ | Not Auth from Paris| 54 | | Urduliz | ❌ | Not Auth from Paris| 55 | | Paris | ❌ | Because its Paris 🤡🤡🤡| 56 | 57 | Regarding other campuses the status is unknown. 58 | 59 | --- 60 | 61 | # Technical comparison 62 | ## MiniLibX 63 | The miniLibX has many problems that have been around for almost 10+ years: 64 | 65 | - `Not maintained`: The library is practically dead, and it's unclear who is available to fix the bugs. It hasn't had any changes in a long time, and student pull requests on Github for the X11 version go unnoticed. Issues are also being ignored. 66 | 67 | - `Poor documentation`: The documentation consists of a few man pages that are outdated and an online documentation created by another student. It's understandable that students have to learn on their own, but a library with an extensive API requires proper documentation for anyone to start understanding it. 68 | 69 | - `Poor execution`: There are multiple versions: OpenGL, Swift, and X11. Instead of becoming better with each new iteration, they stay the same with no real improvement. It should be future-proof and not dependent on a specific platform. People are struggling with the there being so many different versions that they start losing track where the problem actually is. 70 | 71 | - `Not cross-platform`: Students constantly encounter the same problem: at school they work with MacOS, but at home they use Linux. Or they write their project on Linux, but want to show it to their parents using Windows or MacOS. At each point, miniLibX fails to fill that gap. The pandemic in 2019 showed just how much students struggled to evaluate each other using different machines and versions. 72 | 73 | --- 74 | 75 | ## MLX42 76 | 77 | The main goal of MLX42 is to address all of these shortcomings of the original versions. There are some differences, mainly in the way images are rendered, but everything else is basically the same. 78 | 79 | So far, all of the drawbacks of miniLibX have been taken care of, and students using it are enjoying it! 80 | 81 | ### Rendering 82 | 83 | One of the biggest differences between the two libraries is the way rendering is handled. 84 | 85 | In `miniLibX`, students change the buffer of an image and then push it to the window. 86 | In `MLX42`, students put the image to the window and can change the buffer at any time, resulting in an immediate update to the image. 87 | 88 | MLX42 uses instances instead. An image is like the original painting, while instances are individual copies of this painting on the window. 89 | 90 | There is no window clearing function because students need to learn how to properly manage their images. They can still delete images and turn instances on or off, of course. 91 | 92 | Internally, it uses batched rendering to further improve performance. The actual documentation and the code itself have more details. 93 | 94 | ### Maintained & Open-source 95 | 96 | The main goal of MLX42 is to empower students and pedagogues by giving them the ability to maintain and fix bugs, instead of leaving their complaints unaddressed. By being open-source, students can explore the code and submit pull requests. 97 | 98 | ### Documentation 99 | The repository comes with a well-maintained [Wiki](https://github.com/codam-coding-college/MLX42/wiki) and documentation in the form of `markdown` files in the repository root. 100 | 101 | ### Build system 102 | 103 | MLX42 initially used `make`, but it was inflexible and caused weird bugs for others. Since version 2.3.0 it uses `cmake` for a truly cross-platform build system. 104 | 105 | Students do not need to understand how to use `cmake`, as building the library requires only two shell commands. The instructions on how to build the library are provided to them. 106 | 107 | ### XPM42 108 | For historical reasons, I included my own file format that mimics XPM3. In the original miniLibX, the way XPM files were handled made no sense, as they were supposed to be compiled into the binary. Instead, miniLibX parsed the files and pasted the data into memory. 109 | 110 | In the `tools` folder, there is a python script that converts XPM3 to XPM42. XPM42 is available as an alternative, but it is highly encouraged to use the PNG importer, which does not leak and uses lodepng for parsing. 111 | 112 | --- 113 | 114 | ### How can I migrate? What is necessary to change? 115 | 116 | Migrating to MLX42 is easy and requires minimal effort, it requires just 2 dependencies in order to work. 117 | 118 | #### Dependencies 119 | - [CMake: >= 3.18.0](https://cmake.org/download/) 120 | - [GLFW: >= 3.3.6](https://github.com/glfw/glfw) 121 | 122 | It is up to your pedago staff or system administrator to determine how to distribute MLX42 to students. 123 | 124 | The options are: 125 | 126 | - `A`: Install it on the machine in a location such as /usr/local/lib and use -lmlx42 to link it. 127 | 128 | - `B`: Have students clone the repository, preferably as a submodule, and include it in their repository. 129 | 130 | There is not much else to do besides these steps. It is a straightforward replacement, and the choice of distribution is up to the campus. 131 | 132 | ## F.A.Q 133 | 134 | Q: **_"It has too many features! I think students should implement some of them themselves including some functions..."_** 135 | 136 | A: If there is one frustrating point in this entire F.A.Q then it is this one. Because 42 Network's pedago never actually clarifies what this even means? They don't communicate what is supposededly "too useful". Because if they did then it wouldn't require having to write this entire F.A.Q and pointing out all their flawed arguments. It's been 2 years now and not once have the maintainers been approached by the network's pedago. 137 | 138 | ***WHAT*** functions are too useful you may wonder? Well really there aren't many if any that we can think of. The majority of additional functions are just GLFW wrappers to enable more extensive customization of the window. This includes setting custom cursors or app icons which really don't add any complexity nor are students able to do this themselves anyway. They are available without impeding their learning. 139 | 140 | If there was something too useful that really criticaly hinders the adoption of this library. I would advise that we use communication much like any other species of this planet to resolve it. 141 | 142 | --- 143 | 144 | Q: **_"The `mlx_put_pixel` works too well..."_** 145 | 146 | A: Rather than purposefully sabotaging the library to make a point, I deemed it necessary that the library simply works. By default, this function is already banned by all subjects, and the main idea was to force students to use images. 147 | 148 | In MLX42 it always starts with an image, and students are forced to face images no matter what. They still face the same learning curve of how to modify the buffer of an image and learn the concept of bit shifting one way or another. 149 | 150 | Our proposal is to allow its use in the first three graphics projects (fract-ol, fdf, so_long) and later ban it for the remaining ones (Cub3D, MiniRT), so students can become familiar with it and then need to explore its workings. 151 | 152 | --- 153 | 154 | Q: **_"Are the libraries 1:1 identical?"_** 155 | 156 | A: No, there are 100% breaking changes, and changing from miniLibX to MLX42 will not be a simple drop-in replacement. As in their API is slightly different. That was not the idea eitherway, as it would be impossible to fix the problems present in the current miniLibX. 157 | 158 | In terms of usage, it's practically identical, while the API Prototypes are slightly different, the usage is roughly the same and the only core difference is the way images are handled. 159 | 160 | My suggestion is for students who are using miniLibX to keep using it to finish their project and for new students to prefer the new one or until they reach the next graphical project. 161 | 162 | --- 163 | 164 | Q: **_"With regards to how the rendering is done, won't that make it too easy for students?"_** 165 | 166 | A: After almost a year in use, both new students who didn't use the old one and students who used both managed just fine and even appreciated this change in the way rendering works in MLX42. 167 | 168 | It is not taking away from the learning experience, it's just shifting a function call from one place to another. Students still find it challenging to finish the actual project, as they still need to learn a whole new library and how it operates. 169 | 170 | --- 171 | 172 | Q: **_"OpenGL? Isn't that a bit old by now?"_** 173 | 174 | A: Is OpenGL old? Sure. But in the end, students don't care. You could argue that it's less future-proof and that Vulkan should be used instead. But ask yourself, does one need an artillery cannon to hunt for a rabbit in a forest? If your answer is no, then why does one need Vulkan to render a bunch of quads on a window? 175 | 176 | OpenGL is easy to learn, widely supported and it could be argued that it's actually useful for students who are interested in graphics. Anyway, if one day Vulkan is required, a branch and PR can be created and merged! 177 | 178 | Just a small reminder that it takes roughly [700+ lines of code](https://github.com/SaschaWillems/Vulkan/blob/master/examples/triangle/triangle.cpp) to render a triangle with vulkan... 179 | 180 | Here is the equivalent in [OpenGL](https://learnopengl.com/code_viewer_gh.php?code=src/1.getting_started/2.1.hello_triangle/hello_triangle.cpp). 181 | 182 | --- 183 | 184 | Q: **_"CMake? Won't that confuse them?"_** 185 | 186 | A: It is 2 simple commands that are described in the README, and it's a good opportunity to learn a new way to build your projects. 187 | 188 | --- 189 | 190 | Q: **_"Who will maintain MLX42? How long can this be guaranteed?"_** 191 | 192 | A: Currently it is being maintained by `lde-la-h` (W2Wizard). However commits from the 42 Pedago or really anyone are very much welcome. 193 | I myself have been taking care of it since the 1st of January 2021 and aim to continue to support it until I can't. 194 | 195 | Once the time comes, someone else will take the lead of maintaining the library whoever that may be. 196 | 197 | --- 198 | 199 | Q: **_"Do we need to update translations or subjects, etc??"_** 200 | 201 | A: Regarding the subjects, as long as the library not being adopted there's little room for change in this regard. However the good news is that it basically requires zero effort besides updating the links on the intra and maybe changing the name referenced inside the pdf's. There is really nothing necessary to change besides minor things and to adapt these changes would literally just require at most a day of effort. 202 | 203 | If you're really unusure, you as a pedago / campus can just choose to adopt it and mention to students that they can git clone it from here. 204 | 205 | --- 206 | 207 | Q: **_"What if we want to ban some functions?"_** 208 | 209 | A: Contact the maintainer or make a PR with the suggestion and watch it get merged or rejected. All it requires is communication... Any change is welcome if it so desired. You don't have to accept anything as is, that is the point of all of this. That if something needs to change, it can actually happen. 210 | 211 | --- 212 | 213 | Q: **_"I don't like the fact that it uses GLFW for the window..."_** 214 | 215 | A: `¯\_(ツ)_/¯` Well it's better than using the native windowing framework, at least it is portable, at least if something is wrong with it students can actually fix it by making a PR to the respective repository. Additionally GLFW is pretty standard for things like this, simply checkout any graphics demo and somewhere you will end up with GLFW under the hood quite often. 216 | 217 | -------------------------------------------------------------------------------- /docs/Basics.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Basics 7 | 8 | Before starting please read the [index page](./index.md). 9 | Starting with MLX42 is very straightforward. Here we will make a simple program that will use a makefile and compile 10 | MLX42. 11 | 12 | ## Makefile Example 13 | 14 | If you're curious as to how to configure a makefile with MLX42 here is a Makefile example from a project. 15 | Use this as a guide on how to compile MLX42 and use it in your application. 16 | 17 | First of all we need a makefile that can compile our program, below you can see a sample makefile: 18 | 19 | ```makefile 20 | NAME := Game 21 | CFLAGS := -Wextra -Wall -Werror -Wunreachable-code -Ofast 22 | LIBMLX := ./lib/MLX42 23 | 24 | HEADERS := -I ./include -I $(LIBMLX)/include 25 | LIBS := $(LIBMLX)/build/libmlx42.a -ldl -lglfw -pthread -lm 26 | SRCS := $(shell find ./src -iname "*.c") 27 | OBJS := ${SRCS:.c=.o} 28 | 29 | all: libmlx $(NAME) 30 | 31 | libmlx: 32 | @cmake $(LIBMLX) -B $(LIBMLX)/build && make -C $(LIBMLX)/build -j4 33 | 34 | %.o: %.c 35 | @$(CC) $(CFLAGS) -o $@ -c $< $(HEADERS) && printf "Compiling: $(notdir $<)" 36 | 37 | $(NAME): $(OBJS) 38 | @$(CC) $(OBJS) $(LIBS) $(HEADERS) -o $(NAME) 39 | 40 | clean: 41 | @rm -rf $(OBJS) 42 | @rm -rf $(LIBMLX)/build 43 | 44 | fclean: clean 45 | @rm -rf $(NAME) 46 | 47 | re: clean all 48 | 49 | .PHONY: all, clean, fclean, re, libmlx 50 | ``` 51 | 52 | ## Main 53 | 54 | Below is a simple main into starting a window. MLX42 has several nice features that allow you to predefine how it should behave during runtime such as `MLX_HEADLESS` running it without opening a window or `MLX_STRETCH_IMAGE` which stretches the window content with the window size. 55 | 56 | The exact structure `mlx_init()` is basically a handle that stores important information 57 | regarding the window and looks as follows: 58 | 59 | ```c 60 | /** 61 | * Main MLX handle, carries important data in regards to the program. 62 | * @param window The window itself. 63 | * @param context Abstracted opengl data. 64 | * @param width The width of the window. 65 | * @param height The height of the window. 66 | * @param delta_time The time difference between the previous frame and the current frame. 67 | */ 68 | typedef struct mlx 69 | { 70 | void* window; 71 | void* context; 72 | int32_t width; 73 | int32_t height; 74 | double delta_time; 75 | } mlx_t; 76 | ``` 77 | 78 | Between initializations you can do everything that is required such as drawing your image or opening files. 79 | Once `mlx_loop()` is reached the program remains open until a shutdown is somehow requested, e.g: closing the window. 80 | 81 | Because we want programs to be interactive and do stuff it's very useful to hook into the looping process of `mlx_loop()`. 82 | In order to achieve this we use [hooks](./Hooks.md). 83 | 84 | `NOTE: Compile MLX42 with DEBUG=1 to see assertions and to add debug flags. This can help you find critical mistakes during development!` 85 | 86 | ```c 87 | // Written by Bruh 88 | 89 | #include 90 | #include 91 | #include 92 | #include "MLX42/MLX42.h" 93 | #define WIDTH 256 94 | #define HEIGHT 256 95 | 96 | // Exit the program as failure. 97 | static void ft_error(void) 98 | { 99 | fprintf(stderr, "%s", mlx_strerror(mlx_errno)); 100 | exit(EXIT_FAILURE); 101 | } 102 | 103 | // Print the window width and height. 104 | static void ft_hook(void* param) 105 | { 106 | const mlx_t* mlx = param; 107 | 108 | printf("WIDTH: %d | HEIGHT: %d\n", mlx->width, mlx->height); 109 | } 110 | 111 | int32_t main(void) 112 | { 113 | 114 | // MLX allows you to define its core behaviour before startup. 115 | mlx_set_setting(MLX_MAXIMIZED, true); 116 | mlx_t* mlx = mlx_init(WIDTH, HEIGHT, "42Balls", true); 117 | if (!mlx) 118 | ft_error(); 119 | 120 | /* Do stuff */ 121 | 122 | // Create and display the image. 123 | mlx_image_t* img = mlx_new_image(mlx, 256, 256); 124 | if (!img || (mlx_image_to_window(mlx, img, 0, 0) < 0)) 125 | ft_error(); 126 | 127 | // Even after the image is being displayed, we can still modify the buffer. 128 | mlx_put_pixel(img, 0, 0, 0xFF0000FF); 129 | 130 | // Register a hook and pass mlx as an optional param. 131 | // NOTE: Do this before calling mlx_loop! 132 | mlx_loop_hook(mlx, ft_hook, mlx); 133 | mlx_loop(mlx); 134 | mlx_terminate(mlx); 135 | return (EXIT_SUCCESS); 136 | } 137 | ``` 138 | -------------------------------------------------------------------------------- /docs/Colors.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Colors 7 | Colors are a fundamental concept in graphics programming. A common color model is RGBA, which MLX uses for storing and displaying images. 8 | 9 | ## Composition 10 | Colors are commonly represented as `4-byte` integers. This 4-byte integer is a grouping of four individual channels: red, green, blue and alpha, with alpha representing transparency. Additionally, colors are usually shown in hexadecimal to make each channel value identifiable: 11 | 12 | Channel | Description | RGBA representation 13 | :------:|:-------------:|:-------------------: 14 | `R` | Red Channel | `0xFF000000` 15 | `G` | Green Channel | `0x00FF0000` 16 | `B` | Blue Channel | `0x0000FF00` 17 | `A` | Alpha Channel | `0x000000FF` 18 | 19 | Combining these four channel values into one will result in a non-transparent, white color. 20 | 21 | ## Encoding & Decoding 22 | 23 | In order to set each channel's byte we can use bit-shifting operations. 24 | 25 | A function that combines four individual channel bytes into a single integer using bit-shifting looks like this: 26 | 27 | ```c 28 | // 'Encodes' four individual bytes into an int. 29 | int get_rgba(int r, int g, int b, int a) 30 | { 31 | return (r << 24 | g << 16 | b << 8 | a); 32 | } 33 | ``` 34 | 35 | We can also do this in reverse to retrieve each individual byte again: 36 | 37 | ```c 38 | // Get the red channel. 39 | int get_r(int rgba) 40 | { 41 | // Move 3 bytes to the right and mask out the first byte. 42 | return ((rgba >> 24) & 0xFF); 43 | } 44 | 45 | // Get the green channel. 46 | int get_g(int rgba) 47 | { 48 | // Move 2 bytes to the right and mask out the first byte. 49 | return ((rgba >> 16) & 0xFF); 50 | } 51 | 52 | // Get the blue channel. 53 | int get_b(int rgba) 54 | { 55 | // Move 1 byte to the right and mask out the first byte. 56 | return ((rgba >> 8) & 0xFF); 57 | } 58 | 59 | // Get the alpha channel. 60 | int get_a(int rgba) 61 | { 62 | // Move 0 bytes to the right and mask out the first byte. 63 | return (rgba & 0xFF); 64 | } 65 | ``` 66 | 67 | ## Example 68 | 69 | In this small example we will create a white image: 70 | 71 | ```c 72 | #include "MLX42/MLX42.h" 73 | #include 74 | #include 75 | 76 | // Bytes Per Pixel. Since each pixel is represented as an integer, it will be four bytes for four channels. 77 | #define BPP sizeof(int32_t) 78 | 79 | int32_t main(void) 80 | { 81 | // Init mlx with a canvas size of 256x256 and the ability to resize the window. 82 | mlx_t* mlx = mlx_init(256, 256, "MLX42", true); 83 | 84 | if (!mlx) exit(EXIT_FAILURE); 85 | 86 | // Create a 128x128 image. 87 | mlx_image_t* img = mlx_new_image(mlx, 128, 128); 88 | 89 | // Set the channels of each pixel in our image to the maximum byte value of 255. 90 | memset(img->pixels, 255, img->width * img->height * BPP); 91 | 92 | // Draw the image at coordinate (0, 0). 93 | mlx_image_to_window(mlx, img, 0, 0); 94 | 95 | // Run the main loop and terminate on quit. 96 | mlx_loop(mlx); 97 | mlx_terminate(mlx); 98 | 99 | return (EXIT_SUCCESS); 100 | } 101 | 102 | ``` 103 | -------------------------------------------------------------------------------- /docs/Functions.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Functions 7 | 8 | A list of all functions can be found here: [Functions](https://bit.ly/3aWZL7C) -------------------------------------------------------------------------------- /docs/Hooks.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Hooks in MLX42 7 | 8 | Hooks allow you to add your own functions to the main loop execution of the program, aka these functions get executed every frame. 9 | They also serve to intercept certain keypresses such as scrolling or pressing enter. 10 | 11 | Only one hook can be set at a time! You cannot attach/have multiple specialized hooks. 12 | You can however have multiple generic hooks. 13 | 14 | ## Specialized Hooks 15 | 16 | ### Scroll hook 17 | ```c 18 | 19 | /** 20 | * Callback function used to handle scrolling. 21 | * 22 | * @param[in] x The mouse x delta. 23 | * @param[in] y The mouse y delta. 24 | * @param[in] param Additional parameter to pass to the function. 25 | */ 26 | typedef void (*mlx_scrollfunc)(double xdelta, double ydelta, void* param); 27 | 28 | /** 29 | * This function sets the scroll callback, which is called when a scrolling 30 | * device is used, such as a mouse wheel. 31 | * 32 | * @param[in] mlx The MLX instance handle. 33 | * @param[in] func The scroll wheel callback function. 34 | * @param[in] param An additional optional parameter. 35 | */ 36 | void mlx_scroll_hook(mlx_t* mlx, mlx_scrollfunc func, void* param); 37 | ``` 38 | 39 | ### Close Hook 40 | 41 | ```c 42 | 43 | /** 44 | * Callback function used to handle window closing which is called when the user attempts 45 | * to close the window, for example by clicking the close widget in the title bar. 46 | * 47 | * @param[in] param Additional parameter to pass to the function. 48 | */ 49 | typedef void (*mlx_closefunc)(void* param); 50 | 51 | /** 52 | * This function sets the close callback, which is called in attempt to close 53 | * the window device such as a close window widget used in the window bar. 54 | * 55 | * @param[in] mlx The MLX instance handle. 56 | * @param[in] func The close callback function. 57 | * @param[in] param An additional optional parameter. 58 | */ 59 | void mlx_close_hook(mlx_t* mlx, mlx_closefunc func, void* param); 60 | ``` 61 | 62 | ### Resize Hook 63 | 64 | ```c 65 | 66 | /** 67 | * Callback function used to handle window resizing. 68 | * 69 | * @param[in] width The new width of the window. 70 | * @param[in] height The new height of the window. 71 | * @param[in] param Additional parameter to pass to the function. 72 | */ 73 | typedef void (*mlx_resizefunc)(int32_t width, int32_t height, void* param); 74 | 75 | /** 76 | * This function sets the resize callback, which is called when the window is 77 | * resized 78 | * 79 | * @param[in] mlx The MLX instance handle. 80 | * @param[in] func The resize callback function. 81 | * @param[in] param An additional optional parameter. 82 | */ 83 | void mlx_resize_hook(mlx_t* mlx, mlx_resizefunc func, void* param); 84 | ``` 85 | 86 | ### Key hook 87 | 88 | Use a key hook if you want single keypress detection or more precision as to how a key is pressed, such as checking for modifier keys or getting the raw os keycode. 89 | 90 | ```c 91 | 92 | /** 93 | * Key function callback data. 94 | * Data related to the mlx_key_hook function 95 | * 96 | * @param key The key that was pressed. 97 | * @param action The action that was done with the key. 98 | * @param os_key The os_key is unique for every key, and will have a 99 | * different value/keycode depending on the platform. 100 | * They may be consistent on different platforms. 101 | * @param modifier The modifier key that was pressed, 0 if none. 102 | */ 103 | typedef struct mlx_key_data 104 | { 105 | keys_t key; 106 | action_t action; 107 | int32_t os_key; 108 | modifier_key_t modifier; 109 | } mlx_key_data_t; 110 | 111 | 112 | /** 113 | * Callback function used to handle keypresses. 114 | * 115 | * @param[in] keydata The callback data, contains info on key, action, ... 116 | * @param[in] param Additional parameter to pass to the function. 117 | */ 118 | typedef void (*mlx_keyfunc)(mlx_key_data_t keydata, void* param); 119 | 120 | /** 121 | * This function sets the key callback, which is called when a key is pressed 122 | * on the keyboard. Useful for single key press detection. 123 | * 124 | * @param[in] mlx The MLX instance handle. 125 | * @param[in] func The key press callback function. 126 | * @param[in] param An additional optional parameter. 127 | */ 128 | void mlx_key_hook(mlx_t* mlx, mlx_keyfunc func, void* param); 129 | ``` 130 | 131 | ## Generic Hook 132 | 133 | Generic hooks execute each frame and are useful for stuff that needs to be updated every frame. 134 | 135 | ```c 136 | /** 137 | * Generic loop hook for any custom hooks to add to the main loop. 138 | * Executes a function per frame, so be careful. 139 | * 140 | * @param[in] mlx The MLX instance handle. 141 | * @param[in] f The function. 142 | * @param[in] param The parameter to pass onto the function. 143 | * @returns Wether the hook was added successfully. 144 | */ 145 | bool mlx_loop_hook(mlx_t* mlx, void (*f)(void*), void* param); 146 | ``` 147 | 148 | # Examples 149 | 150 | Here are some simple examples on how to implement each one of the hooks in a simple fashion. 151 | 152 | ## Key Hook 153 | 154 | ```c 155 | 156 | #include 157 | #include 158 | #include 159 | #include "MLX42/MLX42.h" 160 | #define WIDTH 720 161 | #define HEIGHT 480 162 | 163 | void my_keyhook(mlx_key_data_t keydata, void* param) 164 | { 165 | // If we PRESS the 'J' key, print "Hello". 166 | if (keydata.key == MLX_KEY_J && keydata.action == MLX_PRESS) 167 | puts("Hello "); 168 | 169 | // If we RELEASE the 'K' key, print "World". 170 | if (keydata.key == MLX_KEY_K && keydata.action == MLX_RELEASE) 171 | puts("World"); 172 | 173 | // If we HOLD the 'L' key, print "!". 174 | if (keydata.key == MLX_KEY_L && keydata.action == MLX_REPEAT) 175 | puts("!"); 176 | } 177 | 178 | int32_t main(void) 179 | { 180 | mlx_t* mlx; 181 | 182 | if (!(mlx = mlx_init(WIDTH, HEIGHT, "MLX42", true))) 183 | return (EXIT_FAILURE); 184 | 185 | mlx_key_hook(mlx, &my_keyhook, NULL); 186 | mlx_loop(mlx); 187 | mlx_terminate(mlx); 188 | return (EXIT_SUCCESS); 189 | } 190 | ``` 191 | 192 | ## Scroll Example 193 | 194 | ```c 195 | #include 196 | #include 197 | #include 198 | #include "MLX42/MLX42.h" 199 | #define WIDTH 720 200 | #define HEIGHT 480 201 | 202 | void my_scrollhook(double xdelta, double ydelta, void* param) 203 | { 204 | // Simple up or down detection. 205 | if (ydelta > 0) 206 | puts("Up!"); 207 | else if (ydelta < 0) 208 | puts("Down!"); 209 | 210 | // Can also detect a mousewheel that goes along the X (e.g: MX Master 3) 211 | if (xdelta < 0) 212 | puts("Sliiiide to the left!"); 213 | else if (xdelta > 0) 214 | puts("Sliiiide to the right!"); 215 | } 216 | 217 | int32_t main(void) 218 | { 219 | mlx_t* mlx; 220 | 221 | if (!(mlx = mlx_init(WIDTH, HEIGHT, "MLX42", true))) 222 | return (EXIT_FAILURE); 223 | 224 | mlx_scroll_hook(mlx, &my_scrollhook, NULL); 225 | mlx_loop(mlx); 226 | mlx_terminate(mlx); 227 | return (EXIT_SUCCESS); 228 | } 229 | ``` 230 | -------------------------------------------------------------------------------- /docs/Images.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Images 7 | 8 | Images are like a canvas for a painting, they are used to display pixel information onto the window and work with something called instances. 9 | Instances are copies of the canvas and let you display an image multiple times throughout the window. The idea behind it is that you already 10 | have the pixel information in memory and you can simply create duplicates everywhere. 11 | 12 | Each instance has an X, Y and Z parameter to determine their position and depth. 13 | A change in the image's buffer results in a change for all currently displayed instances. 14 | 15 | An image on its own is very simple: 16 | ```c 17 | /** 18 | * An image with an individual buffer that can be rendered. 19 | * Any value can be modified except the width/height and context. 20 | * 21 | * @param width The width of the image. 22 | * @param height The height of the image. 23 | * @param pixels The literal pixel data. 24 | * @param instances An instance carries the X, Y, Z location data. 25 | * @param count The element count of the instances array. 26 | * @param enabled If true the image is drawn onto the screen, else it's not. 27 | * @param context Abstracted OpenGL data. 28 | */ 29 | typedef struct mlx_image 30 | { 31 | const uint32_t width; 32 | const uint32_t height; 33 | uint8_t* pixels; 34 | mlx_instance_t* instances; 35 | int32_t count; 36 | bool enabled; 37 | void* context; 38 | } mlx_image_t; 39 | ``` 40 | 41 | To display the image all that is needed is to call the `mlx_image_to_window` function to create a new copy/instance: 42 | ```c 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include "MLX42/MLX42.h" 48 | #define WIDTH 5120 49 | #define HEIGHT 2880 50 | 51 | static void error(void) 52 | { 53 | puts(mlx_strerror(mlx_errno)); 54 | exit(EXIT_FAILURE); 55 | } 56 | 57 | int32_t main(void) 58 | { 59 | // Start mlx 60 | mlx_t* mlx = mlx_init(WIDTH, HEIGHT, "Test", true); 61 | if (!mlx) 62 | error(); 63 | 64 | // Create a new image 65 | mlx_image_t* img = mlx_new_image(mlx, 512, 512); 66 | if (!img) 67 | error(); 68 | 69 | // Set every pixel to white 70 | memset(img->pixels, 255, img->width * img->height * sizeof(int32_t)); 71 | 72 | // Display an instance of the image 73 | if (mlx_image_to_window(mlx, img, 0, 0) < 0) 74 | error(); 75 | 76 | mlx_loop(mlx); 77 | 78 | // Optional, terminate will clean up any leftovers, this is just to demonstrate. 79 | mlx_delete_image(mlx, img); 80 | mlx_terminate(mlx); 81 | return (EXIT_SUCCESS); 82 | } 83 | ``` 84 | 85 | After we have put an instance of an image onto the window we can simply change the position of the image at any time 86 | we want it to be moved: 87 | ```c 88 | // Modify the x & y position of an already existing instance. 89 | img->instances[0].x += 5; 90 | img->instances[0].y += 5; 91 | ``` 92 | 93 | ## Transparency 94 | In regards to transparency, aka the `z` value, use `mlx_set_instance_depth` to set the z/depth value of the image. 95 | The z value determines the depth of the image, as in, is it in the foreground or background. 96 | 97 | If two instances are on the same z layer and are transparent, the transparency breaks and the instances cut off each other. 98 | To prevent this by default any new instances put onto window will be on their own layer. 99 | 100 | ## Internals 101 | A noticeable feature of MLX42 is that it partly takes care of the rendering for you, that is, after you've created your image you just display it 102 | and after that feel free to modify it without having to re-put it onto the window. In short MLX takes care of updating your images at all times. 103 | 104 | Internally this is done via a render queue, anytime the `mlx_image_to_window` function is used, a new entry is added to a linked list. 105 | Every frame MLX will iterate over this linked list and execute a drawcall to draw that image onto the window. 106 | 107 | ## Common functions 108 | 109 | ```c 110 | // Creates a whole new image. 111 | mlx_image_t* mlx_new_image(mlx_t* mlx, uint16_t width, uint16_t height) 112 | ``` 113 | 114 | ```c 115 | // Creates a new instance/copy of an already existing image. 116 | void mlx_image_to_window(mlx_image_t* img, int32_t x, int32_t y) 117 | ``` 118 | 119 | ```c 120 | // Deletes an image and removes it from the render queue. 121 | void mlx_delete_image(mlx* mlx, mlx_image_t* image) 122 | ``` 123 | -------------------------------------------------------------------------------- /docs/Input.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Input methods 7 | 8 | MLX42 provides various ways of detecting input, you can read about all the available hooks on the [Hooks](./Hooks.md) page. 9 | 10 | ## Key Detection 11 | 12 | ### Generic Hook + function 13 | 14 | The easiest way of detecting continuous keypressing is via a generic hook and then checking if the specific key is down. 15 | This is the 'traditional' way of detecting if a key is down even in modern game engines. It provides the quickest feedback and if it's used to, say, move a character, the smoothest result. 16 | 17 | ```c 18 | void hook(void *param) 19 | { 20 | mlx_t *mlx; 21 | 22 | mlx = param; 23 | if (mlx_is_key_down(param, MLX_KEY_ESCAPE)) 24 | mlx_close_window(param); 25 | if (mlx_is_key_down(param, MLX_KEY_UP)) 26 | g_img->instances[0].y -= 5; 27 | if (mlx_is_key_down(param, MLX_KEY_DOWN)) 28 | g_img->instances[0].y += 5; 29 | if (mlx_is_key_down(param, MLX_KEY_LEFT)) 30 | g_img->instances[0].x -= 5; 31 | if (mlx_is_key_down(param, MLX_KEY_RIGHT)) 32 | g_img->instances[0].x += 5; 33 | } 34 | ``` 35 | 36 | ### Hook Function 37 | 38 | For more exact input detection such as checking if the key was pressed with `Alt` or `ctrl` you should use the actual Key hook. 39 | Keep in mind that using a keyhook results in a slower feedback compared to using a generic hook but grants you more control in key detection. 40 | 41 | ```c 42 | void key_hook(mlx_key_data_t keydata, void* param) 43 | { 44 | if (keydata.key == MLX_KEY_A && keydata.action == MLX_RELEASE && keydata.modifier == MLX_CONTROL) 45 | puts("Gotta grab it all!"); 46 | } 47 | ``` -------------------------------------------------------------------------------- /docs/Shaders.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Shaders 7 | 8 | In computer graphics, a shader is a type of computer program used for shading in 3D/2D scenes (the production of appropriate levels of light, darkness, and color in a rendered image). MLX42 exposes the shaders and compiles them into the library for portability. 9 | 10 | ```glsl 11 | 12 | // Example of shader code, GLSL is similar to C but not quite. 13 | 14 | #version 330 core 15 | 16 | layout(location = 0) in vec3 aPos; 17 | layout(location = 1) in vec2 aTexCoord; 18 | 19 | out vec2 TexCoord; 20 | uniform mat4 ProjMatrix; 21 | 22 | void main() 23 | { 24 | gl_Position = ProjMatrix * vec4(aPos, 1.0); 25 | TexCoord = aTexCoord; 26 | } 27 | 28 | ``` 29 | 30 | ## Beware 31 | 32 | Shaders aren't really meant to be used by students but are more there for the convenience of developers. (though some advanced students might make some use of them) 33 | 34 | # Compilation 35 | 36 | Shaders are converted into a `.c` appropriate format and then compiled into the library and referenced via a `extern` global variable appropriately named `vert_shader` & `frag_shader`. The reason this is done is to keep the final game/executable portable, that is being able to use it at any given location within a filesystem, while still being easy to work on the shaders instead of having to mess with it in the `.c` files directly. 37 | 38 | -------------------------------------------------------------------------------- /docs/Textures.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # Textures 7 | 8 | Textures are disk loaded images stored in memory and hold a buffer of pixel data along with information 9 | about the image such as width, height, and bytes per pixel. 10 | 11 | Textures on their own are not displayed to the screen but have to be displayed using [Images](./Images.md). 12 | To do so you can use the `mlx_texture_to_image` function that creates an image large enough to store the 13 | texture which then can be displayed. 14 | 15 | ## Textures vs Images 16 | 17 | There might be a bit of confusion at first between what an image and a texture is. 18 | 19 | Textures: 20 | * Can be interpreted as a painter's "color palette". 21 | * Created by loading an image file FROM disk. 22 | * Simply contain the pixels, width, height and bytes per pixel information. 23 | * Do not get displayed on the window directly. 24 | 25 | Images: 26 | * Can be interpreted as a painter's "canvas". 27 | * Can be created FROM a texture or an empty buffer! 28 | * Carries more information besides what the image buffer is such as instance count. 29 | * Also holds pixel data but is shared among its instances, it is not loaded from disk but stored in memory. 30 | 31 | ## Example 32 | 33 | To summarize, in order to display a sprite image onto our window we would first load the texture from 34 | disk into our memory and store the information in a `mlx_texture_t*`. After that we create a new `mlx_image_t*` 35 | based on the information given by the texture and then we can display our image onto the window. 36 | 37 | Below is a small code example of how this would be achieved: 38 | 39 | ```C 40 | #include 41 | #include 42 | #include 43 | #include "MLX42/MLX42.h" 44 | #define WIDTH 512 45 | #define HEIGHT 512 46 | 47 | static void error(void) 48 | { 49 | puts(mlx_strerror(mlx_errno)); 50 | exit(EXIT_FAILURE); 51 | } 52 | 53 | int32_t main(void) 54 | { 55 | // Start mlx 56 | mlx_t* mlx = mlx_init(WIDTH, HEIGHT, "Test", true); 57 | if (!mlx) 58 | error(); 59 | 60 | // Try to load the file 61 | mlx_texture_t* texture = mlx_load_png("./temp/sus.png"); 62 | if (!texture) 63 | error(); 64 | 65 | // Convert texture to a displayable image 66 | mlx_image_t* img = mlx_texture_to_image(mlx, texture); 67 | if (!img) 68 | error(); 69 | 70 | // Display the image 71 | if (mlx_image_to_window(mlx, img, 0, 0) < 0) 72 | error(); 73 | 74 | mlx_loop(mlx); 75 | 76 | // Optional, terminate will clean up any leftovers, this is just to demonstrate. 77 | mlx_delete_image(mlx, img); 78 | mlx_delete_texture(texture); 79 | mlx_terminate(mlx); 80 | return (EXIT_SUCCESS); 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /docs/XPM42.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | # XPM42 7 | 8 | XPM42 is a custom file format made for MLX42 to provide an easy to use and understand image file format to learn how 9 | images can be stored. The format is based on the actual [XPM3](https://en.wikipedia.org/wiki/X_PixMap) format. 10 | 11 | An XPM file basically stores a look-up table inside of it to fetch which character corresponds to which color. Additionally in the 12 | header there is a character per pixel count, this is due to the limitation of the amount of characters. Each 'Pixel' in the XPM data can 13 | be represented by multiple characters. 14 | 15 | For example `*.` would be viewed as a single pixel if the characters per pixel count was 2. 16 | 17 | ## Layout 18 | 19 | The file format looks as follows: 20 | 21 | ``` 22 | !XPM42 <- File declaration 23 | 16 7 2 1 c <- Width | Height | Color count | Characters per Pixel | Mode (C: Color or M: Monochrome) 24 | * #FF0000FF <- Entry always: Colors MUST have all four channels (RGBA) 25 | . #00000000 26 | **..*........... <- Literal pixel data 27 | *.*.*........... 28 | **..*..**.**..** 29 | *.*.*.*.*.*..*.* 30 | **..*..**.*...** 31 | ...............* 32 | .............**. 33 | ``` 34 | 35 | ## Inner workings 36 | 37 | Reading an XPM42 does a whole bunch of stuff but in essence it reads the file header and inserts each color entry into a hash table for fast lookups of the color value, the hash used is FNV-1a. Why, because it's an easy to use hash and also my favourite. After the header is read and the color values are inserted into the table each line is then read and each character is processed and inserted into the pixel buffer of the XPM. There is no collision checking for the lookup table, so artefacts may be present. 38 | 39 | ## Tools 40 | 41 | In the root of the repository is a tools directory in which a python script can convert an existing XPM3 file to XPM42. 42 | Use this script if you wish to use the XPM42 file format. 43 | 44 | ## Example 45 | 46 | ```C 47 | #include 48 | #include 49 | #include 50 | #include "MLX42/MLX42.h" 51 | #define WIDTH 5120 52 | #define HEIGHT 2880 53 | 54 | static void error(void) 55 | { 56 | puts(mlx_strerror(mlx_errno)); 57 | exit(EXIT_FAILURE); 58 | } 59 | 60 | int32_t main(void) 61 | { 62 | // Start mlx 63 | mlx_t* mlx = mlx_init(WIDTH, HEIGHT, "Test", true); 64 | if (!mlx) 65 | error(); 66 | 67 | // Try to load the file 68 | xpm_t* xpm = mlx_load_xpm42("./temp/42.xpm42"); 69 | if (!xpm) 70 | error(); 71 | 72 | // Convert texture to a displayable image 73 | mlx_image_t* img = mlx_texture_to_image(mlx, &xpm->texture); 74 | if (!img) 75 | error(); 76 | 77 | // Display the image 78 | if (mlx_image_to_window(mlx, img, 0, 0) < 0) 79 | error(); 80 | 81 | mlx_loop(mlx); 82 | 83 | // Optional, terminate will clean up any leftovers, this is just to demonstrate. 84 | mlx_delete_image(mlx, img); 85 | mlx_delete_xpm42(xpm); 86 | mlx_terminate(mlx); 87 | return (EXIT_SUCCESS); 88 | } 89 | ``` 90 | 91 | 92 | ![Example](./assets/XPM_Demo.png) 93 | -------------------------------------------------------------------------------- /docs/assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/MLX42/ce254c3a19af8176787601a2ac3490100a5c4c61/docs/assets/demo.gif -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/MLX42/ce254c3a19af8176787601a2ac3490100a5c4c61/docs/assets/logo.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 |
8 | 42MLX_Logo 9 |
10 |
11 | Written by W2.Wizard for the 42 Network 12 |
13 |
14 | 15 |
16 |

Welcome to the MLX42 documentation

17 |
18 | 19 | # Introduction 20 | 21 | MLX42 is a performant, easy to use, cross-platform windowing library to create 22 | graphical applications without having to work directly with the native Windowing 23 | Framework of the given operating system. 24 | 25 | Additionally it provides primitive tools for drawing onto the window such as 26 | displaying images from disk or creating a drawing surface to draw pixels on. 27 | 28 | --- 29 | 30 | ## Toc 31 | 32 | * [Basics](./Basics.md) 33 | * [Colors](./Colors.md) 34 | * [Functions](./Functions.md) 35 | * [Hooks](./Hooks.md) 36 | * [Images](./Images.md) 37 | * [Input](./Input.md) 38 | * [Shaders](./Shaders.md) 39 | * [Textures](./Textures.md) 40 | * [XPM42](./XPM42.md) 41 | 42 | --- 43 | 44 | ## Support 45 | 46 | Currently supported operating systems: 47 | | Operating system | Version | 48 | |------------------ |--------------------------------------------- | 49 | | `Windows NT` | Windows 7 - Windows 11 | 50 | | `MacOS` | Mojave - Monterey (Including Apple Silicon) | 51 | | `Linux` | Anything running X11 / Wayland | 52 | 53 | ## Initialization 54 | 55 | The very first step for initialization is to execute the mlx_init function. 56 | It is responsible for setting up the GLFW windowing library which 57 | creates a connection between your software and the display. It also loads the 58 | OpenGL function pointers, compiling the shaders and more. 59 | 60 | It returns a mlx_t* which is a structure containing the current window instance. 61 | With this instance you can manipulate, hook onto and control what happens inside 62 | your window instance. For example you would use it to send graphical instructions such as 63 | creating an image, which is used to display pixel data. You can also detect key 64 | interactions such as checking if the W, A, S or D key is currently being pressed. 65 | 66 | | Function | Description | 67 | |-------------------|--------------------------------------------------------------| 68 | | `mlx_init()` | Initialize and run a new window instance. | 69 | | `mlx_loop()` | Keep the window open as long as a shutdown is not requested. | 70 | | `mlx_terminate()` | Destroy and clean up all images and mlx resources. | 71 | 72 | If mlx_init() fails to set up the connection to the graphical system, it will 73 | return NULL, otherwise a non-null pointer is returned as a handle for the window. 74 | 75 | ## Linking 76 | 77 | In order to use the functions in MLX, you'll need to link it with your application. 78 | 79 | To do this, simply add the following arguments at the linking stage: 80 | | Operating System | Flags | 81 | |------------------|---------------------------------------------------------------| 82 | | `Windows NT` | -lglfw3 -lopengl32 -lgdi32 | 83 | | `MacOS` | -lglfw(3) -framework Cocoa -framework OpenGL -framework IOKit | 84 | | `Linux` | -ldl -lglfw(3) -pthread -lm | 85 | 86 | **NOTE: For some UNIX systems the flag for glfw might be with or without a 3 at the end.** 87 | 88 | ## Build options 89 | 90 | When building MLX42 you can pass certain build options to cmake. 91 | 92 | The options are passed as follows `cmake -DDEBUG=1 -DGLFW_FETCH=0`. 93 | 94 | ### Available options 95 | 96 | * `DEBUG`: Enables assertion macros and compiles with -g in order for debugging with lldb. 97 | * `GLFW_FETCH`: Fetches GLFW if it can't be found on the system at all, allows you to then install it with `sudo make install` under the `build/_deps` folder. 98 | 99 | ## Debugging 100 | 101 | MLX was designed with ease of debugging in mind, therefore if the project is built with 102 | **cmake -DDEBUG=1** it will keep in the assertion macros and notify you of any bad input 103 | given to functions. Additionally it comes with its own runtime error checking via 104 | **mlx_errno** and **mlx_strerror** to properly identify what went wrong during the runtime 105 | of the library. 106 | 107 | ## Notes 108 | 109 | Keep in mind that while technically MLX42 110 | does support multiple window instances it currently has no functional support for 111 | it. That is, no proper way of handling multiple windows. 112 | 113 | --- 114 | 115 | ## F.A.Q 116 | 117 | Q: **_"It'S NoT In ThE SuBjeCt!"_** 118 | 119 | A: So what? Subjects can change and so if something is not working correctly it should be replaced. Sure you can argue this point but you can also be the reason that it CAN be in the subject instead. Have an open mind :) 120 | 121 | Q: **_"Ok, so, can I use it ?"_** 122 | 123 | A: Officially, _no_. However, ask your head of studies first about using it, see what they think. Some students might be evangelical enthusiasts about what is stated in the subject and are technically in every right to fail you as long as this library is not endorsed, if you were to ask me (W2) then yes why not? 124 | 125 | Q: **_"Is it faster?"_** 126 | 127 | A: From my personal projects there was a considerable performance gain, especially when compiled with `-Ofast`. Projects such as FDF could rotate their maps mindblowingly smooth and even larger maps with a width and height of 1000+ points moved/rotated relatively smooth, so in short, yes. 128 | 129 | Q: **_"Can I just drag and drop it into my old project and just not do anything?"_** 130 | 131 | A: No. That's not how libraries work. Sure they target and do sort of the same thing but the functions each library provides are too different, even a little bit in terms of behavior. And no there is no easy way to convert from the "old" to the "new" it will be somewhat tedious work. 132 | 133 | Q: **_"We should be able to deal with the fact that MiniLibX is not perfect, it is by design and makes us better programmers."_** 134 | 135 | A: Struggle does bring out the best in most people but it is also not ideal in this case. I think so at least, that it's really expected that libraries that are publicly available should be usable, stable, easy to use and well documented. Nobody uses a library because it is annoying to work with and afterwards think to themselves they have learned something after they are done struggling. The only thing people learn from this is how to navigate around the shortcomings instead. 136 | 137 | Q: **_"Why not use some other library? Why this one and not any other library."_** 138 | 139 | A: It is your choice what to use! I wrote this in my free time in an attempt to introduce some good change and to improve the learning experience at 42. If you don't like my library at least let me know what it is so I can improve on it. 140 | 141 | Q: **_"Do you hate MiniLibX? Is this some personal vendetta, do you work for the CIA ?"_** 142 | 143 | A: No, I just want to improve 42, that's it. 144 | -------------------------------------------------------------------------------- /include/KHR/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2018 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * The master copy of khrplatform.h is maintained in the Khronos EGL 30 | * Registry repository at https://github.com/KhronosGroup/EGL-Registry 31 | * The last semantic modification to khrplatform.h was at commit ID: 32 | * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 33 | * 34 | * Adopters may modify this file to suit their platform. Adopters are 35 | * encouraged to submit platform specific modifications to the Khronos 36 | * group so that they can be included in future versions of this file. 37 | * Please submit changes by filing pull requests or issues on 38 | * the EGL Registry repository linked above. 39 | * 40 | * 41 | * See the Implementer's Guidelines for information about where this file 42 | * should be located on your system and for more details of its use: 43 | * http://www.khronos.org/registry/implementers_guide.pdf 44 | * 45 | * This file should be included as 46 | * #include 47 | * by Khronos client API header files that use its types and defines. 48 | * 49 | * The types in khrplatform.h should only be used to define API-specific types. 50 | * 51 | * Types defined in khrplatform.h: 52 | * khronos_int8_t signed 8 bit 53 | * khronos_uint8_t unsigned 8 bit 54 | * khronos_int16_t signed 16 bit 55 | * khronos_uint16_t unsigned 16 bit 56 | * khronos_int32_t signed 32 bit 57 | * khronos_uint32_t unsigned 32 bit 58 | * khronos_int64_t signed 64 bit 59 | * khronos_uint64_t unsigned 64 bit 60 | * khronos_intptr_t signed same number of bits as a pointer 61 | * khronos_uintptr_t unsigned same number of bits as a pointer 62 | * khronos_ssize_t signed size 63 | * khronos_usize_t unsigned size 64 | * khronos_float_t signed 32 bit floating point 65 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 66 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 67 | * nanoseconds 68 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 69 | * khronos_boolean_enum_t enumerated boolean type. This should 70 | * only be used as a base type when a client API's boolean type is 71 | * an enum. Client APIs which use an integer or other type for 72 | * booleans cannot use this as the base type for their boolean. 73 | * 74 | * Tokens defined in khrplatform.h: 75 | * 76 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 77 | * 78 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 79 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 80 | * 81 | * Calling convention macros defined in this file: 82 | * KHRONOS_APICALL 83 | * KHRONOS_APIENTRY 84 | * KHRONOS_APIATTRIBUTES 85 | * 86 | * These may be used in function prototypes as: 87 | * 88 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 89 | * int arg1, 90 | * int arg2) KHRONOS_APIATTRIBUTES; 91 | */ 92 | 93 | #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) 94 | # define KHRONOS_STATIC 1 95 | #endif 96 | 97 | /*------------------------------------------------------------------------- 98 | * Definition of KHRONOS_APICALL 99 | *------------------------------------------------------------------------- 100 | * This precedes the return type of the function in the function prototype. 101 | */ 102 | #if defined(KHRONOS_STATIC) 103 | /* If the preprocessor constant KHRONOS_STATIC is defined, make the 104 | * header compatible with static linking. */ 105 | # define KHRONOS_APICALL 106 | #elif defined(_WIN32) 107 | # define KHRONOS_APICALL __declspec(dllimport) 108 | #elif defined (__SYMBIAN32__) 109 | # define KHRONOS_APICALL IMPORT_C 110 | #elif defined(__ANDROID__) 111 | # define KHRONOS_APICALL __attribute__((visibility("default"))) 112 | #else 113 | # define KHRONOS_APICALL 114 | #endif 115 | 116 | /*------------------------------------------------------------------------- 117 | * Definition of KHRONOS_APIENTRY 118 | *------------------------------------------------------------------------- 119 | * This follows the return type of the function and precedes the function 120 | * name in the function prototype. 121 | */ 122 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) 123 | /* Win32 but not WinCE */ 124 | # define KHRONOS_APIENTRY __stdcall 125 | #else 126 | # define KHRONOS_APIENTRY 127 | #endif 128 | 129 | /*------------------------------------------------------------------------- 130 | * Definition of KHRONOS_APIATTRIBUTES 131 | *------------------------------------------------------------------------- 132 | * This follows the closing parenthesis of the function prototype arguments. 133 | */ 134 | #if defined (__ARMCC_2__) 135 | #define KHRONOS_APIATTRIBUTES __softfp 136 | #else 137 | #define KHRONOS_APIATTRIBUTES 138 | #endif 139 | 140 | /*------------------------------------------------------------------------- 141 | * basic type definitions 142 | *-----------------------------------------------------------------------*/ 143 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 144 | 145 | 146 | /* 147 | * Using 148 | */ 149 | #include 150 | typedef int32_t khronos_int32_t; 151 | typedef uint32_t khronos_uint32_t; 152 | typedef int64_t khronos_int64_t; 153 | typedef uint64_t khronos_uint64_t; 154 | #define KHRONOS_SUPPORT_INT64 1 155 | #define KHRONOS_SUPPORT_FLOAT 1 156 | /* 157 | * To support platform where unsigned long cannot be used interchangeably with 158 | * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. 159 | * Ideally, we could just use (u)intptr_t everywhere, but this could result in 160 | * ABI breakage if khronos_uintptr_t is changed from unsigned long to 161 | * unsigned long long or similar (this results in different C++ name mangling). 162 | * To avoid changes for existing platforms, we restrict usage of intptr_t to 163 | * platforms where the size of a pointer is larger than the size of long. 164 | */ 165 | #if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) 166 | #if __SIZEOF_POINTER__ > __SIZEOF_LONG__ 167 | #define KHRONOS_USE_INTPTR_T 168 | #endif 169 | #endif 170 | 171 | #elif defined(__VMS ) || defined(__sgi) 172 | 173 | /* 174 | * Using 175 | */ 176 | #include 177 | typedef int32_t khronos_int32_t; 178 | typedef uint32_t khronos_uint32_t; 179 | typedef int64_t khronos_int64_t; 180 | typedef uint64_t khronos_uint64_t; 181 | #define KHRONOS_SUPPORT_INT64 1 182 | #define KHRONOS_SUPPORT_FLOAT 1 183 | 184 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 185 | 186 | /* 187 | * Win32 188 | */ 189 | typedef __int32 khronos_int32_t; 190 | typedef unsigned __int32 khronos_uint32_t; 191 | typedef __int64 khronos_int64_t; 192 | typedef unsigned __int64 khronos_uint64_t; 193 | #define KHRONOS_SUPPORT_INT64 1 194 | #define KHRONOS_SUPPORT_FLOAT 1 195 | 196 | #elif defined(__sun__) || defined(__digital__) 197 | 198 | /* 199 | * Sun or Digital 200 | */ 201 | typedef int khronos_int32_t; 202 | typedef unsigned int khronos_uint32_t; 203 | #if defined(__arch64__) || defined(_LP64) 204 | typedef long int khronos_int64_t; 205 | typedef unsigned long int khronos_uint64_t; 206 | #else 207 | typedef long long int khronos_int64_t; 208 | typedef unsigned long long int khronos_uint64_t; 209 | #endif /* __arch64__ */ 210 | #define KHRONOS_SUPPORT_INT64 1 211 | #define KHRONOS_SUPPORT_FLOAT 1 212 | 213 | #elif 0 214 | 215 | /* 216 | * Hypothetical platform with no float or int64 support 217 | */ 218 | typedef int khronos_int32_t; 219 | typedef unsigned int khronos_uint32_t; 220 | #define KHRONOS_SUPPORT_INT64 0 221 | #define KHRONOS_SUPPORT_FLOAT 0 222 | 223 | #else 224 | 225 | /* 226 | * Generic fallback 227 | */ 228 | #include 229 | typedef int32_t khronos_int32_t; 230 | typedef uint32_t khronos_uint32_t; 231 | typedef int64_t khronos_int64_t; 232 | typedef uint64_t khronos_uint64_t; 233 | #define KHRONOS_SUPPORT_INT64 1 234 | #define KHRONOS_SUPPORT_FLOAT 1 235 | 236 | #endif 237 | 238 | 239 | /* 240 | * Types that are (so far) the same on all platforms 241 | */ 242 | typedef signed char khronos_int8_t; 243 | typedef unsigned char khronos_uint8_t; 244 | typedef signed short int khronos_int16_t; 245 | typedef unsigned short int khronos_uint16_t; 246 | 247 | /* 248 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 249 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 250 | * to be the only LLP64 architecture in current use. 251 | */ 252 | #ifdef KHRONOS_USE_INTPTR_T 253 | typedef intptr_t khronos_intptr_t; 254 | typedef uintptr_t khronos_uintptr_t; 255 | #elif defined(_WIN64) 256 | typedef signed long long int khronos_intptr_t; 257 | typedef unsigned long long int khronos_uintptr_t; 258 | #else 259 | typedef signed long int khronos_intptr_t; 260 | typedef unsigned long int khronos_uintptr_t; 261 | #endif 262 | 263 | #if defined(_WIN64) 264 | typedef signed long long int khronos_ssize_t; 265 | typedef unsigned long long int khronos_usize_t; 266 | #else 267 | typedef signed long int khronos_ssize_t; 268 | typedef unsigned long int khronos_usize_t; 269 | #endif 270 | 271 | #if KHRONOS_SUPPORT_FLOAT 272 | /* 273 | * Float type 274 | */ 275 | typedef float khronos_float_t; 276 | #endif 277 | 278 | #if KHRONOS_SUPPORT_INT64 279 | /* Time types 280 | * 281 | * These types can be used to represent a time interval in nanoseconds or 282 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 283 | * of nanoseconds since some arbitrary system event (e.g. since the last 284 | * time the system booted). The Unadjusted System Time is an unsigned 285 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 286 | * may be either signed or unsigned. 287 | */ 288 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 289 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 290 | #endif 291 | 292 | /* 293 | * Dummy value used to pad enum types to 32 bits. 294 | */ 295 | #ifndef KHRONOS_MAX_ENUM 296 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 297 | #endif 298 | 299 | /* 300 | * Enumerated boolean type 301 | * 302 | * Values other than zero should be considered to be true. Therefore 303 | * comparisons should not be made against KHRONOS_TRUE. 304 | */ 305 | typedef enum { 306 | KHRONOS_FALSE = 0, 307 | KHRONOS_TRUE = 1, 308 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 309 | } khronos_boolean_enum_t; 310 | 311 | #endif /* __khrplatform_h_ */ 312 | -------------------------------------------------------------------------------- /include/MLX42/MLX42_Int.h: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* MLX42_Int.h :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2021/12/27 23:55:34 by W2Wizard #+# #+# */ 9 | /* Updated: 2022/07/21 10:46:43 by sbos ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #ifndef MLX42_INT_H 14 | # define MLX42_INT_H 15 | # define LODEPNG_NO_COMPILE_ALLOCATORS 16 | # include "MLX42/MLX42.h" 17 | # include "lodepng/lodepng.h" 18 | # include "glad/glad.h" 19 | # include "KHR/khrplatform.h" 20 | # if defined(__APPLE__) 21 | # define GL_SILENCE_DEPRECATION 22 | # endif 23 | # include 24 | # include 25 | # include 26 | # include 27 | # if defined(__linux__) 28 | # include 29 | # else 30 | # include 31 | # endif 32 | # include /* isspace, isprint, ... */ 33 | # include /* strlen, memmove, ... */ 34 | # include /* va_arg, va_end, ... */ 35 | # include /* assert, static_assert, ... */ 36 | # ifndef MLX_SWAP_INTERVAL 37 | # define MLX_SWAP_INTERVAL 1 38 | # endif 39 | # ifndef MLX_BATCH_SIZE 40 | # define MLX_BATCH_SIZE 12000 41 | # endif 42 | # define BPP sizeof(int32_t) /* Only support RGBA */ 43 | # define GETLINE_BUFF 1280 44 | # define MLX_MAX_STRING 512 /* Arbitrary string limit */ 45 | # define MLX_ASSERT(cond, msg) assert(cond && msg); 46 | # define MLX_NONNULL(var) MLX_ASSERT(var, "Value can't be null"); /* Assert instead of attribute */ 47 | 48 | /** 49 | * The shader code is extracted from the shader files 50 | * and converted to a .c file as a single string at 51 | * compile time. This keeps shader files external but 52 | * still integrated into the program letting you use 53 | * the executable anywhere without having to take the 54 | * shaders with you. 55 | * 56 | * Most modern frameworks like .NET do this by having resource files 57 | * instead. 58 | * 59 | * See: https://bit.ly/3LJYG0r 60 | */ 61 | 62 | extern const char* vert_shader; 63 | extern const char* frag_shader; 64 | 65 | // Flag to indicate if the render queue has to be sorted. 66 | extern bool sort_queue; 67 | 68 | // Settings array, use the enum 'key' to get the value. 69 | extern int32_t mlx_settings[MLX_SETTINGS_MAX]; 70 | 71 | //= Types =// 72 | 73 | // A single vertex, identical to the layout in the shader. 74 | typedef struct vertex 75 | { 76 | float x; 77 | float y; 78 | float z; 79 | float u; 80 | float v; 81 | int8_t tex; 82 | } vertex_t; 83 | 84 | // Layout for linked list. 85 | typedef struct mlx_list 86 | { 87 | void* content; 88 | struct mlx_list* next; 89 | struct mlx_list* prev; 90 | } mlx_list_t; 91 | 92 | //= Hook structs =// 93 | /** 94 | * There are 2 types of hooks, special and generics. 95 | * 96 | * Specials: Specials are specific callback functions to a specific action 97 | * such as window resizing or key presses. These are attached to the 98 | * callbacks of glfw. In case MLX itself needs the callback we call 99 | * the specials in that callback since there can only ever be a single 100 | * callback. 101 | * 102 | * Generics: Generics are MLX42 specific hooks and can have multiple 103 | * hooks at the same time, these are executed every frame and can be 104 | * used as an alternative for keypresses or animations for instance. 105 | * 106 | * NOTE: Hooks could be achieved with va_args to have any amount 107 | * of args sized functor but we can't/don't want to let the user 108 | * deal with va_args and having to look up what args are what, etc... 109 | * 110 | * We want to keep it straightforward with functors already describing 111 | * what params they have. 112 | */ 113 | 114 | typedef struct mlx_srcoll 115 | { 116 | void* param; 117 | mlx_scrollfunc func; 118 | } mlx_scroll_t; 119 | 120 | typedef struct mlx_mouse 121 | { 122 | void* param; 123 | mlx_mousefunc func; 124 | } mlx_mouse_t; 125 | 126 | typedef struct mlx_cursor 127 | { 128 | void* param; 129 | mlx_cursorfunc func; 130 | } mlx_cursor_t; 131 | 132 | typedef struct mlx_close 133 | { 134 | void* param; 135 | mlx_closefunc func; 136 | } mlx_close_t; 137 | 138 | typedef struct mlx_resize 139 | { 140 | void* param; 141 | mlx_resizefunc func; 142 | } mlx_resize_t; 143 | 144 | typedef struct mlx_key 145 | { 146 | void* param; 147 | mlx_keyfunc func; 148 | } mlx_key_t; 149 | 150 | typedef struct mlx_hook 151 | { 152 | void* param; 153 | void (*func)(void*); 154 | } mlx_hook_t; 155 | 156 | //= Rendering =// 157 | /** 158 | * For rendering we need to store most of OpenGL's stuff 159 | * such as the vertex array object, vertex buffer object & 160 | * the shader program as well as hooks and the zdepth level. 161 | * 162 | * Additionally we represent draw calls with a linked list 163 | * queue that points to the image and the index of its instance. 164 | * Again, instances only carry XYZ data, so coupled with the image it 165 | * lets us know where to draw a copy of the image. 166 | * 167 | * Texture contexts are kept in a struct alongside the capacity 168 | * of the array of instances, since the array is realloced like a vector. 169 | */ 170 | 171 | // MLX instance context. 172 | typedef struct mlx_ctx 173 | { 174 | GLuint vao; 175 | GLuint vbo; 176 | GLuint shaderprogram; 177 | 178 | int32_t initialWidth; 179 | int32_t initialHeight; 180 | 181 | mlx_list_t* hooks; 182 | mlx_list_t* images; 183 | mlx_list_t* render_queue; 184 | 185 | mlx_scroll_t scroll_hook; 186 | mlx_mouse_t mouse_hook; 187 | mlx_cursor_t cursor_hook; 188 | mlx_key_t key_hook; 189 | mlx_resize_t resize_hook; 190 | mlx_close_t close_hook; 191 | 192 | int32_t zdepth; 193 | int32_t bound_textures[16]; 194 | int32_t batch_size; 195 | vertex_t batch_vertices[MLX_BATCH_SIZE]; 196 | } mlx_ctx_t; 197 | 198 | // Draw call queue entry. 199 | typedef struct draw_queue 200 | { 201 | mlx_image_t* image; 202 | int32_t instanceid; 203 | } draw_queue_t; 204 | 205 | // Image context. 206 | typedef struct mlx_image_ctx 207 | { 208 | GLuint texture; 209 | size_t instances_capacity; 210 | } mlx_image_ctx_t; 211 | 212 | //= Functions =// 213 | /** 214 | * All sorts of internal functions shared in the library that 215 | * should not be accessible to the user! No touch! 216 | */ 217 | 218 | //= Linked List Functions =// 219 | 220 | mlx_list_t* mlx_lstnew(void* content); 221 | mlx_list_t* mlx_lstlast(mlx_list_t* lst); 222 | int32_t mlx_lstsize(mlx_list_t* lst); 223 | void mlx_lstclear(mlx_list_t** lst, void (*del)(void*)); 224 | void mlx_lstadd_back(mlx_list_t** lst, mlx_list_t* new); 225 | void mlx_lstadd_front(mlx_list_t** lst, mlx_list_t* new); 226 | mlx_list_t* mlx_lstremove(mlx_list_t** lst, void* value, bool (*comp)(void*, void*)); 227 | void mlx_sort_renderqueue(mlx_list_t** lst); 228 | 229 | //= Misc functions =// 230 | 231 | bool mlx_equal_image(void* lstcontent, void* value); 232 | bool mlx_equal_inst(void* lstcontent, void* value); 233 | void mlx_draw_pixel(uint8_t* pixel, uint32_t color); 234 | 235 | //= Error/log Handling Functions =// 236 | 237 | bool mlx_error(mlx_errno_t val); 238 | bool mlx_freen(int32_t count, ...); 239 | 240 | //= OpenGL Functions =// 241 | 242 | void mlx_update_matrix(const mlx_t* mlx); 243 | void mlx_draw_instance(mlx_ctx_t* mlx, mlx_image_t* img, mlx_instance_t* instance); 244 | void mlx_flush_batch(mlx_ctx_t* mlx); 245 | 246 | // Utils Functions =// 247 | 248 | bool mlx_getline(char** out, size_t* out_size, FILE* file); 249 | uint32_t mlx_rgba_to_mono(uint32_t color); 250 | int32_t mlx_atoi_base(const char* str, int32_t base); 251 | uint64_t mlx_fnv_hash(char* str, size_t len); 252 | #endif 253 | -------------------------------------------------------------------------------- /shaders/default.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec2 TexCoord; 4 | flat in int TexIndex; 5 | 6 | out vec4 FragColor; 7 | 8 | uniform sampler2D Texture0; 9 | uniform sampler2D Texture1; 10 | uniform sampler2D Texture2; 11 | uniform sampler2D Texture3; 12 | uniform sampler2D Texture4; 13 | uniform sampler2D Texture5; 14 | uniform sampler2D Texture6; 15 | uniform sampler2D Texture7; 16 | uniform sampler2D Texture8; 17 | uniform sampler2D Texture9; 18 | uniform sampler2D Texture10; 19 | uniform sampler2D Texture11; 20 | uniform sampler2D Texture12; 21 | uniform sampler2D Texture13; 22 | uniform sampler2D Texture14; 23 | uniform sampler2D Texture15; 24 | 25 | void main() 26 | { 27 | vec4 outColor = vec4(1.0, 0.0, 0.0, 1.0); 28 | switch (int(TexIndex)) { 29 | case 0: outColor = texture(Texture0, TexCoord); break; 30 | case 1: outColor = texture(Texture1, TexCoord); break; 31 | case 2: outColor = texture(Texture2, TexCoord); break; 32 | case 3: outColor = texture(Texture3, TexCoord); break; 33 | case 4: outColor = texture(Texture4, TexCoord); break; 34 | case 5: outColor = texture(Texture5, TexCoord); break; 35 | case 6: outColor = texture(Texture6, TexCoord); break; 36 | case 7: outColor = texture(Texture7, TexCoord); break; 37 | case 8: outColor = texture(Texture8, TexCoord); break; 38 | case 9: outColor = texture(Texture9, TexCoord); break; 39 | case 10: outColor = texture(Texture10, TexCoord); break; 40 | case 11: outColor = texture(Texture11, TexCoord); break; 41 | case 12: outColor = texture(Texture12, TexCoord); break; 42 | case 13: outColor = texture(Texture13, TexCoord); break; 43 | case 14: outColor = texture(Texture14, TexCoord); break; 44 | case 15: outColor = texture(Texture15, TexCoord); break; 45 | default: outColor = vec4(1.0, 0.0, 0.0, 1.0); break; 46 | } 47 | FragColor = outColor; 48 | } 49 | -------------------------------------------------------------------------------- /shaders/default.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | layout(location = 0) in vec3 aPos; 4 | layout(location = 1) in vec2 aTexCoord; 5 | layout(location = 2) in int aTexIndex; 6 | 7 | out vec2 TexCoord; 8 | flat out int TexIndex; 9 | 10 | uniform mat4 ProjMatrix; 11 | 12 | void main() 13 | { 14 | gl_Position = ProjMatrix * vec4(aPos, 1.0); 15 | TexCoord = aTexCoord; 16 | TexIndex = aTexIndex; 17 | } 18 | -------------------------------------------------------------------------------- /src/font/mlx_font.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_font.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2022/02/22 12:01:37 by W2Wizard #+# #+# */ 9 | /* Updated: 2022/06/27 19:53:36 by lde-la-h ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "font.h" 14 | #include "MLX42/MLX42_Int.h" 15 | 16 | //= Private =// 17 | 18 | /** 19 | * Does the actual copying of pixels form the atlas buffer to the 20 | * image buffer. 21 | * 22 | * Skips any non-printable characters. 23 | * 24 | * @param image The image to draw on. 25 | * @param texture The font_atlas. 26 | * @param texoffset The character texture X offset. 27 | * @param imgoffset The image X offset. 28 | */ 29 | static void mlx_draw_char(mlx_image_t* image, int32_t texoffset, int32_t imgoffset) 30 | { 31 | if (texoffset < 0) 32 | return; 33 | 34 | char* pixelx; 35 | uint8_t* pixeli; 36 | for (uint32_t y = 0; y < FONT_HEIGHT; y++) 37 | { 38 | pixelx = &font_atlas.pixels[(y * font_atlas.width + texoffset) * BPP]; 39 | pixeli = image->pixels + ((y * image->width + imgoffset) * BPP); 40 | memcpy(pixeli, pixelx, FONT_WIDTH * BPP); 41 | } 42 | } 43 | 44 | //= Public =// 45 | 46 | const mlx_texture_t* mlx_get_font(void) 47 | { 48 | return ((const mlx_texture_t*)&font_atlas); 49 | } 50 | 51 | int32_t mlx_get_texoffset(char c) 52 | { 53 | const bool _isprint = isprint(c); 54 | 55 | // NOTE: Cheesy branchless operation :D 56 | // +2 To skip line separator in texture 57 | return (-1 * !_isprint + ((FONT_WIDTH + 2) * (c - 32)) * _isprint); 58 | } 59 | 60 | mlx_image_t* mlx_put_string(mlx_t* mlx, const char* str, int32_t x, int32_t y) 61 | { 62 | MLX_NONNULL(mlx); 63 | MLX_NONNULL(str); 64 | 65 | mlx_image_t* strimage; 66 | const size_t len = strlen(str); 67 | if (len > MLX_MAX_STRING) 68 | return ((void*)mlx_error(MLX_STRTOOBIG)); 69 | if (!(strimage = mlx_new_image(mlx, len * FONT_WIDTH, FONT_HEIGHT))) 70 | return (NULL); 71 | 72 | // Draw the text itself 73 | int32_t imgoffset = 0; 74 | for (size_t i = 0; i < len; i++, imgoffset += FONT_WIDTH) 75 | mlx_draw_char(strimage, mlx_get_texoffset(str[i]), imgoffset); 76 | 77 | if (mlx_image_to_window(mlx, strimage, x, y) == -1) 78 | return (mlx_delete_image(mlx, strimage), NULL); 79 | return (strimage); 80 | } 81 | -------------------------------------------------------------------------------- /src/mlx_cursor.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_cursor.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2022/01/18 20:10:54 by W2Wizard #+# #+# */ 9 | /* Updated: 2023/03/09 11:11:45 by W2Wizard ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Public =// 16 | 17 | mlx_win_cursor_t* mlx_create_std_cursor(cursor_t type) 18 | { 19 | MLX_ASSERT(type >= MLX_CURSOR_ARROW && type < MLX_CURSOR_VRESIZE, "Invalid standard cursor type"); 20 | 21 | GLFWcursor* cursor; 22 | if ((cursor = glfwCreateStandardCursor(type))) 23 | return (cursor); 24 | return ((void *)mlx_error(MLX_MEMFAIL)); 25 | } 26 | 27 | mlx_win_cursor_t* mlx_create_cursor(mlx_texture_t* texture) 28 | { 29 | MLX_NONNULL(texture); 30 | 31 | GLFWcursor* cursor; 32 | GLFWimage image = (GLFWimage) { 33 | .width = texture->width, 34 | .height = texture->height, 35 | .pixels = texture->pixels 36 | }; 37 | 38 | if ((cursor = glfwCreateCursor(&image, 0, 0))) 39 | return (cursor); 40 | return ((void *)mlx_error(MLX_MEMFAIL)); 41 | } 42 | 43 | void mlx_destroy_cursor(mlx_win_cursor_t* cursor) 44 | { 45 | MLX_NONNULL(cursor); 46 | 47 | glfwDestroyCursor(cursor); 48 | } 49 | 50 | void mlx_set_cursor(mlx_t* mlx, mlx_win_cursor_t* cursor) 51 | { 52 | MLX_NONNULL(mlx); 53 | MLX_NONNULL(cursor); 54 | 55 | glfwSetCursor(mlx->window, cursor); 56 | } 57 | 58 | void mlx_set_cursor_mode(mlx_t* mlx, mouse_mode_t mode) 59 | { 60 | MLX_NONNULL(mlx); 61 | 62 | glfwSetInputMode(mlx->window, GLFW_CURSOR, mode); 63 | } 64 | -------------------------------------------------------------------------------- /src/mlx_exit.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_exit.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2021/12/28 02:43:22 by W2Wizard #+# #+# */ 9 | /* Updated: 2023/06/08 18:12:20 by XEDGit ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | static void mlx_free_image(void* content) 18 | { 19 | mlx_image_t* img = content; 20 | 21 | mlx_freen(4, img->context, img->pixels, img->instances, img); 22 | } 23 | 24 | //= Public =// 25 | 26 | void mlx_close_window(mlx_t* mlx) 27 | { 28 | MLX_NONNULL(mlx); 29 | glfwSetWindowShouldClose(mlx->window, true); 30 | } 31 | 32 | /** 33 | * All of glfw & glads resources are cleaned up by the terminate function. 34 | * Now it's time to clean up our own mess. 35 | */ 36 | void mlx_terminate(mlx_t* mlx) 37 | { 38 | MLX_NONNULL(mlx); 39 | 40 | mlx_ctx_t *const mlxctx = mlx->context; 41 | 42 | glUseProgram(0); 43 | glLinkProgram(mlxctx->shaderprogram); 44 | glDeleteProgram(mlxctx->shaderprogram); 45 | glfwTerminate(); 46 | mlx_lstclear((mlx_list_t**)(&mlxctx->hooks), &free); 47 | mlx_lstclear((mlx_list_t**)(&mlxctx->render_queue), &free); 48 | mlx_lstclear((mlx_list_t**)(&mlxctx->images), &mlx_free_image); 49 | mlx_freen(2, mlxctx, mlx); 50 | } 51 | -------------------------------------------------------------------------------- /src/mlx_images.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_images.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2021/12/28 02:29:06 by W2Wizard #+# #+# */ 9 | /* Updated: 2023/03/30 16:36:39 by ntamayo- ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | void mlx_flush_batch(mlx_ctx_t* mlx) 18 | { 19 | if (mlx->batch_size <= 0) 20 | return; 21 | 22 | glBindBuffer(GL_ARRAY_BUFFER, mlx->vbo); 23 | glBufferData(GL_ARRAY_BUFFER, mlx->batch_size * sizeof(vertex_t), mlx->batch_vertices, GL_STATIC_DRAW); 24 | glDrawArrays(GL_TRIANGLES, 0, mlx->batch_size); 25 | 26 | mlx->batch_size = 0; 27 | memset(mlx->bound_textures, 0, sizeof(mlx->bound_textures)); 28 | } 29 | 30 | static int8_t mlx_bind_texture(mlx_ctx_t* mlx, mlx_image_t* img) 31 | { 32 | const GLint handle = (GLint)((mlx_image_ctx_t*)img->context)->texture; 33 | 34 | // Attempt to bind the texture, or obtain the index if it is already bound. 35 | for (int8_t i = 0; i < 16; i++) 36 | { 37 | if (mlx->bound_textures[i] == handle) 38 | return (i); 39 | 40 | if (mlx->bound_textures[i] == 0) 41 | { 42 | mlx->bound_textures[i] = handle; 43 | 44 | glActiveTexture(GL_TEXTURE0 + i); 45 | glBindTexture(GL_TEXTURE_2D, handle); 46 | return (i); 47 | } 48 | } 49 | 50 | // If no free slot was found, flush the batch and assign the texture to the first available slot 51 | mlx_flush_batch(mlx); 52 | 53 | mlx->bound_textures[0] = handle; 54 | glActiveTexture(GL_TEXTURE0); 55 | glBindTexture(GL_TEXTURE_2D, handle); 56 | return (0); 57 | } 58 | 59 | /** 60 | * Internal function to draw a single instance of an image 61 | * to the screen. 62 | */ 63 | void mlx_draw_instance(mlx_ctx_t* mlx, mlx_image_t* img, mlx_instance_t* instance) 64 | { 65 | float w = (float) img->width; 66 | float h = (float) img->height; 67 | float x = (float) instance->x; 68 | float y = (float) instance->y; 69 | float z = (float) instance->z; 70 | int8_t tex = mlx_bind_texture(mlx, img); 71 | 72 | vertex_t vertices[6] = { 73 | (vertex_t){x, y, z, 0.f, 0.f, tex}, 74 | (vertex_t){x + w, y + h, z, 1.f, 1.f, tex}, 75 | (vertex_t){x + w, y, z, 1.f, 0.f, tex}, 76 | (vertex_t){x, y, z, 0.f, 0.f, tex}, 77 | (vertex_t){x, y + h, z, 0.f, 1.f, tex}, 78 | (vertex_t){x + w, y + h, z, 1.f, 1.f, tex}, 79 | }; 80 | memmove(mlx->batch_vertices + mlx->batch_size, vertices, sizeof(vertices)); 81 | mlx->batch_size += 6; 82 | 83 | if (mlx->batch_size >= MLX_BATCH_SIZE) 84 | mlx_flush_batch(mlx); 85 | } 86 | 87 | mlx_instance_t* mlx_grow_instances(mlx_image_t* img, bool* did_realloc) 88 | { 89 | mlx_image_ctx_t* const ctx = img->context; 90 | if (img->count >= ctx->instances_capacity) 91 | { 92 | if (ctx->instances_capacity == 0) 93 | ctx->instances_capacity = img->count; 94 | else 95 | ctx->instances_capacity *= 2; 96 | *did_realloc = true; 97 | return realloc(img->instances, ctx->instances_capacity * sizeof(mlx_instance_t)); 98 | } 99 | *did_realloc = false; 100 | return img->instances; 101 | } 102 | 103 | //= Public =// 104 | 105 | void mlx_set_instance_depth(mlx_instance_t* instance, int32_t zdepth) 106 | { 107 | MLX_NONNULL(instance); 108 | 109 | if (instance->z == zdepth) 110 | return; 111 | instance->z = zdepth; 112 | 113 | /** 114 | * NOTE: The reason why we don't sort directly is that 115 | * the user might call this function multiple times in a row and we don't 116 | * want to sort for every change. Pre-loop wise that is. 117 | */ 118 | sort_queue = true; 119 | } 120 | 121 | int32_t mlx_image_to_window(mlx_t* mlx, mlx_image_t* img, int32_t x, int32_t y) 122 | { 123 | MLX_NONNULL(mlx); 124 | MLX_NONNULL(img); 125 | 126 | // Allocate buffers... 127 | img->count++; 128 | bool did_realloc; 129 | mlx_instance_t* instances = mlx_grow_instances(img, &did_realloc); 130 | draw_queue_t* queue = calloc(1, sizeof(draw_queue_t)); 131 | if (!instances || !queue) 132 | { 133 | if (did_realloc) 134 | free(instances); 135 | return (free(queue), mlx_error(MLX_MEMFAIL), -1); 136 | } 137 | 138 | // Set data... 139 | queue->image = img; 140 | int32_t index = queue->instanceid = img->count - 1; 141 | img->instances = instances; 142 | img->instances[index].x = x; 143 | img->instances[index].y = y; 144 | 145 | // NOTE: We keep updating the Z for the convenience of the user. 146 | // Always update Z depth to prevent overlapping images by default. 147 | img->instances[index].z = ((mlx_ctx_t*)mlx->context)->zdepth++; 148 | img->instances[index].enabled = true; 149 | 150 | // Add draw call... 151 | sort_queue = true; 152 | mlx_list_t* templst; 153 | if ((templst = mlx_lstnew(queue))) 154 | { 155 | mlx_lstadd_front(&((mlx_ctx_t*)mlx->context)->render_queue, templst); 156 | return (index); 157 | } 158 | return (mlx_freen(2, instances, queue), mlx_error(MLX_MEMFAIL), -1); 159 | } 160 | 161 | mlx_image_t* mlx_new_image(mlx_t* mlx, uint32_t width, uint32_t height) 162 | { 163 | MLX_NONNULL(mlx); 164 | 165 | if (!width || !height || width > INT16_MAX || height > INT16_MAX) 166 | return ((void*)mlx_error(MLX_INVDIM)); 167 | 168 | const mlx_ctx_t* mlxctx = mlx->context; 169 | mlx_image_t* newimg = calloc(1, sizeof(mlx_image_t)); 170 | mlx_image_ctx_t* newctx = calloc(1, sizeof(mlx_image_ctx_t)); 171 | if (!newimg || !newctx) 172 | { 173 | mlx_freen(2, newimg, newctx); 174 | return ((void *)mlx_error(MLX_MEMFAIL)); 175 | } 176 | newimg->enabled = true; 177 | newimg->context = newctx; 178 | (*(uint32_t*)&newimg->width) = width; 179 | (*(uint32_t*)&newimg->height) = height; 180 | if (!(newimg->pixels = calloc(width * height, sizeof(int32_t)))) 181 | { 182 | mlx_freen(2, newimg, newctx); 183 | return ((void *)mlx_error(MLX_MEMFAIL)); 184 | } 185 | 186 | mlx_list_t* newentry; 187 | if (!(newentry = mlx_lstnew(newimg))) 188 | { 189 | mlx_freen(3, newimg->pixels, newimg->context, newimg); 190 | return ((void *)mlx_error(MLX_MEMFAIL)); 191 | } 192 | 193 | // Generate OpenGL texture 194 | glGenTextures(1, &newctx->texture); 195 | glBindTexture(GL_TEXTURE_2D, newctx->texture); 196 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 197 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 198 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 199 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 200 | mlx_lstadd_front((mlx_list_t**)(&mlxctx->images), newentry); 201 | return (newimg); 202 | } 203 | 204 | void mlx_delete_image(mlx_t* mlx, mlx_image_t* image) 205 | { 206 | MLX_NONNULL(mlx); 207 | MLX_NONNULL(image); 208 | 209 | mlx_ctx_t* mlxctx = mlx->context; 210 | 211 | // Delete all instances in the render queue 212 | mlx_list_t* quelst; 213 | while ((quelst = mlx_lstremove(&mlxctx->render_queue, image, &mlx_equal_inst))) 214 | mlx_freen(2, quelst->content, quelst); 215 | 216 | mlx_list_t* imglst; 217 | if ((imglst = mlx_lstremove(&mlxctx->images, image, &mlx_equal_image))) 218 | { 219 | glDeleteTextures(1, &((mlx_image_ctx_t*)image->context)->texture); 220 | mlx_freen(5, image->pixels, image->instances, image->context, imglst, image); 221 | } 222 | } 223 | 224 | bool mlx_resize_image(mlx_image_t* img, uint32_t nwidth, uint32_t nheight) 225 | { 226 | MLX_NONNULL(img); 227 | 228 | if (!nwidth || !nheight || nwidth > INT16_MAX || nheight > INT16_MAX) 229 | return (mlx_error(MLX_INVDIM)); 230 | if (nwidth != img->width || nheight != img->height) 231 | { 232 | uint32_t* origin = (uint32_t*)img->pixels; 233 | float wstep = (float)img->width / nwidth; 234 | float hstep = (float)img->height / nheight; 235 | 236 | uint8_t* tempbuff = calloc(nwidth * nheight, BPP); 237 | if (!tempbuff) 238 | return (mlx_error(MLX_MEMFAIL)); 239 | img->pixels = tempbuff; 240 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nwidth, nheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->pixels); 241 | 242 | uint32_t* destin = (uint32_t*)img->pixels; 243 | for (uint32_t j = 0; j < nheight; j++) 244 | for (uint32_t i = 0; i < nwidth; i++) 245 | destin[j * nwidth + i] = origin[(uint32_t)(j * hstep) * img->width + (uint32_t)(i * wstep)]; 246 | (*(uint32_t*)&img->width) = nwidth; 247 | (*(uint32_t*)&img->height) = nheight; 248 | free(origin); 249 | } 250 | return (true); 251 | } 252 | -------------------------------------------------------------------------------- /src/mlx_init.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_init.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2021/12/28 00:24:30 by W2Wizard #+# #+# */ 9 | /* Updated: 2023/06/08 18:16:19 by XEDGit ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | static void framebuffer_callback(GLFWwindow *window, int width, int height) 18 | { 19 | (void)window; 20 | glViewport(0, 0, width, height); 21 | } 22 | 23 | static bool mlx_create_buffers(mlx_t* mlx) 24 | { 25 | mlx_ctx_t* mlxctx = mlx->context; 26 | 27 | mlxctx->zdepth = 0; 28 | glActiveTexture(GL_TEXTURE0); 29 | glGenVertexArrays(1, &(mlxctx->vao)); 30 | glGenBuffers(1, &(mlxctx->vbo)); 31 | glBindVertexArray(mlxctx->vao); 32 | glBindBuffer(GL_ARRAY_BUFFER, mlxctx->vbo); 33 | 34 | // Vertex XYZ coordinates 35 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), NULL); 36 | glEnableVertexAttribArray(0); 37 | 38 | // UV Coordinates 39 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void *)(sizeof(float) * 3)); 40 | glEnableVertexAttribArray(1); 41 | 42 | // Texture index 43 | glVertexAttribIPointer(2, 1, GL_BYTE, sizeof(vertex_t), (void *)(sizeof(float) * 5)); 44 | glEnableVertexAttribArray(2); 45 | 46 | glEnable(GL_BLEND); 47 | glEnable(GL_DEPTH_TEST); 48 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 49 | 50 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture0"), 0); 51 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture1"), 1); 52 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture2"), 2); 53 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture3"), 3); 54 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture4"), 4); 55 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture5"), 5); 56 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture6"), 6); 57 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture7"), 7); 58 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture8"), 8); 59 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture9"), 9); 60 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture10"), 10); 61 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture11"), 11); 62 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture12"), 12); 63 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture13"), 13); 64 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture14"), 14); 65 | glUniform1i(glGetUniformLocation(mlxctx->shaderprogram, "Texture15"), 15); 66 | 67 | return (true); 68 | } 69 | 70 | /** 71 | * Compiles the given shader source code of a given shader type. 72 | * Returns shader object via param. 73 | * 74 | * @param code The shader source code. 75 | * @param Type GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, ... 76 | * @return Non-zero on success, else 0. 77 | */ 78 | static uint32_t mlx_compile_shader(const char* code, int32_t type) 79 | { 80 | GLuint shader; 81 | int32_t success; 82 | char infolog[512] = {0}; 83 | 84 | if (!code || (shader = glCreateShader(type)) == 0) 85 | return (0); 86 | 87 | GLint len = strlen(code); 88 | glShaderSource(shader, 1, &code, &len); 89 | glCompileShader(shader); 90 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 91 | if (!success) 92 | { 93 | glGetShaderInfoLog(shader, sizeof(infolog), NULL, infolog); 94 | fprintf(stderr, "%s", infolog); 95 | glDeleteShader(shader); 96 | return (0); 97 | } 98 | return (shader); 99 | } 100 | 101 | static bool mlx_init_render(mlx_t* mlx) 102 | { 103 | uint32_t vshader = 0; 104 | uint32_t fshader = 0; 105 | char infolog[512] = {0}; 106 | mlx_ctx_t* mlxctx = mlx->context; 107 | 108 | glfwMakeContextCurrent(mlx->window); 109 | glfwSetFramebufferSizeCallback(mlx->window, framebuffer_callback); 110 | glfwSetWindowUserPointer(mlx->window, mlx); 111 | glfwSwapInterval(MLX_SWAP_INTERVAL); 112 | 113 | // Load all OpenGL function pointers 114 | if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) 115 | return (mlx_error(MLX_GLADFAIL)); 116 | if (!(vshader = mlx_compile_shader(vert_shader, GL_VERTEX_SHADER))) 117 | return (mlx_error(MLX_VERTFAIL)); 118 | if (!(fshader = mlx_compile_shader(frag_shader, GL_FRAGMENT_SHADER))) 119 | return (mlx_error(MLX_FRAGFAIL));; 120 | if (!(mlxctx->shaderprogram = glCreateProgram())) 121 | { 122 | glDeleteShader(fshader); 123 | glDeleteShader(vshader); 124 | return (mlx_error(MLX_SHDRFAIL)); 125 | } 126 | glAttachShader(mlxctx->shaderprogram, vshader); 127 | glAttachShader(mlxctx->shaderprogram, fshader); 128 | glLinkProgram(mlxctx->shaderprogram); 129 | 130 | int32_t success; 131 | glGetProgramiv(mlxctx->shaderprogram, GL_LINK_STATUS, &success); 132 | if (!success) 133 | { 134 | glGetProgramInfoLog(mlxctx->shaderprogram, sizeof(infolog), NULL, infolog); 135 | fprintf(stderr, "%s", infolog); 136 | glDeleteProgram(mlxctx->shaderprogram); 137 | glDeleteShader(vshader); 138 | glDeleteShader(fshader); 139 | return (mlx_error(MLX_SHDRFAIL)); 140 | } 141 | 142 | // Detach shaders after linking but before deleting them 143 | glDetachShader(mlxctx->shaderprogram, vshader); 144 | glDetachShader(mlxctx->shaderprogram, fshader); 145 | 146 | // Delete shaders 147 | glDeleteShader(vshader); 148 | glDeleteShader(fshader); 149 | glUseProgram(mlxctx->shaderprogram); 150 | for (size_t i = 0; i < 16; i++) 151 | mlxctx->bound_textures[i] = 0; 152 | return (true); 153 | } 154 | 155 | //= Public =// 156 | 157 | // NOTE: https://www.glfw.org/docs/3.3/group__window.html 158 | 159 | // Default settings 160 | int32_t mlx_settings[MLX_SETTINGS_MAX] = {false, false, false, true, false}; 161 | mlx_errno_t mlx_errno = MLX_SUCCESS; 162 | bool sort_queue = false; 163 | 164 | mlx_t* mlx_init(int32_t width, int32_t height, const char* title, bool resize) 165 | { 166 | MLX_NONNULL(title); 167 | MLX_ASSERT(width > 0, "Window width must be positive"); 168 | MLX_ASSERT(height > 0, "Window height must be positive"); 169 | 170 | bool init; 171 | mlx_t* mlx; 172 | if (!(init = glfwInit())) 173 | return ((void*)mlx_error(MLX_GLFWFAIL)); 174 | if (!(mlx = calloc(1, sizeof(mlx_t)))) 175 | return ((void*)mlx_error(MLX_MEMFAIL)); 176 | if (!(mlx->context = calloc(1, sizeof(mlx_ctx_t)))) 177 | return (free(mlx), (void*)mlx_error(MLX_MEMFAIL)); 178 | 179 | mlx_ctx_t* const mlxctx = mlx->context; 180 | mlx->window = NULL; 181 | mlx->width = width; 182 | mlx->height = height; 183 | mlxctx->initialWidth = width; 184 | mlxctx->initialHeight = height; 185 | 186 | // NOTE(W2): For emscripten, this value will be ignored anyway. 187 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 188 | #ifdef EMSCRIPTEN 189 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); 190 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 191 | glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); 192 | glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); 193 | glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); 194 | #else 195 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 196 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 197 | glfwWindowHint(GLFW_MAXIMIZED, mlx_settings[MLX_MAXIMIZED]); 198 | glfwWindowHint(GLFW_DECORATED, mlx_settings[MLX_DECORATED]); 199 | glfwWindowHint(GLFW_VISIBLE, !mlx_settings[MLX_HEADLESS]); 200 | #endif 201 | #ifdef __APPLE__ 202 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 203 | #endif 204 | glfwWindowHint(GLFW_RESIZABLE, resize ? GLFW_TRUE : GLFW_FALSE); 205 | if (!(mlx->window = glfwCreateWindow(width, height, title, mlx_settings[MLX_FULLSCREEN] ? glfwGetPrimaryMonitor() : NULL, NULL))) 206 | return (glfwTerminate(), (void*)mlx_error(MLX_WINFAIL)); 207 | if (!mlx_init_render(mlx) || !mlx_create_buffers(mlx)) 208 | return (mlx_terminate(mlx), NULL); 209 | glfwMakeContextCurrent(mlx->window); 210 | return (mlx); 211 | } 212 | 213 | void mlx_set_setting(mlx_settings_t setting, int32_t value) 214 | { 215 | MLX_ASSERT(setting >= 0 && setting < MLX_SETTINGS_MAX, "Invalid settings value"); 216 | mlx_settings[setting] = value; 217 | } 218 | -------------------------------------------------------------------------------- /src/mlx_keys.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_keys.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2022/01/01 21:06:45 by W2Wizard #+# #+# */ 9 | /* Updated: 2023/02/13 12:24:40 by W2Wizard ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | static void mlx_key_callback(GLFWwindow* window, int32_t key, int32_t scancode, int32_t action, int32_t mods) 18 | { 19 | const mlx_t* mlx = glfwGetWindowUserPointer(window); 20 | const mlx_key_t key_hook = ((mlx_ctx_t*)mlx->context)->key_hook; 21 | const mlx_key_data_t callback_data = { 22 | key, 23 | action, 24 | scancode, 25 | mods, 26 | }; 27 | 28 | key_hook.func(callback_data, key_hook.param); 29 | } 30 | 31 | //= Public =// 32 | 33 | void mlx_key_hook(mlx_t* mlx, mlx_keyfunc func, void* param) 34 | { 35 | MLX_NONNULL(mlx); 36 | MLX_NONNULL(func); 37 | 38 | mlx_ctx_t* mlxctx = mlx->context; 39 | mlxctx->key_hook.func = func; 40 | mlxctx->key_hook.param = param; 41 | glfwSetKeyCallback(mlx->window, mlx_key_callback); 42 | } 43 | 44 | bool mlx_is_key_down(mlx_t* mlx, keys_t key) 45 | { 46 | MLX_NONNULL(mlx); 47 | 48 | return (glfwGetKey(mlx->window, key) == GLFW_PRESS); 49 | } 50 | -------------------------------------------------------------------------------- /src/mlx_loop.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_loop.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2021/12/28 01:24:36 by W2Wizard #+# #+# */ 9 | /* Updated: 2023/03/28 16:34:17 by W2Wizard ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | static void mlx_exec_loop_hooks(mlx_t* mlx) 18 | { 19 | const mlx_ctx_t* mlxctx = mlx->context; 20 | 21 | mlx_list_t* lstcpy = mlxctx->hooks; 22 | while (lstcpy && !glfwWindowShouldClose(mlx->window)) 23 | { 24 | mlx_hook_t* hook = ((mlx_hook_t*)lstcpy->content); 25 | hook->func(hook->param); 26 | lstcpy = lstcpy->next; 27 | } 28 | } 29 | 30 | static void mlx_render_images(mlx_t* mlx) 31 | { 32 | mlx_ctx_t* mlxctx = mlx->context; 33 | mlx_list_t* imglst = mlxctx->images; 34 | 35 | if (sort_queue) 36 | { 37 | sort_queue = false; 38 | mlx_sort_renderqueue(&mlxctx->render_queue); 39 | } 40 | 41 | // Upload image textures to GPU 42 | while (imglst) 43 | { 44 | mlx_image_t* image; 45 | if (!(image = imglst->content)) { 46 | mlx_error(MLX_INVIMG); 47 | return; 48 | } 49 | 50 | glBindTexture(GL_TEXTURE_2D, ((mlx_image_ctx_t*)image->context)->texture); 51 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); 52 | imglst = imglst->next; 53 | } 54 | 55 | // Execute draw calls 56 | mlx_list_t* render_queue = mlxctx->render_queue; 57 | while (render_queue) 58 | { 59 | draw_queue_t* drawcall = render_queue->content; 60 | mlx_instance_t* instance = &drawcall->image->instances[drawcall->instanceid]; 61 | 62 | if (drawcall && drawcall->image->enabled && instance->enabled) 63 | mlx_draw_instance(mlx->context, drawcall->image, instance); 64 | render_queue = render_queue->next; 65 | } 66 | } 67 | 68 | //= Public =// 69 | 70 | bool mlx_loop_hook(mlx_t* mlx, void (*f)(void*), void* param) 71 | { 72 | MLX_NONNULL(mlx); 73 | MLX_NONNULL(f); 74 | 75 | mlx_hook_t* hook; 76 | if (!(hook = malloc(sizeof(mlx_hook_t)))) 77 | return (mlx_error(MLX_MEMFAIL)); 78 | 79 | mlx_list_t* lst; 80 | if (!(lst = mlx_lstnew(hook))) 81 | { 82 | free(hook); 83 | return (mlx_error(MLX_MEMFAIL)); 84 | } 85 | hook->func = f; 86 | hook->param = param; 87 | const mlx_ctx_t *mlxctx = mlx->context; 88 | mlx_lstadd_back((mlx_list_t**)(&mlxctx->hooks), lst); 89 | return (true); 90 | } 91 | 92 | // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 93 | /** 94 | * In Emscripten the lood is defined differently, there the this function 95 | * is passed to the while loop instead 96 | */ 97 | void mlx_loop(mlx_t* mlx) 98 | { 99 | MLX_NONNULL(mlx); 100 | 101 | #ifdef EMSCRIPTEN 102 | static double start, oldstart = 0; 103 | #else 104 | double start, oldstart = 0; 105 | while (!glfwWindowShouldClose(mlx->window)) 106 | { 107 | #endif 108 | start = glfwGetTime(); 109 | mlx->delta_time = start - oldstart; 110 | oldstart = start; 111 | 112 | glClearColor(0.2f, 0.2f, 0.2f, 1.0f); 113 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 114 | glfwGetWindowSize(mlx->window, &(mlx->width), &(mlx->height)); 115 | 116 | if ((mlx->width > 1 || mlx->height > 1)) 117 | mlx_update_matrix(mlx); 118 | 119 | mlx_exec_loop_hooks(mlx); 120 | mlx_render_images(mlx); 121 | mlx_flush_batch(mlx->context); 122 | 123 | glfwSwapBuffers(mlx->window); 124 | glfwPollEvents(); 125 | #ifndef EMSCRIPTEN 126 | } 127 | #endif 128 | } 129 | -------------------------------------------------------------------------------- /src/mlx_monitor.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_monitor.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2022/01/19 17:18:59 by W2Wizard #+# #+# */ 9 | /* Updated: 2022/06/27 20:02:38 by lde-la-h ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Public =// 16 | 17 | void mlx_get_monitor_size(int32_t index, int32_t* width, int32_t* height) 18 | { 19 | MLX_ASSERT(index >= 0, "Index out of bounds"); 20 | MLX_NONNULL(width); 21 | MLX_NONNULL(height); 22 | 23 | *width = 0; 24 | *height = 0; 25 | 26 | int32_t monitor_count; 27 | GLFWmonitor** monitors = glfwGetMonitors(&monitor_count); 28 | if (index > monitor_count || !monitors) 29 | return; 30 | 31 | const GLFWvidmode* vidmode; 32 | if ((vidmode = glfwGetVideoMode(monitors[index]))) 33 | { 34 | *width = vidmode->width; 35 | *height = vidmode->height; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/mlx_mouse.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_mouse.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2022/01/01 23:20:13 by W2Wizard #+# #+# */ 9 | /* Updated: 2022/06/29 15:34:25 by lde-la-h ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | static void mlx_scroll_cb(GLFWwindow* window, double xoffset, double yoffset) 18 | { 19 | const mlx_t* mlx = glfwGetWindowUserPointer(window); 20 | const mlx_scroll_t scroll_hook = ((mlx_ctx_t*)mlx->context)->scroll_hook; 21 | 22 | scroll_hook.func(xoffset, yoffset, scroll_hook.param); 23 | } 24 | 25 | static void mlx_mouse_cb(GLFWwindow* window, int32_t button, int32_t action, int32_t mods) 26 | { 27 | const mlx_t* mlx = glfwGetWindowUserPointer(window); 28 | const mlx_mouse_t mouse_hook = ((mlx_ctx_t*)mlx->context)->mouse_hook; 29 | 30 | mouse_hook.func(button, action, mods, mouse_hook.param); 31 | } 32 | 33 | static void mlx_cursor_cb(GLFWwindow* window, double xpos, double ypos) 34 | { 35 | const mlx_t* mlx = glfwGetWindowUserPointer(window); 36 | const mlx_cursor_t cursor_hook = ((mlx_ctx_t*)mlx->context)->cursor_hook; 37 | 38 | cursor_hook.func(xpos, ypos, cursor_hook.param); 39 | } 40 | 41 | //= Public =// 42 | 43 | void mlx_scroll_hook(mlx_t* mlx, mlx_scrollfunc func, void* param) 44 | { 45 | MLX_NONNULL(mlx); 46 | MLX_NONNULL(func); 47 | 48 | mlx_ctx_t* const mlxctx = mlx->context; 49 | mlxctx->scroll_hook.func = func; 50 | mlxctx->scroll_hook.param = param; 51 | glfwSetScrollCallback(mlx->window, mlx_scroll_cb); 52 | } 53 | 54 | void mlx_mouse_hook(mlx_t* mlx, mlx_mousefunc func, void* param) 55 | { 56 | MLX_NONNULL(mlx); 57 | MLX_NONNULL(func); 58 | 59 | mlx_ctx_t* const mlxctx = mlx->context; 60 | mlxctx->mouse_hook.func = func; 61 | mlxctx->mouse_hook.param = param; 62 | glfwSetMouseButtonCallback(mlx->window, mlx_mouse_cb); 63 | } 64 | 65 | void mlx_cursor_hook(mlx_t* mlx, mlx_cursorfunc func, void* param) 66 | { 67 | MLX_NONNULL(mlx); 68 | MLX_NONNULL(func); 69 | 70 | mlx_ctx_t* const mlxctx = mlx->context; 71 | mlxctx->cursor_hook.func = func; 72 | mlxctx->cursor_hook.param = param; 73 | glfwSetCursorPosCallback(mlx->window, mlx_cursor_cb); 74 | } 75 | 76 | bool mlx_is_mouse_down(mlx_t* mlx, mouse_key_t key) 77 | { 78 | MLX_NONNULL(mlx); 79 | 80 | return (glfwGetMouseButton(mlx->window, key) == GLFW_PRESS); 81 | } 82 | 83 | void mlx_set_mouse_pos(mlx_t* mlx, int32_t x, int32_t y) 84 | { 85 | MLX_NONNULL(mlx); 86 | 87 | glfwSetCursorPos(mlx->window, (double)x, (double)y); 88 | } 89 | 90 | void mlx_get_mouse_pos(mlx_t* mlx, int32_t* x, int32_t* y) 91 | { 92 | MLX_NONNULL(mlx); 93 | MLX_NONNULL(x); 94 | MLX_NONNULL(y); 95 | 96 | double xd, yd; 97 | glfwGetCursorPos(mlx->window, &xd, &yd); 98 | *x = (int32_t)xd; 99 | *y = (int32_t)yd; 100 | } 101 | -------------------------------------------------------------------------------- /src/mlx_put_pixel.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_put_pixel.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2021/12/28 03:30:13 by W2Wizard #+# #+# */ 9 | /* Updated: 2022/06/29 16:00:30 by lde-la-h ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | // BUG: Linux may experience a red hue instead due to endianness 16 | void mlx_draw_pixel(uint8_t* pixel, uint32_t color) 17 | { 18 | *(pixel++) = (uint8_t)(color >> 24); 19 | *(pixel++) = (uint8_t)(color >> 16); 20 | *(pixel++) = (uint8_t)(color >> 8); 21 | *(pixel++) = (uint8_t)(color & 0xFF); 22 | } 23 | 24 | //= Public =// 25 | 26 | void mlx_put_pixel(mlx_image_t* image, uint32_t x, uint32_t y, uint32_t color) 27 | { 28 | MLX_NONNULL(image); 29 | MLX_ASSERT(x < image->width, "Pixel is out of bounds"); 30 | MLX_ASSERT(y < image->height, "Pixel is out of bounds"); 31 | 32 | uint8_t* pixelstart = &image->pixels[(y * image->width + x) * BPP]; 33 | mlx_draw_pixel(pixelstart, color); 34 | } 35 | -------------------------------------------------------------------------------- /src/mlx_window.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_window.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2022/02/08 01:14:59 by W2wizard #+# #+# */ 9 | /* Updated: 2022/11/22 09:06:54 by jvan-hal ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | /** 18 | * Recalculate the view projection matrix, used by images for screen pos 19 | * Reference: https://bit.ly/3KuHOu1 (Matrix View Projection) 20 | */ 21 | void mlx_update_matrix(const mlx_t* mlx) 22 | { 23 | const mlx_ctx_t* mlxctx = mlx->context; 24 | const float depth = mlxctx->zdepth; 25 | 26 | /** 27 | * In case the setting to stretch the image is set, we maintain the width and height but not 28 | * the depth. 29 | */ 30 | const float width = mlx_settings[MLX_STRETCH_IMAGE] ? mlxctx->initialWidth : mlx->width; 31 | const float height = mlx_settings[MLX_STRETCH_IMAGE] ? mlxctx->initialHeight : mlx->height; 32 | 33 | const float matrix[16] = { 34 | 2.f / width, 0, 0, 0, 35 | 0, 2.f / -(height), 0, 0, 36 | 0, 0, -2.f / (depth - -depth), 0, 37 | -1, -(height / -height), 38 | -((depth + -depth) / (depth - -depth)), 1 39 | }; 40 | 41 | glUniformMatrix4fv(glGetUniformLocation(mlxctx->shaderprogram, "ProjMatrix"), 1, GL_FALSE, matrix); 42 | } 43 | 44 | static void mlx_resize_callback(GLFWwindow* window, int32_t width, int32_t height) 45 | { 46 | const mlx_t* mlx = glfwGetWindowUserPointer(window); 47 | const mlx_ctx_t* mlxctx = mlx->context; 48 | 49 | if (mlxctx->resize_hook.func) 50 | mlxctx->resize_hook.func(width, height, mlxctx->resize_hook.param); 51 | } 52 | 53 | static void mlx_close_callback(GLFWwindow* window) 54 | { 55 | const mlx_t* mlx = glfwGetWindowUserPointer(window); 56 | const mlx_close_t close_hook = ((mlx_ctx_t*)mlx->context)->close_hook; 57 | 58 | close_hook.func(close_hook.param); 59 | } 60 | 61 | //= Public =// 62 | 63 | void mlx_close_hook(mlx_t* mlx, mlx_closefunc func, void* param) 64 | { 65 | MLX_NONNULL(mlx); 66 | MLX_NONNULL(func); 67 | 68 | mlx_ctx_t* mlxctx = mlx->context; 69 | mlxctx->close_hook.func = func; 70 | mlxctx->close_hook.param = param; 71 | glfwSetWindowCloseCallback(mlx->window, mlx_close_callback); 72 | } 73 | 74 | void mlx_resize_hook(mlx_t* mlx, mlx_resizefunc func, void* param) 75 | { 76 | MLX_NONNULL(mlx); 77 | MLX_NONNULL(func); 78 | 79 | mlx_ctx_t* mlxctx = mlx->context; 80 | mlxctx->resize_hook.func = func; 81 | mlxctx->resize_hook.param = param; 82 | glfwSetWindowSizeCallback(mlx->window, mlx_resize_callback); 83 | } 84 | 85 | void mlx_set_icon(mlx_t* mlx, mlx_texture_t* image) 86 | { 87 | MLX_NONNULL(mlx); 88 | MLX_NONNULL(image); 89 | 90 | const GLFWimage icon = { 91 | .width = image->width, 92 | .height = image->height, 93 | .pixels = image->pixels 94 | }; 95 | 96 | glfwSetWindowIcon(mlx->window, 1, &icon); 97 | } 98 | 99 | void mlx_set_window_pos(mlx_t* mlx, int32_t xpos, int32_t ypos) 100 | { 101 | MLX_NONNULL(mlx); 102 | 103 | glfwSetWindowPos(mlx->window, xpos, ypos); 104 | } 105 | 106 | void mlx_get_window_pos(mlx_t* mlx, int32_t* xpos, int32_t* ypos) 107 | { 108 | MLX_NONNULL(mlx); 109 | MLX_NONNULL(xpos); 110 | MLX_NONNULL(ypos); 111 | 112 | glfwGetWindowPos(mlx->window, xpos, ypos); 113 | } 114 | 115 | void mlx_set_window_size(mlx_t* mlx, int32_t new_width, int32_t new_height) 116 | { 117 | MLX_NONNULL(mlx); 118 | 119 | mlx->width = new_width; 120 | mlx->height = new_height; 121 | glfwSetWindowSize(mlx->window, new_width, new_height); 122 | } 123 | 124 | void mlx_set_window_limit(mlx_t* mlx, int32_t min_w, int32_t min_h, int32_t max_w, int32_t max_h) 125 | { 126 | MLX_NONNULL(mlx); 127 | 128 | glfwSetWindowSizeLimits(mlx->window, min_w, min_h, max_w, max_h); 129 | } 130 | 131 | void mlx_set_window_title(mlx_t* mlx, const char* title) 132 | { 133 | MLX_NONNULL(mlx); 134 | MLX_NONNULL(title); 135 | 136 | glfwSetWindowTitle(mlx->window, title); 137 | } 138 | -------------------------------------------------------------------------------- /src/textures/mlx_png.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_png.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2022/02/16 23:11:29 by W2Wizard #+# #+# */ 9 | /* Updated: 2022/06/27 19:55:06 by lde-la-h ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Public =// 16 | 17 | mlx_texture_t* mlx_load_png(const char* path) 18 | { 19 | MLX_NONNULL(path); 20 | 21 | mlx_texture_t* image; 22 | if (!(image = malloc(sizeof(mlx_texture_t)))) 23 | return ((void*)mlx_error(MLX_MEMFAIL)); 24 | 25 | uint32_t error; 26 | image->bytes_per_pixel = BPP; 27 | if ((error = lodepng_decode32_file(&image->pixels, &image->width, &image->height, path))) 28 | { 29 | free(image); 30 | // Explicitly print error on purpose 31 | fprintf(stderr, "MLX42: LodePNG: %s\n", lodepng_error_text(error)); 32 | return ((void*)mlx_error(MLX_INVPNG)); 33 | } 34 | return (image); 35 | } 36 | -------------------------------------------------------------------------------- /src/textures/mlx_texture.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_texture.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2022/02/17 01:02:24 by W2Wizard #+# #+# */ 9 | /* Updated: 2023/03/09 11:03:47 by W2Wizard ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Public =// 16 | 17 | mlx_image_t* mlx_texture_to_image(mlx_t* mlx, mlx_texture_t* texture) 18 | { 19 | MLX_NONNULL(mlx); 20 | MLX_NONNULL(texture); 21 | 22 | mlx_image_t* image = mlx_new_image(mlx, texture->width, texture->height); 23 | if (image == NULL) 24 | return (NULL); 25 | 26 | uint8_t* pixelx; 27 | uint8_t* pixeli; 28 | for (uint32_t i = 0; i < texture->height; i++) 29 | { 30 | pixelx = &texture->pixels[(i * texture->width) * texture->bytes_per_pixel]; 31 | pixeli = &image->pixels[(i * image->width) * texture->bytes_per_pixel]; 32 | memmove(pixeli, pixelx, texture->width * texture->bytes_per_pixel); 33 | } 34 | return (image); 35 | } 36 | 37 | void mlx_delete_texture(mlx_texture_t* texture) 38 | { 39 | MLX_NONNULL(texture); 40 | 41 | mlx_freen(2, texture->pixels, texture); 42 | } 43 | -------------------------------------------------------------------------------- /src/textures/mlx_xpm42.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_xpm42.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2021/12/28 03:42:29 by W2Wizard #+# #+# */ 9 | /* Updated: 2022/06/27 19:58:33 by lde-la-h ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | /** 16 | * XPM is an obscure image format which can't seem to make up its mind 17 | * whether it wants to be written in C code or not. 18 | * 19 | * https://en.wikipedia.org/wiki/X_PixMap 20 | * 21 | * This might anger some but instead I decided to write my own 22 | * image format, very similar to XPM2, which seems to be the better 23 | * option between the 3 versions. The only difference is in the 24 | * header which carries the file type, width, height, color count 25 | * and finally color type aka 'c' for RGBA8 or 'm' for monochrome 26 | * output. 27 | * 28 | * The changes, in my opinion, very much simplify the XPM format 29 | * into something literally anybody can use without much guessing 30 | * as to what does what. 31 | * 32 | * Additionally with the C style format, the idea is that you simply include 33 | * it directly into the compilation of the program (since it's just C). 34 | * 35 | * As convenient as this is, I just find it hideous especially the XPM3 variant. 36 | * By sticking to the XPM style format, conversion should be very easy and 37 | * straightforward to this format however. 38 | */ 39 | 40 | //= Private =// 41 | 42 | /** 43 | * Parses HEX color channel e.g: "0F" 44 | * 45 | * @param channel The 2 character string to parse. 46 | * @return Int value of the channel. 47 | */ 48 | static uint8_t mlx_parse_hex_channel(char* channel) 49 | { 50 | char temp_chan[] = {channel[0], channel[1], '\0'}; 51 | return (strtol(temp_chan, NULL, 16)); 52 | } 53 | 54 | /** 55 | * Parses the XPM color value entry e.g: ".X #00FF00FF" 56 | * into the color table while also verifying the format. 57 | * 58 | * @param xpm The XPM. 59 | * @param line The line to parse. 60 | * @param ctable The color hash table. 61 | * @param s Size of the hash table 62 | * @return True or false depending on if it successfully parsed the line. 63 | */ 64 | static bool mlx_insert_xpm_entry(xpm_t* xpm, char* line, uint32_t* ctable, size_t s) 65 | { 66 | // NOTE: uintptr because windows likes to complain... 67 | // Verify the length of the Pixel string by checking backwards for the first 68 | // occurrence of a space and then check the distance by comparing with cpp. 69 | if (((uintptr_t)strrchr(line, ' ') - (uintptr_t)line) != (uint64_t)xpm->cpp) 70 | return (false); 71 | if (!isspace(line[xpm->cpp]) || line[xpm->cpp + 1] != '#' || !isalnum(line[xpm->cpp + 2])) 72 | return (false); 73 | 74 | uint32_t color = 0; 75 | size_t start_offset = xpm->cpp + 2; 76 | color |= mlx_parse_hex_channel(line + start_offset) << 24; 77 | color |= mlx_parse_hex_channel(line + start_offset + 2) << 16; 78 | color |= mlx_parse_hex_channel(line + start_offset + 4) << 8; 79 | color |= mlx_parse_hex_channel(line + start_offset + 6); 80 | 81 | int32_t index = mlx_fnv_hash(line, xpm->cpp) % s; 82 | ctable[index] = xpm->mode == 'm' ? mlx_rgba_to_mono(color) : color; 83 | return (true); 84 | } 85 | 86 | /** 87 | * Retrieves the pixel data line by line and then processes each pixel 88 | * by hashing the characters and looking it up from the color table. 89 | * 90 | * @param xpm The XPM. 91 | * @param file The filepath to the XPM42 file. 92 | * @param ctable The color hash table. 93 | * @param s Size of the hash table. 94 | * @return True or false depending on if it successfully parsed the line. 95 | */ 96 | static bool mlx_read_data(xpm_t* xpm, FILE* file, uint32_t* ctable, size_t s) 97 | { 98 | size_t line_len; 99 | char* line = NULL; 100 | 101 | for (int64_t y_xpm = 0; y_xpm < xpm->texture.height; y_xpm++) 102 | { 103 | if (!mlx_getline(&line, &line_len, file)) 104 | return (free(line), false); 105 | if (line[line_len - 1] == '\n') 106 | line_len--; 107 | if (line_len != xpm->texture.width * xpm->cpp) 108 | return (free(line), false); 109 | 110 | // NOTE: Copy pixel by pixel as we need to retrieve the hash table. 111 | for (int64_t x_xpm = 0, x_line = 0; x_xpm < xpm->texture.width; x_xpm++, x_line += xpm->cpp) 112 | { 113 | uint8_t* pixelstart = &xpm->texture.pixels[(y_xpm * xpm->texture.width + x_xpm) * BPP]; 114 | mlx_draw_pixel(pixelstart, ctable[mlx_fnv_hash(&line[x_line], xpm->cpp) % s]); 115 | } 116 | } 117 | free(line); 118 | return (true); 119 | } 120 | 121 | /** 122 | * For quick lookups we basically create a stack allocated lookup 123 | * table with every ascii character in it. This should help avoid a O(n) 124 | * case and give us a O(1) for very fast look ups. 125 | * 126 | * Downside is we still need to iterate over each pixel to solve its color. 127 | * So I hope this makes it at least a bit faster. 128 | * 129 | * TODO: This buffer might be way to big! Do actual collision checks, 130 | * for now just straight up raw dog this. 131 | */ 132 | static bool mlx_read_table(xpm_t* xpm, FILE* file) 133 | { 134 | char* line = NULL; 135 | size_t line_len; 136 | uint32_t ctable[UINT16_MAX] = {0}; 137 | 138 | for (int32_t i = 0; i < xpm->color_count; i++) 139 | { 140 | if (!mlx_getline(&line, &line_len, file)) 141 | return (free(line), false); 142 | if (!mlx_insert_xpm_entry(xpm, line, ctable, (sizeof(ctable) / BPP))) 143 | return (free(line), false); 144 | } 145 | free(line); 146 | return (mlx_read_data(xpm, file, ctable, (sizeof(ctable) / BPP))); 147 | } 148 | 149 | /** 150 | * Reads the XPM42 file header which usually consists of a 151 | * file type declaration of "!XPM42" followed by the next line 152 | * containing image information such as width, height, unique color 153 | * count and finally the color mode. Which is either c for Color or 154 | * m for Monochrome. 155 | */ 156 | static bool mlx_read_xpm_header(xpm_t* xpm, FILE *file) 157 | { 158 | int32_t flagc; 159 | char buffer[64] = {0}; 160 | 161 | // Check file type dec... 162 | if (!fgets(buffer, sizeof(buffer), file)) 163 | return (false); 164 | if (strncmp(buffer, "!XPM42\n", sizeof(buffer)) != 0) 165 | return (false); 166 | 167 | // Get header info ... 168 | if (!fgets(buffer, sizeof(buffer), file)) 169 | return (false); 170 | flagc = sscanf(buffer, "%i %i %i %i %c\n", &xpm->texture.width, &xpm->texture.height, &xpm->color_count, &xpm->cpp, &xpm->mode); 171 | if (flagc < 4 || xpm->texture.width > INT16_MAX || xpm->texture.height > INT16_MAX || \ 172 | !(xpm->mode == 'c' || xpm->mode == 'm') || xpm->cpp > 10) 173 | return (false); 174 | xpm->texture.bytes_per_pixel = BPP; 175 | xpm->texture.pixels = calloc(xpm->texture.width * xpm->texture.height, sizeof(int32_t)); 176 | return (xpm->texture.pixels != NULL ? mlx_read_table(xpm, file) : false); 177 | } 178 | 179 | //= Public =// 180 | 181 | xpm_t* mlx_load_xpm42(const char* path) 182 | { 183 | FILE* file; 184 | xpm_t* xpm = NULL; 185 | 186 | MLX_NONNULL(path); 187 | if (!strstr(path, ".xpm42")) 188 | return ((void*)mlx_error(MLX_INVEXT)); 189 | if (!(file = fopen(path, "r"))) 190 | return ((void*)mlx_error(MLX_INVFILE)); 191 | if (!(xpm = calloc(1, sizeof(xpm_t)))) 192 | return ((void*)mlx_error(MLX_MEMFAIL)); 193 | if (!mlx_read_xpm_header(xpm, file)) 194 | { 195 | mlx_freen(2, xpm->texture.pixels, xpm); 196 | mlx_error(MLX_INVXPM); 197 | xpm = NULL; 198 | } 199 | fclose(file); 200 | return (xpm); 201 | } 202 | 203 | void mlx_delete_xpm42(xpm_t* xpm) 204 | { 205 | MLX_NONNULL(xpm); 206 | free(xpm->texture.pixels); 207 | free(xpm); 208 | } 209 | -------------------------------------------------------------------------------- /src/utils/mlx_compare.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_comparison.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: jvan-hal +#+ */ 7 | /* +#+ */ 8 | /* Created: 2023/01/31 17:20:19 by jvan-hal #+# #+# */ 9 | /* Updated: 2023/01/31 17:23:49 by jvan-hal ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | bool mlx_equal_image(void* lstcontent, void* value) 18 | { 19 | const mlx_image_t* lcontent = lstcontent; 20 | const mlx_image_t* lvalue = value; 21 | 22 | return (lcontent == lvalue); 23 | } 24 | 25 | bool mlx_equal_inst(void* lstcontent, void* value) 26 | { 27 | const draw_queue_t* lcontent = lstcontent; 28 | const mlx_image_t* lvalue = value; 29 | 30 | return (lcontent->image == lvalue); 31 | } -------------------------------------------------------------------------------- /src/utils/mlx_error.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_error.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2021/12/28 02:51:54 by W2Wizard #+# #+# */ 9 | /* Updated: 2022/11/22 08:50:15 by jvan-hal ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | // English description of the error codes. 18 | static const char* mlx_errors[MLX_ERRMAX] = { 19 | "No Errors", 20 | "File has invalid extension", 21 | "Failed to open the file", 22 | "PNG file is invalid or corrupted", 23 | "XPM42 file is invalid or corrupted", 24 | "The specified X or Y positions are out of bounds", 25 | "The specified Width or Height dimensions are out of bounds", 26 | "The provided image is invalid, might indicate mismanagement of images", 27 | "Failed to compile the vertex shader.", 28 | "Failed to compile the fragment shader.", 29 | "Failed to compile the shaders.", 30 | "Failed to allocate memory", 31 | "Failed to initialize GLAD", 32 | "Failed to initialize GLFW", 33 | "Failed to create window", 34 | "String is too big to be drawn", 35 | }; 36 | 37 | /** 38 | * Functions to set the error number, simply for convenience. 39 | * 40 | * @param val The error value. 41 | * @return Always false 42 | */ 43 | bool mlx_error(mlx_errno_t val) 44 | { 45 | mlx_errno = val; 46 | #ifndef NDEBUG 47 | fprintf(stderr, "MLX42: %s", mlx_strerror(mlx_errno)); 48 | #endif 49 | return (false); 50 | } 51 | 52 | //= Public =// 53 | 54 | const char* mlx_strerror(mlx_errno_t val) 55 | { 56 | MLX_ASSERT(val >= 0, "Index must be positive"); 57 | MLX_ASSERT(val < MLX_ERRMAX, "Index out of bounds"); 58 | 59 | return (mlx_errors[val]); 60 | } 61 | -------------------------------------------------------------------------------- /src/utils/mlx_list.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_list.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2021/12/28 01:53:51 by W2Wizard #+# #+# */ 9 | /* Updated: 2023/02/27 11:31:01 by W2Wizard ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | int32_t mlx_lstsize(mlx_list_t* lst) 18 | { 19 | int32_t i; 20 | 21 | for (i = 0; lst != NULL; i++) 22 | lst = lst->next; 23 | return (i); 24 | } 25 | 26 | static void mlx_lstdelone(mlx_list_t* lst, void (*del)(void *)) 27 | { 28 | if (del != NULL) 29 | del(lst->content); 30 | free(lst); 31 | } 32 | 33 | void mlx_lstclear(mlx_list_t** lst, void (*del)(void*)) 34 | { 35 | mlx_list_t* next_lst; 36 | 37 | while (*lst != NULL) 38 | { 39 | next_lst = (*lst)->next; 40 | mlx_lstdelone(*lst, del); 41 | *lst = next_lst; 42 | } 43 | } 44 | 45 | mlx_list_t* mlx_lstnew(void* content) 46 | { 47 | mlx_list_t* out = NULL; 48 | 49 | if ((out = malloc(sizeof(mlx_list_t)))) 50 | { 51 | out->content = content; 52 | out->next = NULL; 53 | out->prev = NULL; 54 | } 55 | return (out); 56 | } 57 | 58 | mlx_list_t* mlx_lstlast(mlx_list_t* lst) 59 | { 60 | if (!lst) 61 | return (NULL); 62 | while (lst->next) 63 | lst = lst->next; 64 | return (lst); 65 | } 66 | 67 | void mlx_lstadd_back(mlx_list_t** lst, mlx_list_t* new) 68 | { 69 | if (!lst || !new) 70 | return; 71 | if (!*lst) 72 | *lst = new; 73 | else 74 | { 75 | mlx_list_t* temp = mlx_lstlast(*lst); 76 | new->prev = temp; 77 | temp->next = new; 78 | } 79 | } 80 | 81 | void mlx_lstadd_front(mlx_list_t** lst, mlx_list_t* new) 82 | { 83 | if (!lst || !new) 84 | return; 85 | if ((*lst) != NULL) 86 | (*lst)->prev = new; 87 | new->next = *lst; 88 | new->prev = NULL; 89 | *lst = new; 90 | } 91 | 92 | /** 93 | * Removes the specified content from the list, if found. 94 | * Also fixes any relinking that might be needed. 95 | * 96 | * @param[in] lst The list 97 | * @param[in] comp Function to check if the content and value are the same. 98 | * @returns The removed element, clean up as you wish. 99 | */ 100 | mlx_list_t* mlx_lstremove(mlx_list_t** lst, void* value, bool (*comp)(void*, void*)) 101 | { 102 | mlx_list_t* lstcpy = *lst; 103 | 104 | while (lstcpy && !comp(lstcpy->content, value)) 105 | lstcpy = lstcpy->next; 106 | if (lstcpy == NULL) 107 | return (NULL); 108 | if (lstcpy == *lst) 109 | *lst = lstcpy->next; 110 | if (lstcpy->next != NULL) 111 | lstcpy->next->prev = lstcpy->prev; 112 | if (lstcpy->prev != NULL) 113 | lstcpy->prev->next = lstcpy->next; 114 | return (lstcpy); 115 | } 116 | 117 | // Retrieve Z value from queue. 118 | static int32_t mlx_getzdata(mlx_list_t* entry) 119 | { 120 | const draw_queue_t* queue = entry->content; 121 | 122 | return (queue->image->instances[queue->instanceid].z); 123 | } 124 | 125 | // Insert the entry back into head sorted. 126 | static void mlx_insertsort(mlx_list_t** head, mlx_list_t* new) 127 | { 128 | mlx_list_t* current; 129 | 130 | if (*head == NULL) 131 | *head = new; 132 | else if (mlx_getzdata(*head) >= mlx_getzdata(new)) 133 | { 134 | new->next = *head; 135 | new->next->prev = new; 136 | *head = new; 137 | } 138 | else 139 | { 140 | current = *head; 141 | 142 | // Find insertion location. 143 | while (current->next != NULL && mlx_getzdata(current->next) < mlx_getzdata(new)) 144 | current = current->next; 145 | new->next = current->next; 146 | 147 | // Insert at the end 148 | if (current->next != NULL) 149 | new->next->prev = new; 150 | current->next = new; 151 | new->prev = current; 152 | } 153 | } 154 | 155 | /** 156 | * Okay-ish sorting algorithm to sort the render queue / doubly linked list. 157 | * We need to do this to fix transparency. 158 | * 159 | * @param lst The render queue. 160 | */ 161 | void mlx_sort_renderqueue(mlx_list_t** lst) 162 | { 163 | mlx_list_t* sorted = NULL; 164 | mlx_list_t* lstcpy = *lst; 165 | 166 | while (lstcpy != NULL) 167 | { 168 | mlx_list_t* next = lstcpy->next; 169 | 170 | // Separate entry out of list and insert it back but sorted. 171 | lstcpy->prev = lstcpy->next = NULL; 172 | mlx_insertsort(&sorted, lstcpy); 173 | lstcpy = next; 174 | } 175 | *lst = sorted; 176 | } 177 | -------------------------------------------------------------------------------- /src/utils/mlx_utils.c: -------------------------------------------------------------------------------- 1 | /* ************************************************************************** */ 2 | /* */ 3 | /* :::::::: */ 4 | /* mlx_utils.c :+: :+: */ 5 | /* +:+ */ 6 | /* By: W2Wizard +#+ */ 7 | /* +#+ */ 8 | /* Created: 2022/01/03 20:13:17 by W2Wizard #+# #+# */ 9 | /* Updated: 2022/11/22 10:56:09 by jvan-hal ######## odam.nl */ 10 | /* */ 11 | /* ************************************************************************** */ 12 | 13 | #include "MLX42/MLX42_Int.h" 14 | 15 | //= Private =// 16 | 17 | /** 18 | * Function to read a file stream line by line, reusing the same output pointer. 19 | * Since the same output pointer is reused it should only be freed once, either on success or failure. 20 | * This function is made to be somewhat similar to getline. 21 | * Getline can't be used directly since it's not standard and therefore not available on all platforms. 22 | * 23 | * @param out Pointer to store output string. 24 | * @param out_size Pointer to store output strings length. 25 | * @param file File stream to read from. 26 | * @return True if line was read, false if EOF was reached or an error occurred. 27 | */ 28 | bool mlx_getline(char** out, size_t* out_size, FILE* file) 29 | { 30 | MLX_NONNULL(out); 31 | MLX_NONNULL(out_size); 32 | MLX_NONNULL(file); 33 | 34 | size_t size = 0; 35 | char* temp = NULL; 36 | static char BUFF[GETLINE_BUFF + 1]; // Add space for '\0' 37 | 38 | if (*out) *out[0] = '\0'; 39 | 40 | while (fgets(BUFF, sizeof(BUFF), file)) 41 | { 42 | size += strlen(BUFF); 43 | if (!(temp = realloc(*out, sizeof(char) * size + 1))) 44 | return (false); 45 | if (*out == NULL) 46 | memset(temp, '\0', size); 47 | temp[size] = '\0'; 48 | 49 | *out = temp; 50 | *out_size = size; 51 | 52 | strncat(*out, BUFF, size); 53 | if (strrchr(BUFF, '\n')) 54 | return (true); 55 | memset(BUFF, '\0', sizeof(BUFF)); 56 | } 57 | return (size); 58 | } 59 | 60 | /** 61 | * String hashing algorithm using FNV-1a. 62 | * Source: https://bit.ly/3JcRGHa 63 | * 64 | * @param str The string to hash 65 | * @param len The length of the string. 66 | * @return The hashed output. 67 | */ 68 | uint64_t mlx_fnv_hash(char* str, size_t len) 69 | { 70 | const uint64_t fnv_prime = 0x100000001b3; 71 | const uint64_t fnv_offset = 0xcbf29ce484222325; 72 | uint64_t hash = fnv_offset; 73 | 74 | for (size_t i = 0; i < len; i++) 75 | { 76 | hash ^= str[i]; 77 | hash *= fnv_prime; 78 | } 79 | return (hash); 80 | } 81 | 82 | /** 83 | * Utility function that lets you free x amount of pointers. 84 | * 85 | * @param count The amount of args provided. 86 | * @param ... Any form of pointer. 87 | * @return False, this is simply for convenience when necessary. 88 | */ 89 | bool mlx_freen(int32_t count, ...) 90 | { 91 | va_list args; 92 | 93 | va_start(args, count); 94 | for (int32_t i = 0; i < count; i++) 95 | free(va_arg(args, void*)); 96 | va_end(args); 97 | return (false); 98 | } 99 | 100 | /** 101 | * Converts an RGBA value to a monochrome/grayscale value. 102 | * It does so using specific weights for each channel. 103 | * 104 | * @see https://goodcalculators.com/rgb-to-grayscale-conversion-calculator/ 105 | * 106 | * @param color The input RGBA value. 107 | * @return The rgba value converted to a grayscale color. 108 | */ 109 | uint32_t mlx_rgba_to_mono(uint32_t color) 110 | { 111 | const uint8_t r = 0.299f * ((color >> 24) & 0xFF); 112 | const uint8_t g = 0.587f * ((color >> 16) & 0xFF); 113 | const uint8_t b = 0.114f * ((color >> 8) & 0xFF); 114 | const uint8_t y = r + g + b; 115 | 116 | return (y << 24 | y << 16 | y << 8 | (color & 0xFF)); 117 | } 118 | 119 | //= Public =// 120 | 121 | double mlx_get_time(void) 122 | { 123 | return (glfwGetTime()); 124 | } 125 | 126 | void mlx_focus(mlx_t* mlx) 127 | { 128 | MLX_NONNULL(mlx); 129 | 130 | glfwFocusWindow(mlx->window); 131 | } 132 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Codam Coding College, Amsterdam @ 2022-2023 by Jelle van Kraaij. 3 | # See README in the root project for more information. 4 | # ----------------------------------------------------------------------------- 5 | 6 | set(TEST_EXECUTABLE_NAME unit_tests) 7 | 8 | # Download GoogleTest 9 | # ----------------------------------------------------------------------------- 10 | include(GoogleTest) 11 | include(FetchContent) 12 | 13 | FetchContent_Declare( 14 | googletest 15 | DOWNLOAD_EXTRACT_TIMESTAMP 16 | GIT_REPOSITORY https://github.com/google/googletest 17 | GIT_TAG v1.13.0 18 | ) 19 | 20 | FetchContent_MakeAvailable(googletest) 21 | 22 | # ----------------------------------------------------------------------------- 23 | add_executable( 24 | ${TEST_EXECUTABLE_NAME} 25 | tests.cpp 26 | ) 27 | 28 | target_link_libraries( 29 | ${TEST_EXECUTABLE_NAME} 30 | GTest::gtest_main 31 | mlx42 32 | ) 33 | 34 | set_property(TARGET ${TEST_EXECUTABLE_NAME} PROPERTY CXX_STANDARD 14) 35 | 36 | # Add tests to CTest 37 | # Set working directory to the testing folder so that the test can find their test files 38 | # ----------------------------------------------------------------------------- 39 | gtest_discover_tests(${TEST_EXECUTABLE_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} TEST_PREFIX "MLX42.") 40 | enable_testing() 41 | -------------------------------------------------------------------------------- /tests/WindowFixture.hpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Codam Coding College, Amsterdam @ 2022-2023 by Jelle van Kraaij. 3 | // See README in the root project for more information. 4 | // ----------------------------------------------------------------------------- 5 | 6 | # pragma once 7 | 8 | # include 9 | # include 10 | 11 | class Window : public ::testing::Test 12 | { 13 | protected: 14 | mlx_t* mlx = nullptr; 15 | 16 | static constexpr const char* name = "MLX42"; 17 | static const int32_t height = 400; 18 | static const int32_t width = 400; 19 | 20 | inline void SetUp() override 21 | { 22 | // reset error code as it is shared between tests 23 | mlx_errno = MLX_SUCCESS; 24 | mlx_set_setting(MLX_HEADLESS, true); 25 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 26 | mlx = mlx_init(width, height, name, false); 27 | ASSERT_NE(mlx, nullptr); 28 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 29 | } 30 | 31 | inline void TearDown() override 32 | { 33 | ASSERT_NE(mlx, nullptr); 34 | mlx_terminate(mlx); 35 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /tests/tests.cpp: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------------------------- 2 | // Codam Coding College, Amsterdam @ 2022-2023 by Jelle van Kraaij. 3 | // See README in the root project for more information. 4 | // ----------------------------------------------------------------------------- 5 | 6 | // If your new to gtest follow the following documentation: 7 | // http://google.github.io/googletest/primer.html 8 | 9 | #include "WindowFixture.hpp" 10 | 11 | // -------------------------------------------- 12 | // Fixture for window tests 13 | // For every TEST_F(window, ...) the SetUp() and TearDown() functions are called 14 | // MLX can be accessed via the mlx variable in each test 15 | // For the implementation of the fixture see tests/windowFixture.hpp 16 | // -------------------------------------------- 17 | 18 | TEST_F(Window, Basic) 19 | { 20 | // Basic window is already tested in the fixture 21 | } 22 | 23 | 24 | // NOTE: This test cannot be run with a fixture because the settings need to be set before the window is created 25 | TEST(MWindow, Settings) 26 | { 27 | mlx_errno = MLX_SUCCESS; 28 | mlx_set_setting(MLX_STRETCH_IMAGE, true); 29 | mlx_set_setting(MLX_MAXIMIZED, true); 30 | mlx_set_setting(MLX_DECORATED, true); 31 | mlx_set_setting(MLX_FULLSCREEN, true); 32 | 33 | mlx_set_setting(MLX_HEADLESS, true); 34 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 35 | mlx_t *mlx = mlx_init(400, 400, "MLX42", false); 36 | ASSERT_NE(mlx, nullptr); 37 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 38 | 39 | mlx_terminate(mlx); 40 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 41 | 42 | // Set all settings to default 43 | mlx_set_setting(MLX_STRETCH_IMAGE, false); 44 | mlx_set_setting(MLX_FULLSCREEN, false); 45 | mlx_set_setting(MLX_MAXIMIZED, false); 46 | mlx_set_setting(MLX_DECORATED, true); 47 | } 48 | 49 | TEST_F(Window, SingleImage) 50 | { 51 | mlx_image_t* img = mlx_new_image(mlx, Window::width / 2, Window::height / 2); 52 | ASSERT_NE(img, nullptr); 53 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 54 | 55 | int32_t val = mlx_image_to_window(mlx, img, Window::width / 4 , Window::height / 4); 56 | EXPECT_GE(val, 0); 57 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 58 | 59 | mlx_delete_image(mlx, img); 60 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 61 | } 62 | 63 | TEST_F(Window, MultipleImages) 64 | { 65 | mlx_image_t* img1 = mlx_new_image(mlx, Window::width / 2, Window::height / 2); 66 | ASSERT_NE(img1, nullptr); 67 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 68 | 69 | mlx_image_t* img2 = mlx_new_image(mlx, Window::width, Window::height); 70 | ASSERT_NE(img2, nullptr); 71 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 72 | 73 | int32_t val1 = mlx_image_to_window(mlx, img1, Window::width / 4, Window::height / 4); 74 | EXPECT_GE(val1, 0); 75 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 76 | 77 | int32_t val2 = mlx_image_to_window(mlx, img2, 0, 0); 78 | EXPECT_GE(val2, 0); 79 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 80 | 81 | mlx_delete_image(mlx, img1); 82 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 83 | 84 | mlx_delete_image(mlx, img2); 85 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 86 | } 87 | 88 | 89 | static void ft_draw(void* param) 90 | { 91 | static char buf[256]; 92 | static int32_t count = 0; 93 | static mlx_image_t* img = nullptr; 94 | mlx_t* mlx = (mlx_t*)param; 95 | 96 | if (img == nullptr) 97 | { 98 | mlx_delete_image(mlx, img); 99 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 100 | } 101 | 102 | // Cheap itoa lol 103 | memset(buf, '\0', sizeof(buf)); 104 | snprintf(buf, sizeof(buf), "%d", count); 105 | 106 | img = mlx_put_string(mlx, buf, 0, 0); 107 | ASSERT_NE(img, nullptr); 108 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 109 | 110 | if (count >= 420) 111 | { 112 | mlx_close_window(mlx); 113 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 114 | } 115 | count++; 116 | } 117 | 118 | TEST_F(Window, stringTortureTest) 119 | { 120 | mlx_image_t *img = mlx_new_image(mlx, Window::width / 2, Window::height / 2); 121 | ASSERT_NE(img, nullptr); 122 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 123 | 124 | memset(img->pixels, 255, sizeof(int32_t) * img->width * img->height); 125 | 126 | int32_t val_window = mlx_image_to_window(mlx, img, Window::width / 4 , Window::height / 4); 127 | EXPECT_GE(val_window, 0); 128 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 129 | 130 | bool val_hook = mlx_loop_hook(mlx, ft_draw, mlx); 131 | EXPECT_EQ(val_hook, true); 132 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 133 | 134 | mlx_loop(mlx); 135 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 136 | 137 | mlx_delete_image(mlx, img); 138 | ASSERT_EQ(mlx_errno, MLX_SUCCESS); 139 | } 140 | -------------------------------------------------------------------------------- /tools/compile_shader.bat: -------------------------------------------------------------------------------- 1 | :: ----------------------------------------------------------------------------- 2 | :: Codam Coding College, Amsterdam @ 2022-2023 by W2Wizard. 3 | :: See README in the root project for more information. 4 | :: ----------------------------------------------------------------------------- 5 | 6 | @echo off 7 | SETLOCAL EnableDelayedExpansion 8 | 9 | :: If no arguments have been given to the script 10 | IF "%~1"=="" ( 11 | echo ERROR: missing arguments, use as follows: %~nx0 ^ ^ 1>&2 12 | ENDLOCAL 13 | EXIT /B 1 14 | ) 15 | 16 | :: Check if file exists 17 | IF NOT EXIST "%~1" ( 18 | echo ERROR: shader file not found: %~1 1>&2 19 | ENDLOCAL 20 | EXIT /B 2 21 | ) 22 | 23 | :: Extract the shader type (file extension without the dot) 24 | SET "SHADERTYPE=%~x1" 25 | SET "SHADERTYPE=%SHADERTYPE:~1%" 26 | 27 | echo // ----------------------------------------------------------------------------- 28 | echo // Codam Coding College, Amsterdam @ 2022-2023 by W2Wizard. 29 | echo // See README in the root project for more information. 30 | echo // ----------------------------------------------------------------------------- 31 | echo. 32 | echo // If you wish to modify this file edit the .vert or .frag file! 33 | echo. 34 | echo #include "MLX42/MLX42_Int.h" 35 | echo. 36 | 37 | :: Check the Mode argument (WASM specific output if Mode == 1) 38 | IF "%~2"=="1" ( 39 | echo const char* %SHADERTYPE%_shader = "#version 300 es\n" 40 | echo "precision mediump float;\n" 41 | ) ELSE ( 42 | FOR /F "delims=" %%A IN ('more +0 "%~1"') DO ( 43 | echo const char* %SHADERTYPE%_shader = "%%A\n" 44 | GOTO next 45 | ) 46 | ) 47 | 48 | :next 49 | :: Read and process the rest of the shader file 50 | FOR /F "usebackq delims=" %%A IN ("%~1") DO ( 51 | IF NOT "%%A"=="" ( 52 | IF "%%A"=="}" ( 53 | echo "%%A"; 54 | ) ELSE ( 55 | echo "%%A" 56 | ) 57 | ) 58 | ) 59 | 60 | ENDLOCAL 61 | EXIT /B 0 62 | -------------------------------------------------------------------------------- /tools/compile_shader.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ----------------------------------------------------------------------------- 3 | # Codam Coding College, Amsterdam @ 2022-2023 by W2Wizard. 4 | # See README in the root project for more information. 5 | # ----------------------------------------------------------------------------- 6 | 7 | # If no arguments have been given 8 | if [ "$#" -ne 2 ]; then 9 | echo "ERROR: missing arguments, use as follows: $0 " 1>&2 10 | exit 1 11 | fi 12 | 13 | # If file cannot be found 14 | if [ ! -f "$1" ]; then 15 | echo "ERROR: shader file not found: $1" 1>&2 16 | exit 2 17 | fi 18 | 19 | SHADERTYPE="${1##*.}" 20 | 21 | echo "// -----------------------------------------------------------------------------" 22 | echo "// Codam Coding College, Amsterdam @ 2022-2023 by W2Wizard. " 23 | echo "// See README in the root project for more information. " 24 | echo "// -----------------------------------------------------------------------------" 25 | echo "" 26 | echo "// If you wish to modify this file edit the .vert or .frag file!" 27 | echo "" 28 | 29 | # Include the MLX42 header 30 | echo "#include \"MLX42/MLX42_Int.h\"" 31 | echo "" 32 | 33 | { 34 | if [ "$2" -eq 1 ]; then # Output WASM specific lines 35 | echo "const char* ${SHADERTYPE}_shader = \"#version 300 es\\n\"" 36 | echo " \"precision mediump float;\"" 37 | else # Non-Wasm, output the original shader version 38 | echo "const char* ${SHADERTYPE}_shader = \"$(sed -n '1{p;q;}' "$1")\\n\"" 39 | fi 40 | 41 | # Read the rest of the shader file 42 | read 43 | while IFS= read -r LINE; do 44 | if [ ! "${LINE}" = "" ]; then 45 | if [ "${LINE}" = "}" ]; then 46 | echo " \"${LINE}\";" 47 | else 48 | echo " \"${LINE}\"" 49 | fi 50 | fi 51 | done 52 | } < "$1" 53 | 54 | exit 0 55 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # Web 2 | 3 | MLX42 supports compilation towards [WASM](https://webassembly.org/). What this means is you can run any application written in C directly in the browser! 4 | This overcomes a lot of issues with for instance showing projects towards others or have an environment where building natively just won't work. 5 | 6 | In this README you will learn how to compile your project towards Webassembly and later deploy it on github! 7 | 8 | ## Pre-requisites 9 | 10 | - [Emscripten](https://emscripten.org/), you can install this via brew or read the instructions they provide for [Windows or Linux](https://emscripten.org/docs/getting_started/downloads.html) 11 | 12 | ## Building 13 | 14 | Once you made sure you have emscripten installed (check if `emcc` and `emcake` work). 15 | 16 | Run: 17 | ```bash 18 | emcmake cmake -B build && cmake --build build --parallel 19 | ``` 20 | 21 | ## Modifications 22 | 23 | You're only required to do a few modifications to your `main.c`. 24 | For this we will use the demo main provided in the root [readme](../README.md). 25 | 26 | Add the following headers at the top: 27 | ```c 28 | #include 29 | #include 30 | ``` 31 | 32 | Modify your main: 33 | ```c 34 | // Invoked instead of mlx_loop directly. 35 | void emscripten_main_loop() { 36 | mlx_loop(mlx); 37 | } 38 | 39 | int32_t main(int argc, char **argv) 40 | { 41 | // Gotta error check this stuff 42 | if (!(mlx = mlx_init(WIDTH, HEIGHT, "MLX42", true))) 43 | { 44 | puts(mlx_strerror(mlx_errno)); 45 | return(EXIT_FAILURE); 46 | } 47 | if (!(image = mlx_new_image(mlx, 128, 128))) 48 | { 49 | mlx_close_window(mlx); 50 | puts(mlx_strerror(mlx_errno)); 51 | return(EXIT_FAILURE); 52 | } 53 | if (mlx_image_to_window(mlx, image, 0, 0) == -1) 54 | { 55 | mlx_close_window(mlx); 56 | puts(mlx_strerror(mlx_errno)); 57 | return(EXIT_FAILURE); 58 | } 59 | 60 | mlx_loop_hook(mlx, ft_randomize, mlx); 61 | mlx_loop_hook(mlx, ft_hook, mlx); 62 | 63 | // This function will set up the main loop 64 | emscripten_set_main_loop(emscripten_main_loop, 0, true); 65 | mlx_terminate(mlx); 66 | return (EXIT_SUCCESS); 67 | } 68 | ``` 69 | 70 | Thats actually it! It may or may not be necessary to modify your own source code depending on what you do but that's most often not the case. 71 | It is that easy to just re-deploy your own app into webassembly. 72 | 73 | ## Building 74 | 75 | ```bash 76 | # Compile C into JS/WASM 77 | emcc -O3 -I include -I mlx -pthread main.c \ 78 | -o ./web/demo.js \ 79 | ./build/libmlx42.a \ 80 | -s USE_GLFW=3 -s USE_WEBGL2=1 -s FULL_ES3=1 -s WASM=1 \ 81 | -s NO_EXIT_RUNTIME=1 -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' \ 82 | -s ALLOW_MEMORY_GROWTH 83 | 84 | # Navigate into the web folder (if you're running this directly from this repo) 85 | cd web 86 | 87 | # Launch local webserver, this is required to make the service worker function. 88 | python3 -m http.server 8000 89 | ``` 90 | 91 | Once the server is up and running all you need to do now is go to [localhost](http://localhost:8000/index.html). 92 | There you can locally develop your application without having to do any git commits or actions shenanigans. 93 | 94 | # Deploying to Github Pages 95 | 96 | For a free, quick and easy hosting solution you can realistically deploy this anywhere. 97 | However for now we will only focus on putting this up via github pages. 98 | 99 | What you need in your repository is a `.github/workflows/static.yml` file. 100 | It can be named anything `static`, `ci`, whatever. Later on if you learn more about CI Pipelines you can use this to do a lot of useful things. 101 | 102 | ## Enabling github pages 103 | Follow this step: https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow 104 | 105 | Once selected, you need to commit an actions file. 106 | For now you can copy paste MLX42's `wasm.yml` file which functionally does the exact same. 107 | ```yml 108 | # Simple workflow for deploying static content to GitHub Pages 109 | name: Deploy static content to Pages 110 | 111 | on: 112 | push: 113 | branches: ["master"] # Change to main or whatever fancy name 114 | workflow_dispatch: 115 | 116 | permissions: 117 | contents: read 118 | pages: write 119 | id-token: write 120 | 121 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 122 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 123 | concurrency: 124 | group: "pages" 125 | cancel-in-progress: false 126 | 127 | # Single deploy job since we're just deploying 128 | jobs: 129 | deploy: 130 | environment: 131 | name: github-pages 132 | url: ${{ steps.deployment.outputs.page_url }} 133 | runs-on: ubuntu-latest 134 | #TODO: add a build step to get the wasm file instead of commiting it. 135 | #Doesn't really matter atm since the git history is polluted anyway 136 | steps: 137 | - name: Checkout 138 | uses: actions/checkout@v4 139 | - name: Setup Pages 140 | uses: actions/configure-pages@v5 141 | - name: Upload artifact 142 | uses: actions/upload-pages-artifact@v3 143 | with: 144 | path: './web' # <= Set this variable to the directory relative to the root of the repo. 145 | - name: Deploy to GitHub Pages 146 | id: deployment 147 | uses: actions/deploy-pages@v4 148 | ``` 149 | 150 | Once you commit this file github will do it's magic and create a deployment. 151 | You should then get a link to where you can access you program. Now you can access your app anywhere! 152 | -------------------------------------------------------------------------------- /web/coi-serviceworker.js: -------------------------------------------------------------------------------- 1 | /*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */ 2 | let coepCredentialless = false; 3 | if (typeof window === 'undefined') { 4 | self.addEventListener("install", () => self.skipWaiting()); 5 | self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim())); 6 | 7 | self.addEventListener("message", (ev) => { 8 | if (!ev.data) { 9 | return; 10 | } else if (ev.data.type === "deregister") { 11 | self.registration 12 | .unregister() 13 | .then(() => { 14 | return self.clients.matchAll(); 15 | }) 16 | .then(clients => { 17 | clients.forEach((client) => client.navigate(client.url)); 18 | }); 19 | } else if (ev.data.type === "coepCredentialless") { 20 | coepCredentialless = ev.data.value; 21 | } 22 | }); 23 | 24 | self.addEventListener("fetch", function (event) { 25 | const r = event.request; 26 | if (r.cache === "only-if-cached" && r.mode !== "same-origin") { 27 | return; 28 | } 29 | 30 | const request = (coepCredentialless && r.mode === "no-cors") 31 | ? new Request(r, { 32 | credentials: "omit", 33 | }) 34 | : r; 35 | event.respondWith( 36 | fetch(request) 37 | .then((response) => { 38 | if (response.status === 0) { 39 | return response; 40 | } 41 | 42 | const newHeaders = new Headers(response.headers); 43 | newHeaders.set("Cross-Origin-Embedder-Policy", 44 | coepCredentialless ? "credentialless" : "require-corp" 45 | ); 46 | if (!coepCredentialless) { 47 | newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin"); 48 | } 49 | newHeaders.set("Cross-Origin-Opener-Policy", "same-origin"); 50 | 51 | return new Response(response.body, { 52 | status: response.status, 53 | statusText: response.statusText, 54 | headers: newHeaders, 55 | }); 56 | }) 57 | .catch((e) => console.error(e)) 58 | ); 59 | }); 60 | 61 | } else { 62 | (() => { 63 | const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf"); 64 | window.sessionStorage.removeItem("coiReloadedBySelf"); 65 | const coepDegrading = (reloadedBySelf == "coepdegrade"); 66 | 67 | // You can customize the behavior of this script through a global `coi` variable. 68 | const coi = { 69 | shouldRegister: () => !reloadedBySelf, 70 | shouldDeregister: () => false, 71 | coepCredentialless: () => true, 72 | coepDegrade: () => true, 73 | doReload: () => window.location.reload(), 74 | quiet: false, 75 | ...window.coi 76 | }; 77 | 78 | const n = navigator; 79 | const controlling = n.serviceWorker && n.serviceWorker.controller; 80 | 81 | // Record the failure if the page is served by serviceWorker. 82 | if (controlling && !window.crossOriginIsolated) { 83 | window.sessionStorage.setItem("coiCoepHasFailed", "true"); 84 | } 85 | const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed"); 86 | 87 | if (controlling) { 88 | // Reload only on the first failure. 89 | const reloadToDegrade = coi.coepDegrade() && !( 90 | coepDegrading || window.crossOriginIsolated 91 | ); 92 | n.serviceWorker.controller.postMessage({ 93 | type: "coepCredentialless", 94 | value: (reloadToDegrade || coepHasFailed && coi.coepDegrade()) 95 | ? false 96 | : coi.coepCredentialless(), 97 | }); 98 | if (reloadToDegrade) { 99 | !coi.quiet && console.log("Reloading page to degrade COEP."); 100 | window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade"); 101 | coi.doReload("coepdegrade"); 102 | } 103 | 104 | if (coi.shouldDeregister()) { 105 | n.serviceWorker.controller.postMessage({ type: "deregister" }); 106 | } 107 | } 108 | 109 | // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are 110 | // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. 111 | if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return; 112 | 113 | if (!window.isSecureContext) { 114 | !coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required."); 115 | return; 116 | } 117 | 118 | // In some environments (e.g. Firefox private mode) this won't be available 119 | if (!n.serviceWorker) { 120 | !coi.quiet && console.error("COOP/COEP Service Worker not registered, perhaps due to private mode."); 121 | return; 122 | } 123 | 124 | n.serviceWorker.register(window.document.currentScript.src).then( 125 | (registration) => { 126 | !coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope); 127 | 128 | registration.addEventListener("updatefound", () => { 129 | !coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker."); 130 | window.sessionStorage.setItem("coiReloadedBySelf", "updatefound"); 131 | coi.doReload(); 132 | }); 133 | 134 | // If the registration is active, but it's not controlling the page 135 | if (registration.active && !n.serviceWorker.controller) { 136 | !coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker."); 137 | window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling"); 138 | coi.doReload(); 139 | } 140 | }, 141 | (err) => { 142 | !coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err); 143 | } 144 | ); 145 | })(); 146 | } 147 | -------------------------------------------------------------------------------- /web/demo.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codam-coding-college/MLX42/ce254c3a19af8176787601a2ac3490100a5c4c61/web/demo.wasm -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebAssembly Test 8 | 105 | 106 | 107 | 108 |
109 |

WebAssembly Test

110 | Visit MLX42 GitHub Repo 111 |
112 |
113 |

114 | This little demo demonstrates how you compile MLX42 using emscripten to leverage the power of WebAssembly 115 | and run any graphical project directly in the web! 116 |

117 |
118 |
119 | Loading... 120 | 121 |
122 |
123 |

Use to move the element in the canvas.

124 |
125 | 126 | 144 | 145 | 146 | 147 | 148 | --------------------------------------------------------------------------------