Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@gdamore
Copy link
Owner

@gdamore gdamore commented Dec 14, 2025

Summary by CodeRabbit

  • New Features

    • Event queuing and wake-up hooks to promptly interrupt blocked waits.
    • Enhanced event-wait API with timeout/reschedule semantics and batched event retrieval.
  • Refactor

    • Unified event-loop model across core and demos for multi-event processing.
    • Demos: continuous rendering in colors demo, expanded mouse/input handling, and a text color tweak in the hello demo.

✏️ Tip: You can customize this high-level summary in your review settings.

This new API allows applications to use the events as both an
input and an output range (FIFO).  It also is designed to support
use with external poll loops (once we have exposed the file for the tty.)
This should allow applications to integrate dcell into external
polling loops.  These applications should use the events and
waitForEvent APIs to adjust their poll intervals as needed.

A cautionary note is given for macOS, because macOS bizarrely does
not support poll or kqueue with tty devices.  (The only way to
work on such systems is to use a separate thread and the self-pipe
trick.)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 14, 2025

Walkthrough

This PR replaces single-event blocking with a queued, batched event model across the terminal layer and demos: adds an EventQ, exposes Screen.events(), introduces Screen.waitForEvent(...), adds Tty.wakeUp and wake plumbing in posix/win backends, rewrites TtyScreen to use an internal EventQ, and updates demos to iterate all pending events per wake.

Changes

Cohort / File(s) Summary
Event queue core
source/dcell/event.d
Adds public EventQ FIFO range with put(Event), front(), empty(), and popFront(); imports std.range.
Screen API
source/dcell/screen.d
Removes waitEvent(...); adds bool waitForEvent(Duration timeout, ref Duration resched), final bool waitForEvent(Duration timeout = Duration.max) and EventQ events() to expose queued events.
Tty API
source/dcell/tty.d
Adds void wakeUp() to the Tty interface.
Posix tty backend
source/dcell/termio.d
Adds PosixTty.this(File f) constructor and void wakeUp() implementation that writes a wake byte; minor condition-order reorderings.
Windows tty backend
source/dcell/wintty.d
Adds void wakeUp() implementation signaling the event handle via SetEvent.
TtyScreen implementation
source/dcell/ttyscreen.d
Replaces Event[] with a nested TtyEventQ : EventQ; adds TtyEventQ evq, initializes it, exposes EventQ events(), and replaces waitEvent() with bool waitForEvent(Duration timeout, ref Duration resched).
Demos — batched event loops
demos/hello/source/hello.d, demos/styles/source/styles.d, demos/mouse/source/mouse.d
Replace single-event waitEvent() usage with waitForEvent() and foreach(ev; s.events()) to process all pending events per wake; expand event-type handling and state updates (mouse/demo-specific additions).
Colors demo refactor
demos/colors/source/colors.d
Moves main logic into ColorBoxes class: adds fields Screen s, bool done, constructor this(Screen scr), makeBox() (no arg), and a new run() method; main() now uses new ColorBoxes(newScreen()).run().

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant App as Demo App
    participant Screen as Screen / TtyScreen
    participant Tty as Tty backend
    participant EvQ as EventQ

    App->>Screen: waitForEvent(timeout, ref resched)
    Screen->>Tty: poll/read with timeout
    Tty-->>Screen: push events into evq (wake)
    Screen-->>App: true (events) / false (timeout)

    alt events available
        App->>Screen: events()
        Screen-->>App: reference to EvQ
        loop for each pending event
            App->>EvQ: front / popFront
            EvQ-->>App: Event
            App->>App: handle/dispatch Event
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect source/dcell/ttyscreen.d for correctness of evq initialization, TtyEventQ.put semantics, and proper wake signaling.
  • Review source/dcell/termio.d and source/dcell/wintty.d wakeUp implementations for race conditions and correct fd/handle usage.
  • Validate demos (demos/mouse/source/mouse.d) for comprehensive event handling and correct redraw/selection state updates.

Possibly related PRs

Poem

🐰 I hop through queues with tiny feet,

I nudge the screen, then skip to greet.
Events pile up, I sort them neat,
Wait, wake, iterate — a rhythmic beat.
A rabbit's loop: responsive and fleet.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: redesigning the events API from single-event polling to a range-based API that supports external event loop integration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gdamore/events-range

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
demos/mouse/source/mouse.d (1)

123-123: Uninitialized lb may cause incorrect color when drawing box on first button release.

