Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions .github/workflows/build-full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ jobs:
- name: Install GrabNim
if: runner.os != 'Windows'
run: |
curl -fsSL https://codeberg.org/janAkali/grabnim/raw/branch/master/misc/install.sh | sh
curl --fail --silent --show-error --location --http1.1 \
--retry 3 --retry-all-errors \
--output "$RUNNER_TEMP/grabnim-install.sh" \
https://codeberg.org/janAkali/grabnim/raw/branch/master/misc/install.sh
sh "$RUNNER_TEMP/grabnim-install.sh"
echo "PATH=$HOME/.nimble/bin:$HOME/.local/share/grabnim/current/bin:$PATH" >> $GITHUB_ENV

- name: Install Nim
Expand Down Expand Up @@ -105,7 +109,7 @@ jobs:
env:
NIMFLAGS: ${{ runner.os == 'Windows' && '--cc:vcc' || '' }}
run: |
"$NIMBLE_BIN" install -y -d --features:dev
"$NIMBLE_BIN" install -y -d --features:siwin_examples

- name: Build Tests
if: runner.os == 'Linux'
Expand All @@ -118,7 +122,7 @@ jobs:
VK_ICD_FILENAMES: "/usr/share/vulkan/icd.d/lvp_icd.x86_64.json"
VK_DRIVER_FILES: "/usr/share/vulkan/icd.d/lvp_icd.x86_64.json"
run: |
xvfb-run -a -s "-screen 0 1280x1024x24" "$NIMBLE_BIN" --features:dev test
xvfb-run -a -s "-screen 0 1280x1024x24" "$NIMBLE_BIN" --features:siwin_examples test

- name: Test Wayland event loop
if: runner.os == 'Linux'
Expand Down Expand Up @@ -166,21 +170,21 @@ jobs:
- name: Build Tests
if: runner.os == 'macOS'
run: |
"$NIMBLE_BIN" --features:dev test
"$NIMBLE_BIN" --features:siwin_examples test

- name: Build Tests
if: runner.os == 'Windows'
env:
NIMFLAGS: "--cc:vcc -d:siwin.vulkan=off"
run: |
"$NIMBLE_BIN" --features:dev test
"$NIMBLE_BIN" --features:siwin_examples test

- name: Build Tests (Vulkan Run Check)
if: runner.os == 'Windows'
env:
NIMFLAGS: "--cc:vcc -d:siwin.vulkan=on"
run: |
"$NIMBLE_BIN" test
"$NIMBLE_BIN" --features:siwin_examples test

- name: Upload Test Output (on failure)
if: failure()
Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,11 @@ run window, WindowEventsHandler(
glEnd()
)
```
note: call redraw(window) every time you want window.render to be called. siwin will automatically call window.render only when window resizes.
Call `redraw(window)` when application content changes. Siwin also schedules
`onRender` when the window resizes or its native surface needs repainting, such
as X11 `Expose` and WinAPI `WM_PAINT` damage. An event-loop wake does not itself
request a redraw. Render a complete frame in `onRender`, including when your
application state is unchanged.
note: opengl 1.x and 2.x functions (like `glBegin`), is not supported on Wayland, due to Wayland only beeng able to initialize with EGL

## Vulkan
Expand Down Expand Up @@ -343,7 +347,7 @@ discard globals.waitEvents(timeUntilNextAnimation)
window.serviceWindow()
```

See [text_input_demo.nim](examples/text_input_demo.nim) for a complete loop that combines native input, cursor blinking, and scroll-decay deadlines.
See [text_input.nim](examples/text_input.nim) for a complete loop that combines native input, cursor blinking, and scroll-decay deadlines.

<h2 align="center">running multiple windows</h2>

