{"id":23414179,"url":"https://github.com/betterbuiltfool/simple_events","last_synced_at":"2025-08-12T22:38:45.117Z","repository":{"id":261717775,"uuid":"884543939","full_name":"BetterBuiltFool/simple_events","owner":"BetterBuiltFool","description":"Pygame event handling system","archived":false,"fork":false,"pushed_at":"2024-12-01T14:49:36.000Z","size":210,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-12T05:44:41.282Z","etag":null,"topics":["pygame-ce","pygame-library","pygame-wasm","python-3","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BetterBuiltFool.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-07T00:11:45.000Z","updated_at":"2025-01-12T18:30:15.000Z","dependencies_parsed_at":null,"dependency_job_id":"a0067a6b-6ad4-4a7c-913d-0496982a3c70","html_url":"https://github.com/BetterBuiltFool/simple_events","commit_stats":null,"previous_names":["betterbuiltfool/event_handler","betterbuiltfool/simple_events"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/BetterBuiltFool/simple_events","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetterBuiltFool%2Fsimple_events","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetterBuiltFool%2Fsimple_events/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetterBuiltFool%2Fsimple_events/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetterBuiltFool%2Fsimple_events/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BetterBuiltFool","download_url":"https://codeload.github.com/BetterBuiltFool/simple_events/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetterBuiltFool%2Fsimple_events/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270148249,"owners_count":24535696,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-12T02:00:09.011Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pygame-ce","pygame-library","pygame-wasm","python-3","python3"],"created_at":"2024-12-22T19:57:22.640Z","updated_at":"2025-08-12T22:38:45.084Z","avatar_url":"https://github.com/BetterBuiltFool.png","language":"Python","readme":"\u003c!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 --\u003e\n\u003ca id=\"readme-top\"\u003e\u003c/a\u003e\n\u003c!--\n*** Thanks for checking out the Best-README-Template. If you have a suggestion\n*** that would make this better, please fork the repo and create a pull request\n*** or simply open an issue with the tag \"enhancement\".\n*** Don't forget to give the project a star!\n*** Thanks again! Now go create something AMAZING! :D\n--\u003e\n\n\n\n\u003c!-- PROJECT SHIELDS --\u003e\n\u003c!--\n*** I'm using markdown \"reference style\" links for readability.\n*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).\n*** See the bottom of this document for the declaration of the reference variables\n*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.\n*** https://www.markdownguide.org/basic-syntax/#reference-style-links\n--\u003e\n[![Contributors][contributors-shield]][contributors-url]\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n[![MIT License][license-shield]][license-url]\n\u003c!--\n[![LinkedIn][linkedin-shield]][linkedin-url]\n--\u003e\n\n\n\n\u003c!-- PROJECT LOGO --\u003e\n\u003cbr /\u003e\n\u003c!--\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://github.com/BetterBuiltFool/simple_events\"\u003e\n    \u003cimg src=\"images/logo.png\" alt=\"Logo\" width=\"80\" height=\"80\"\u003e\n  \u003c/a\u003e\n--\u003e\n\n\u003ch3 align=\"center\"\u003eSimple Events\u003c/h3\u003e\n\n  \u003cp align=\"center\"\u003e\n    A simple, decorator-based event system for Pygame.\n    \u003cbr /\u003e\n    \u003ca href=\"https://github.com/BetterBuiltFool/simple_events\"\u003e\u003cstrong\u003eExplore the docs »\u003c/strong\u003e\u003c/a\u003e\n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    \u003c!--\n    \u003ca href=\"https://github.com/BetterBuiltFool/simple_events\"\u003eView Demo\u003c/a\u003e\n    ·\n    --\u003e\n    \u003ca href=\"https://github.com/BetterBuiltFool/simple_events/issues/new?labels=bug\u0026template=bug-report---.md\"\u003eReport Bug\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/BetterBuiltFool/simple_events/issues/new?labels=enhancement\u0026template=feature-request---.md\"\u003eRequest Feature\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n\n\n\u003c!-- TABLE OF CONTENTS --\u003e\n\u003cdetails\u003e\n  \u003csummary\u003eTable of Contents\u003c/summary\u003e\n  \u003col\u003e\n    \u003cli\u003e\n      \u003ca href=\"#about-the-project\"\u003eAbout The Project\u003c/a\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\n      \u003ca href=\"#getting-started\"\u003eGetting Started\u003c/a\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\u003c/li\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"#event-manager\"\u003eEvent Manager\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#key-listener\"\u003eKey Listener\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#key-maps\"\u003eKey Maps\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#controller-maps\"\u003eController Maps\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#passing-events-to-the-managers\"\u003ePassing Events to the Managers\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#concurrency\"\u003eConcurrency\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#async-aware-concurrency\"\u003eAsync-Aware Concurrency\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003cli\u003e\u003ca href=\"#roadmap\"\u003eRoadmap\u003c/a\u003e\u003c/li\u003e\n    \u003c!--\u003cli\u003e\u003ca href=\"#contributing\"\u003eContributing\u003c/a\u003e\u003c/li\u003e--\u003e\n    \u003cli\u003e\u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#contact\"\u003eContact\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#acknowledgments\"\u003eAcknowledgments\u003c/a\u003e\u003c/li\u003e\n  \u003c/ol\u003e\n\u003c/details\u003e\n\n\n\n\u003c!-- ABOUT THE PROJECT --\u003e\n## About The Project\n\n\u003c!--\n[![Product Name Screen Shot][product-screenshot]](https://example.com)\n--\u003e\n\nSimple Events is a simple system that uses decorator syntax to register functions to Pygame events, allowing those function to be fired whenever the assigned event occurs.\nIt also features a keybind manager, which similarly can assign functions to remappable keybinds and controller binds.\n\nSimple Events also offers runtime-configurable compatibility with async-aware runtimes, such as pygbag.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\u003c!-- GETTING STARTED --\u003e\n## Getting Started\n\nSimple events is written in pure python, with no system dependencies, and should be OS-agnostic.\n\n### Installation\n\nSimple events can be installed from the [PyPI][pypi-url] using [pip][pip-url]:\n\n```sh\npip install pygame_simple_events\n```\n\nand can be imported for use with:\n```python\nimport simple_events\n```\n\nSimple events also require Pygame Community edition to be installed.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\n\u003c!-- USAGE EXAMPLES --\u003e\n## Usage\n\nEventManagers and KeyListeners are instantiated like loggers from the built-in python logging library.\nIf you run basicConfig, it should be done in your main entry point, such as main.py or equivalent. It needs to be run before your main game loop.\n\n\u003c!--\n_For more examples, please refer to the [Documentation](https://example.com)_\n--\u003e\n\n### Event Manager\n\n```python\nimport simple_events\n\nLOCAL_MANAGER = simple_events.getEventManager(\"Example\")\n```\n\nThis will generate an instance with the handle \"Example\", which will be stored by the manager system. If another module calls for that same handle, both modules will share the same event manager. Modules can even have multiple event managers to allow for control over execution context. Really, you can use as many or as few managers as desired.\n\nThe variable to which the event manager is assigned does not need to be written as a constant, though it is recommended for noticeability and avoiding accidental reassignment. The variable name has no special meaning to the event manager system.\n\nFunctions are registered using the register decorator along with the Pygame event type it wants to respond to.\nFor example, we will use pygame.QUIT\n\n```python\n@LOCAL_MANAGER.register(pygame.QUIT)\ndef quit_function(event: pygame.Event) -\u003e None:\n    # Do\n    # Things\n    # Here\n    # When\n    # Quitting\n```\n\nThe function can have any syntactically valid name, and can even be used elsewhere as a normal function.\n\nThe event manager will pass on the event to the function, so the function must be able to accept an event being passed to it as its first parameter, even if it has no use for event-specific data. This can mean using either an underscore or the *args syntax to ignore the incoming event data.\nDecorated functions cannot accept any additional positional arguments, unless using *args. The event manager will not provide any arguments beyond the event, so additional arguments must be optional, and are generally not recommended.\n\nAdditionally, a function can be assigned to multiple events.\n\nAlternatively, a function does not need to use decorator syntax for registration.\n\n```python\nLOCAL_MANAGER.register(pygame.USEREVENT)(quit_function)\n```\n\nThis method is useful for late binding a function.\n\n\nEvent managers can also be used on objects!\n\n```python\n@LOCAL_MANAGER.register_class\nclass TestClass:\n\n    @LOCAL_MANAGER.register_method(pygame.QUIT)\n    def sample_method(self, event: pygame.Event) -\u003e None:\n        # Do\n        # The\n        # Things\n```\n\nWith this, the event manager will track all instances of TestClass, and whenever the assigned event is called, it will call the registered methods on all instances. As with regular registered callables, it must be able to accept the event as an argument, but also uses self to allow access to the instance within the function.\n\nEvery manager that registers a method in a class should also register that class. If a manager registers a method but not the class, the method cannot be called, and will have a dangling attribute left over from the registration process.\n\n```python\n@LOCAL_MANAGER.register_class\nclass TestClass:\n\n    @LOCAL_MANAGER.register_method(pygame.QUIT)\n    def sample_method(self, event: pygame.Event) -\u003e None:\n        # Do\n        # The\n        # Things\n\n    @OTHER_MANAGER.register_method(pygame.QUIT)  # This will not work as expected\n    def other_sample_method(self, event: pygame.Event) -\u003e None:\n        # Do\n        # Other\n        # Things\n```\n\nMethods cannot be late registered, unlike regular functions. Classes can be late registered, but the event manager will not pick up on existing instances.\n\n\nFor more information on Pygame events, including a list of event type with descriptions, see [here](https://pyga.me/docs/ref/event.html)\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Key Listener\n\n```python\nimport simple_events\n\nKEYBINDS = simple_events.getKeyListener(\"Example\")\n```\n\nKey Listeners are seperate from event managers and can share handles without conflict.\n\nKey binds are slightly more involved to set up than regular events. They require a bind name, and can accept an optional default key as well as mod keys. They have the same function signature requirements as regular event binds.\n\n```python\n@KEYBINDS.bind(\"example_name\", pygame.K_p, pygame.KMOD_SHIFT)\ndef some_function(_):\n    # Does\n    # Something\n    # When\n    # Shift+P\n    # Is pressed\n```\n\nDefault key specifies the initial key needed to activate the bind, and can be left blank, but this will make the bind \"unbound\" and unable to be called.\nWith a default key set, the mod key specifies what additional mod keys (such as Alt, Control, or Shift) need to be pressed to activate the bind. If none is set, the bind will be called _regardless_ of mod keys. If pygame.KMOD_NONE is used, the bind will fire _only_ if no mod keys are pressed.\n\nOptionally, an event may be specified. By default, it uses key down for key events, and  for controller inputs it will attempt to intuit the desired event. The callable will be called only when the required function is called.\n\nIf the event type does not match the input type, the bind will never be called.\n\n```python\n@KEYBINDS.bind(\"example_name\", pygame.K_p, pygame.KMOD_SHIFT, pygame.KEYUP)\ndef some_function(_):\n    # Does\n    # Something\n    # When\n    # Shift+P\n    # Is released\n```\n\n\nIf a bind is used for multiple functions, the first processed call is used to establish the default keys.\n\n```python\n@KEYBINDS.bind(\"example2\", pygame.K_o)\ndef func1(_):\n    ...\n\n@KEYBINDS.bind(\"example2\", pygame.K_z, pygame.KMOD_CTRL)\ndef func2(_):\n    ...\n```\n\nIn this example, pressing the \"o\" key will activate both functions, even though func2 asks for Ctrl+Z.\n\nIf you are registering binds in multiple files, it may not always be obvious where your binds are being first called. You'll need to follow the chain of imports to figure it out. Alternatively, you can load a keymap file, which will guarantee control over your bindings. A loaded keymap has the highest priority for specifying binds.\n\nKey Listeners also work with classes and methods, following similar syntax as the Event Manager.\n\nFor more information on pygame and key handling, including a list of key names, see [here](https://pyga.me/docs/ref/key.html)\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Key Maps\n\nKey Listeners rely on the Key Map to determine which binds to call when a key is pressed. Every Key Listener uses the same Key Map.\n\nKey Maps can be modified via a Key Listener.\n\n```python\nimport simple_events\n\nKEYBINDS = simple_events.getKeyListener(\"Remapper\")\n@KEYBINDS.bind(\"example_name\", pygame.K_p, pygame.KMOD_SHIFT)\ndef some_function(_):\n    ...\n\nKEYBINDS.rebind(\"example_name\", pygame.K_m, pygame.KMOD_ALT)\n```\n\nThis changes the key for all functions bound to \"example_name\", which now get called when Alt+M is pressed instead of Shift+P.\n\nKey Maps and Joy Maps can also be saved and loaded from file. This requires a path to the desired file location, including the file name and extension. It supports almost any kind of path, including strings, and pathlib paths. If the file type is supported, it will be intuited from the file extension. Unsupported file types can have a File Parser class passed to force a specific encoding.\n\n```python\nimport simple_events\n\nKEYBINDS = simple_events.getKeyListener(\"Saveloader\")\n\nKEYBINDS.save_to_file(\"path/to/the/file.json\")\n```\n\nIn this case, the current KeyMap will be saved to file.json, and will use the JSON format.\n\nA Key Map can be loaded similarly.\n\n```python\nimport simple_events\n\nKEYBINDS = simple_events.getKeyListener(\"Saveloader\")\n\nKEYBINDS.load_from_file(\"path/to/source/key_binds.json\")\n```\n\nThis will try to load key_binds.json and merge the key binds into the current Key Map. The loaded key binds take precedence over existing ones, but if a key bind exists in the current map but not the loaded one, it is carried over without modification.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Controller Maps\n\nAlso known as JoyMaps.\n\nJoyMaps are to controller event what KeyMaps are to keyboard events. They have many of the same properties to KeyMaps, but use a dictionary of a key and value for the binding.\n\n```python\nimport simple_events\n\nCONTROLLER = simple_events.getKeyListener(\"controller_stuff\")\n\n@CONTROLLER.bind(\"example\", {\"button\": 0})\ndef test_func(_):\n    pass\n```\n\nIn this case, it will look for the \"button\" attribute of a Joystick event, and will be called on button 0. It should be noted, different controller types label their buttons differently.\n\nInstance-specific attributes like \"instance_id\" and \"value\" are disregarded by the Joy Map. \n\nFor more information on pygame and joystick/controller behavior, including examples of button maps, see [here](https://pyga.me/docs/ref/joystick.html)\n\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Passing Events to the Managers\n\nWith functions registered to the managers, you now need to tie the managers into the event queue.\n\nThere are two options:\n\n1. Notify All\n\n```python\nimport simple_events\n\nimport pygame\n\n# pygame setup and initialization\n\nwhile game_is_running:\n    # Frame rate handling\n    for event in pygame.event.get():\n        simple_events.notifyEventManagers(event)\n        simple_events.notifyKeyListeners(event)\n    # Game Loop stuff\n\n```\n\nThis ensures that every manager is being fed events as they happen.\n\n2. Direct Notification\n\n```python\nimport simple_events\n\nimport pygame\n\n# pygame setup and initialization\n\nMANAGER = simple_events.getEventManager(\"Example\") # Remember, the handle needs to be the same as wherever events are assigned\nMANAGER2 = simple_events.getEventManager(\"Example2\")\nKEYBINDS = simple_events.getKeyListener(\"Example\")\n\nwhile game_is_running:\n    # Frame rate handling\n    for event in pygame.event.get():\n        MANAGER.notify(event)\n        MANAGER2.notify(event)\n        KEYBINDS.notify(event)\n    # Game Loop stuff\n\n```\n\nThe developer must track the managers and is responsible for feeding them the events. This allows greater control over if and when a given manager is activated.\nFor example, it may be desirable to have a manager that handles menu functions, and another gameplay functions. This way, the game loop can test for game state, and run only the menu functions when in menu, and only gameplay functions while playing.\n\nAdditionally, event managers and key listeners support calling _only_ sequential and _only_ concurrent functions and methods, if desired. This may be done using the \\[manager variable\\].notify_sequential(event) and \\[manager variable\\].notify_concurrent(event) methods, respectively. It should be noted that calling both the general and specific notifies on the same frame will call those functions twice.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Concurrency\n\nBy default, functions are called using Python's threading library. This means that the called functions can be blocked, such as by using time.sleep, without blocking the rest of the program.\n\n_However_, this comes at the cost of thread safety. These functions may be able to change state at unpredictable times, and generate race conditions. Always use caution when dealing with concurrency, and investigate [Python's threading library](https://docs.python.org/3/library/threading.html#threading.Lock) for more info on best practices regarding concurrency.\n\nOptionally, you can use\n```python\n@KEYLISTENER.sequential\n```\nto mark a function as sequential. Sequential functions and methods will called after the concurrent functions and methods, and will run one after the other. They lose out on being easily blockable, but reduce the risk of forming race conditions, especially if not sharing resources with any concurrent functions.\n\nThe sequential tag is applied below the register or bind decorator.\n```python\n@KEYLISTENER.bind(\"test_bind\", pygame.K_Space)\n@KEYLISTENER.sequential\ndef test_func(event: pygame.Event) -\u003e None:\n    # Some stuff\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Async-Aware Concurrency\n\nWhen using an async-aware gameplay loop, such as with tools such as [pygbag](https://pypi.org/project/pygbag/), you'll need to change up the format of your concurrent functions and methods, as online pygame games made using pygbag do not work with traditional threads. Instead, you'll need to use asyncio.\n\nWith Simple Events, the transition is simple. Go from this:\n```python\n@EVENTS.register(pygame.MOUSEBUTTONUP)\ndef mouse_click(event: pygame.Event) -\u003e None:\n    # Do Something\n    time.sleep(1)\n    # Do Something Else\n```\n\nTo this:\n```python\n@EVENTS.register(pygame.MOUSEBUTTONUP)\nasync def mouse_click(event: pygame.Event) -\u003e None:\n    # Do Something\n    await asyncio.sleep(1)\n    # Do Something Else\n```\n\nYou will also need to run the simple_events.basicConfig function at your main entry point, before you run your main coroutine.\n\n```python\nsimple_events.basicConfig(is_async=True)\nasyncio.run(main())\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\n\u003c!-- ROADMAP --\u003e\n## Roadmap\n\n- [ ] Add support for additional formats for saving and loading keybinds.\n- [ ] Add a utility for simplifying the default input for controller binds.\n\u003c!--\n- [ ] Feature 2\n- [ ] Feature 3\n    - [ ] Nested Feature\n--\u003e\n\nSee the [open issues](https://github.com/BetterBuiltFool/simple_events/issues) for a full list of proposed features (and known issues).\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\n\u003c!-- CONTRIBUTING --\u003e\n\u003c!--\n## Contributing\n\nContributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.\n\nIf you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag \"enhancement\".\nDon't forget to give the project a star! Thanks again!\n\n1. Fork the Project\n2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)\n3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)\n4. Push to the Branch (`git push origin feature/AmazingFeature`)\n5. Open a Pull Request\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n### Top contributors:\n\n\u003ca href=\"https://github.com/BetterBuiltFool/simple_events/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=BetterBuiltFool/simple_events\" alt=\"contrib.rocks image\" /\u003e\n\u003c/a\u003e\n--\u003e\n\n\n\n\u003c!-- LICENSE --\u003e\n## License\n\nDistributed under the MIT License. See `LICENSE.txt` for more information.\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\n\u003c!-- CONTACT --\u003e\n## Contact\n\nBetter Built Fool - betterbuiltfool@gmail.com\n\nBluesky - [@betterbuiltfool.bsky.social](https://bsky.app/profile/betterbuiltfool.bsky.social)\n\u003c!--\n - [@twitter_handle](https://twitter.com/twitter_handle)\n--\u003e\n\nProject Link: [https://github.com/BetterBuiltFool/simple_events](https://github.com/BetterBuiltFool/simple_events)\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\n\n\u003c!-- ACKNOWLEDGMENTS --\u003e\n\u003c!--## Acknowledgments\n\n* []()\n* []()\n* []()\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n--\u003e\n\n\n\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\u003c!-- https://www.markdownguide.org/basic-syntax/#reference-style-links --\u003e\n[contributors-shield]: https://img.shields.io/github/contributors/BetterBuiltFool/simple_events.svg?style=for-the-badge\n[contributors-url]: https://github.com/BetterBuiltFool/simple_events/graphs/contributors\n[forks-shield]: https://img.shields.io/github/forks/BetterBuiltFool/simple_events.svg?style=for-the-badge\n[forks-url]: https://github.com/BetterBuiltFool/simple_events/network/members\n[stars-shield]: https://img.shields.io/github/stars/BetterBuiltFool/simple_events.svg?style=for-the-badge\n[stars-url]: https://github.com/BetterBuiltFool/simple_events/stargazers\n[issues-shield]: https://img.shields.io/github/issues/BetterBuiltFool/simple_events.svg?style=for-the-badge\n[issues-url]: https://github.com/BetterBuiltFool/simple_events/issues\n[license-shield]: https://img.shields.io/github/license/BetterBuiltFool/simple_events.svg?style=for-the-badge\n[license-url]: https://github.com/BetterBuiltFool/simple_events/blob/main/LICENSE\n[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge\u0026logo=linkedin\u0026colorB=555\n[linkedin-url]: https://linkedin.com/in/linkedin_username\n[product-screenshot]: images/screenshot.png\n[Next.js]: https://img.shields.io/badge/next.js-000000?style=for-the-badge\u0026logo=nextdotjs\u0026logoColor=white\n[Next-url]: https://nextjs.org/\n[python.org]: https://img.shields.io/badge/python-3670A0?style=for-the-badge\u0026logo=python\u0026logoColor=ffdd54\n[python-url]: https://www.python.org/\n[React.js]: https://img.shields.io/badge/React-20232A?style=for-the-badge\u0026logo=react\u0026logoColor=61DAFB\n[React-url]: https://reactjs.org/\n[Vue.js]: https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge\u0026logo=vuedotjs\u0026logoColor=4FC08D\n[Vue-url]: https://vuejs.org/\n[Angular.io]: https://img.shields.io/badge/Angular-DD0031?style=for-the-badge\u0026logo=angular\u0026logoColor=white\n[Angular-url]: https://angular.io/\n[Svelte.dev]: https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge\u0026logo=svelte\u0026logoColor=FF3E00\n[Svelte-url]: https://svelte.dev/\n[Laravel.com]: https://img.shields.io/badge/Laravel-FF2D20?style=for-the-badge\u0026logo=laravel\u0026logoColor=white\n[Laravel-url]: https://laravel.com\n[Bootstrap.com]: https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge\u0026logo=bootstrap\u0026logoColor=white\n[Bootstrap-url]: https://getbootstrap.com\n[JQuery.com]: https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge\u0026logo=jquery\u0026logoColor=white\n[JQuery-url]: https://jquery.com \n[pypi-url]: https://pypi.org/project/pygame_simple_events\n[pip-url]: https://pip.pypa.io/en/stable/","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetterbuiltfool%2Fsimple_events","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbetterbuiltfool%2Fsimple_events","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetterbuiltfool%2Fsimple_events/lists"}