├── .github └── FUNDING.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── .reuse └── dep5 ├── CMakeLists.txt ├── LICENSES ├── CC0-1.0.txt └── MIT.txt ├── include ├── ResourceConstants.h ├── about_window.h ├── base_app.h ├── base_control.h ├── base_window.h ├── browser_app.h ├── browser_control.h ├── browser_document.h ├── browser_window.h ├── helpers.h ├── machine.h ├── oserr_exception.h ├── quickdraw_container.h ├── quickdraw_font.h ├── root_control.h └── user_control.h ├── make.sh ├── make_and_run.sh ├── run.sh ├── src ├── CMakeLists.txt ├── about_window.cpp ├── base_app.cpp ├── base_control.cpp ├── base_window.cpp ├── browser_app.cpp ├── browser_control.cpp ├── browser_document.cpp ├── browser_window.cpp ├── cmake │ └── text_to_rez.cmake ├── config.h.in ├── hellolite.r ├── helpers.cpp ├── home.html ├── machine.cpp ├── main.cpp ├── oserr_exception.cpp ├── quickdraw_container.cpp ├── quickdraw_font.cpp ├── root_control.cpp └── user_control.cpp └── third_party ├── CMakeLists.txt └── dlmalloc ├── CMakeLists.txt ├── macos-dlmalloc.c ├── malloc.c └── malloc.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | github: [ryandesign] 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | *.html 6 | *.orig 7 | *.patch 8 | .vscode 9 | build 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | [submodule "third_party/litehtml"] 6 | path = third_party/litehtml 7 | url = https://github.com/litehtml/litehtml.git 8 | branch = master 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | repos: 6 | - repo: https://github.com/fsfe/reuse-tool 7 | rev: v1.0.0 8 | hooks: 9 | - id: reuse 10 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 6 | Upstream-Name: 7 | Upstream-Contact: Ryan C Schmidt 8 | Source: 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | # 3.18 needed for string(HEX ...) 6 | cmake_minimum_required(VERSION 3.18 FATAL_ERROR) 7 | project(hellolite 8 | LANGUAGES C CXX 9 | VERSION 0.0.0.0 10 | ) 11 | set(PROJECT_DEVELOPMENT_STAGE development) 12 | 13 | math(EXPR PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR} + 0") 14 | math(EXPR PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH} + 0") 15 | math(EXPR PROJECT_VERSION_TWEAK "${PROJECT_VERSION_TWEAK} + 0") 16 | if(PROJECT_DEVELOPMENT_STAGE STREQUAL "development" OR PROJECT_DEVELOPMENT_STAGE STREQUAL "alpha" OR PROJECT_DEVELOPMENT_STAGE STREQUAL "beta") 17 | string(SUBSTRING "${PROJECT_DEVELOPMENT_STAGE}" 0 1 dev_stage) 18 | set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${dev_stage}${PROJECT_VERSION_TWEAK}") 19 | elseif(PROJECT_DEVELOPMENT_STAGE STREQUAL "release" OR PROJECT_DEVELOPMENT_STAGE STREQUAL "final") 20 | if(PROJECT_VERSION_TWEAK GREATER 0) 21 | message(FATAL_ERROR "PROJECT_VERSION_TWEAK must be 0 for this PROJECT_DEVELOPMENT_STAGE") 22 | endif() 23 | set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") 24 | else() 25 | message(FATAL_ERROR "PROJECT_DEVELOPMENT_STAGE must be one of development, alpha, beta, or release") 26 | endif() 27 | 28 | option(USE_DLMALLOC "Use dlmalloc" ON) 29 | option(USE_LITEHTML "Use litehtml" ON) 30 | 31 | # Make each function and global/static variable a separate section so that the 32 | # linker can remove unused ones later. Set globally here so that *all* files are 33 | # compiled with these options. 34 | add_compile_options(-fdata-sections;-ffunction-sections) 35 | 36 | # Retro68 renames strings.h to bsdstrings.h for the benefit of developers using 37 | # case-insensitive filesystems compiling old classic Mac OS software that 38 | # includes but this is not needed for newer classic Mac OS software 39 | # that includes instead of and it is not ok for modern 40 | # software that includes . 41 | # https://github.com/autc04/Retro68/issues/163 42 | set(include_dir ${CMAKE_CURRENT_BINARY_DIR}/include) 43 | execute_process(COMMAND 44 | ${CMAKE_COMMAND} -E make_directory ${include_dir} 45 | ) 46 | execute_process(COMMAND 47 | ${CMAKE_COMMAND} -E create_symlink 48 | ${CMAKE_SYSTEM_PREFIX_PATH}include/bsdstrings.h 49 | ${include_dir}/strings.h 50 | ) 51 | include_directories(BEFORE SYSTEM ${include_dir}) 52 | 53 | add_subdirectory(third_party) 54 | add_subdirectory(src) 55 | -------------------------------------------------------------------------------- /LICENSES/CC0-1.0.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to 7 | deal in the Software without restriction, including without limitation the 8 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | sell copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/ResourceConstants.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #define k_move_horizontally (1 << 0) 6 | #define k_move_vertically (1 << 1) 7 | #define k_resize_horizontally (1 << 2) 8 | #define k_resize_vertically (1 << 3) 9 | 10 | #define k_toolbar_icons_base 28672 11 | 12 | #define r_ALRT_fatal_error 128 13 | 14 | #define r_CNTL_header 128 15 | #define r_CNTL_back_button 129 16 | #define r_CNTL_forward_button 130 17 | #define r_CNTL_home_button 131 18 | #define r_CNTL_reload_button 132 19 | #define r_CNTL_address_bar 133 20 | #define r_CNTL_browser 134 21 | #define r_CNTL_horizontal_scroll_bar 135 22 | #define r_CNTL_vertical_scroll_bar 136 23 | #define r_CNTL_about_text 192 24 | 25 | #define r_DITL_fatal_error 128 26 | 27 | #define r_icsx_back (k_toolbar_icons_base + 0) 28 | #define r_icsx_forward (k_toolbar_icons_base + 1) 29 | #define r_icsx_home (k_toolbar_icons_base + 2) 30 | #define r_icsx_reload (k_toolbar_icons_base + 3) 31 | #define r_icsx_stop (k_toolbar_icons_base + 4) 32 | 33 | #define r_MBAR 128 34 | 35 | #define r_MENU_apple 128 36 | #define k_apple_menu_id 1 37 | 38 | #define r_MENU_file 129 39 | #define k_file_menu_id 2 40 | #define i_new_window 1 41 | #define i_close_window 2 42 | #define i_quit 4 43 | 44 | #define r_MENU_edit 130 45 | #define k_edit_menu_id 3 46 | #define i_undo 1 47 | #define i_cut 3 48 | #define i_copy 4 49 | #define i_paste 5 50 | #define i_clear 6 51 | #define i_select_all 7 52 | 53 | #define r_MENU_window 131 54 | #define k_window_menu_id 4 55 | #define i_zoom 1 56 | #define i_collapse 2 57 | 58 | #define r_SIZE -1 59 | #define kPreferredSize 6144 60 | #define kMinimumSize 2048 61 | 62 | #define r_STRx_error_messages 128 63 | #define e_no_appearance 1 64 | #define e_no_resource 2 65 | #define e_no_memory 3 66 | #define e_unknown_error 4 67 | 68 | #define r_STRx_menu_items 129 69 | #define k_collapse 1 70 | #define k_expand 2 71 | 72 | #define r_STRx_about 130 73 | #define i_app_name 1 74 | 75 | #define r_TEXT_html 128 76 | 77 | #define r_vers_program 1 78 | #define r_vers_package 2 79 | 80 | #define r_WIND_browser 128 81 | #define r_WIND_about 129 82 | -------------------------------------------------------------------------------- /include/about_window.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef ABOUT_WINDOW_H 6 | #define ABOUT_WINDOW_H 7 | 8 | #include "base_control.h" 9 | #include "base_window.h" 10 | #include "root_control.h" 11 | 12 | class about_window : public base_window 13 | { 14 | public: 15 | about_window(); 16 | ~about_window(); 17 | void close() override; 18 | 19 | private: 20 | root_control m_root_control; 21 | base_control m_text; 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/base_app.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef BASE_APP_H 6 | #define BASE_APP_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // TODO: Reevaluate visibility in all classes. Probably make most things private. 13 | class base_app 14 | { 15 | public: 16 | base_app(int32_t extra_stack_bytes = 0, int16_t extra_master_pointers = 0); 17 | virtual ~base_app(); 18 | void run(); 19 | OSErr load_menu_bar(int16_t mbar_id); 20 | void consume_event(); 21 | void on_event(EventRecord& event); 22 | void on_idle_event(EventRecord const& event); 23 | void on_mouse_down_event(EventRecord& event); 24 | void content_click(WindowRecord& window, EventRecord& event); 25 | void grow_window(WindowRecord& window, EventRecord const& event); 26 | void close_window(WindowRecord& window); 27 | void zoom_window(WindowRecord& window, int16_t part); 28 | void on_key_down_event(EventRecord const& event); 29 | void on_activate_event(EventRecord const& event); 30 | void activate_window(WindowRecord& window, bool activate, EventRecord const& event); 31 | void on_update_event(EventRecord const& event); 32 | void on_disk_event(EventRecord const& event); 33 | void on_os_event(EventRecord const& event); 34 | void on_high_level_event(EventRecord const& event); 35 | void on_suspend_resume_event(EventRecord const& event); 36 | void on_menu_event(int32_t menu_result); 37 | void set_menu_enabled(MenuHandle menu, bool enabled); 38 | void set_menu_item_enabled(MenuHandle menu, int16_t item, bool enabled); 39 | void quit(); 40 | 41 | protected: 42 | virtual void try_consume_event(); 43 | virtual uint32_t get_sleep(); 44 | virtual RgnHandle get_cursor_region(); 45 | virtual void adjust_menu_bar(); 46 | virtual void adjust_menu_items(); 47 | virtual void on_menu(int16_t menu_id, int16_t menu_item); 48 | 49 | private: 50 | static constexpr uint32_t k_visual_delay = 8; 51 | static constexpr uint32_t k_sleep_time = 0x7FFFFFFF; 52 | static constexpr int16_t i_about = 1; 53 | 54 | void expand_stack(int32_t extra_bytes); 55 | void more_masters(int16_t num_masters); 56 | virtual void will_suspend(); 57 | virtual void will_resume(); 58 | virtual void about(); 59 | 60 | uint32_t m_menu_unhighlight_ticks; 61 | int16_t m_apple_menu_id; 62 | int16_t m_num_apple_menu_items; 63 | bool m_done; 64 | bool m_is_in_foreground; 65 | }; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /include/base_control.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef BASE_CONTROL_H 6 | #define BASE_CONTROL_H 7 | 8 | #include 9 | #include 10 | 11 | #include "base_window.h" 12 | 13 | class base_control 14 | { 15 | friend class root_control; 16 | 17 | public: 18 | base_control(int16_t resource_id, WindowRecord& window); 19 | // base_control(ControlHandle control); 20 | virtual ~base_control(); 21 | static base_control *get_from_control(const ControlHandle control); 22 | static base_control *get_from_control(ControlRecord const& control); 23 | Rect const& get_rect() const; 24 | virtual base_window& get_window(); 25 | bool is_visible() const; 26 | void show_and_inval(); 27 | void show_and_draw(); 28 | void hide_and_inval(); 29 | void hide_and_draw(); 30 | int16_t get_maximum() const; 31 | void set_maximum(int16_t maximum); 32 | int16_t get_minimum() const; 33 | void set_minimum(int16_t minimum); 34 | int16_t get_value() const; 35 | void set_value(int16_t value); 36 | OSErr set_data(ControlPartCode part, ResType tag, size_t size, void const* data); 37 | OSErr embed(base_control& control); 38 | void window_did_resize(int16_t dx, int16_t dy); 39 | void on_mouse_down(EventRecord const& event, int16_t part); 40 | void on_mouse_up(EventRecord const& event, int16_t part); 41 | virtual ControlActionUPP get_action_proc(int16_t part); 42 | 43 | protected: 44 | ControlHandle m_control; 45 | 46 | private: 47 | base_control(); 48 | void init(); 49 | 50 | bool m_move_horizontally; 51 | bool m_move_vertically; 52 | bool m_resize_horizontally; 53 | bool m_resize_vertically; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/base_window.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef WINDOW_H 6 | #define WINDOW_H 7 | 8 | #include 9 | 10 | class base_window 11 | { 12 | public: 13 | static constexpr int16_t k_default_minimum_height = 80; 14 | static constexpr int16_t k_default_minimum_width = 80; 15 | static constexpr int16_t k_default_maximum_height = 32766; 16 | static constexpr int16_t k_default_maximum_width = 32766; 17 | 18 | base_window(int16_t resource_id); 19 | virtual ~base_window(); 20 | static base_window *get_from_window(const WindowPtr window); 21 | static base_window *get_from_window(WindowRecord const& window); 22 | virtual int16_t get_minimum_height(); 23 | virtual int16_t get_minimum_width(); 24 | virtual int16_t get_maximum_height(); 25 | virtual int16_t get_maximum_width(); 26 | virtual void did_activate(EventRecord const& event); 27 | virtual void did_deactivate(EventRecord const& event); 28 | // TODO: Do windows really need to be able to adjust and handle menus? 29 | virtual void adjust_menu_bar(); 30 | virtual void adjust_menu_items(); 31 | virtual void on_mouse_down(EventRecord const& event); 32 | virtual void idle(EventRecord const& event); 33 | virtual void on_key_down(EventRecord const& event); 34 | virtual void on_menu(int16_t menu, int16_t item); 35 | virtual void did_resize(int16_t dx, int16_t dy, int16_t part); 36 | virtual void update(EventRecord const& event); 37 | virtual bool has_grow_icon(); 38 | virtual void get_grow_icon_region(RgnHandle rgn); 39 | virtual void draw_grow_icon(); 40 | virtual bool should_close(); 41 | virtual void close(); 42 | void select(); 43 | void show(); 44 | void hide(); 45 | Rect const& get_rect() const; 46 | void set_title(char const* const title); 47 | 48 | protected: 49 | WindowRecord m_window; 50 | 51 | private: 52 | static constexpr intptr_t k_move_to_front = -1L; 53 | static constexpr int16_t k_object_window_kind = 0x4242; 54 | int16_t m_procid; 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/browser_app.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef BROWSER_APP_H 6 | #define BROWSER_APP_H 7 | 8 | #include "base_app.h" 9 | 10 | class browser_app : public base_app 11 | { 12 | public: 13 | browser_app(); 14 | ~browser_app() override; 15 | 16 | void fatal_error_alert(int16_t error_number); 17 | void error_alert(int16_t error_number); 18 | // void oserr_alert(OSErr err); 19 | void on_file_menu(int16_t menu_item); 20 | void on_edit_menu(int16_t menu_item); 21 | void on_window_menu(int16_t menu_item); 22 | void on_quit(); 23 | 24 | protected: 25 | void try_consume_event() override; 26 | void adjust_menu_bar() override; 27 | void adjust_menu_items() override; 28 | void on_menu(int16_t menu_id, int16_t menu_item) override; 29 | 30 | private: 31 | void about() override; 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/browser_control.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef BROWSER_CONTROL_H 6 | #define BROWSER_CONTROL_H 7 | 8 | #include 9 | 10 | #ifdef USE_LITEHTML 11 | #include "quickdraw_container.h" 12 | #endif 13 | #include "user_control.h" 14 | 15 | class browser_document; 16 | 17 | class browser_control : 18 | #ifdef USE_LITEHTML 19 | public quickdraw_container, 20 | #endif 21 | public user_control 22 | { 23 | public: 24 | browser_control(int16_t resource_id, WindowRecord& window); 25 | ~browser_control(); 26 | void set_document(std::shared_ptr document); 27 | 28 | #ifdef USE_LITEHTML 29 | // quickdraw_container overrides: 30 | void get_client_rect(litehtml::position& client) const override; 31 | void set_caption(char const *caption) override; 32 | #endif 33 | 34 | // user_control overrides: 35 | virtual void draw(int16_t part) const override; 36 | 37 | private: 38 | std::shared_ptr m_document; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /include/browser_document.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef BROWSER_DOCUMENT_H 6 | #define BROWSER_DOCUMENT_H 7 | 8 | #include 9 | #ifdef USE_LITEHTML 10 | #include 11 | #endif 12 | #include 13 | 14 | #include "browser_control.h" 15 | 16 | class browser_document 17 | { 18 | public: 19 | typedef std::shared_ptr ptr; 20 | 21 | browser_document(); 22 | ~browser_document(); 23 | void set_html(const char *const html, browser_control& control); 24 | void render_if_needed(int width); 25 | void draw(Rect const& rect); 26 | Point get_dimensions(); 27 | 28 | private: 29 | #ifdef USE_LITEHTML 30 | litehtml::document::ptr m_lite_document; 31 | int16_t m_rendered_width; 32 | #endif 33 | Point m_scroll; 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/browser_window.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef BROWSER_WINDOW_H 6 | #define BROWSER_WINDOW_H 7 | 8 | #include 9 | 10 | #include "base_control.h" 11 | #include "base_window.h" 12 | #include "browser_control.h" 13 | #include "browser_document.h" 14 | #include "root_control.h" 15 | 16 | class browser_window : public base_window 17 | { 18 | public: 19 | browser_window(); 20 | ~browser_window(); 21 | int16_t get_minimum_height() override; 22 | int16_t get_minimum_width() override; 23 | void did_resize(int16_t dx, int16_t dy, int16_t part) override; 24 | void render_if_needed_and_update_scrollbars(); 25 | 26 | private: 27 | browser_document::ptr m_selected_document; 28 | root_control m_root_control; 29 | base_control m_header; 30 | base_control m_address_bar; 31 | browser_control m_browser; 32 | base_control m_back_button; 33 | base_control m_forward_button; 34 | base_control m_home_button; 35 | base_control m_reload_button; 36 | base_control m_horizontal_scroll_bar; 37 | base_control m_vertical_scroll_bar; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/helpers.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef HELPERS_H 6 | #define HELPERS_H 7 | 8 | #include 9 | #include 10 | 11 | bool is_desk_accessory_window(WindowPtr window); 12 | bool is_desk_accessory_window(WindowRecord const& window); 13 | int16_t rect_height(Rect const& rect); 14 | int16_t rect_width(Rect const& rect); 15 | std::string& pappend(std::string& dst, ConstStr255Param src); 16 | void debugprintf(char const *format, ...); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/machine.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef MACHINE_H 6 | #define MACHINE_H 7 | 8 | #include 9 | #include 10 | 11 | namespace machine 12 | { 13 | bool has_128k_rom(); 14 | bool has_gestalt(); 15 | int16_t get_system_version(); 16 | bool has_appearance(); 17 | bool trap_available(uint16_t trap); 18 | TrapType get_trap_type(uint16_t trap); 19 | int16_t get_num_toolbox_traps(); 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/oserr_exception.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef OSERR_EXCEPTION_H 6 | #define OSERR_EXCEPTION_H 7 | 8 | #include 9 | #include 10 | 11 | class oserr_exception : public std::exception 12 | { 13 | public: 14 | oserr_exception(OSErr err); 15 | ~oserr_exception(); 16 | OSErr err() const; 17 | 18 | protected: 19 | OSErr m_err; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/quickdraw_container.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef QUICKDRAW_CONTAINER_H 6 | #define QUICKDRAW_CONTAINER_H 7 | 8 | // Mac headers 9 | 10 | // System headers 11 | 12 | // Third-party headers 13 | #include 14 | 15 | // My headers 16 | 17 | class quickdraw_container : public litehtml::document_container 18 | { 19 | public: 20 | quickdraw_container(); 21 | ~quickdraw_container(); 22 | 23 | litehtml::uint_ptr create_font(const char *face_name, int size, int weight, litehtml::font_style italic_style, unsigned int decoration, litehtml::font_metrics *fm); 24 | void delete_font(litehtml::uint_ptr hFont); 25 | int text_width(const char *text, litehtml::uint_ptr hFont); 26 | void draw_text(litehtml::uint_ptr hdc, const char *text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos); 27 | int pt_to_px(int pt) const; 28 | int get_default_font_size() const; 29 | const char *get_default_font_name() const; 30 | void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker); 31 | void load_image(const char *src, const char *base_url, bool redraw_on_ready); 32 | void get_image_size(const char *src, const char *base_url, litehtml::size& sz); 33 | void draw_background(litehtml::uint_ptr hdc, const litehtml::background_paint& bg); 34 | void draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root); 35 | void set_base_url(const char *base_url); 36 | void link(const std::shared_ptr& doc, const litehtml::element::ptr& el); 37 | void on_anchor_click(const char *url, const litehtml::element::ptr& el); 38 | void set_cursor(const char *cursor); 39 | void transform_text(litehtml::string& text, litehtml::text_transform tt); 40 | void import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& base_url); 41 | void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& radiuses, bool valid_x, bool valid_y); 42 | void del_clip(); 43 | std::shared_ptr create_element(const char *tag_name, const litehtml::string_map &attributes, const std::shared_ptr &doc); 44 | void get_media_features(litehtml::media_features& media) const; 45 | void get_language(litehtml::string& language, litehtml::string& culture) const; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/quickdraw_font.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef QUICKDRAW_FONT_H 6 | #define QUICKDRAW_FONT_H 7 | 8 | // Mac headers 9 | #include 10 | #include 11 | 12 | // System headers 13 | 14 | // Third-party headers 15 | #include 16 | 17 | // My headers 18 | 19 | class quickdraw_font 20 | { 21 | public: 22 | quickdraw_font(const char *name, int16_t size, StyleParameter style = normal); 23 | quickdraw_font(int16_t id, int16_t size, StyleParameter style = normal); 24 | ~quickdraw_font(); 25 | void draw(const char *text, const litehtml::position& pos); 26 | int16_t width(const char *text); 27 | FontInfo& metrics(); 28 | 29 | private: 30 | int16_t m_id; 31 | int16_t m_size; 32 | StyleParameter m_style; 33 | FontInfo m_metrics; 34 | GrafPort m_port; 35 | 36 | void construct(); 37 | void set_port_font(); 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/root_control.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef ROOT_CONTROL_H 6 | #define ROOT_CONTROL_H 7 | 8 | #include "base_control.h" 9 | 10 | class root_control final : public base_control 11 | { 12 | public: 13 | root_control(WindowRecord& window); 14 | ~root_control(); 15 | }; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/user_control.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #ifndef USER_CONTROL_H 6 | #define USER_CONTROL_H 7 | 8 | #include "base_control.h" 9 | 10 | class user_control : public base_control 11 | { 12 | public: 13 | user_control(int16_t resource_id, WindowRecord& window); 14 | ~user_control(); 15 | 16 | private: 17 | static pascal void draw_proc(ControlHandle control, int16_t part); 18 | virtual void draw(int16_t part) const; 19 | static pascal void activate_proc(ControlHandle control, Boolean activating); 20 | virtual void activate(bool activating); 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 4 | # 5 | # SPDX-License-Identifier: MIT 6 | 7 | set -euo pipefail 8 | 9 | if [[ ${BASH_SOURCE[0]} = */* ]]; then 10 | cd -- "${BASH_SOURCE%/*}/" || exit 11 | fi 12 | sourcedir="$PWD" 13 | 14 | : "${RETRO68:=/opt/local/libexec/Retro68}" 15 | : "${RESINFO:="$RETRO68"/bin/ResInfo}" 16 | : "${REZ:="$RETRO68"/bin/Rez}" 17 | : "${CMAKE:=cmake}" 18 | : "${CMAKE_BUILD_TYPE:=RelWithDebInfo}" 19 | : "${BUILD_DIR:=build}" 20 | : "${BUILD_TYPE_DIR:="$BUILD_DIR/$CMAKE_BUILD_TYPE"}" 21 | : "${CMAKE_BUILD_PARALLEL_LEVEL:=$(sysctl -n hw.activecpu 2>/dev/null || nproc 2>/dev/null || echo 1)}" 22 | : "${JOBS:="$CMAKE_BUILD_PARALLEL_LEVEL"}" 23 | : "${TERM:=dumb}" 24 | 25 | build_68k=0 26 | build_ppc=0 27 | build_fat=0 28 | 29 | while (($# > 0)); do 30 | case $1 in 31 | 68k) 32 | build_68k=1 33 | ;; 34 | ppc) 35 | build_ppc=1 36 | ;; 37 | fat) 38 | build_fat=1 39 | ;; 40 | *) 41 | printf 'unknown option %s\n' "$1" >&2 42 | exit 1 43 | esac 44 | shift 45 | done 46 | 47 | if ((!build_68k && !build_ppc && !build_fat)); then 48 | build_fat=1 49 | fi 50 | if ((build_fat)); then 51 | build_68k=1 52 | build_ppc=1 53 | fi 54 | 55 | build_dir_68k="$BUILD_TYPE_DIR/m68k" 56 | build_dir_ppc="$BUILD_TYPE_DIR/powerpc" 57 | build_dir_fat="$BUILD_TYPE_DIR/fat" 58 | 59 | common_cmake_flags=( 60 | -S "$sourcedir" 61 | -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" 62 | -DCMAKE_VERBOSE_MAKEFILE=ON 63 | ) 64 | 65 | heading() { 66 | local -r msg="${1:-}" 67 | local -r msglen=${#msg} 68 | local -r cols=$(tput cols) 69 | local -r leftlen=$(((cols-msglen)/2-1)) 70 | local -r rightlen=$((leftlen+msglen%2)) 71 | local i 72 | for ((i=0; i/dev/null 132 | printf '[%3d%%] Created fat binary for %s\n' "$((100*step_number/total_steps))" "$name" 133 | } 134 | 135 | if ((build_fat)); then 136 | heading 'Building fat binary' 137 | 138 | mkdir -p "$build_dir_fat/src" 139 | 140 | make_fat 'app' 141 | fi 142 | -------------------------------------------------------------------------------- /make_and_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 4 | # 5 | # SPDX-License-Identifier: MIT 6 | 7 | set -euo pipefail 8 | 9 | if [[ ${BASH_SOURCE[0]} = */* ]]; then 10 | cd -- "${BASH_SOURCE%/*}/" || exit 11 | fi 12 | 13 | ./make.sh "$@" && ./run.sh "$@" 14 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 4 | # 5 | # SPDX-License-Identifier: MIT 6 | 7 | set -euo pipefail 8 | 9 | if [[ ${BASH_SOURCE[0]} = */* ]]; then 10 | cd -- "${BASH_SOURCE%/*}/" || exit 11 | fi 12 | 13 | : "${RETRO68:=/opt/local/libexec/Retro68}" 14 | : "${CMAKE_BUILD_TYPE:=RelWithDebInfo}" 15 | : "${BUILD_DIR:=build}" 16 | : "${BUILD_TYPE_DIR:="$BUILD_DIR/$CMAKE_BUILD_TYPE"}" 17 | 18 | arch=fat 19 | while (($# > 0)); do 20 | case $1 in 21 | 68k|fat|ppc) 22 | arch="$1" 23 | ;; 24 | *) 25 | printf 'unknown option %s\n' "$1" >&2 26 | exit 1 27 | esac 28 | shift 29 | done 30 | 31 | set -x 32 | 33 | build_dir_68k="$BUILD_TYPE_DIR/m68k" 34 | build_dir_ppc="$BUILD_TYPE_DIR/powerpc" 35 | build_dir_fat="$BUILD_TYPE_DIR/fat" 36 | 37 | build_dir_var="build_dir_$arch" 38 | 39 | "$RETRO68"/bin/LaunchAPPL "${!build_dir_var}"/src/app.bin 40 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | configure_file(config.h.in config.h @ONLY) 6 | 7 | set(REZ_INCLUDE_PATH "${CMAKE_CURRENT_BINARY_DIR};${hellolite_SOURCE_DIR}/include;${REZ_INCLUDE_PATH}") 8 | 9 | set(app_SOURCES 10 | about_window.cpp 11 | base_app.cpp 12 | base_control.cpp 13 | base_window.cpp 14 | browser_app.cpp 15 | browser_control.cpp 16 | browser_document.cpp 17 | browser_window.cpp 18 | hellolite.r 19 | helpers.cpp 20 | machine.cpp 21 | main.cpp 22 | root_control.cpp 23 | user_control.cpp 24 | ) 25 | 26 | if(USE_LITEHTML) 27 | list(APPEND app_SOURCES 28 | ${CMAKE_CURRENT_BINARY_DIR}/home.html.r 29 | quickdraw_container.cpp 30 | quickdraw_font.cpp 31 | ) 32 | endif() 33 | 34 | add_application(app 35 | ${app_SOURCES} 36 | TYPE "APPL" 37 | CREATOR "????" 38 | ) 39 | 40 | target_include_directories(app PRIVATE 41 | ${hellolite_SOURCE_DIR}/include 42 | ) 43 | 44 | if(NOT CMAKE_SYSTEM_NAME STREQUAL Retro68) 45 | target_link_libraries(app PRIVATE 46 | AppearanceLib 47 | ) 48 | endif() 49 | 50 | set_target_properties(app PROPERTIES 51 | C_STANDARD 99 52 | CXX_STANDARD 11 53 | ) 54 | 55 | set_target_properties(app PROPERTIES 56 | # TODO: Add -Wall and -Wextra eventually. Currently causes too many errors. 57 | COMPILE_OPTIONS "-Werror" 58 | ) 59 | # TODO: -Wl,--gc-sections causes crashes 60 | # https://github.com/autc04/Retro68/issues/184 61 | # https://github.com/autc04/Retro68/issues/191 62 | # LINK_FLAGS "-Wl,--gc-sections" 63 | 64 | if(USE_DLMALLOC) 65 | add_dependencies(app dlmalloc) 66 | 67 | target_link_libraries(app PRIVATE 68 | ${dlmalloc_LIBRARY} 69 | ) 70 | 71 | set_property(TARGET app APPEND_STRING PROPERTY 72 | LINK_FLAGS " ${dlmalloc_LINK_FLAGS}" 73 | ) 74 | endif() 75 | 76 | if(USE_LITEHTML) 77 | ExternalProject_Get_Property(litehtml SOURCE_DIR) 78 | 79 | set(text_file ${CMAKE_CURRENT_SOURCE_DIR}/home.html) 80 | set(rez_file ${CMAKE_CURRENT_BINARY_DIR}/home.html.r) 81 | add_custom_command( 82 | COMMAND ${CMAKE_COMMAND} 83 | -Dinfile=${text_file} 84 | -Doutfile=${rez_file} 85 | -Drsrc_type=TEXT 86 | -Drsrc_id=r_TEXT_html 87 | -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/text_to_rez.cmake 88 | DEPENDS ${text_file} 89 | OUTPUT ${rez_file} 90 | VERBATIM 91 | ) 92 | add_custom_target(make_html_rez DEPENDS ${rez_file}) 93 | 94 | add_dependencies(app litehtml make_html_rez) 95 | 96 | target_include_directories(app SYSTEM PRIVATE 97 | ${litehtml_INCLUDE_DIR} 98 | ) 99 | 100 | target_link_libraries(app PRIVATE 101 | ${litehtml_LIBRARY} 102 | ) 103 | 104 | target_compile_definitions(app PRIVATE USE_LITEHTML) 105 | endif() 106 | -------------------------------------------------------------------------------- /src/about_window.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "about_window.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "ResourceConstants.h" 14 | #include "helpers.h" 15 | 16 | about_window::about_window() 17 | : base_window(r_WIND_about), 18 | m_root_control(m_window), 19 | m_text(r_CNTL_about_text, m_window) 20 | { 21 | std::string text; 22 | { 23 | Str255 app_name; 24 | GetIndString(app_name, r_STRx_about, i_app_name); 25 | if (ResError()) 26 | throw std::bad_alloc(); 27 | pappend(text, app_name); 28 | } 29 | { 30 | Handle vers = Get1Resource('vers', r_vers_program); 31 | if (!vers) 32 | throw std::bad_alloc(); 33 | text += " "; 34 | HLock(vers); 35 | pappend(text, (**reinterpret_cast(vers)).shortVersion); 36 | ReleaseResource(vers); 37 | } 38 | m_text.set_data(kControlNoPart, kControlStaticTextTextTag, text.size(), text.data()); 39 | } 40 | 41 | about_window::~about_window() 42 | { 43 | } 44 | 45 | void about_window::close() 46 | { 47 | hide(); 48 | } 49 | -------------------------------------------------------------------------------- /src/base_app.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "base_app.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "base_control.h" 20 | #include "base_window.h" 21 | #include "helpers.h" 22 | #include "machine.h" 23 | 24 | base_app::base_app(int32_t extra_stack_bytes, int16_t extra_master_pointers) 25 | : m_done(false), m_is_in_foreground(true), m_menu_unhighlight_ticks(0) 26 | { 27 | expand_stack(extra_stack_bytes); 28 | MaxApplZone(); 29 | more_masters(extra_master_pointers); 30 | InitGraf(&qd.thePort); 31 | InitFonts(); 32 | InitWindows(); 33 | InitMenus(); 34 | TEInit(); 35 | InitDialogs(nullptr); 36 | FlushEvents(everyEvent, 0); 37 | InitCursor(); 38 | GetDateTime(reinterpret_cast(&qd.randSeed)); 39 | 40 | if (machine::has_appearance()) 41 | RegisterAppearanceClient(); 42 | } 43 | 44 | base_app::~base_app() 45 | { 46 | } 47 | 48 | void base_app::run() 49 | { 50 | while (!m_done) 51 | try_consume_event(); 52 | } 53 | 54 | struct mbar { 55 | int16_t length; 56 | int16_t resource_id[]; 57 | }; 58 | typedef mbar *mbar_ptr; 59 | typedef mbar **mbar_handle; 60 | 61 | OSErr base_app::load_menu_bar(int16_t mbar_id) 62 | { 63 | { 64 | mbar_handle mbar = reinterpret_cast(Get1Resource('MBAR', mbar_id)); 65 | if (!mbar) 66 | return resNotFound; 67 | m_apple_menu_id = 0; 68 | m_num_apple_menu_items = 0; 69 | for (int i = 0; i < (**mbar).length; ++i) 70 | { 71 | MenuHandle menu = reinterpret_cast(Get1Resource('MENU', (**mbar).resource_id[i])); 72 | if (!menu) 73 | return resNotFound; 74 | if (EqualString((**menu).menuData, "\p\x14", true, true)) 75 | { 76 | m_apple_menu_id = (**menu).menuID; 77 | break; 78 | } 79 | } 80 | } 81 | 82 | if (!m_apple_menu_id) 83 | return resNotFound; 84 | 85 | { 86 | Handle menu_bar = GetNewMBar(mbar_id); 87 | if (!menu_bar) 88 | return resNotFound; 89 | SetMenuBar(menu_bar); 90 | DisposeHandle(reinterpret_cast(menu_bar)); 91 | MenuHandle menu = GetMenuHandle(m_apple_menu_id); 92 | m_num_apple_menu_items = CountMItems(menu); 93 | AppendResMenu(menu, 'DRVR'); 94 | InvalMenuBar(); 95 | } 96 | 97 | return noErr; 98 | } 99 | 100 | void base_app::consume_event() 101 | { 102 | EventRecord event; 103 | 104 | if (WaitNextEvent(everyEvent, &event, get_sleep(), get_cursor_region())) 105 | on_event(event); 106 | else 107 | on_idle_event(event); 108 | 109 | if (m_menu_unhighlight_ticks && TickCount() >= m_menu_unhighlight_ticks) 110 | { 111 | HiliteMenu(0); 112 | m_menu_unhighlight_ticks = 0; 113 | } 114 | } 115 | 116 | void base_app::on_event(EventRecord& event) 117 | { 118 | WindowPeek window; 119 | 120 | switch (event.what) 121 | { 122 | case mouseDown: 123 | on_mouse_down_event(event); 124 | break; 125 | case keyDown: 126 | case autoKey: 127 | on_key_down_event(event); 128 | break; 129 | case activateEvt: 130 | on_activate_event(event); 131 | break; 132 | case updateEvt: 133 | on_update_event(event); 134 | break; 135 | case diskEvt: 136 | on_disk_event(event); 137 | break; 138 | case osEvt: 139 | on_os_event(event); 140 | break; 141 | case kHighLevelEvent: 142 | on_high_level_event(event); 143 | break; 144 | } 145 | } 146 | 147 | void base_app::on_idle_event(EventRecord const& event) 148 | { 149 | if (base_window *window_obj = base_window::get_from_window(FrontWindow())) 150 | window_obj->idle(event); 151 | } 152 | 153 | void base_app::on_mouse_down_event(EventRecord& event) 154 | { 155 | WindowPtr window; 156 | int16_t part = FindWindow(event.where, &window); 157 | switch (part) 158 | { 159 | case inMenuBar: 160 | adjust_menu_items(); 161 | on_menu_event(MenuSelect(event.where)); 162 | break; 163 | case inSysWindow: 164 | SystemClick(&event, window); 165 | break; 166 | case inContent: 167 | if (FrontWindow() == window) 168 | content_click(*reinterpret_cast(window), event); 169 | else 170 | SelectWindow(window); 171 | break; 172 | case inDrag: 173 | DragWindow(window, event.where, &(**GetGrayRgn()).rgnBBox); 174 | break; 175 | case inGrow: 176 | grow_window(*reinterpret_cast(window), event); 177 | break; 178 | case inGoAway: 179 | if (TrackGoAway(window, event.where)) 180 | close_window(*reinterpret_cast(window)); 181 | break; 182 | case inZoomIn: 183 | case inZoomOut: 184 | if (TrackBox(window, event.where, part)) 185 | zoom_window(*reinterpret_cast(window), part); 186 | break; 187 | } 188 | } 189 | 190 | void base_app::content_click(WindowRecord& window, EventRecord& event) 191 | { 192 | GrafPtr saved_port; 193 | GetPort(&saved_port); 194 | SetPort(reinterpret_cast(&window)); 195 | GlobalToLocal(&event.where); 196 | ControlHandle control; 197 | int16_t part; 198 | if (machine::has_appearance()) 199 | control = FindControlUnderMouse(event.where, reinterpret_cast(&window), &part); 200 | else 201 | part = FindControl(event.where, reinterpret_cast(&window), &control); 202 | if (base_control *control_obj = base_control::get_from_control(control)) 203 | control_obj->on_mouse_down(event, part); 204 | else if (base_window *window_obj = base_window::get_from_window(window)) 205 | window_obj->on_mouse_down(event); 206 | SetPort(saved_port); 207 | } 208 | 209 | void base_app::grow_window(WindowRecord& window, EventRecord const& event) 210 | { 211 | int16_t min_width, min_height, max_width, max_height; 212 | base_window *window_obj = base_window::get_from_window(window); 213 | if (window_obj) 214 | { 215 | min_width = window_obj->get_minimum_width(); 216 | min_height = window_obj->get_minimum_height(); 217 | max_width = window_obj->get_maximum_width(); 218 | max_height = window_obj->get_maximum_height(); 219 | } 220 | else 221 | { 222 | min_width = base_window::k_default_minimum_width; 223 | min_height = base_window::k_default_minimum_height; 224 | max_width = base_window::k_default_maximum_width; 225 | max_height = base_window::k_default_maximum_height; 226 | } 227 | Rect limit_rect; 228 | SetRect(&limit_rect, min_width, min_height, max_width, max_height); 229 | if (int32_t new_size = GrowWindow(reinterpret_cast(&window), event.where, &limit_rect)) 230 | { 231 | int16_t new_width = LoWord(new_size); 232 | int16_t new_height = HiWord(new_size); 233 | int16_t dx = new_width - rect_width(window.port.portRect); 234 | int16_t dy = new_height - rect_height(window.port.portRect); 235 | SizeWindow(reinterpret_cast(&window), new_width, new_height, true); 236 | if (window_obj) 237 | { 238 | GrafPtr saved_port; 239 | GetPort(&saved_port); 240 | SetPort(reinterpret_cast(&window)); 241 | window_obj->did_resize(dx, dy, inGrow); 242 | SetPort(saved_port); 243 | } 244 | } 245 | } 246 | 247 | void base_app::close_window(WindowRecord& window) 248 | { 249 | if (is_desk_accessory_window(window)) 250 | CloseDeskAcc(window.windowKind); 251 | else if (base_window *window_obj = base_window::get_from_window(window)) 252 | { 253 | if (window_obj->should_close()) 254 | window_obj->close(); 255 | } 256 | else 257 | DisposeWindow(reinterpret_cast(&window)); 258 | adjust_menu_bar(); 259 | } 260 | 261 | void base_app::zoom_window(WindowRecord& window, int16_t part) 262 | { 263 | GrafPtr saved_port; 264 | GetPort(&saved_port); 265 | SetPort(reinterpret_cast(&window)); 266 | EraseRect(&window.port.portRect); 267 | int16_t dx = rect_width(window.port.portRect); 268 | int16_t dy = rect_height(window.port.portRect); 269 | ZoomWindow(reinterpret_cast(&window), part, true); 270 | if (base_window *window_obj = base_window::get_from_window(window)) 271 | { 272 | dx = rect_width(window.port.portRect) - dx; 273 | dy = rect_height(window.port.portRect) - dy; 274 | window_obj->did_resize(dx, dy, part); 275 | } 276 | SetPort(saved_port); 277 | } 278 | 279 | void base_app::on_key_down_event(EventRecord const& event) 280 | { 281 | if (event.modifiers & cmdKey) 282 | { 283 | if (keyDown == event.what) 284 | { 285 | adjust_menu_items(); 286 | int32_t menu_result; 287 | if (machine::has_appearance()) 288 | menu_result = MenuEvent(&event); 289 | else 290 | menu_result = MenuKey(event.message & charCodeMask); 291 | on_menu_event(menu_result); 292 | } 293 | } 294 | else if (base_window *window_obj = base_window::get_from_window(FrontWindow())) 295 | window_obj->on_key_down(event); 296 | } 297 | 298 | void base_app::on_activate_event(EventRecord const& event) 299 | { 300 | if (WindowPeek window = reinterpret_cast(event.message)) 301 | activate_window(*window, event.modifiers & activeFlag, event); 302 | } 303 | 304 | void base_app::activate_window(WindowRecord& window, bool activate, EventRecord const& event) 305 | { 306 | if (base_window *window_obj = base_window::get_from_window(window)) 307 | { 308 | ControlHandle control; 309 | OSErr err = GetRootControl(reinterpret_cast(&window), &control); 310 | if (activate) 311 | { 312 | if (err == noErr) 313 | ActivateControl(control); 314 | window_obj->did_activate(event); 315 | } 316 | else 317 | { 318 | if (err == noErr) 319 | DeactivateControl(control); 320 | window_obj->did_deactivate(event); 321 | } 322 | } 323 | } 324 | 325 | void base_app::on_update_event(EventRecord const& event) 326 | { 327 | if (WindowPtr window = reinterpret_cast(event.message)) 328 | { 329 | BeginUpdate(window); 330 | if (base_window *window_obj = base_window::get_from_window(window)) 331 | { 332 | GrafPtr saved_port; 333 | GetPort(&saved_port); 334 | SetPort(window); 335 | window_obj->update(event); 336 | SetPort(saved_port); 337 | } 338 | EndUpdate(window); 339 | } 340 | } 341 | 342 | void base_app::on_disk_event(EventRecord const& event) 343 | { 344 | if (HiWord(event.message)) 345 | { 346 | // System 7 and later ignores this point and centers the dialog. 347 | Point top_left; 348 | SetPt(&top_left, 98, 98); 349 | DILoad(); 350 | OSErr err = DIBadMount(top_left, event.message); 351 | DIUnload(); 352 | } 353 | } 354 | 355 | void base_app::on_os_event(EventRecord const& event) 356 | { 357 | switch ((event.message >> 24) & 0xFF) 358 | { 359 | case mouseMovedMessage: 360 | on_idle_event(event); 361 | break; 362 | case suspendResumeMessage: 363 | on_suspend_resume_event(event); 364 | break; 365 | } 366 | } 367 | 368 | void base_app::on_high_level_event(EventRecord const& event) 369 | { 370 | } 371 | 372 | void base_app::on_suspend_resume_event(EventRecord const& event) 373 | { 374 | m_is_in_foreground = event.message & resumeFlag; 375 | if (m_is_in_foreground) 376 | will_suspend(); 377 | else 378 | will_resume(); 379 | if (WindowPeek window = reinterpret_cast(FrontWindow())) 380 | activate_window(*window, m_is_in_foreground, event); 381 | } 382 | 383 | void base_app::on_menu_event(int32_t menu_result) 384 | { 385 | int16_t menu_id = HiWord(menu_result); 386 | m_menu_unhighlight_ticks = TickCount(); 387 | if (menu_id) 388 | { 389 | bool handled = false; 390 | m_menu_unhighlight_ticks += k_visual_delay; 391 | int16_t menu_item = LoWord(menu_result); 392 | if (menu_id == m_apple_menu_id) 393 | { 394 | if (menu_item > m_num_apple_menu_items) 395 | { 396 | if (MenuHandle menu = GetMenuHandle(menu_id)) 397 | { 398 | Str255 item_name; 399 | GetMenuItemText(menu, menu_item, item_name); 400 | int16_t ref_num = OpenDeskAcc(item_name); 401 | adjust_menu_bar(); 402 | } 403 | handled = true; 404 | } 405 | else if (menu_item == i_about) 406 | { 407 | about(); 408 | handled = true; 409 | } 410 | } 411 | if (!handled) 412 | on_menu(menu_id, menu_item); 413 | } 414 | } 415 | 416 | void base_app::set_menu_enabled(MenuHandle menu, bool enabled) 417 | { 418 | set_menu_item_enabled(menu, 0, enabled); 419 | } 420 | 421 | void base_app::set_menu_item_enabled(MenuHandle menu, int16_t item, bool enabled) 422 | { 423 | if (menu && *menu) 424 | { 425 | if (0 == item && enabled != (**menu).enableFlags & 1L) 426 | InvalMenuBar(); 427 | if (enabled) 428 | EnableItem(menu, item); 429 | else 430 | DisableItem(menu, item); 431 | } 432 | } 433 | 434 | void base_app::quit() 435 | { 436 | m_done = true; 437 | } 438 | 439 | void base_app::try_consume_event() 440 | { 441 | try 442 | { 443 | consume_event(); 444 | } 445 | catch (...) 446 | { 447 | Debugger(); 448 | } 449 | } 450 | 451 | uint32_t base_app::get_sleep() 452 | { 453 | if (m_menu_unhighlight_ticks > 0) 454 | return m_menu_unhighlight_ticks - TickCount(); 455 | return k_sleep_time; 456 | } 457 | 458 | RgnHandle base_app::get_cursor_region() 459 | { 460 | return nullptr; 461 | } 462 | 463 | void base_app::adjust_menu_bar() 464 | { 465 | if (base_window *window_obj = base_window::get_from_window(FrontWindow())) 466 | window_obj->adjust_menu_bar(); 467 | } 468 | 469 | void base_app::adjust_menu_items() 470 | { 471 | if (base_window *window_obj = base_window::get_from_window(FrontWindow())) 472 | window_obj->adjust_menu_items(); 473 | } 474 | 475 | void base_app::on_menu(int16_t menu_id, int16_t menu_item) 476 | { 477 | } 478 | 479 | void base_app::expand_stack(int32_t extra_stack_bytes) 480 | { 481 | if (extra_stack_bytes) 482 | SetApplLimit(GetApplLimit() - extra_stack_bytes); 483 | } 484 | 485 | void base_app::more_masters(int16_t extra_master_pointers) 486 | { 487 | if (extra_master_pointers > 0) 488 | { 489 | THz zone = GetZone(); 490 | int16_t saved_num_masters = zone->moreMast; 491 | zone->moreMast = extra_master_pointers; 492 | MoreMasters(); 493 | zone->moreMast = saved_num_masters; 494 | } 495 | } 496 | 497 | void base_app::will_suspend() 498 | { 499 | } 500 | 501 | void base_app::will_resume() 502 | { 503 | } 504 | 505 | void base_app::about() 506 | { 507 | } 508 | -------------------------------------------------------------------------------- /src/base_control.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "base_control.h" 6 | 7 | #include 8 | 9 | #include "ResourceConstants.h" 10 | #include "machine.h" 11 | 12 | base_control::base_control(int16_t resource_id, WindowRecord& window) 13 | { 14 | m_control = GetNewControl(resource_id, reinterpret_cast(&window)); 15 | if (nullptr == m_control) 16 | throw std::bad_alloc(); 17 | int32_t flags = GetControlReference(m_control); 18 | m_move_horizontally = flags & k_move_horizontally; 19 | m_move_vertically = flags & k_move_vertically; 20 | m_resize_horizontally = flags & k_resize_horizontally; 21 | m_resize_vertically = flags & k_resize_vertically; 22 | init(); 23 | } 24 | 25 | /* 26 | base_control::base_control(ControlHandle control) 27 | : m_control(control) 28 | { 29 | init(); 30 | } 31 | */ 32 | 33 | base_control::base_control() 34 | : m_control(nullptr), 35 | m_move_horizontally(0), 36 | m_move_vertically(0), 37 | m_resize_horizontally(0), 38 | m_resize_vertically(0) 39 | { 40 | } 41 | 42 | base_control *base_control::get_from_control(const ControlHandle control) 43 | { 44 | base_control *control_obj = nullptr; 45 | if (control && *control && GetControlReference(control)) 46 | control_obj = base_control::get_from_control(**control); 47 | return control_obj; 48 | } 49 | 50 | base_control *base_control::get_from_control(ControlRecord const& control) 51 | { 52 | base_control *control_obj = nullptr; 53 | if (control.contrlRfCon) 54 | control_obj = reinterpret_cast(control.contrlRfCon); 55 | return control_obj; 56 | } 57 | 58 | void base_control::init() 59 | { 60 | SetControlReference(m_control, reinterpret_cast(this)); 61 | } 62 | 63 | base_control::~base_control() 64 | { 65 | DisposeControl(m_control); 66 | } 67 | 68 | Rect const& base_control::get_rect() const 69 | { 70 | return (**m_control).contrlRect; 71 | } 72 | 73 | base_window& base_control::get_window() 74 | { 75 | return *base_window::get_from_window((**m_control).contrlOwner); 76 | } 77 | 78 | bool base_control::is_visible() const 79 | { 80 | return (**m_control).contrlVis; 81 | } 82 | 83 | void base_control::show_and_inval() 84 | { 85 | (**m_control).contrlVis = 0xFF; 86 | InvalRect(&get_rect()); 87 | } 88 | 89 | void base_control::show_and_draw() 90 | { 91 | ShowControl(m_control); 92 | } 93 | 94 | void base_control::hide_and_inval() 95 | { 96 | (**m_control).contrlVis = 0; 97 | InvalRect(&get_rect()); 98 | } 99 | 100 | void base_control::hide_and_draw() 101 | { 102 | HideControl(m_control); 103 | } 104 | 105 | int16_t base_control::get_maximum() const 106 | { 107 | return GetControlMaximum(m_control); 108 | } 109 | 110 | void base_control::set_maximum(int16_t maximum) 111 | { 112 | SetControlMaximum(m_control, maximum); 113 | } 114 | 115 | int16_t base_control::get_minimum() const 116 | { 117 | return GetControlMinimum(m_control); 118 | } 119 | 120 | void base_control::set_minimum(int16_t minimum) 121 | { 122 | SetControlMinimum(m_control, minimum); 123 | } 124 | 125 | int16_t base_control::get_value() const 126 | { 127 | return GetControlValue(m_control); 128 | } 129 | 130 | void base_control::set_value(int16_t value) 131 | { 132 | SetControlValue(m_control, value); 133 | } 134 | 135 | OSErr base_control::set_data(ControlPartCode part, ResType tag, size_t size, void const *data) 136 | { 137 | return SetControlData(m_control, part, tag, size, data); 138 | } 139 | 140 | OSErr base_control::embed(base_control& control) 141 | { 142 | return EmbedControl(control.m_control, m_control); 143 | } 144 | 145 | void base_control::window_did_resize(int16_t dx, int16_t dy) 146 | { 147 | if ((dx && (m_move_horizontally || m_resize_horizontally)) || 148 | (dy && (m_move_vertically || m_resize_vertically))) 149 | { 150 | bool was_visible = is_visible(); 151 | if (was_visible) 152 | hide_and_inval(); 153 | if (m_resize_horizontally) 154 | (**m_control).contrlRect.right += dx; 155 | if (m_resize_vertically) 156 | (**m_control).contrlRect.bottom += dy; 157 | if (!m_move_horizontally) 158 | dx = 0; 159 | if (!m_move_vertically) 160 | dy = 0; 161 | if (dx || dy) 162 | OffsetRect(&(**m_control).contrlRect, dx, dy); 163 | if (was_visible) 164 | show_and_inval(); 165 | } 166 | } 167 | 168 | void base_control::on_mouse_down(EventRecord const& event, int16_t part) 169 | { 170 | if (machine::has_appearance()) 171 | part = HandleControlClick(m_control, event.where, event.modifiers, get_action_proc(part)); 172 | else 173 | part = TrackControl(m_control, event.where, get_action_proc(part)); 174 | if (part) 175 | on_mouse_up(event, part); 176 | } 177 | 178 | void base_control::on_mouse_up(EventRecord const& event, int16_t part) 179 | { 180 | } 181 | 182 | ControlActionUPP base_control::get_action_proc(int16_t part) 183 | { 184 | return nullptr; 185 | } 186 | -------------------------------------------------------------------------------- /src/base_window.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "base_window.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "base_control.h" 13 | #include "machine.h" 14 | //#include "oserr_exception.h" 15 | 16 | struct wind { 17 | Rect bounds; 18 | int16_t procid; 19 | // More fields we don't care about. 20 | }; 21 | typedef wind *wind_ptr; 22 | typedef wind **wind_handle; 23 | 24 | base_window::base_window(int16_t resource_id) 25 | { 26 | // I can't find a way to get the procid after the window has been created 27 | // so I load the resource manually and extract the procid from it before 28 | // creating the window. 29 | wind_handle wind = reinterpret_cast(Get1Resource('WIND', resource_id)); 30 | if (!wind) 31 | throw std::bad_alloc(); 32 | m_procid = (**wind).procid; 33 | if (!GetNewCWindow(resource_id, &m_window, reinterpret_cast(k_move_to_front))) 34 | throw std::bad_alloc()/*oserr_exception(memFullErr)*/; 35 | m_window.windowKind = k_object_window_kind; 36 | m_window.refCon = reinterpret_cast(this); 37 | } 38 | 39 | base_window::~base_window() 40 | { 41 | // Use CloseWindow, not DisposeWindow, because we allocated the memory for 42 | // the window record (within this object) so we don't want Mac OS to try to 43 | // dispose it for us. 44 | CloseWindow(reinterpret_cast(&m_window)); 45 | } 46 | 47 | base_window *base_window::get_from_window(const WindowPtr window) 48 | { 49 | base_window *window_obj = nullptr; 50 | if (window) 51 | window_obj = base_window::get_from_window(*reinterpret_cast(window)); 52 | return window_obj; 53 | } 54 | 55 | base_window *base_window::get_from_window(WindowRecord const& window) 56 | { 57 | base_window *window_obj = nullptr; 58 | if (k_object_window_kind == window.windowKind && window.refCon) 59 | window_obj = reinterpret_cast(window.refCon); 60 | return window_obj; 61 | } 62 | 63 | int16_t base_window::get_minimum_height() 64 | { 65 | return k_default_minimum_height; 66 | } 67 | 68 | int16_t base_window::get_minimum_width() 69 | { 70 | return k_default_minimum_width; 71 | } 72 | 73 | int16_t base_window::get_maximum_height() 74 | { 75 | return k_default_maximum_height; 76 | } 77 | 78 | int16_t base_window::get_maximum_width() 79 | { 80 | return k_default_maximum_width; 81 | } 82 | 83 | void base_window::did_activate(EventRecord const& event) 84 | { 85 | } 86 | 87 | void base_window::did_deactivate(EventRecord const& event) 88 | { 89 | } 90 | 91 | void base_window::adjust_menu_bar() 92 | { 93 | } 94 | 95 | void base_window::adjust_menu_items() 96 | { 97 | } 98 | 99 | void base_window::on_mouse_down(EventRecord const& event) 100 | { 101 | } 102 | 103 | void base_window::idle(EventRecord const& event) 104 | { 105 | } 106 | 107 | void base_window::on_key_down(EventRecord const& event) 108 | { 109 | } 110 | 111 | void base_window::on_menu(int16_t menu, int16_t item) 112 | { 113 | } 114 | 115 | void base_window::did_resize(int16_t dx, int16_t dy, int16_t part) 116 | { 117 | ControlHandle control = reinterpret_cast(m_window.controlList); 118 | while (control) 119 | { 120 | base_control *control_obj = base_control::get_from_control(control); 121 | if (control_obj) 122 | control_obj->window_did_resize(dx, dy); 123 | control = (**control).nextControl; 124 | } 125 | } 126 | 127 | void base_window::update(EventRecord const& event) 128 | { 129 | UpdateControls(reinterpret_cast(&m_window), m_window.port.visRgn); 130 | draw_grow_icon(); 131 | } 132 | 133 | bool base_window::has_grow_icon() 134 | { 135 | bool has_grow; 136 | uint32_t features; 137 | if (machine::has_appearance() && noErr == GetWindowFeatures(reinterpret_cast(&m_window), &features)) 138 | has_grow = features & kWindowCanGrow; 139 | else 140 | switch (m_procid) 141 | { 142 | case documentProc: 143 | case zoomDocProc: 144 | case floatGrowProc: 145 | case floatZoomGrowProc: 146 | case floatSideGrowProc: 147 | case floatSideZoomGrowProc: 148 | has_grow = true; 149 | break; 150 | default: 151 | has_grow = false; 152 | } 153 | return has_grow; 154 | } 155 | 156 | void base_window::get_grow_icon_region(RgnHandle rgn) 157 | { 158 | if (machine::has_appearance() && noErr == GetWindowRegion(reinterpret_cast(&m_window), kWindowGrowRgn, rgn)) 159 | { 160 | Point offset; 161 | SetPt(&offset, 0, 0); 162 | LocalToGlobal(&offset); 163 | OffsetRgn(rgn, -offset.h, -offset.v); 164 | } 165 | else 166 | { 167 | Rect rect; 168 | rect.right = m_window.port.portRect.right + 1; 169 | rect.bottom = m_window.port.portRect.bottom + 1; 170 | rect.left = rect.right - 16; 171 | rect.top = rect.bottom - 16; 172 | RectRgn(rgn, &rect); 173 | } 174 | } 175 | 176 | void base_window::draw_grow_icon() 177 | { 178 | // When using Appearance windows directly, calling DrawGrowIcon is not 179 | // necessary but is presumably not harmful. When using non-Appearance 180 | // windows in mapping mode, DrawGrowIcon must be called once but it's 181 | // presumably not harmful to call it every time. 182 | if (has_grow_icon()) 183 | if (RgnHandle grow_icon_region = NewRgn()) 184 | { 185 | // Mask out everything but the grow icon itself to avoid drawing the 186 | // possibly undesirable scroll bar delimiting lines. 187 | get_grow_icon_region(grow_icon_region); 188 | RgnHandle saved_clip_region = m_window.port.clipRgn; 189 | m_window.port.clipRgn = grow_icon_region; 190 | DrawGrowIcon(reinterpret_cast(&m_window)); 191 | m_window.port.clipRgn = saved_clip_region; 192 | DisposeRgn(grow_icon_region); 193 | } 194 | } 195 | 196 | bool base_window::should_close() 197 | { 198 | return true; 199 | } 200 | 201 | void base_window::close() 202 | { 203 | delete this; 204 | } 205 | 206 | void base_window::select() 207 | { 208 | SelectWindow(reinterpret_cast(&m_window)); 209 | } 210 | 211 | void base_window::show() 212 | { 213 | ShowWindow(reinterpret_cast(&m_window)); 214 | } 215 | 216 | void base_window::hide() 217 | { 218 | HideWindow(reinterpret_cast(&m_window)); 219 | } 220 | 221 | Rect const& base_window::get_rect() const 222 | { 223 | return m_window.port.portRect; 224 | } 225 | 226 | void base_window::set_title(char const* const title) 227 | { 228 | setwtitle(reinterpret_cast(&m_window), title); 229 | } 230 | -------------------------------------------------------------------------------- /src/browser_app.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "browser_app.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "ResourceConstants.h" 14 | #include "about_window.h" 15 | #include "browser_document.h" 16 | #include "browser_window.h" 17 | #include "helpers.h" 18 | #include "machine.h" 19 | //#include "oserr_exception.h" 20 | 21 | browser_app::browser_app() : base_app() 22 | { 23 | if (!machine::has_appearance()) 24 | fatal_error_alert(e_no_appearance); 25 | 26 | if (load_menu_bar(r_MBAR) != noErr) 27 | fatal_error_alert(e_no_resource); 28 | 29 | adjust_menu_bar(); 30 | adjust_menu_items(); 31 | } 32 | 33 | browser_app::~browser_app() 34 | { 35 | } 36 | 37 | void browser_app::fatal_error_alert(int16_t error_number) 38 | { 39 | #ifdef __m68k__ 40 | bool has_autopositioning = machine::get_system_version() >= 0x0700; 41 | AlertTHndl alrt; 42 | Point offset; 43 | if (!has_autopositioning) 44 | { 45 | if (machine::has_128k_rom()) 46 | alrt = reinterpret_cast(Get1Resource('ALRT', r_ALRT_fatal_error)); 47 | else 48 | alrt = reinterpret_cast(GetResource('ALRT', r_ALRT_fatal_error)); 49 | if (alrt) 50 | { 51 | offset.h = (rect_width(qd.screenBits.bounds) - rect_width((**alrt).boundsRect) >> 1) - (**alrt).boundsRect.left; 52 | offset.v = LMGetMBarHeight() + 64 - (**alrt).boundsRect.top; 53 | OffsetRect(&(**alrt).boundsRect, offset.h, offset.v); 54 | } 55 | } 56 | #endif 57 | 58 | SetCursor(&qd.arrow); 59 | Str255 error_message; 60 | GetIndString(error_message, r_STRx_error_messages, error_number); 61 | ParamText(error_message, "\p", "\p", "\p"); 62 | int16_t item_hit = StopAlert(r_ALRT_fatal_error, nil); 63 | 64 | #ifdef __m68k__ 65 | if (!has_autopositioning && alrt) 66 | OffsetRect(&(**alrt).boundsRect, -offset.h, -offset.v); 67 | #endif 68 | 69 | ExitToShell(); 70 | } 71 | 72 | void browser_app::error_alert(int16_t error_number) 73 | { 74 | int16_t item_hit; 75 | Str255 error_message; 76 | OSErr err; 77 | AlertStdAlertParamRec params; 78 | 79 | params.movable = false; 80 | params.helpButton = false; 81 | params.filterProc = nil; 82 | params.defaultText = reinterpret_cast(kAlertDefaultOKText); 83 | params.cancelText = nil; 84 | params.otherText = nil; 85 | params.defaultButton = kAlertStdAlertOKButton; 86 | params.cancelButton = 0; 87 | params.position = kWindowDefaultPosition; 88 | GetIndString(error_message, r_STRx_error_messages, error_number); 89 | err = StandardAlert(kAlertStopAlert, error_message, nil, ¶ms, &item_hit); 90 | } 91 | 92 | /* 93 | void browser_app::oserr_alert(OSErr err) 94 | { 95 | int16_t error_number; 96 | 97 | switch (err) 98 | { 99 | case memFullErr: 100 | error_number = e_no_memory; 101 | break; 102 | default: 103 | error_number = e_unknown_error; 104 | } 105 | 106 | error_alert(error_number); 107 | } 108 | */ 109 | 110 | void browser_app::about() 111 | { 112 | static about_window *window_obj = nullptr; 113 | if (!window_obj) 114 | window_obj = new about_window(); 115 | window_obj->show(); 116 | window_obj->select(); 117 | adjust_menu_bar(); 118 | } 119 | 120 | void browser_app::on_file_menu(int16_t menu_item) 121 | { 122 | switch (menu_item) 123 | { 124 | case i_new_window: 125 | { 126 | browser_window *window_obj = new browser_window(); 127 | window_obj->show(); 128 | adjust_menu_bar(); 129 | break; 130 | } 131 | case i_close_window: 132 | { 133 | if (WindowPtr window = FrontWindow()) 134 | close_window(*reinterpret_cast(window)); 135 | break; 136 | } 137 | case i_quit: 138 | { 139 | on_quit(); 140 | break; 141 | } 142 | } 143 | } 144 | 145 | void browser_app::on_edit_menu(int16_t menu_item) 146 | { 147 | if (!SystemEdit(menu_item - 1)) 148 | { 149 | } 150 | } 151 | 152 | void browser_app::on_window_menu(int16_t menu_item) 153 | { 154 | switch (menu_item) 155 | { 156 | case i_collapse: 157 | if (WindowPtr window = FrontWindow()) 158 | CollapseWindow(window, !IsWindowCollapsed(window)); 159 | break; 160 | case i_zoom: 161 | if (WindowPeek window = reinterpret_cast(FrontWindow())) 162 | zoom_window(*window, EqualRect(&(**window->contRgn).rgnBBox, &(**reinterpret_cast(window->dataHandle)).userState) ? inZoomOut : inZoomIn); 163 | break; 164 | } 165 | } 166 | 167 | void browser_app::on_quit() 168 | { 169 | while (WindowPtr window = FrontWindow()) 170 | close_window(*reinterpret_cast(window)); 171 | quit(); 172 | } 173 | 174 | void browser_app::try_consume_event() 175 | { 176 | try 177 | { 178 | consume_event(); 179 | } 180 | /* 181 | catch (oserr_exception const& e) 182 | { 183 | oserr_alert(e.err()); 184 | } 185 | */ 186 | catch (std::bad_alloc const& e) 187 | { 188 | error_alert(e_no_memory); 189 | } 190 | catch (...) 191 | { 192 | error_alert(e_unknown_error); 193 | } 194 | } 195 | 196 | void browser_app::adjust_menu_bar() 197 | { 198 | WindowPtr window = FrontWindow(); 199 | set_menu_enabled(GetMenuHandle(k_edit_menu_id), is_desk_accessory_window(window)); 200 | set_menu_enabled(GetMenuHandle(k_window_menu_id), nullptr != window); 201 | } 202 | 203 | void browser_app::adjust_menu_items() 204 | { 205 | WindowPtr window = FrontWindow(); 206 | uint32_t features; 207 | bool can_zoom, can_collapse, is_collapsed; 208 | if (window && (noErr == GetWindowFeatures(window, &features))) 209 | { 210 | can_zoom = features & kWindowCanZoom; 211 | can_collapse = features & kWindowCanCollapse; 212 | is_collapsed = IsWindowCollapsed(window); 213 | } 214 | else 215 | { 216 | can_zoom = false; 217 | can_collapse = false; 218 | is_collapsed = false; 219 | } 220 | MenuHandle menu = GetMenuHandle(k_file_menu_id); 221 | set_menu_item_enabled(menu, i_close_window, nullptr != window); 222 | menu = GetMenuHandle(k_window_menu_id); 223 | set_menu_item_enabled(menu, i_zoom, can_zoom); 224 | set_menu_item_enabled(menu, i_collapse, can_collapse); 225 | Str255 menu_item_text; 226 | GetIndString(menu_item_text, r_STRx_menu_items, is_collapsed ? k_expand : k_collapse); 227 | SetMenuItemText(menu, i_collapse, menu_item_text); 228 | } 229 | 230 | void browser_app::on_menu(int16_t menu_id, int16_t menu_item) 231 | { 232 | switch (menu_id) 233 | { 234 | case k_file_menu_id: 235 | on_file_menu(menu_item); 236 | break; 237 | case k_edit_menu_id: 238 | on_edit_menu(menu_item); 239 | break; 240 | case k_window_menu_id: 241 | on_window_menu(menu_item); 242 | break; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/browser_control.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "browser_control.h" 6 | 7 | #include "browser_document.h" 8 | #include "browser_window.h" 9 | #include "helpers.h" 10 | 11 | browser_control::browser_control(int16_t resource_id, WindowRecord& window) 12 | : user_control(resource_id, window), 13 | m_document(nullptr) 14 | { 15 | } 16 | 17 | browser_control::~browser_control() 18 | { 19 | } 20 | 21 | void browser_control::set_document(std::shared_ptr document) 22 | { 23 | m_document = document; 24 | } 25 | 26 | #ifdef USE_LITEHTML 27 | 28 | // quickdraw_container overrides: 29 | 30 | void browser_control::get_client_rect(litehtml::position& client) const 31 | { 32 | Rect rect = get_rect(); 33 | client.x = rect.left; 34 | client.y = rect.top; 35 | client.width = rect_width(rect); 36 | client.height = rect_height(rect); 37 | } 38 | 39 | void browser_control::set_caption(char const *caption) 40 | { 41 | get_window().set_title(caption); 42 | } 43 | 44 | #endif 45 | 46 | // user_control overrides: 47 | 48 | void browser_control::draw(int16_t part) const 49 | { 50 | if (0 == part && m_document) 51 | { 52 | Rect rect = get_rect(); 53 | Point origin; 54 | SetPt(&origin, rect.left, rect.top); 55 | SectRect(&(**(**m_control).contrlOwner->visRgn).rgnBBox, &rect, &rect); 56 | EraseRect(&rect); 57 | OffsetRect(&rect, -origin.h, -origin.v); 58 | SetOrigin(-origin.h, -origin.v); 59 | m_document->draw(rect); 60 | SetOrigin(0, 0); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/browser_document.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "browser_document.h" 6 | 7 | #include 8 | 9 | #include "helpers.h" 10 | 11 | browser_document::browser_document() 12 | : m_scroll({0, 0}) 13 | { 14 | } 15 | 16 | browser_document::~browser_document() 17 | { 18 | } 19 | 20 | void browser_document::set_html(const char *const html, browser_control& control) 21 | { 22 | #ifdef USE_LITEHTML 23 | m_lite_document = litehtml::document::createFromString(html, &control); 24 | m_rendered_width = 0; 25 | #endif 26 | SetPt(&m_scroll, 0, 0); 27 | } 28 | 29 | void browser_document::render_if_needed(int width) 30 | { 31 | #ifdef USE_LITEHTML 32 | if (m_lite_document && width != m_rendered_width) 33 | { 34 | m_lite_document->render(width); 35 | m_rendered_width = width; 36 | } 37 | #endif 38 | } 39 | 40 | void browser_document::draw(Rect const& rect) 41 | { 42 | #ifdef USE_LITEHTML 43 | if (m_lite_document) 44 | { 45 | litehtml::position clip(rect.left + m_scroll.h, rect.top + m_scroll.v, rect_width(rect), rect_height(rect)); 46 | m_lite_document->draw(0, -m_scroll.h, -m_scroll.v, &clip); 47 | //debugprintf("draw doc origin x=%d y=%d clip x=%d y=%d w=%d h=%d", -m_scroll.h, -m_scroll.v, clip.x, clip.y, clip.width, clip.height); 48 | } 49 | #endif 50 | } 51 | 52 | Point browser_document::get_dimensions() 53 | { 54 | Point dimensions; 55 | #ifdef USE_LITEHTML 56 | if (m_lite_document) 57 | SetPt(&dimensions, m_lite_document->width(), m_lite_document->height()); 58 | else 59 | #endif 60 | SetPt(&dimensions, 0, 0); 61 | return dimensions; 62 | } 63 | -------------------------------------------------------------------------------- /src/browser_window.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "browser_window.h" 6 | 7 | #include 8 | #include "ResourceConstants.h" 9 | #include "helpers.h" 10 | 11 | browser_window::browser_window() 12 | : base_window(r_WIND_browser), 13 | m_selected_document(std::make_shared()), 14 | m_root_control(m_window), 15 | m_header(r_CNTL_header, m_window), 16 | m_back_button(r_CNTL_back_button, m_window), 17 | m_forward_button(r_CNTL_forward_button, m_window), 18 | m_home_button(r_CNTL_home_button, m_window), 19 | m_reload_button(r_CNTL_reload_button, m_window), 20 | m_address_bar(r_CNTL_address_bar, m_window), 21 | m_browser(r_CNTL_browser, m_window), 22 | m_horizontal_scroll_bar(r_CNTL_horizontal_scroll_bar, m_window), 23 | m_vertical_scroll_bar(r_CNTL_vertical_scroll_bar, m_window) 24 | { 25 | m_header.embed(m_back_button); 26 | m_header.embed(m_forward_button); 27 | m_header.embed(m_home_button); 28 | m_header.embed(m_reload_button); 29 | m_header.embed(m_address_bar); 30 | #ifdef USE_LITEHTML 31 | Handle html = Get1Resource('TEXT', r_TEXT_html); 32 | if (!html) 33 | throw std::bad_alloc(); 34 | int16_t saved_state = HGetState(html); 35 | HLock(html); 36 | m_selected_document->set_html(*html, m_browser); 37 | HSetState(html, saved_state); 38 | m_browser.set_document(m_selected_document); 39 | render_if_needed_and_update_scrollbars(); 40 | #endif 41 | } 42 | 43 | browser_window::~browser_window() 44 | { 45 | } 46 | 47 | int16_t browser_window::get_minimum_height() 48 | { 49 | return 128; 50 | } 51 | 52 | int16_t browser_window::get_minimum_width() 53 | { 54 | return 128; 55 | } 56 | 57 | void browser_window::did_resize(int16_t dx, int16_t dy, int16_t part) 58 | { 59 | base_window::did_resize(dx, dy, part); 60 | render_if_needed_and_update_scrollbars(); 61 | } 62 | 63 | void browser_window::render_if_needed_and_update_scrollbars() 64 | { 65 | Rect rect = m_browser.get_rect(); 66 | int16_t width = rect_width(rect); 67 | m_selected_document->render_if_needed(width); 68 | Point dimensions = m_selected_document->get_dimensions(); 69 | dimensions.h -= width; 70 | if (dimensions.h < 0) dimensions.h = 0; 71 | dimensions.v -= rect_height(rect); 72 | if (dimensions.v < 0) dimensions.v = 0; 73 | m_vertical_scroll_bar.set_maximum(dimensions.v); 74 | m_horizontal_scroll_bar.set_maximum(dimensions.h); 75 | } 76 | -------------------------------------------------------------------------------- /src/cmake/text_to_rez.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | file(READ ${infile} data) 6 | set(result 0) 7 | while(NOT ${result} EQUAL -1) 8 | string(FIND "${data}" "" result) 11 | if(NOT ${result} EQUAL -1) 12 | string(REGEX REPLACE "-->(.*)" "<><>\\1" data "${data}") 13 | string(REGEX REPLACE " 6 | 7 | 8 | 9 | Hello World 10 | 11 | 12 |