Expand Down Expand Up @@ -436,7 +440,7 @@ if window.supports(wvcBackdropMaterial):
window.setBackdrop(initWindowBackdrop(wbmSidebar))
```

See [backdrop_blur_demo.nim](examples/backdrop_blur_demo.nim) for a runnable cross-platform example.
See [backdrop_blur.nim](examples/backdrop_blur.nim) for a runnable cross-platform example.

<h2 align="center">all methods and events</h2>

Expand All @@ -462,7 +466,6 @@ If you want to support this project, here is some tasks to do:
* if you doing very big refactoring, first create issue to ask is all your changes needed, and if it is, refactor
* Documentation
* Optimization
* MacOS support
* Android/IOS support
* Web support
* copy/paste images
Expand Down
64 changes: 1 addition & 63 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,68 +6,9 @@
- [x] Wayland: apply surface/output scale (including fractional scale support when available) to mouse move/click coordinates so input coordinates match physical pixel size.
- [x] X11: keep native X11 window and pointer coordinates in physical pixels while exposing `Xft.dpi` through `window.uiScale`.

## Event-Driven Application Loop

- [x] Define `pollEvents` and `waitEvents` as application-thread-only operations that synchronously dispatch pending native callbacks from the platform application queue.
- [x] Define `EventLoopWaker` as a narrow, opaque, copyable capability so worker threads do not need to retain or access the complete `SiwinGlobals` object.
- [x] Define `EventLoopWaker.wake` as thread-safe, data-free notification: producers must enqueue their work first and then wake the loop; wakeups may be coalesced.
- [x] Keep `wakeEventLoop(globals)` as a convenience wrapper around `globals.eventLoopWaker().wake()` for callers already on or otherwise holding the globals owner.
- [x] Define waker lifetime and shutdown semantics explicitly, including what happens when a copied waker outlives its `SiwinGlobals`; waking a stopped/destroyed loop must be harmless and must not access a closed OS handle.
- [x] Document the integration pattern for other event loops or queues: install one waker on the application-thread destination queue, enqueue each message before calling `wake`, then drain that queue after `waitEvents` returns.
- [x] Support animation scheduling either by having a timer or queue producer enqueue a tick and wake the loop, or by passing the next animation deadline to timed `waitEvents`; `examples/text_input_demo.nim` demonstrates the deadline-based form and individual animations do not register with Siwin.
- [x] Keep arbitrary external FD/source registration outside this initial API; enqueue-plus-wake is sufficient for renderer completions, image loading, animation schedulers, and other application queues.
- [x] Make `serviceWindow` nonblocking and responsible only for per-window tick, redraw/render, buffer swap, and presentation work on Cocoa, Winapi, X11, and Wayland.
- [x] Preserve the existing source and C ABI: keep no-argument `Window.step()` and `siwin_window_step` behavior available as compatibility wrappers while embedders opt into `pollEvents`/`waitEvents` plus `serviceWindow`.
- [x] Do not add a wake callback to `WindowEventsHandler`, because wakeup is application-loop control rather than a window event and changing the handler layout could break ABI consumers.
- [x] Add corresponding additive C ABI functions: `siwin_poll_events`, `siwin_wait_events`, `siwin_wake_event_loop`, and `siwin_window_service`.
- [x] Give non-Nim consumers an independently retained opaque `SiwinEventLoopWaker` handle so producer threads need not retain `SiwinGlobals`; waking it after globals shutdown is harmless.
- [x] Add `runEventDriven` and `runMultipleEventDriven`, which wait once and then service every window; keep `run` and `runMultiple` unchanged so their continuous `onTick` behavior remains compatible.
- [x] Support an infinite idle wait and immediate/nonblocking `pollEvents` and zero-timeout paths.
- [x] Ensure every backend's finite wait and internal scheduling deadlines remain monotonic across wall-clock changes.
- [x] Add cross-platform tests for idle blocking/CPU use, zero timeout, copied cross-thread wakers, shutdown safety, wakeup coalescing, nonblocking window service, and existing `step()` compatibility.
- [x] Exercise the X11 wait path under Xvfb and the Wayland wait path under headless Weston in Linux CI.
- [x] Extend global-loop coverage to multiple windows, redraw scheduled by a worker wake, and repeated producer/consumer races that stress no-lost-wake behavior.

### Cocoa (macOS)

- [x] Move the application-global `NSApp` event draining out of `WindowCocoa.step`; the compatibility wrapper now delegates to the global pump before servicing its window.
- [x] Implement `pollEvents` by draining immediately available events in default, event-tracking/live-resize, and modal-panel run-loop modes.
- [x] Implement `waitEvents` with `nextEventMatchingMask`, using `distantFuture` plus a monotonic dispatch-timer sentinel for finite waits, then drain immediately available events before returning.
- [x] Implement `wakeEventLoop` by posting a coalesced application-defined `NSEvent`, which AppKit permits from a secondary thread.
- [x] Recognize and consume the Siwin wake sentinel without forwarding it to a window or invoking a `WindowEventsHandler` callback; clear the coalescing flag before the application drains its work queues.
- [x] Keep all AppKit dispatch and window callbacks on the application thread.

### X11

- [x] Wait with `poll()` on both `ConnectionNumber(display)` and a self-pipe owned by `SiwinGlobalsX11`.
- [x] Implement `wakeEventLoop` by signaling only the wake FD; do not call Xlib from the producer thread or require `XInitThreads`.
- [x] Add a window registry to `SiwinGlobalsX11` mapping X11 window IDs to `WindowX11` objects.
- [x] Replace the current per-window `XCheckIfEvent` loop with direct global `XPending`/`XNextEvent` dispatch that routes each event through the registry while preserving key-repeat lookahead, clipboard, drag-and-drop, and sync-request behavior.
- [x] Drain/reset the wake FD without losing a wake that races with event dispatch.
- [x] Flush X11 output at the appropriate global/per-window boundaries without introducing a blocking `XNextEvent` call after the readiness check.

### Wayland

- [x] Wait with `poll()` on `wl_display_get_fd()` and a self-pipe owned by `SiwinGlobalsWayland`.
- [x] Add bindings for `wl_display_prepare_read`, `wl_display_read_events`, and `wl_display_cancel_read`.
- [x] Implement the required race-free sequence: dispatch pending events until `prepare_read` succeeds, flush, poll, call `read_events` when the display is readable or `cancel_read` when another source wakes the loop, then dispatch pending events.
- [x] Replace the per-window `wl_display_roundtrip()` in `WindowWayland.step` with the application-global event pump; retain roundtrips only where synchronous initialization/configuration genuinely requires them.
- [x] Continue routing callbacks through the existing `SiwinGlobalsWayland.associatedWindows` and Wayland proxy state.
- [x] Include keyboard-repeat deadlines in the next wait timeout so held keys continue repeating without idle polling.
- [x] Verify that libdecor dispatch and flushing are integrated with the same display wait without adding a second polling loop.

### Windows (Winapi)

- [x] Store the application thread/event-loop state in a Winapi-specific `SiwinGlobals`, including an auto-reset Win32 event used for cross-thread wakeups.
- [x] Implement `waitEvents` with `MsgWaitForMultipleObjectsEx`, monitoring both the wake handle and the thread message queue with `QS_ALLINPUT` and `MWMO_INPUTAVAILABLE`.
- [x] Implement `wakeEventLoop` with `SetEvent`; signaling before the wait remains observable and repeated signals may be coalesced.
- [x] Move the global `PeekMessage`/`TranslateMessage`/`DispatchMessage` loop out of `WindowWinapi.step` and remove its idle `sleep(1)` path.
- [x] Let normal Win32 dispatch continue routing messages to the correct `HWND` window procedure, while `serviceWindow` handles only per-window tick/render/presentation work.
- [x] Map finite `Duration` values safely to the millisecond timeout accepted by `MsgWaitForMultipleObjectsEx`, including zero and infinite waits.

## Wayland
- [x] Make `KeyEvent.modifiers` reflect effective xkb modifier state (including remaps like Caps-as-Ctrl), not only raw pressed key symbols.
- [x] Fix keyboard repeat handling in `text_input_demo`/Wayland path (robust hold/repeat behavior with sane fallback repeat settings).
- [x] Fix keyboard repeat handling in `text_input`/Wayland path (robust hold/repeat behavior with sane fallback repeat settings).
- [ ] Use current xkb state for key mapping so `KeyEvent` respects active layout/group, not only unmodified symbols.
- [ ] Improve scroll handling by consuming `axis_source`, `axis_discrete`, and `axis_value120` events instead of relying on a fixed divisor.
- [ ] Revisit scroll normalization to avoid hardcoded `kde_default_mousewheel_scroll_length = 15`.
Expand All @@ -80,9 +21,6 @@
- [ ] Investigate support for user scroll preferences (direction/speed) where available.
- [ ] Improve XIM text-input path to handle multi-stage IME composition updates more explicitly.

## Testing
- [x] Skip `tests/t_opengl.nim` and `tests/t_opengl_es.nim` when targeting macOS in Nimble test runners.

## Cocoa (macOS)
- [x] Implement missing core window methods on Cocoa: `close`, `size=`, `pos=`, `fullscreen=`, `maximized=`, `minimized=`, `resizable=`, `minSize=`, `maxSize=`, `vsync=`, `icon=`.
- [x] Fix selector typo for `otherMouseUp:` so extra mouse button release events are dispatched correctly.
Expand Down
10 changes: 9 additions & 1 deletion bindings/siwin.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

#include <stdint.h>

#ifdef __cplusplus
namespace siwin {
#endif
Expand All @@ -9,6 +11,12 @@ typedef struct {} *Screen;
typedef struct {} *Clipboard;
typedef struct {} *SiwinEventLoopWaker;

#if UINTPTR_MAX == UINT64_MAX
typedef void* VulkanSurface;
#else
typedef uint64_t VulkanSurface;
#endif


typedef struct NimRtti {
void* destructor;
Expand Down Expand Up @@ -417,7 +425,7 @@ typedef struct WindowEventHandler {
extern void siwin_window_make_current(Window window);

extern char siwin_window_set_vsync(Window window, char v);
extern void* siwin_window_vulkan_surface(Window window);
extern VulkanSurface siwin_window_vulkan_surface(Window window);
extern Clipboard siwin_window_clipboard(Window window);
extern Clipboard siwin_window_selection_clipboard(Window window);
extern Clipboard siwin_window_dragndrop_clipboard(Window window);
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
5 changes: 0 additions & 5 deletions examples/popup_demo.nim → examples/popup.nim
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ proc fillRect(
for px in x0 ..< x1:
rgbaBuffer[row + px] = color

proc px(scale: float32, value: int32): int32 =
round(value.float64 * scale.float64).int32

proc pf(scale: float32, value: float32): float32 =
(value.float64 * scale.float64).float32

proc popupButtonRect(size: IVec2): tuple[x, y, w, h: int32] =
(
Expand Down
File renamed without changes.
Loading
Loading