dchar lb; is default-initialized to dchar.init (0xFFFF). If a Buttons.none event occurs before any button press event sets lb, the expression cast(Color)(lb - '0') at line 240 will produce an unexpected color value.

Consider initializing lb to a safe default:

-    dchar lb;
+    dchar lb = '0';
🧹 Nitpick comments (2)
source/dcell/event.d (1)

74-104: LGTM: EventQ correctly implements FIFO queue semantics.

The EventQ class properly implements the input range protocol with front(), empty(), and popFront(). The use of array concatenation in put() is acceptable for typical event queue usage patterns where event bursts are small.

For high-throughput scenarios, consider using std.array.Appender to reduce allocations:

import std.array : Appender;

private:
    Appender!(Event[]) events;

However, this optimization is likely unnecessary for typical UI event handling.

demos/mouse/source/mouse.d (1)

282-282: Remove commented-out code.

This commented line appears to be leftover from refactoring and should be removed to keep the code clean.

-                // mousePos = ev.mouse.pos;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd5e640 and 01a7a77.

📒 Files selected for processing (10)
  • demos/colors/source/colors.d (5 hunks)
  • demos/hello/source/hello.d (2 hunks)
  • demos/mouse/source/mouse.d (3 hunks)
  • demos/styles/source/styles.d (1 hunks)
  • source/dcell/event.d (2 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/termio.d (4 hunks)
  • source/dcell/tty.d (1 hunks)
  • source/dcell/ttyscreen.d (4 hunks)
  • source/dcell/wintty.d (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/wintty.d
  • source/dcell/termio.d
  • source/dcell/ttyscreen.d
🔇 Additional comments (14)
source/dcell/tty.d (1)

90-94: LGTM: Clean interface extension for wake-up capability.

The new wakeUp() method enables responsive interruption of blocked read operations, which is essential for integrating with external event loops and supporting the new batched event processing model introduced in this PR.

source/dcell/wintty.d (1)

209-212: LGTM: Correct Windows event signaling.

The implementation properly signals the event handle to wake up readers blocked in read() (which waits on eventH at line 133). This is consistent with the stop() method's approach.

demos/hello/source/hello.d (1)

82-86: LGTM: Event loop updated to batched processing pattern.

The refactored event loop correctly uses waitForEvent() followed by iterating over all pending events via ts.events(), enabling more responsive event handling compared to the previous single-event approach.

demos/styles/source/styles.d (1)

173-177: LGTM: Consistent adoption of batched event processing.

The event loop correctly implements the new pattern introduced across all demos in this PR, ensuring all pending events are processed in each iteration.

source/dcell/termio.d (2)

64-81: LGTM: Well-documented File-based constructor.

The new constructor properly validates the file is open and initializes the TTY from an existing File descriptor. The documentation clearly explains the use case and platform-specific limitations (macOS poll/kqueue).


320-328: LGTM: Correct wake-up implementation using pipe signaling.

The implementation writes a byte to the wake-up pipe (sigWfd), which will unblock any reader waiting on sigRfd in the read() methods (lines 213, 268). The nothrow specification is appropriate since failures can be safely ignored.

demos/colors/source/colors.d (3)

41-51: LGTM: Improved encapsulation with Screen parameter.

The refactored constructor properly initializes the Screen field, enabling better encapsulation and supporting the new event-driven architecture.


159-207: LGTM: Well-structured event loop with labeled break.

The new run() method effectively encapsulates the event loop logic. Key improvements:

  • Uses labeled break (break loop) to exit cleanly from nested event processing
  • Processes all pending events via foreach(ev; s.events())
  • Provides smooth animation timing with waitForEvent(msecs(50))
  • Properly handles scope exit to ensure stop() is called

209-222: LGTM: Cleaner main() with improved separation of concerns.

The refactored main() delegates event loop responsibilities to the ColorBoxes.run() method, resulting in cleaner code organization and better testability.

demos/mouse/source/mouse.d (2)

160-296: Event processing loop looks well-structured.

The refactored event loop correctly iterates over all pending events and handles each type appropriately. The switch statement covers key, mouse, paste, focus, and resize events with proper state management.


145-145: The review comment is incorrect. A zero-argument overload of waitForEvent() exists in source/dcell/screen.d:116 as a final method with a default parameter: bool waitForEvent(Duration timeout = Duration.max). This overload calls the two-parameter variant internally, making the call s.waitForEvent() at line 145 valid and correct. Multiple demos in the codebase (styles.d, hello.d, mouse.d) successfully use this zero-argument form.

Likely an incorrect or invalid review comment.

source/dcell/ttyscreen.d (3)

449-502: Timeout and residual parsing logic looks correct.

The waitForEvent implementation properly handles:

  • Timeout expiration with MonoTime calculations
  • At least one read attempt before returning on timeout (readOnce flag)
  • Shorter wake-up intervals (5ms) when partial escape sequences are pending
  • Returning resched hint to caller for rescheduling incomplete sequences

504-507: LGTM!

The events() accessor correctly returns the internal event queue with appropriate nothrow @safe @nogc attributes.


160-160: Event queue initialization order is correct.

The evq is initialized after ti is set (line 157), ensuring ti.wakeUp() calls in TtyEventQ.put() won't encounter a null reference.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01a7a77 and 748b20d.

📒 Files selected for processing (4)
  • demos/mouse/source/mouse.d (4 hunks)
  • source/dcell/event.d (2 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/ttyscreen.d (4 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: gdamore
Repo: gdamore/dcell PR: 53
File: source/dcell/screen.d:96-126
Timestamp: 2025-12-14T19:56:40.073Z
Learning: In dcell, the EventQ class is intentionally designed as both an input range and an output range of Event. The output range functionality allows applications to inject synthetic events into the event stream.
Learnt from: gdamore
Repo: gdamore/dcell PR: 53
File: source/dcell/ttyscreen.d:557-577
Timestamp: 2025-12-14T19:58:59.815Z
Learning: In source/dcell/ttyscreen.d, the TtyEventQ class intentionally has different behavior between put() and opOpAssign methods. The put() method calls ti.wakeUp() for external callers injecting synthetic events, while opOpAssign (used via ~= operator) bypasses wakeUp() as a performance optimization when appending events internally during tty event processing where the thread is already active.
📚 Learning: 2025-12-14T19:58:59.815Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 53
File: source/dcell/ttyscreen.d:557-577
Timestamp: 2025-12-14T19:58:59.815Z
Learning: In source/dcell/ttyscreen.d, the TtyEventQ class intentionally has different behavior between put() and opOpAssign methods. The put() method calls ti.wakeUp() for external callers injecting synthetic events, while opOpAssign (used via ~= operator) bypasses wakeUp() as a performance optimization when appending events internally during tty event processing where the thread is already active.

Applied to files:

  • source/dcell/screen.d
  • source/dcell/ttyscreen.d
  • source/dcell/event.d
📚 Learning: 2025-12-14T19:56:40.073Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 53
File: source/dcell/screen.d:96-126
Timestamp: 2025-12-14T19:56:40.073Z
Learning: In dcell, the EventQ class is intentionally designed as both an input range and an output range of Event. The output range functionality allows applications to inject synthetic events into the event stream.

Applied to files:

  • source/dcell/screen.d
  • source/dcell/ttyscreen.d
  • source/dcell/event.d
📚 Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/ttyscreen.d
🔇 Additional comments (10)
source/dcell/screen.d (2)

96-120: LGTM! Clean API design with primary + convenience overload pattern.

The two-overload design provides both flexibility (with resched for integration into external event loops) and simplicity (for basic polling). The convenience overload correctly delegates to the primary implementation using a local resched variable.


122-126: LGTM! EventQ accessor is well-documented.

The documentation correctly describes EventQ as both an input and output range, which allows applications to inject synthetic events as needed.

Based on learnings, the dual range functionality is intentional.

source/dcell/ttyscreen.d (4)

160-160: LGTM! EventQ initialization is correct.

The TtyEventQ is properly initialized in the constructor.


449-502: LGTM! waitForEvent logic correctly handles timeouts and partial sequences.

The implementation properly:

  • Guards against overflow when setting expiration time
  • Ensures at least one read attempt before timing out (via readOnce flag)
  • Handles incomplete escape sequences with shorter intervals for residual data
  • Sets appropriate resched values based on whether residual data remains

504-507: LGTM! EventQ accessor is straightforward.

Returns the internal event queue for consumption.


603-603: LGTM! EventQ field properly declared.

demos/mouse/source/mouse.d (3)

13-13: LGTM! Import updated for range operations.

The change from std.stdio to std.range aligns with the new event iteration model.


123-123: LGTM! Proper initialization prevents undefined behavior.

Initializing lb to 0 ensures it has a defined value before first use.


145-295: LGTM! Demo properly migrated to batched event processing model.

The changes correctly:

  • Use waitForEvent() to wait for at least one event
  • Iterate over all pending events via foreach(ev; s.events())
  • Handle all event types comprehensively (resize, paste, key, mouse, focus)

This demonstrates the new event API design effectively.

source/dcell/event.d (1)

74-104: LGTM! EventQ implementation is clean and correct.

The class properly implements both input and output range interfaces:

  • put(Event ev) provides output range functionality for injecting events
  • front(), empty(), popFront() provide input range functionality for consuming events
  • Internal Event[] array is correctly managed: popFront() calls the array's popFront which slices away consumed events

The range method attributes are appropriate:

  • Input range methods use nothrow pure @nogc @safe since they only manipulate array slices
  • put() correctly omits @nogc since ~= can allocate

Based on learnings, the dual input/output range design is intentional and allows applications to inject synthetic events into the event stream.

@gdamore gdamore force-pushed the gdamore/events-range branch from 748b20d to 5200bc5 Compare December 14, 2025 23:04
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
source/dcell/event.d (1)

82-85: Consider adding @safe attribute to put() method.

The put() method uses array concatenation (~=) which is memory-safe. Adding @safe would make it consistent with the other methods and the events() return type declared as @safe in screen.d.

Apply this diff:

-    void put(Event ev)
+    void put(Event ev) @safe
     {
         events ~= ev;
     }
demos/mouse/source/mouse.d (1)

145-160: Consider checking the return value of waitForEvent().

The waitForEvent() call on line 145 returns a boolean indicating whether events are available, but the return value is not checked. While the subsequent foreach will simply skip if no events are present, explicitly checking the return value could make the flow clearer and potentially avoid unnecessary work.

Consider this pattern:

         emitStr(s, pos, white, format(pasteFmt, pStr.length, ps));
         s.show();
-        s.waitForEvent();
+        if (!s.waitForEvent())
+            continue; // timeout with no events
         Style st;

Alternatively, if processing the draw state updates is intentional even when no events arrive, the current code is fine. In that case, you might want to add a comment explaining the intent.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 748b20d and 5200bc5.

📒 Files selected for processing (4)
  • demos/mouse/source/mouse.d (4 hunks)
  • source/dcell/event.d (2 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/ttyscreen.d (4 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: gdamore
Repo: gdamore/dcell PR: 53
File: source/dcell/screen.d:96-126
Timestamp: 2025-12-14T19:56:40.073Z
Learning: In dcell, the EventQ class is intentionally designed as both an input range and an output range of Event. The output range functionality allows applications to inject synthetic events into the event stream.
Learnt from: gdamore
Repo: gdamore/dcell PR: 53
File: source/dcell/ttyscreen.d:557-577
Timestamp: 2025-12-14T19:58:59.815Z
Learning: In source/dcell/ttyscreen.d, the TtyEventQ class intentionally has different behavior between put() and opOpAssign methods. The put() method calls ti.wakeUp() for external callers injecting synthetic events, while opOpAssign (used via ~= operator) bypasses wakeUp() as a performance optimization when appending events internally during tty event processing where the thread is already active.
📚 Learning: 2025-12-14T19:56:40.073Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 53
File: source/dcell/screen.d:96-126
Timestamp: 2025-12-14T19:56:40.073Z
Learning: In dcell, the EventQ class is intentionally designed as both an input range and an output range of Event. The output range functionality allows applications to inject synthetic events into the event stream.

Applied to files:

  • source/dcell/event.d
  • source/dcell/ttyscreen.d
  • source/dcell/screen.d
📚 Learning: 2025-12-14T19:58:59.815Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 53
File: source/dcell/ttyscreen.d:557-577
Timestamp: 2025-12-14T19:58:59.815Z
Learning: In source/dcell/ttyscreen.d, the TtyEventQ class intentionally has different behavior between put() and opOpAssign methods. The put() method calls ti.wakeUp() for external callers injecting synthetic events, while opOpAssign (used via ~= operator) bypasses wakeUp() as a performance optimization when appending events internally during tty event processing where the thread is already active.

Applied to files:

  • source/dcell/event.d
  • source/dcell/ttyscreen.d
  • source/dcell/screen.d
📚 Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/ttyscreen.d
🔇 Additional comments (11)
source/dcell/screen.d (2)

96-120: LGTM!

The dual-overload design is excellent. The primary waitForEvent with resched provides fine-grained control for integrating into external event loops, while the convenience wrapper simplifies typical polling scenarios.


122-126: LGTM!

The events() API correctly exposes the event queue as both an input and output range. Based on learnings, the output range capability is intentional for injecting synthetic events.

demos/mouse/source/mouse.d (4)

13-13: LGTM!

Moving std.stdio to a local import within main() (line 93) and adding std.range at module level is appropriate for the new event iteration pattern.


123-123: LGTM!

Initializing lb to 0 prevents undefined behavior. This is a good fix.


176-183: LGTM!

The double-ESC exit pattern is a good UX design, preventing accidental exits. The implementation correctly increments the counter and exits only on the second press.


184-196: LGTM!

The key handling for Graph events is well-designed:

  • C/c clears the screen
  • Ctrl-L triggers a redraw following UNIX conventions (line 189)

The modifier check ev.key.mod == Modifiers.ctrl ensures only plain Ctrl-L triggers the sync.

source/dcell/ttyscreen.d (4)

557-584: LGTM!

The TtyEventQ design is excellent. Based on learnings, the distinction between put() (which calls wakeUp() for external synthetic events) and opOpAssign (which bypasses wakeUp() for internal parser events) is intentional and provides a performance optimization. The comments clearly explain this design decision.


449-502: LGTM!

The waitForEvent implementation is well-designed:

  1. Overflow protection: Line 452 guards against Duration.max overflow
  2. Parser event flushing: Line 461 ensures parser events are consumed
  3. Resize detection: Lines 462-468 inject resize events when needed
  4. Smart timeout handling: Lines 480-488 ensure at least one read attempt even after timeout expires
  5. Partial escape handling: Lines 495-498 reduce the interval to 5ms when a partial escape sequence is detected, and line 484 sets resched to 25ms to request caller retry

The intervals (5ms for waiting on partial escapes, 25ms for rescheduling) are reasonable values for interactive responsiveness.


504-507: LGTM!

The events() accessor correctly returns the internal event queue with appropriate attributes (nothrow @safe @nogc).


160-160: LGTM!

The evq initialization is correctly placed after ti.start() (line 158). Since TtyEventQ.put() calls ti.wakeUp() (line 562), the ti field must be set before constructing the event queue.

source/dcell/event.d (1)

97-100: The popFront() attributes are correct and properly validated.

The popFront() method correctly delegates to std.range.popFront() with the attributes nothrow pure @nogc @safe. According to the D standard library, popFront() for dynamic arrays has the signature pure nothrow @nogc @safe void popFront(T)(ref scope inout(T)[] a), making the declared attributes accurate. Array slicing (advancing the slice via a = a[1 .. $]) is indeed O(1) and does not allocate, confirming the @nogc attribute is valid.

Comment on lines +232 to +234
// NB: this does is the unmasked value!
// It also does not support chording mouse buttons
switch (ev.mouse.btn)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify or fix comment about mouse button handling.

The comment on line 232-233 states "NB: this does is the unmasked value!" but the switch on line 234 uses ev.mouse.btn which is the original unmasked value (before the button variable was masked on line 225). However, the comment's wording "this does is" appears to be a typo and the intent is unclear.

Consider clarifying the comment:

-                // NB: this does is the unmasked value!
-                // It also does not support chording mouse buttons
+                // NB: this switch uses the unmasked btn value (includes wheel events)!
+                // It does not support chord detection (multiple buttons simultaneously)
                 switch (ev.mouse.btn)

This makes it clear that the switch intentionally uses the full ev.mouse.btn value (not the masked button variable), which allows individual case branches to match exact button combinations.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// NB: this does is the unmasked value!
// It also does not support chording mouse buttons
switch (ev.mouse.btn)
// NB: this switch uses the unmasked btn value (includes wheel events)!
// It does not support chord detection (multiple buttons simultaneously)
switch (ev.mouse.btn)
🤖 Prompt for AI Agents
In demos/mouse/source/mouse.d around lines 232-234, the inline comment contains
a typo ("this does is") and is unclear about why the switch uses ev.mouse.btn
instead of the masked button variable; update the comment to correct the typo
and explicitly state that the switch intentionally uses the full unmasked
ev.mouse.btn value (not the masked button from line 225) so case branches can
match exact button combinations and support chording or combined button values.

@gdamore gdamore merged commit 534aae0 into main Dec 14, 2025
5 checks passed
@gdamore gdamore deleted the gdamore/events-range branch December 14, 2025 23:39
@gdamore gdamore restored the gdamore/events-range branch December 15, 2025 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants