Skip to content
Merged
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
2 changes: 1 addition & 1 deletion ios/ios_event.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ int axWaitMessage(longTime_t msec) {
}

void axRemoveFromQueue(void *target) {
ios_remove_messages(target);
ios_remove_messages(target, 0, AX_ANY_WPARAM);
for (NSNumber *key in ios_timers.allKeys) {
if ([objc_getAssociatedObject(ios_timers[key], &ios_timer_owner) pointerValue] == target) axCancelTimer(key.unsignedIntValue);
}
Expand Down
3 changes: 2 additions & 1 deletion ios/ios_message_queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ void ios_post_display_paint(uint32_t size);
// Freeze the readable queue at the start of a UIKit display callback.
void ios_begin_message_frame(void);
int ios_messages_ready(void);
void ios_remove_messages(void *target);
// Remove queued messages for target; message 0 matches any type.
void ios_remove_messages(void *target, uint32_t message, uint32_t wparam);
void ios_shutdown_messages(void);

#endif
9 changes: 7 additions & 2 deletions ios/ios_message_queue.m
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,14 @@ int axPeekMessage(struct AXmessage *msg) {
return ready;
}

void ios_remove_messages(void *target) {
void ios_remove_messages(void *target, uint32_t message, uint32_t wparam) {
pthread_mutex_lock(&ios_queue_mutex);
ios_message_t **link = &ios_head;
ios_tail = NULL;
while (*link) {
ios_message_t *p = *link;
if (p->value.target == target) {
if (p->value.target == target && (!message || p->value.message == message) &&
(wparam == AX_ANY_WPARAM || p->value.wParam == wparam)) {
*link = p->next;
if (p->value.message == kEventDragDrop) free(p->value.lParam);
free(p);
Expand All @@ -115,6 +116,10 @@ void ios_remove_messages(void *target) {
pthread_mutex_unlock(&ios_queue_mutex);
}

void axRemoveMessages(void *target, uint32_t message, uint32_t wparam) {
if (message) ios_remove_messages(target, message, wparam);
}

void ios_shutdown_messages(void) {
pthread_mutex_lock(&ios_queue_mutex);
while (ios_head) {
Expand Down
11 changes: 11 additions & 0 deletions macos/macos_event.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ uint32_t KEY_GetKeyName(uint32_t keycode) {
axCancelTimer(s_timers[i].id);
}

void
axRemoveMessages(void* target, uint32_t message, uint32_t wparam)
{
// Cocoa events refer to queue slots by index, so clear matches in place.
for (uint16_t r = queue.read; r != queue.write; r++) {
struct AXmessage const *e = &queue.data[r];
if (e->target == target && e->message == message && (wparam == AX_ANY_WPARAM || e->wParam == wparam))
memset(&queue.data[r], 0, sizeof(queue.data[r]));
}
}

void
axPostMessageW(void* obj, uint32_t Msg, wParam_t wParam, lParam_t lParam)
{
Expand Down
19 changes: 19 additions & 0 deletions platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,25 @@ axGetMessage(struct AXmessage* msg);
AX_API void
axRemoveFromQueue(void* target);

/** @brief Wildcard for #axRemoveMessages: match an event whatever its wParam. */
#define AX_ANY_WPARAM ((uint32_t)-1)

/**
* @brief Remove queued events of one type for one target.
*
* Removes every queued event whose target is @p target and whose type is
* @p message, keeping the order of the others. Pass #AX_ANY_WPARAM as
* @p wparam to match any wParam, or a value to remove only the events that
* carry it, such as the ticks of one cancelled timer. Unlike
* #axRemoveFromQueue, this leaves the target's timers running.
*
* @param target Window or object handle to match against.
* @param message Event type to remove (e.g. #kEventTimer).
* @param wparam wParam to match, or #AX_ANY_WPARAM for any.
*/
AX_API void
axRemoveMessages(void* target, uint32_t message, uint32_t wparam);

/**
* @brief Notify the event system that a file was dropped onto the window.
*
Expand Down
12 changes: 12 additions & 0 deletions qnx/qnx_system.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,18 @@ axRemoveFromQueue(void *target)
memset(&queue.data[r], 0, sizeof(queue.data[r]));
}

void
axRemoveMessages(void *target, uint32_t message, uint32_t wparam)
{
uint16_t r = queue.read, w = queue.write, nw = queue.read;
while (r != w) {
struct AXmessage const *e = &queue.data[r++];
if (e->target != target || e->message != message || (wparam != AX_ANY_WPARAM && e->wParam != wparam))
queue.data[nw++] = *e;
}
queue.write = nw;
}

int
axWaitMessage(longTime_t msec)
{
Expand Down
24 changes: 21 additions & 3 deletions tests/test_ios_message_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ static void test_removal_and_reset(void) {
axPostMessageW(&first, kEventDragDrop, 0, strdup("removed file"));
ios_begin_message_frame();
axPostMessageW(&second, kEventKeyUp, 3, NULL);
ios_remove_messages(&first);
ios_remove_messages(&first, 0, AX_ANY_WPARAM);
assert(axPeekMessage(&msg) && msg.target == &second && msg.wParam == 2);
assert(!axPeekMessage(&msg));
ios_remove_messages(&second);
ios_remove_messages(&second, 0, AX_ANY_WPARAM);
axPostMessageW(&first, kEventKeyDown, 4, NULL);
assert(!axPeekMessage(&msg));
ios_begin_message_frame();
Expand All @@ -89,6 +89,23 @@ static void test_removal_and_reset(void) {
assert(!axPeekMessage(&msg));
}

static void test_remove_by_type(void) {
ios_shutdown_messages();
int first, second;
struct AXmessage msg;
axPostMessageW(&first, kEventTimer, 1, NULL);
axPostMessageW(&first, kEventTimer, 2, NULL);
axPostMessageW(&second, kEventTimer, 1, NULL);
axPostMessageW(&first, kEventKeyDown, 3, NULL);
axRemoveMessages(&first, kEventTimer, 1);
assert(axPeekMessage(&msg) && msg.target == &first && msg.message == kEventTimer && msg.wParam == 2);
assert(axPeekMessage(&msg) && msg.target == &second && msg.wParam == 1);
axPostMessageW(&first, kEventTimer, 4, NULL);
axRemoveMessages(&first, kEventTimer, AX_ANY_WPARAM);
assert(axPeekMessage(&msg) && msg.message == kEventKeyDown && msg.wParam == 3);
assert(!axPeekMessage(&msg));
}

static void test_sequence_wrap(void) {
ios_shutdown_messages();
ios_test_message_sequence(UINT32_MAX - 1);
Expand Down Expand Up @@ -116,8 +133,9 @@ int main(void) {
test_inertia_interrupt();
test_complete_snapshot();
test_removal_and_reset();
test_remove_by_type();
test_sequence_wrap();
ios_shutdown_messages();
puts("PASS: iOS queue frame boundaries, touch interruption, FIFO, removal, reset and sequence wrap");
puts("PASS: iOS queue frame boundaries, touch interruption, FIFO, removal by target and type, reset and sequence wrap");
return 0;
}
23 changes: 23 additions & 0 deletions tests/test_macos_events.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,28 @@ static void test_event_lifetime(void)
}
}

// Removed messages leave cleared slots that Cocoa still points at.
static void test_remove_messages(void)
{
struct AXmessage msg;
void *a = (void *)&test_remove_messages, *b = (void *)&drain_events;
drain_events();
axPostMessageW(a, kEventTimer, 1, NULL);
axPostMessageW(a, kEventTimer, 2, NULL);
axPostMessageW(b, kEventTimer, 1, NULL);
axPostMessageW(a, kEventKeyDown, 3, NULL);
axPostMessageW(a, kEventTimer, 1, NULL);
axRemoveMessages(a, kEventTimer, 1);
assert(axPeekMessage(&msg) == 1 && msg.target == a && msg.message == kEventTimer && msg.wParam == 2);
assert(axPeekMessage(&msg) == 1 && msg.target == b && msg.message == kEventTimer && msg.wParam == 1);
assert(axPeekMessage(&msg) == 1 && msg.target == a && msg.message == kEventKeyDown);
axPostMessageW(a, kEventTimer, 4, NULL);
axPostMessageW(a, kEventKeyUp, 5, NULL);
axRemoveMessages(a, kEventTimer, AX_ANY_WPARAM);
assert(axPeekMessage(&msg) == 1 && msg.message == kEventKeyUp && msg.wParam == 5);
drain_events();
}

static void test_fullscreen(void)
{
assert(axCreateWindow("Fullscreen event test", 640, 480,
Expand Down Expand Up @@ -67,6 +89,7 @@ int main(int argc, char **argv)
axInit();
drain_events();
test_event_lifetime();
test_remove_messages();
if (argc > 1 && !strcmp(argv[1], "--fullscreen")) test_fullscreen();
puts("macOS event lifetime tests passed.");
}
Expand Down
43 changes: 42 additions & 1 deletion tests/test_messages.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/*
* test_messages.c – behavioral tests for the platform event-queue API.
*
* Tests axPostMessageW, axPeekMessage, and axRemoveFromQueue without
* Tests axPostMessageW, axPeekMessage, axRemoveFromQueue and
* axRemoveMessages without
* requiring a live display server or windowing context.
*
* On macOS the internal queue is coupled to NSApp, so message-delivery tests
Expand Down Expand Up @@ -136,6 +137,45 @@ static void test_remove_nonexistent_target(void)
assert(axPeekMessage(&msg) == 0);
}

/* -------------------------------------------------------------------
* test_remove_messages
* axRemoveMessages must remove only one type for one target, matching
* wParam exactly unless AX_ANY_WPARAM is given.
* ------------------------------------------------------------------- */
static void test_remove_messages(void)
{
struct AXmessage msg;
void *handle_a = (void *)0xA0A0;
void *handle_b = (void *)0xB0B0;

drain_queue();

axPostMessageW(handle_a, kEventTimer, 1, NULL);
axPostMessageW(handle_a, kEventTimer, 2, NULL);
axPostMessageW(handle_b, kEventTimer, 1, NULL);
axPostMessageW(handle_a, kEventKeyDown, 1, NULL);
axPostMessageW(handle_a, kEventTimer, 1, NULL);

/* One timer of handle_a: other timers, targets and types stay queued */
axRemoveMessages(handle_a, kEventTimer, 1);
assert(axPeekMessage(&msg) == 1);
assert(msg.target == handle_a && msg.message == kEventTimer && msg.wParam == 2);
assert(axPeekMessage(&msg) == 1);
assert(msg.target == handle_b && msg.message == kEventTimer && msg.wParam == 1);
assert(axPeekMessage(&msg) == 1);
assert(msg.target == handle_a && msg.message == kEventKeyDown);
assert(axPeekMessage(&msg) == 0);

/* AX_ANY_WPARAM removes the type whatever its wParam */
axPostMessageW(handle_a, kEventTimer, 3, NULL);
axPostMessageW(handle_a, kEventKeyDown, 4, NULL);
axPostMessageW(handle_a, kEventTimer, 5, NULL);
axRemoveMessages(handle_a, kEventTimer, AX_ANY_WPARAM);
assert(axPeekMessage(&msg) == 1);
assert(msg.message == kEventKeyDown && msg.wParam == 4);
assert(axPeekMessage(&msg) == 0);
}

/* -------------------------------------------------------------------
* test_lparam_roundtrip
* The lParam pointer must survive the queue round-trip unchanged.
Expand Down Expand Up @@ -230,6 +270,7 @@ int main(void)
test_fifo_ordering();
test_remove_from_queue();
test_remove_nonexistent_target();
test_remove_messages();
test_lparam_roundtrip();
test_double_click_sequence();
test_get_message();
Expand Down
15 changes: 15 additions & 0 deletions wayland/wayland_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,21 @@ axRemoveFromQueue(void* hobj)
axCancelTimer(s_timers[i].id);
}

void
axRemoveMessages(void* hobj, uint32_t event, uint32_t wparam)
{
WORD read_idx = events.read;
WORD write_idx = events.write;
WORD new_write = events.read;

while (read_idx != write_idx) {
EVENT const *e = &events.queue[read_idx++];
if (e->target != hobj || e->message != event || (wparam != AX_ANY_WPARAM && e->wParam != wparam))
events.queue[new_write++] = *e;
}
events.write = new_write;
}

void
axNotifyFileDropEvent(char const* filename, float x, float y)
{
Expand Down
15 changes: 15 additions & 0 deletions webgl/webgl_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,21 @@ axRemoveFromQueue(void *hobj)
axCancelTimer(s_timers[i].id);
}

void
axRemoveMessages(void *hobj, uint32_t event, uint32_t wparam)
{
WORD read_idx = events.read;
WORD write_idx = events.write;
WORD new_write = events.read;

while (read_idx != write_idx) {
EVENT const *e = &events.queue[read_idx++];
if (e->target != hobj || e->message != event || (wparam != AX_ANY_WPARAM && e->wParam != wparam))
events.queue[new_write++] = *e;
}
events.write = new_write;
}

void
axNotifyFileDropEvent(char const *filename, float x, float y)
{
Expand Down
15 changes: 15 additions & 0 deletions windows/windows_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,21 @@ axRemoveFromQueue(void *hobj)
axCancelTimer(s_timers[i].id);
}

void
axRemoveMessages(void *hobj, uint32_t event, uint32_t wparam)
{
WORD read_idx = events.read;
WORD write_idx = events.write;
WORD new_write = events.read;

while (read_idx != write_idx) {
EVENT const *e = &events.queue[read_idx++];
if (e->target != hobj || e->message != event || (wparam != AX_ANY_WPARAM && e->wParam != wparam))
events.queue[new_write++] = *e;
}
events.write = new_write;
}

void
axNotifyFileDropEvent(char const *filename, float x, float y)
{
Expand Down
15 changes: 15 additions & 0 deletions x11/x11_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,21 @@ axRemoveFromQueue(void* hobj)
#endif
}

void
axRemoveMessages(void* hobj, uint32_t event, uint32_t wparam)
{
WORD read_idx = events.read;
WORD write_idx = events.write;
WORD new_write = events.read;

while (read_idx != write_idx) {
EVENT const *e = &events.queue[read_idx++];
if (e->target != hobj || e->message != event || (wparam != AX_ANY_WPARAM && e->wParam != wparam))
events.queue[new_write++] = *e;
}
events.write = new_write;
}

void
axNotifyFileDropEvent(char const* filename, float x, float y)
{
Expand Down
Loading