The quick brown fox jumps over the lazy dog.

13 | 14 | 15 | -------------------------------------------------------------------------------- /src/machine.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "machine.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | bool machine::has_128k_rom() 13 | { 14 | #ifdef __m68k__ 15 | return LMGetROM85() >= 0; 16 | #else 17 | return true; 18 | #endif 19 | } 20 | 21 | bool machine::has_gestalt() 22 | { 23 | #ifdef __m68k__ 24 | static int16_t s_has_gestalt = -1; 25 | if (-1 == s_has_gestalt) 26 | s_has_gestalt = has_128k_rom() && trap_available(_Gestalt); 27 | return s_has_gestalt; 28 | #else 29 | return true; 30 | #endif 31 | } 32 | 33 | int16_t machine::get_system_version() 34 | { 35 | static int16_t s_system_version = -1; 36 | if (-1 == s_system_version) 37 | { 38 | int32_t result; 39 | if (has_gestalt() && Gestalt(gestaltSystemVersion, &result) != noErr) 40 | s_system_version = LoWord(result); 41 | else 42 | s_system_version = 0; 43 | } 44 | return s_system_version; 45 | } 46 | 47 | bool machine::has_appearance() 48 | { 49 | static int16_t s_has_appearance = -1; 50 | if (-1 == s_has_appearance) 51 | { 52 | int32_t result; 53 | if (has_gestalt() && Gestalt(gestaltAppearanceAttr, &result) == noErr) 54 | s_has_appearance = LoWord(result) & (1 << gestaltAppearanceExists); 55 | else 56 | s_has_appearance = false; 57 | } 58 | return s_has_appearance; 59 | } 60 | 61 | bool machine::trap_available(uint16_t trap) 62 | { 63 | TrapType trap_type = get_trap_type(trap); 64 | if (ToolTrap == trap_type) { 65 | trap &= 0x03FF; 66 | if (trap >= get_num_toolbox_traps()) 67 | trap = _Unimplemented; 68 | } 69 | return NGetTrapAddress(trap, trap_type) != NGetTrapAddress(_Unimplemented, ToolTrap); 70 | } 71 | 72 | TrapType machine::get_trap_type(uint16_t trap) 73 | { 74 | return (trap & 0x0800) ? ToolTrap : OSTrap; 75 | } 76 | 77 | int16_t machine::get_num_toolbox_traps() 78 | { 79 | return (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap)) ? 0x200 : 0x400; 80 | } 81 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "browser_app.h" 6 | 7 | int main() 8 | { 9 | browser_app().run(); 10 | } 11 | -------------------------------------------------------------------------------- /src/oserr_exception.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "oserr_exception.h" 6 | 7 | oserr_exception::oserr_exception(OSErr err) 8 | : m_err(err) 9 | { 10 | } 11 | 12 | oserr_exception::~oserr_exception() 13 | { 14 | } 15 | 16 | OSErr oserr_exception::err() const 17 | { 18 | return m_err; 19 | } 20 | -------------------------------------------------------------------------------- /src/quickdraw_container.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | // Mac headers 6 | //#include 7 | #include 8 | #include 9 | 10 | // System headers 11 | 12 | // Third-party headers 13 | 14 | // My headers 15 | #include "quickdraw_container.h" 16 | #include "quickdraw_font.h" 17 | 18 | quickdraw_container::quickdraw_container() 19 | { 20 | } 21 | 22 | quickdraw_container::~quickdraw_container() 23 | { 24 | } 25 | 26 | litehtml::uint_ptr quickdraw_container::create_font(const char *face_name, int size, int weight, litehtml::font_style italic_style, unsigned int decoration, litehtml::font_metrics *fm) 27 | { 28 | StyleParameter style = normal; 29 | if (weight >= 700) 30 | style |= bold; 31 | if (litehtml::font_style_italic == italic_style) 32 | style |= italic; 33 | if (decoration & litehtml::font_decoration_underline) 34 | style |= underline; 35 | quickdraw_font *font = new quickdraw_font(face_name, size, style); 36 | FontInfo& metrics = font->metrics(); 37 | fm->height = metrics.ascent + metrics.descent + metrics.leading; 38 | fm->ascent = metrics.ascent; 39 | fm->descent = metrics.descent; 40 | // TODO: find real x-height 41 | fm->x_height = fm->height / 2; 42 | return reinterpret_cast(font); 43 | } 44 | 45 | void quickdraw_container::delete_font(litehtml::uint_ptr hFont) 46 | { 47 | quickdraw_font *font = reinterpret_cast(hFont); 48 | delete font; 49 | } 50 | 51 | int quickdraw_container::text_width(const char *text, litehtml::uint_ptr hFont) 52 | { 53 | quickdraw_font *font = reinterpret_cast(hFont); 54 | return font->width(text); 55 | } 56 | 57 | void quickdraw_container::draw_text(litehtml::uint_ptr hdc, const char *text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) 58 | { 59 | quickdraw_font *font = reinterpret_cast(hFont); 60 | font->draw(text, pos); 61 | } 62 | 63 | int quickdraw_container::pt_to_px(int pt) const 64 | { 65 | return pt; 66 | } 67 | 68 | int quickdraw_container::get_default_font_size() const 69 | { 70 | return pt_to_px(12); 71 | } 72 | 73 | const char *quickdraw_container::get_default_font_name() const 74 | { 75 | return "Helvetica"; 76 | } 77 | 78 | void quickdraw_container::draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker& marker) 79 | { 80 | } 81 | 82 | void quickdraw_container::load_image(const char *src, const char *base_url, bool redraw_on_ready) 83 | { 84 | } 85 | 86 | void quickdraw_container::get_image_size(const char *src, const char *base_url, litehtml::size& sz) 87 | { 88 | } 89 | 90 | void quickdraw_container::draw_background(litehtml::uint_ptr hdc, const litehtml::background_paint& bg) 91 | { 92 | } 93 | 94 | void quickdraw_container::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root) 95 | { 96 | } 97 | 98 | void quickdraw_container::set_base_url(const char *base_url) 99 | { 100 | } 101 | 102 | void quickdraw_container::link(const std::shared_ptr& doc, const litehtml::element::ptr& el) 103 | { 104 | } 105 | 106 | void quickdraw_container::on_anchor_click(const char *url, const litehtml::element::ptr& el) 107 | { 108 | } 109 | 110 | void quickdraw_container::set_cursor(const char *cursor) 111 | { 112 | } 113 | 114 | void quickdraw_container::transform_text(litehtml::string& text, litehtml::text_transform tt) 115 | { 116 | if (!text.empty()) 117 | { 118 | switch (tt) 119 | { 120 | case litehtml::text_transform_capitalize: 121 | UppercaseText(&text[0], 1, smCurrentScript); 122 | break; 123 | case litehtml::text_transform_uppercase: 124 | UppercaseText(&text[0], text.length(), smCurrentScript); 125 | break; 126 | case litehtml::text_transform_lowercase: 127 | LowercaseText(&text[0], text.length(), smCurrentScript); 128 | break; 129 | } 130 | } 131 | } 132 | 133 | void quickdraw_container::import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& base_url) 134 | { 135 | } 136 | 137 | void quickdraw_container::set_clip(const litehtml::position& pos, const litehtml::border_radiuses& radiuses, bool valid_x, bool valid_y) 138 | { 139 | } 140 | 141 | void quickdraw_container::del_clip() 142 | { 143 | } 144 | 145 | std::shared_ptr quickdraw_container::create_element(const char *tag_name, const litehtml::string_map &attributes, const std::shared_ptr &doc) 146 | { 147 | return nullptr; 148 | } 149 | 150 | void quickdraw_container::get_media_features(litehtml::media_features& media) const 151 | { 152 | } 153 | 154 | void quickdraw_container::get_language(litehtml::string& language, litehtml::string& culture) const 155 | { 156 | } 157 | -------------------------------------------------------------------------------- /src/quickdraw_font.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | // Mac headers 6 | #include 7 | #include 8 | 9 | // System headers 10 | 11 | // Third-party headers 12 | 13 | // My headers 14 | #include "quickdraw_font.h" 15 | 16 | quickdraw_font::quickdraw_font(const char *name, int16_t size, StyleParameter style) 17 | : m_size(size), m_style(style) 18 | { 19 | getfnum(name, &m_id); 20 | construct(); 21 | } 22 | 23 | quickdraw_font::quickdraw_font(int16_t id, int16_t size, StyleParameter style) 24 | : m_id(id), m_size(size), m_style(style) 25 | { 26 | construct(); 27 | } 28 | 29 | quickdraw_font::~quickdraw_font() 30 | { 31 | GrafPtr saved_port; 32 | GetPort(&saved_port); 33 | ClosePort(&m_port); 34 | } 35 | 36 | void quickdraw_font::draw(const char *text, const litehtml::position& pos) 37 | { 38 | set_port_font(); 39 | MoveTo(pos.x, pos.y + m_metrics.ascent); 40 | // MoveTo(pos.x, pos.y + pos.height - m_metrics.descent); 41 | DrawText(text, 0, strlen(text)); 42 | /* 43 | Rect r; 44 | SetRect(&r, pos.left(), pos.top(), pos.right(), pos.bottom()); 45 | FrameRect(&r); 46 | */ 47 | } 48 | 49 | int16_t quickdraw_font::width(const char *text) 50 | { 51 | GrafPtr saved_port; 52 | GetPort(&saved_port); 53 | SetPort(&m_port); 54 | int16_t width = TextWidth(text, 0, strlen(text)); 55 | SetPort(saved_port); 56 | return width; 57 | } 58 | 59 | FontInfo& quickdraw_font::metrics() 60 | { 61 | return m_metrics; 62 | } 63 | 64 | void quickdraw_font::construct() 65 | { 66 | GrafPtr saved_port; 67 | GetPort(&saved_port); 68 | OpenPort(&m_port); 69 | set_port_font(); 70 | GetFontInfo(&m_metrics); 71 | SetPort(saved_port); 72 | } 73 | 74 | void quickdraw_font::set_port_font() 75 | { 76 | TextFont(m_id); 77 | TextSize(m_size); 78 | TextFace(m_style); 79 | } 80 | -------------------------------------------------------------------------------- /src/root_control.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "root_control.h" 6 | 7 | #include 8 | 9 | root_control::root_control(WindowRecord& window) : base_control() 10 | { 11 | OSErr err = CreateRootControl(reinterpret_cast(&window), &m_control); 12 | if (noErr != err) 13 | throw std::bad_alloc(); 14 | init(); 15 | } 16 | 17 | root_control::~root_control() 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/user_control.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include 6 | #include 7 | 8 | #include "user_control.h" 9 | 10 | user_control::user_control(int16_t resource_id, WindowRecord& window) 11 | : base_control(resource_id, window) 12 | { 13 | OSErr err; 14 | 15 | { 16 | ControlUserPaneDrawUPP draw_upp = NewControlUserPaneDrawUPP(draw_proc); 17 | err = SetControlData(m_control, 0, kControlUserPaneDrawProcTag, sizeof(draw_upp), reinterpret_cast(&draw_upp)); 18 | DisposeRoutineDescriptor(reinterpret_cast(draw_upp)); 19 | } 20 | { 21 | ControlUserPaneActivateUPP activate_upp = NewControlUserPaneActivateUPP(activate_proc); 22 | err = SetControlData(m_control, 0, kControlUserPaneActivateProcTag, sizeof(activate_upp), reinterpret_cast(&activate_upp)); 23 | DisposeRoutineDescriptor(reinterpret_cast(activate_upp)); 24 | } 25 | } 26 | 27 | user_control::~user_control() 28 | { 29 | } 30 | 31 | void user_control::draw_proc(ControlHandle control, int16_t part) 32 | { 33 | if (user_control *control_obj = static_cast(get_from_control(control))) 34 | control_obj->draw(part); 35 | } 36 | 37 | void user_control::draw(int16_t part) const 38 | { 39 | } 40 | 41 | void user_control::activate_proc(ControlHandle control, Boolean activating) 42 | { 43 | if (user_control *control_obj = static_cast(get_from_control(control))) 44 | control_obj->activate(activating); 45 | } 46 | 47 | void user_control::activate(bool activating) 48 | { 49 | } 50 | -------------------------------------------------------------------------------- /third_party/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | include(ExternalProject) 6 | 7 | get_directory_property(compile_options COMPILE_OPTIONS) 8 | list(JOIN compile_options " " compile_options) 9 | 10 | get_directory_property(include_directories INCLUDE_DIRECTORIES) 11 | list(TRANSFORM include_directories PREPEND "-isystem") 12 | list(JOIN include_directories " " include_directories) 13 | 14 | if(USE_DLMALLOC) 15 | add_subdirectory(dlmalloc) 16 | endif() 17 | 18 | if(USE_LITEHTML) 19 | ExternalProject_Add(litehtml 20 | CMAKE_ARGS 21 | -DBUILD_TESTING=OFF 22 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 23 | "-DCMAKE_C_FLAGS=${include_directories} ${compile_options} -DTYPE_BOOL" 24 | "-DCMAKE_CXX_FLAGS=${include_directories} ${compile_options} -DLITEHTML_NO_THREADS" 25 | -DCMAKE_INSTALL_PREFIX= 26 | -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} 27 | INSTALL_COMMAND "" 28 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/litehtml 29 | ) 30 | 31 | ExternalProject_Get_Property(litehtml BINARY_DIR) 32 | ExternalProject_Get_Property(litehtml SOURCE_DIR) 33 | 34 | set(litehtml_INCLUDE_DIR ${SOURCE_DIR}/include 35 | CACHE PATH "litehtml header directory" 36 | ) 37 | set(litehtml_SOURCE_DIR ${SOURCE_DIR}/src 38 | CACHE PATH "litehtml source directory" 39 | ) 40 | set(litehtml_LIBRARY "${BINARY_DIR}/liblitehtml.a;${BINARY_DIR}/src/gumbo/libgumbo.a" 41 | CACHE STRING "litehtml library" 42 | ) 43 | endif() 44 | -------------------------------------------------------------------------------- /third_party/dlmalloc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | # Using dlmalloc avoids severe performance problems when using Retro68's default 6 | # NewPtr-based malloc implementation. 7 | # https://github.com/autc04/Retro68/issues/185 8 | add_library(dlmalloc OBJECT macos-dlmalloc.c) 9 | 10 | set(dlmalloc_LIBRARY "$" 11 | CACHE FILEPATH "dlmalloc library" 12 | ) 13 | 14 | set(functions 15 | bulk_free calloc free independent_calloc independent_comalloc mallinfo 16 | malloc malloc_footprint malloc_footprint_limit malloc_inspect_all 17 | malloc_max_footprint malloc_set_footprint_limit malloc_stats malloc_trim 18 | malloc_usable_size mallopt memalign posix_memalign pvalloc realloc 19 | realloc_in_place valloc 20 | ) 21 | list(TRANSFORM functions PREPEND "--wrap," OUTPUT_VARIABLE link_flags) 22 | list(PREPEND link_flags "-Wl") 23 | list(JOIN link_flags "," link_flags) 24 | set(dlmalloc_LINK_FLAGS "${link_flags}" 25 | CACHE STRING "dlmalloc link flags" 26 | ) 27 | -------------------------------------------------------------------------------- /third_party/dlmalloc/macos-dlmalloc.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2022 Ryan C Schmidt 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #undef DLMALLOC_DEBUG 6 | #include 7 | #ifdef DLMALLOC_DEBUG 8 | #include 9 | #endif 10 | #include 11 | #include 12 | 13 | #define MAP_ANON 0x1000 14 | #define MAP_FAILED ((void *)-1) 15 | 16 | #ifdef DLMALLOC_DEBUG 17 | StringPtr _retro68_pstrcat(StringPtr s1, StringPtr s2) 18 | { 19 | StringPtr p1 = &s1[s1[0]]; 20 | StringPtr p2 = &s2[0]; 21 | unsigned char s2len = s2[0]; 22 | s1[0] += s2len; 23 | while (s2len--) 24 | *++p1 = *++p2; 25 | return s1; 26 | } 27 | #endif 28 | 29 | void *_retro68_mmap(size_t size) 30 | { 31 | #ifdef DLMALLOC_DEBUG 32 | Str31 str, size_str; 33 | str[0] = 0; 34 | NumToString(size, size_str); 35 | _retro68_pstrcat(str, "\pmmap "); 36 | _retro68_pstrcat(str, size_str); 37 | _retro68_pstrcat(str, "\p bytes"); 38 | #endif 39 | Ptr ptr = NewPtr(size); 40 | if (ptr) 41 | { 42 | #ifdef DLMALLOC_DEBUG 43 | _retro68_pstrcat(str, "\p succeeded"); 44 | DebugStr(str); 45 | #endif 46 | return ptr; 47 | } 48 | #ifdef DLMALLOC_DEBUG 49 | _retro68_pstrcat(str, "\p failed"); 50 | DebugStr(str); 51 | #endif 52 | errno = ENOMEM; 53 | return MAP_FAILED; 54 | } 55 | 56 | void *_retro68_mremap(void *addr, size_t old_size, size_t new_size, int flags) 57 | { 58 | // TODO: Implement mremap using SetPtrSize? 59 | #ifdef DLMALLOC_DEBUG 60 | DebugStr("\pSomeone is calling mremap! Let's implement it."); 61 | #endif 62 | return (void *)~(size_t)0; 63 | (void)addr; 64 | (void)old_size; 65 | (void)new_size; 66 | (void)flags; 67 | } 68 | 69 | int _retro68_munmap(void *addr, size_t size) 70 | { 71 | if (size > 0) 72 | { 73 | #ifdef DLMALLOC_DEBUG 74 | Str31 str, size_str; 75 | str[0] = 0; 76 | NumToString(size, size_str); 77 | _retro68_pstrcat(str, "\pmunmap "); 78 | _retro68_pstrcat(str, size_str); 79 | _retro68_pstrcat(str, "\p bytes"); 80 | DebugStr(str); 81 | if (size != (size_t)GetPtrSize(addr)) 82 | DebugStr("\pDisposing Ptr with wrong size!"); 83 | #endif 84 | DisposePtr(addr); 85 | if (MemError() == 0) 86 | return 0; 87 | } 88 | errno = EINVAL; 89 | return -1; 90 | } 91 | 92 | #define HAVE_MMAP 1 93 | #define HAVE_MREMAP 1 94 | #define MMAP(size) _retro68_mmap((size)) 95 | #define MREMAP(addr, old_size, new_size, flags) _retro68_mremap((addr), (old_size), (new_size), (flags)) 96 | #define MUNMAP(addr, size) _retro68_munmap((addr), (size)) 97 | #define DIRECT_MMAP(s) MFAIL 98 | #define LACKS_SYS_MMAN_H 1 99 | #define MMAP_CLEARS 0 100 | #define HAVE_MORECORE 0 101 | #define malloc_getpagesize ((size_t)4096U) 102 | 103 | #define USE_DL_PREFIX 1 104 | #define dlcalloc __wrap_calloc 105 | #define dlfree __wrap_free 106 | #define dlmalloc __wrap_malloc 107 | #define dlmemalign __wrap_memalign 108 | #define dlposix_memalign __wrap_posix_memalign 109 | #define dlrealloc __wrap_realloc 110 | #define dlrealloc_in_place __wrap_realloc_in_place 111 | #define dlvalloc __wrap_valloc 112 | #define dlpvalloc __wrap_pvalloc 113 | #define dlmallinfo __wrap_mallinfo 114 | #define dlmallopt __wrap_mallopt 115 | #define dlmalloc_trim __wrap_malloc_trim 116 | #define dlmalloc_stats __wrap_malloc_stats 117 | #define dlmalloc_usable_size __wrap_malloc_usable_size 118 | #define dlmalloc_footprint __wrap_malloc_footprint 119 | #define dlmalloc_max_footprint __wrap_malloc_max_footprint 120 | #define dlmalloc_footprint_limit __wrap_malloc_footprint_limit 121 | #define dlmalloc_set_footprint_limit __wrap_malloc_set_footprint_limit 122 | #define dlmalloc_inspect_all __wrap_malloc_inspect_all 123 | #define dlindependent_calloc __wrap_independent_calloc 124 | #define dlindependent_comalloc __wrap_independent_comalloc 125 | #define dlbulk_free __wrap_bulk_free 126 | 127 | #include "malloc.c" 128 | -------------------------------------------------------------------------------- /third_party/dlmalloc/malloc.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 1992 Doug Lea 2 | // 3 | // SPDX-License-Identifier: CC0-1.0 4 | 5 | /* 6 | Default header file for malloc-2.8.x, written by Doug Lea 7 | and released to the public domain, as explained at 8 | http://creativecommons.org/publicdomain/zero/1.0/ 9 | 10 | This header is for ANSI C/C++ only. You can set any of 11 | the following #defines before including: 12 | 13 | * If USE_DL_PREFIX is defined, it is assumed that malloc.c 14 | was also compiled with this option, so all routines 15 | have names starting with "dl". 16 | 17 | * If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this 18 | file will be #included AFTER . This is needed only if 19 | your system defines a struct mallinfo that is incompatible with the 20 | standard one declared here. Otherwise, you can include this file 21 | INSTEAD of your system system . At least on ANSI, all 22 | declarations should be compatible with system versions 23 | 24 | * If MSPACES is defined, declarations for mspace versions are included. 25 | */ 26 | 27 | #ifndef MALLOC_280_H 28 | #define MALLOC_280_H 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | #include /* for size_t */ 35 | 36 | #ifndef ONLY_MSPACES 37 | #define ONLY_MSPACES 0 /* define to a value */ 38 | #elif ONLY_MSPACES != 0 39 | #define ONLY_MSPACES 1 40 | #endif /* ONLY_MSPACES */ 41 | #ifndef NO_MALLINFO 42 | #define NO_MALLINFO 0 43 | #endif /* NO_MALLINFO */ 44 | 45 | #ifndef MSPACES 46 | #if ONLY_MSPACES 47 | #define MSPACES 1 48 | #else /* ONLY_MSPACES */ 49 | #define MSPACES 0 50 | #endif /* ONLY_MSPACES */ 51 | #endif /* MSPACES */ 52 | 53 | #if !ONLY_MSPACES 54 | 55 | #ifndef USE_DL_PREFIX 56 | #define dlcalloc calloc 57 | #define dlfree free 58 | #define dlmalloc malloc 59 | #define dlmemalign memalign 60 | #define dlposix_memalign posix_memalign 61 | #define dlrealloc realloc 62 | #define dlvalloc valloc 63 | #define dlpvalloc pvalloc 64 | #define dlmallinfo mallinfo 65 | #define dlmallopt mallopt 66 | #define dlmalloc_trim malloc_trim 67 | #define dlmalloc_stats malloc_stats 68 | #define dlmalloc_usable_size malloc_usable_size 69 | #define dlmalloc_footprint malloc_footprint 70 | #define dlmalloc_max_footprint malloc_max_footprint 71 | #define dlmalloc_footprint_limit malloc_footprint_limit 72 | #define dlmalloc_set_footprint_limit malloc_set_footprint_limit 73 | #define dlmalloc_inspect_all malloc_inspect_all 74 | #define dlindependent_calloc independent_calloc 75 | #define dlindependent_comalloc independent_comalloc 76 | #define dlbulk_free bulk_free 77 | #endif /* USE_DL_PREFIX */ 78 | 79 | #if !NO_MALLINFO 80 | #ifndef HAVE_USR_INCLUDE_MALLOC_H 81 | #ifndef _MALLOC_H 82 | #ifndef MALLINFO_FIELD_TYPE 83 | #define MALLINFO_FIELD_TYPE size_t 84 | #endif /* MALLINFO_FIELD_TYPE */ 85 | #ifndef STRUCT_MALLINFO_DECLARED 86 | #define STRUCT_MALLINFO_DECLARED 1 87 | struct mallinfo { 88 | MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ 89 | MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ 90 | MALLINFO_FIELD_TYPE smblks; /* always 0 */ 91 | MALLINFO_FIELD_TYPE hblks; /* always 0 */ 92 | MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ 93 | MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ 94 | MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ 95 | MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ 96 | MALLINFO_FIELD_TYPE fordblks; /* total free space */ 97 | MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ 98 | }; 99 | #endif /* STRUCT_MALLINFO_DECLARED */ 100 | #endif /* _MALLOC_H */ 101 | #endif /* HAVE_USR_INCLUDE_MALLOC_H */ 102 | #endif /* !NO_MALLINFO */ 103 | 104 | /* 105 | malloc(size_t n) 106 | Returns a pointer to a newly allocated chunk of at least n bytes, or 107 | null if no space is available, in which case errno is set to ENOMEM 108 | on ANSI C systems. 109 | 110 | If n is zero, malloc returns a minimum-sized chunk. (The minimum 111 | size is 16 bytes on most 32bit systems, and 32 bytes on 64bit 112 | systems.) Note that size_t is an unsigned type, so calls with 113 | arguments that would be negative if signed are interpreted as 114 | requests for huge amounts of space, which will often fail. The 115 | maximum supported value of n differs across systems, but is in all 116 | cases less than the maximum representable value of a size_t. 117 | */ 118 | void* dlmalloc(size_t); 119 | 120 | /* 121 | free(void* p) 122 | Releases the chunk of memory pointed to by p, that had been previously 123 | allocated using malloc or a related routine such as realloc. 124 | It has no effect if p is null. If p was not malloced or already 125 | freed, free(p) will by default cuase the current program to abort. 126 | */ 127 | void dlfree(void*); 128 | 129 | /* 130 | calloc(size_t n_elements, size_t element_size); 131 | Returns a pointer to n_elements * element_size bytes, with all locations 132 | set to zero. 133 | */ 134 | void* dlcalloc(size_t, size_t); 135 | 136 | /* 137 | realloc(void* p, size_t n) 138 | Returns a pointer to a chunk of size n that contains the same data 139 | as does chunk p up to the minimum of (n, p's size) bytes, or null 140 | if no space is available. 141 | 142 | The returned pointer may or may not be the same as p. The algorithm 143 | prefers extending p in most cases when possible, otherwise it 144 | employs the equivalent of a malloc-copy-free sequence. 145 | 146 | If p is null, realloc is equivalent to malloc. 147 | 148 | If space is not available, realloc returns null, errno is set (if on 149 | ANSI) and p is NOT freed. 150 | 151 | if n is for fewer bytes than already held by p, the newly unused 152 | space is lopped off and freed if possible. realloc with a size 153 | argument of zero (re)allocates a minimum-sized chunk. 154 | 155 | The old unix realloc convention of allowing the last-free'd chunk 156 | to be used as an argument to realloc is not supported. 157 | */ 158 | void* dlrealloc(void*, size_t); 159 | 160 | /* 161 | realloc_in_place(void* p, size_t n) 162 | Resizes the space allocated for p to size n, only if this can be 163 | done without moving p (i.e., only if there is adjacent space 164 | available if n is greater than p's current allocated size, or n is 165 | less than or equal to p's size). This may be used instead of plain 166 | realloc if an alternative allocation strategy is needed upon failure 167 | to expand space; for example, reallocation of a buffer that must be 168 | memory-aligned or cleared. You can use realloc_in_place to trigger 169 | these alternatives only when needed. 170 | 171 | Returns p if successful; otherwise null. 172 | */ 173 | void* dlrealloc_in_place(void*, size_t); 174 | 175 | /* 176 | memalign(size_t alignment, size_t n); 177 | Returns a pointer to a newly allocated chunk of n bytes, aligned 178 | in accord with the alignment argument. 179 | 180 | The alignment argument should be a power of two. If the argument is 181 | not a power of two, the nearest greater power is used. 182 | 8-byte alignment is guaranteed by normal malloc calls, so don't 183 | bother calling memalign with an argument of 8 or less. 184 | 185 | Overreliance on memalign is a sure way to fragment space. 186 | */ 187 | void* dlmemalign(size_t, size_t); 188 | 189 | /* 190 | int posix_memalign(void** pp, size_t alignment, size_t n); 191 | Allocates a chunk of n bytes, aligned in accord with the alignment 192 | argument. Differs from memalign only in that it (1) assigns the 193 | allocated memory to *pp rather than returning it, (2) fails and 194 | returns EINVAL if the alignment is not a power of two (3) fails and 195 | returns ENOMEM if memory cannot be allocated. 196 | */ 197 | int dlposix_memalign(void**, size_t, size_t); 198 | 199 | /* 200 | valloc(size_t n); 201 | Equivalent to memalign(pagesize, n), where pagesize is the page 202 | size of the system. If the pagesize is unknown, 4096 is used. 203 | */ 204 | void* dlvalloc(size_t); 205 | 206 | /* 207 | mallopt(int parameter_number, int parameter_value) 208 | Sets tunable parameters The format is to provide a 209 | (parameter-number, parameter-value) pair. mallopt then sets the 210 | corresponding parameter to the argument value if it can (i.e., so 211 | long as the value is meaningful), and returns 1 if successful else 212 | 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, 213 | normally defined in malloc.h. None of these are use in this malloc, 214 | so setting them has no effect. But this malloc also supports other 215 | options in mallopt: 216 | 217 | Symbol param # default allowed param values 218 | M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) 219 | M_GRANULARITY -2 page size any power of 2 >= page size 220 | M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) 221 | */ 222 | int dlmallopt(int, int); 223 | 224 | #define M_TRIM_THRESHOLD (-1) 225 | #define M_GRANULARITY (-2) 226 | #define M_MMAP_THRESHOLD (-3) 227 | 228 | 229 | /* 230 | malloc_footprint(); 231 | Returns the number of bytes obtained from the system. The total 232 | number of bytes allocated by malloc, realloc etc., is less than this 233 | value. Unlike mallinfo, this function returns only a precomputed 234 | result, so can be called frequently to monitor memory consumption. 235 | Even if locks are otherwise defined, this function does not use them, 236 | so results might not be up to date. 237 | */ 238 | size_t dlmalloc_footprint(void); 239 | 240 | /* 241 | malloc_max_footprint(); 242 | Returns the maximum number of bytes obtained from the system. This 243 | value will be greater than current footprint if deallocated space 244 | has been reclaimed by the system. The peak number of bytes allocated 245 | by malloc, realloc etc., is less than this value. Unlike mallinfo, 246 | this function returns only a precomputed result, so can be called 247 | frequently to monitor memory consumption. Even if locks are 248 | otherwise defined, this function does not use them, so results might 249 | not be up to date. 250 | */ 251 | size_t dlmalloc_max_footprint(void); 252 | 253 | /* 254 | malloc_footprint_limit(); 255 | Returns the number of bytes that the heap is allowed to obtain from 256 | the system, returning the last value returned by 257 | malloc_set_footprint_limit, or the maximum size_t value if 258 | never set. The returned value reflects a permission. There is no 259 | guarantee that this number of bytes can actually be obtained from 260 | the system. 261 | */ 262 | size_t dlmalloc_footprint_limit(void); 263 | 264 | /* 265 | malloc_set_footprint_limit(); 266 | Sets the maximum number of bytes to obtain from the system, causing 267 | failure returns from malloc and related functions upon attempts to 268 | exceed this value. The argument value may be subject to page 269 | rounding to an enforceable limit; this actual value is returned. 270 | Using an argument of the maximum possible size_t effectively 271 | disables checks. If the argument is less than or equal to the 272 | current malloc_footprint, then all future allocations that require 273 | additional system memory will fail. However, invocation cannot 274 | retroactively deallocate existing used memory. 275 | */ 276 | size_t dlmalloc_set_footprint_limit(size_t bytes); 277 | 278 | /* 279 | malloc_inspect_all(void(*handler)(void *start, 280 | void *end, 281 | size_t used_bytes, 282 | void* callback_arg), 283 | void* arg); 284 | Traverses the heap and calls the given handler for each managed 285 | region, skipping all bytes that are (or may be) used for bookkeeping 286 | purposes. Traversal does not include include chunks that have been 287 | directly memory mapped. Each reported region begins at the start 288 | address, and continues up to but not including the end address. The 289 | first used_bytes of the region contain allocated data. If 290 | used_bytes is zero, the region is unallocated. The handler is 291 | invoked with the given callback argument. If locks are defined, they 292 | are held during the entire traversal. It is a bad idea to invoke 293 | other malloc functions from within the handler. 294 | 295 | For example, to count the number of in-use chunks with size greater 296 | than 1000, you could write: 297 | static int count = 0; 298 | void count_chunks(void* start, void* end, size_t used, void* arg) { 299 | if (used >= 1000) ++count; 300 | } 301 | then: 302 | malloc_inspect_all(count_chunks, NULL); 303 | 304 | malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. 305 | */ 306 | void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), 307 | void* arg); 308 | 309 | #if !NO_MALLINFO 310 | /* 311 | mallinfo() 312 | Returns (by copy) a struct containing various summary statistics: 313 | 314 | arena: current total non-mmapped bytes allocated from system 315 | ordblks: the number of free chunks 316 | smblks: always zero. 317 | hblks: current number of mmapped regions 318 | hblkhd: total bytes held in mmapped regions 319 | usmblks: the maximum total allocated space. This will be greater 320 | than current total if trimming has occurred. 321 | fsmblks: always zero 322 | uordblks: current total allocated space (normal or mmapped) 323 | fordblks: total free space 324 | keepcost: the maximum number of bytes that could ideally be released 325 | back to system via malloc_trim. ("ideally" means that 326 | it ignores page restrictions etc.) 327 | 328 | Because these fields are ints, but internal bookkeeping may 329 | be kept as longs, the reported values may wrap around zero and 330 | thus be inaccurate. 331 | */ 332 | 333 | struct mallinfo dlmallinfo(void); 334 | #endif /* NO_MALLINFO */ 335 | 336 | /* 337 | independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); 338 | 339 | independent_calloc is similar to calloc, but instead of returning a 340 | single cleared space, it returns an array of pointers to n_elements 341 | independent elements that can hold contents of size elem_size, each 342 | of which starts out cleared, and can be independently freed, 343 | realloc'ed etc. The elements are guaranteed to be adjacently 344 | allocated (this is not guaranteed to occur with multiple callocs or 345 | mallocs), which may also improve cache locality in some 346 | applications. 347 | 348 | The "chunks" argument is optional (i.e., may be null, which is 349 | probably the most typical usage). If it is null, the returned array 350 | is itself dynamically allocated and should also be freed when it is 351 | no longer needed. Otherwise, the chunks array must be of at least 352 | n_elements in length. It is filled in with the pointers to the 353 | chunks. 354 | 355 | In either case, independent_calloc returns this pointer array, or 356 | null if the allocation failed. If n_elements is zero and "chunks" 357 | is null, it returns a chunk representing an array with zero elements 358 | (which should be freed if not wanted). 359 | 360 | Each element must be freed when it is no longer needed. This can be 361 | done all at once using bulk_free. 362 | 363 | independent_calloc simplifies and speeds up implementations of many 364 | kinds of pools. It may also be useful when constructing large data 365 | structures that initially have a fixed number of fixed-sized nodes, 366 | but the number is not known at compile time, and some of the nodes 367 | may later need to be freed. For example: 368 | 369 | struct Node { int item; struct Node* next; }; 370 | 371 | struct Node* build_list() { 372 | struct Node** pool; 373 | int n = read_number_of_nodes_needed(); 374 | if (n <= 0) return 0; 375 | pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); 376 | if (pool == 0) die(); 377 | // organize into a linked list... 378 | struct Node* first = pool[0]; 379 | for (i = 0; i < n-1; ++i) 380 | pool[i]->next = pool[i+1]; 381 | free(pool); // Can now free the array (or not, if it is needed later) 382 | return first; 383 | } 384 | */ 385 | void** dlindependent_calloc(size_t, size_t, void**); 386 | 387 | /* 388 | independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); 389 | 390 | independent_comalloc allocates, all at once, a set of n_elements 391 | chunks with sizes indicated in the "sizes" array. It returns 392 | an array of pointers to these elements, each of which can be 393 | independently freed, realloc'ed etc. The elements are guaranteed to 394 | be adjacently allocated (this is not guaranteed to occur with 395 | multiple callocs or mallocs), which may also improve cache locality 396 | in some applications. 397 | 398 | The "chunks" argument is optional (i.e., may be null). If it is null 399 | the returned array is itself dynamically allocated and should also 400 | be freed when it is no longer needed. Otherwise, the chunks array 401 | must be of at least n_elements in length. It is filled in with the 402 | pointers to the chunks. 403 | 404 | In either case, independent_comalloc returns this pointer array, or 405 | null if the allocation failed. If n_elements is zero and chunks is 406 | null, it returns a chunk representing an array with zero elements 407 | (which should be freed if not wanted). 408 | 409 | Each element must be freed when it is no longer needed. This can be 410 | done all at once using bulk_free. 411 | 412 | independent_comallac differs from independent_calloc in that each 413 | element may have a different size, and also that it does not 414 | automatically clear elements. 415 | 416 | independent_comalloc can be used to speed up allocation in cases 417 | where several structs or objects must always be allocated at the 418 | same time. For example: 419 | 420 | struct Head { ... } 421 | struct Foot { ... } 422 | 423 | void send_message(char* msg) { 424 | int msglen = strlen(msg); 425 | size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; 426 | void* chunks[3]; 427 | if (independent_comalloc(3, sizes, chunks) == 0) 428 | die(); 429 | struct Head* head = (struct Head*)(chunks[0]); 430 | char* body = (char*)(chunks[1]); 431 | struct Foot* foot = (struct Foot*)(chunks[2]); 432 | // ... 433 | } 434 | 435 | In general though, independent_comalloc is worth using only for 436 | larger values of n_elements. For small values, you probably won't 437 | detect enough difference from series of malloc calls to bother. 438 | 439 | Overuse of independent_comalloc can increase overall memory usage, 440 | since it cannot reuse existing noncontiguous small chunks that 441 | might be available for some of the elements. 442 | */ 443 | void** dlindependent_comalloc(size_t, size_t*, void**); 444 | 445 | /* 446 | bulk_free(void* array[], size_t n_elements) 447 | Frees and clears (sets to null) each non-null pointer in the given 448 | array. This is likely to be faster than freeing them one-by-one. 449 | If footers are used, pointers that have been allocated in different 450 | mspaces are not freed or cleared, and the count of all such pointers 451 | is returned. For large arrays of pointers with poor locality, it 452 | may be worthwhile to sort this array before calling bulk_free. 453 | */ 454 | size_t dlbulk_free(void**, size_t n_elements); 455 | 456 | /* 457 | pvalloc(size_t n); 458 | Equivalent to valloc(minimum-page-that-holds(n)), that is, 459 | round up n to nearest pagesize. 460 | */ 461 | void* dlpvalloc(size_t); 462 | 463 | /* 464 | malloc_trim(size_t pad); 465 | 466 | If possible, gives memory back to the system (via negative arguments 467 | to sbrk) if there is unused memory at the `high' end of the malloc 468 | pool or in unused MMAP segments. You can call this after freeing 469 | large blocks of memory to potentially reduce the system-level memory 470 | requirements of a program. However, it cannot guarantee to reduce 471 | memory. Under some allocation patterns, some large free blocks of 472 | memory will be locked between two used chunks, so they cannot be 473 | given back to the system. 474 | 475 | The `pad' argument to malloc_trim represents the amount of free 476 | trailing space to leave untrimmed. If this argument is zero, only 477 | the minimum amount of memory to maintain internal data structures 478 | will be left. Non-zero arguments can be supplied to maintain enough 479 | trailing space to service future expected allocations without having 480 | to re-obtain memory from the system. 481 | 482 | Malloc_trim returns 1 if it actually released any memory, else 0. 483 | */ 484 | int dlmalloc_trim(size_t); 485 | 486 | /* 487 | malloc_stats(); 488 | Prints on stderr the amount of space obtained from the system (both 489 | via sbrk and mmap), the maximum amount (which may be more than 490 | current if malloc_trim and/or munmap got called), and the current 491 | number of bytes allocated via malloc (or realloc, etc) but not yet 492 | freed. Note that this is the number of bytes allocated, not the 493 | number requested. It will be larger than the number requested 494 | because of alignment and bookkeeping overhead. Because it includes 495 | alignment wastage as being in use, this figure may be greater than 496 | zero even when no user-level chunks are allocated. 497 | 498 | The reported current and maximum system memory can be inaccurate if 499 | a program makes other calls to system memory allocation functions 500 | (normally sbrk) outside of malloc. 501 | 502 | malloc_stats prints only the most commonly interesting statistics. 503 | More information can be obtained by calling mallinfo. 504 | 505 | malloc_stats is not compiled if NO_MALLOC_STATS is defined. 506 | */ 507 | void dlmalloc_stats(void); 508 | 509 | #endif /* !ONLY_MSPACES */ 510 | 511 | /* 512 | malloc_usable_size(void* p); 513 | 514 | Returns the number of bytes you can actually use in 515 | an allocated chunk, which may be more than you requested (although 516 | often not) due to alignment and minimum size constraints. 517 | You can use this many bytes without worrying about 518 | overwriting other allocated objects. This is not a particularly great 519 | programming practice. malloc_usable_size can be more useful in 520 | debugging and assertions, for example: 521 | 522 | p = malloc(n); 523 | assert(malloc_usable_size(p) >= 256); 524 | */ 525 | size_t dlmalloc_usable_size(const void*); 526 | 527 | #if MSPACES 528 | 529 | /* 530 | mspace is an opaque type representing an independent 531 | region of space that supports mspace_malloc, etc. 532 | */ 533 | typedef void* mspace; 534 | 535 | /* 536 | create_mspace creates and returns a new independent space with the 537 | given initial capacity, or, if 0, the default granularity size. It 538 | returns null if there is no system memory available to create the 539 | space. If argument locked is non-zero, the space uses a separate 540 | lock to control access. The capacity of the space will grow 541 | dynamically as needed to service mspace_malloc requests. You can 542 | control the sizes of incremental increases of this space by 543 | compiling with a different DEFAULT_GRANULARITY or dynamically 544 | setting with mallopt(M_GRANULARITY, value). 545 | */ 546 | mspace create_mspace(size_t capacity, int locked); 547 | 548 | /* 549 | destroy_mspace destroys the given space, and attempts to return all 550 | of its memory back to the system, returning the total number of 551 | bytes freed. After destruction, the results of access to all memory 552 | used by the space become undefined. 553 | */ 554 | size_t destroy_mspace(mspace msp); 555 | 556 | /* 557 | create_mspace_with_base uses the memory supplied as the initial base 558 | of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this 559 | space is used for bookkeeping, so the capacity must be at least this 560 | large. (Otherwise 0 is returned.) When this initial space is 561 | exhausted, additional memory will be obtained from the system. 562 | Destroying this space will deallocate all additionally allocated 563 | space (if possible) but not the initial base. 564 | */ 565 | mspace create_mspace_with_base(void* base, size_t capacity, int locked); 566 | 567 | /* 568 | mspace_track_large_chunks controls whether requests for large chunks 569 | are allocated in their own untracked mmapped regions, separate from 570 | others in this mspace. By default large chunks are not tracked, 571 | which reduces fragmentation. However, such chunks are not 572 | necessarily released to the system upon destroy_mspace. Enabling 573 | tracking by setting to true may increase fragmentation, but avoids 574 | leakage when relying on destroy_mspace to release all memory 575 | allocated using this space. The function returns the previous 576 | setting. 577 | */ 578 | int mspace_track_large_chunks(mspace msp, int enable); 579 | 580 | #if !NO_MALLINFO 581 | /* 582 | mspace_mallinfo behaves as mallinfo, but reports properties of 583 | the given space. 584 | */ 585 | struct mallinfo mspace_mallinfo(mspace msp); 586 | #endif /* NO_MALLINFO */ 587 | 588 | /* 589 | An alias for mallopt. 590 | */ 591 | int mspace_mallopt(int, int); 592 | 593 | /* 594 | The following operate identically to their malloc counterparts 595 | but operate only for the given mspace argument 596 | */ 597 | void* mspace_malloc(mspace msp, size_t bytes); 598 | void mspace_free(mspace msp, void* mem); 599 | void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); 600 | void* mspace_realloc(mspace msp, void* mem, size_t newsize); 601 | void* mspace_realloc_in_place(mspace msp, void* mem, size_t newsize); 602 | void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); 603 | void** mspace_independent_calloc(mspace msp, size_t n_elements, 604 | size_t elem_size, void* chunks[]); 605 | void** mspace_independent_comalloc(mspace msp, size_t n_elements, 606 | size_t sizes[], void* chunks[]); 607 | size_t mspace_bulk_free(mspace msp, void**, size_t n_elements); 608 | size_t mspace_usable_size(const void* mem); 609 | void mspace_malloc_stats(mspace msp); 610 | int mspace_trim(mspace msp, size_t pad); 611 | size_t mspace_footprint(mspace msp); 612 | size_t mspace_max_footprint(mspace msp); 613 | size_t mspace_footprint_limit(mspace msp); 614 | size_t mspace_set_footprint_limit(mspace msp, size_t bytes); 615 | void mspace_inspect_all(mspace msp, 616 | void(*handler)(void *, void *, size_t, void*), 617 | void* arg); 618 | #endif /* MSPACES */ 619 | 620 | #ifdef __cplusplus 621 | }; /* end of extern "C" */ 622 | #endif 623 | 624 | #endif /* MALLOC_280_H */ 625 | --------------------------------------------------------------------------------