René's URL Explorer Experiment


Title: Windowing & event-loops: who owns what? (Internal vs external event loops) · Issue #28 · processing/libprocessing · GitHub

Open Graph Title: Windowing & event-loops: who owns what? (Internal vs external event loops) · Issue #28 · processing/libprocessing

X Title: Windowing & event-loops: who owns what? (Internal vs external event loops) · Issue #28 · processing/libprocessing

Description: I'd like to discuss a bit our high-level library design. There are basically two different ways that we could have structured our library that comes down to the main event loop that drives the user's application forward: internal or exte...

Open Graph Description: I'd like to discuss a bit our high-level library design. There are basically two different ways that we could have structured our library that comes down to the main event loop that drives the user...

X Description: I'd like to discuss a bit our high-level library design. There are basically two different ways that we could have structured our library that comes down to the main event loop that drives the ...

Opengraph URL: https://github.com/processing/libprocessing/issues/28

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Windowing \u0026 event-loops: who owns what? (Internal vs external event loops)","articleBody":"I'd like to discuss a bit our high-level library design. There are basically two different ways that we could have structured our library that comes down to the main event loop that drives the user's application forward: internal or external. Right now, we are structuring things in a way where we expect the external caller/FFI implementer to drive things forward. I think this is the right architecture but it's worth articulating the design trade-offs here.\n\n## Internal event loop and the callback handler architecture\n\nIn the internal event loop model, the \"Processing runtime\" owns the window and drives everything forward. What this means concretely from an API design perspective is that rather than having a series of imperative FFI functions, we'd rely on the user to provide us function pointers to callbacks that we fire whenever something happens: e.g. setting up your sketch, drawing each frame, receiving user input, etc.\n\nThis is how I built things for Nannou. Basically, we [store a bunch of function pointers for the user's sketch](https://github.com/nannou-org/nannou/blob/bevy-refactor/nannou/src/app.rs#L71-L98) and then [call their handlers each frame](https://github.com/nannou-org/nannou/blob/bevy-refactor/nannou/src/app.rs#L1086-L1156) when asked to do so by the engine. More specifically, here we leave frame pacing to the engine and by default it's using Bevy's game oriented \"run as fast as possible\" pacing.\n\nIn other words, the user doesn't decide when to present to a surface, they instead exist inside an event loop that drives all their handlers when the engine decides to.\n\nThere are advantages to this design in that it means that all the integration between windowing and the user's application can be contained and doesn't need to be exposed. This is also the most performant way to write a modern application and allows decoupling concerns. For example, we can enable a low-power / reactive event loop where we only re-render on user input quite easily here because the event loop is telling *us* when to render, not the other way around.\n\nFrom an FFI perspective, however, this has a big drawback in that it requires expressing everything in terms of function pointers. Typically when codegening bindings in other languages, this makes things a bit more annoying because we have to wrap *our* functions rather than just having the tooling generate a function we can call. It also means defining a ton more data structures at the FFI layer; you have to have types for all the events, for the arguments to each function handler, etc.\n\n## External event loop and the procedural style\n\nFor this reason, when scaffolding libprocessing, it made sense to go for an imperative or procedural style with an external event loop. In this case, the implementer calls into our library at each logical step of the event loop. Our functions are exposed in terms of different operations that update some kind of draw/rendering state but are themselves stateless in their sense of time; you can call \"start frame\" and then wait an hour and call \"end frame\" and from the perspective of the library that's totally fine. The caller decides when to drive things forward.\n\nOf course, any application that needs a window is still subject to an event loop, but rather than an application handler it can be implemented more via a polling/pumping mechanism like we do in GLFW. That is, GLFW doesn't tell us when to render, we just run in a loop and check in with GLFW to see if there are any new events. \n\nIn terms of the FFI boundary, this *greatly* simplifies things, and means that we can expose the API in terms of procedures. For the most part, we don't even need to define any custom FFI structs, as most things can be simple primitive arguments to functions, or getters for some logical API object's properties. This, in turn, makes it *very* easy to codegen. The implementer will get a list of a bajillion functions that they need to integrate with, but for the most part, their implementation will just be a pretty small layer over calling these functions. As demonstrated by our examples, setting up a basic GLFW loop is just a handful of lines of code.\n\n## The engine-eventing interface\n\nSo what would the advantage of integrating with `winit` in Bevy be? First, it would mean that implementers would also not have to worry about managing a window and setting up a surface. Instead, they would pull events from us each frame in a similar fashion to `glfwPollEvents`. This would be nice because it would mean that we wouldn't rely on implementers having to figure out what windowing libraries are available in their language and could reliably just expect us to manage the window for them.\n\nBut, secondly, it would also mean that events are more easily propagated within the engine itself. Right now, if we want to react to user input in Rust, we have to create an API to feed events into libprocessing. For the core engine, this isn't necessary because outside of window resizes there's no engine code that depends on certain user actions (button presses, etc.). However, there are potentially engine features in the future that may require such events. For example, render picking which requires casting a ray from the pointers position, etc.\n\n## The current limit in Bevy\n\nRight now, Bevy uses an internal event loop, and although [`winit` has support for external event loops](https://docs.rs/winit/latest/winit/platform/pump_events/trait.EventLoopExtPumpEvents.html) it's not easy to bridge. I feel confident that we want to continue to maintain an external event loop. The entire Processing API is designed around the assumption that you can call certain methods like `beginDraw`/`endDraw` on a graphics object, and this only makes sense in the context where we imperatively drive things forward / decide when to present. Although this is a certain kind of inefficiency with respect to super high performance graphics, for most art and art installations it's conceptually way simpler.\n\nSo, for now, I feel confident that external event loop is the right design, but we'll eventually probably want to bring windowing into libprocessing so that users don't have to deal with it!","author":{"url":"https://github.com/tychedelia","@type":"Person","name":"tychedelia"},"datePublished":"2025-12-04T23:08:46.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":4},"url":"https://github.com/28/libprocessing/issues/28"}

route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:72a37d7e-ea6c-6b39-c960-2ebebe1b8454
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idCF84:AA45D:187029A:217749F:6964B81E
html-safe-nonce34458cc7dd1432c6de165ee807f87d47f317089b6705d959e259cbeb536bdff6
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDRjg0OkFBNDVEOjE4NzAyOUE6MjE3NzQ5Rjo2OTY0QjgxRSIsInZpc2l0b3JfaWQiOiI2MjU1OTAzMTY0NDM1MDUyNTc0IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0=
visitor-hmacc10a8f4884d316856002c072b63f9be242da705f6fdeccfcc450d39d2c8b132d
hovercard-subject-tagissue:3696921811
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/processing/libprocessing/28/issue_layout
twitter:imagehttps://opengraph.githubassets.com/6e0a921f725c5e06e451bede73748d76b037b6800087c46df153be682a4a665d/processing/libprocessing/issues/28
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/6e0a921f725c5e06e451bede73748d76b037b6800087c46df153be682a4a665d/processing/libprocessing/issues/28
og:image:altI'd like to discuss a bit our high-level library design. There are basically two different ways that we could have structured our library that comes down to the main event loop that drives the user...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernametychedelia
hostnamegithub.com
expected-hostnamegithub.com
None15579c46431b7fd25941c3b09010f74fd1890c7a35226839bbbf40ce70fb3057
turbo-cache-controlno-preview
go-importgithub.com/processing/libprocessing git https://github.com/processing/libprocessing.git
octolytics-dimension-user_id1617169
octolytics-dimension-user_loginprocessing
octolytics-dimension-repository_id1101329545
octolytics-dimension-repository_nwoprocessing/libprocessing
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id1101329545
octolytics-dimension-repository_network_root_nwoprocessing/libprocessing
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release499abb347cb197601d399c346cfeb4b3fa135d5c
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/processing/libprocessing/issues/28#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fprocessing%2Flibprocessing%2Fissues%2F28
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub SparkBuild and deploy intelligent appshttps://github.com/features/spark
GitHub ModelsManage and compare promptshttps://github.com/features/models
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
GitHub SponsorsFund open source developershttps://github.com/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/accelerator
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fprocessing%2Flibprocessing%2Fissues%2F28
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=processing%2Flibprocessing
Reloadhttps://github.com/processing/libprocessing/issues/28
Reloadhttps://github.com/processing/libprocessing/issues/28
Reloadhttps://github.com/processing/libprocessing/issues/28
processing https://github.com/processing
libprocessinghttps://github.com/processing/libprocessing
Notifications https://github.com/login?return_to=%2Fprocessing%2Flibprocessing
Fork 2 https://github.com/login?return_to=%2Fprocessing%2Flibprocessing
Star 20 https://github.com/login?return_to=%2Fprocessing%2Flibprocessing
Code https://github.com/processing/libprocessing
Issues 24 https://github.com/processing/libprocessing/issues
Pull requests 0 https://github.com/processing/libprocessing/pulls
Actions https://github.com/processing/libprocessing/actions
Projects 0 https://github.com/processing/libprocessing/projects
Security Uh oh! There was an error while loading. Please reload this page. https://github.com/processing/libprocessing/security
Please reload this pagehttps://github.com/processing/libprocessing/issues/28
Insights https://github.com/processing/libprocessing/pulse
Code https://github.com/processing/libprocessing
Issues https://github.com/processing/libprocessing/issues
Pull requests https://github.com/processing/libprocessing/pulls
Actions https://github.com/processing/libprocessing/actions
Projects https://github.com/processing/libprocessing/projects
Security https://github.com/processing/libprocessing/security
Insights https://github.com/processing/libprocessing/pulse
New issuehttps://github.com/login?return_to=https://github.com/processing/libprocessing/issues/28
New issuehttps://github.com/login?return_to=https://github.com/processing/libprocessing/issues/28
Windowing & event-loops: who owns what? (Internal vs external event loops)https://github.com/processing/libprocessing/issues/28#top
https://github.com/tychedelia
https://github.com/tychedelia
tychedeliahttps://github.com/tychedelia
on Dec 4, 2025https://github.com/processing/libprocessing/issues/28#issue-3696921811
store a bunch of function pointers for the user's sketchhttps://github.com/nannou-org/nannou/blob/bevy-refactor/nannou/src/app.rs#L71-L98
call their handlers each framehttps://github.com/nannou-org/nannou/blob/bevy-refactor/nannou/src/app.rs#L1086-L1156
winit has support for external event loopshttps://docs.rs/winit/latest/winit/platform/pump_events/trait.EventLoopExtPumpEvents.html
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.