On the first internal test of Uru for Mac, I got some weird feedback from testers. As the player character was walking, the game wasn’t responding smoothly to the mouse. As the player moved the mouse to turn the player, the player would turn in fits and starts.
The way Uru handles the mouse is pretty common for games, especially third person games. You hold down the mouse button, and then move the mouse left or right to make the character move to the left of the right. The mouse cursor is hidden – but the cursor is still there under the hood. That means the cursor may run into the side of the screen, which would cause the cursor not to move and the character to stop moving. To get around that, if the mouse reaches the side of the screen, it’s moved back to center. Effectively this allows for infinite turning of the character without worrying about the mouse hitting the screen edges.
On macOS, the cursor can be re-centered using the command CGWarpMouseCursorPosition. The docs even mention this exact use case!
You can use this function to “warp” or alter the cursor position without generating or posting an event. For example, this function is often used to move the cursor position back to the center of the screen by games that do not want the cursor pinned by display edges.
I created a build of Uru that always showed the macOS cursor, and then ran it. And indeed, there was something wrong with the native mouse handling. Every time I used CGWarpMouseCursorPosition the mouse seemed stuck momentarily. I kept my mouse movements even, but the cursor seemed to not move for a split second after warping. This was causing the chunky mouse behavior testers were noticing.
My first hunch was still mouse acceleration. Maybe the warp function was causing mouse acceleration to reset. That would cause the cursor to start moving slowly again every warp until it reached speed. But when logging the cursor’s X position I found something even weirder. After warp, the cursor wasn’t moving at all.
2023-02-05 12:21:03.912695-0800 plClient[56084:1669557] Reset! 2023-02-05 12:21:03.927519-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:03.927660-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:03.944247-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:03.960842-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:03.977446-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:03.994232-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.011021-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.015652-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.027695-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.031597-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.044252-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.047567-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.060862-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.063555-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.077461-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.079589-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.096340-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.111001-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.113572-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.127380-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.129576-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.144342-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.146081-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.160777-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.162525-0800 plClient[56084:1669557] X pos: 0.500000 2023-02-05 12:21:04.177489-0800 plClient[56084:1669557] X pos: 0.518046 2023-02-05 12:21:04.177664-0800 plClient[56084:1669557] X pos: 0.537489 2023-02-05 12:21:04.194020-0800 plClient[56084:1669557] X pos: 0.574978 2023-02-05 12:21:04.210724-0800 plClient[56084:1669557] X pos: 0.606649 2023-02-05 12:21:04.227363-0800 plClient[56084:1669557] X pos: 0.654614 2023-02-05 12:21:04.243930-0800 plClient[56084:1669557] X pos: 0.704136 2023-02-05 12:21:04.260511-0800 plClient[56084:1669557] X pos: 0.761408 2023-02-05 12:21:04.265584-0800 plClient[56084:1669557] X pos: 0.782302 2023-02-05 12:21:04.277291-0800 plClient[56084:1669557] X pos: 0.801745 2023-02-05 12:21:04.281584-0800 plClient[56084:1669557] X pos: 0.834301 2023-02-05 12:21:04.293937-0800 plClient[56084:1669557] X pos: 0.852347 2023-02-05 12:21:04.297583-0800 plClient[56084:1669557] X pos: 0.871790 2023-02-05 12:21:04.312268-0800 plClient[56084:1669557] X pos: 0.891234 2023-02-05 12:21:04.313994-0800 plClient[56084:1669557] X pos: 0.912127
When resetting/warping the cursor, the cursor stays stuck at the midpoint of 0.5 for a full 0.25 seconds. It’s not slowly accelerating. The cursor actually isn’t moving at all after a warp.
I was fortunate enough to find this Stack Overflow describing exactly whats going on. It seems that, after warping the cursor position, macOS will ignore hardware mouse events for 0.25 seconds. The CGEventSourceSetLocalEventsSuppressionInterval can be used to control this behavior.
By default, the system does not suppress local hardware events from the keyboard or mouse during a short interval after a Quartz event is posted. You can use the function
CGEventSourceSetLocalEventsFilterDuringSuppressionState
to modify this behavior.This function sets the period of time in seconds that local hardware events may be suppressed after posting a Quartz event created with the specified event source. The default suppression interval is 0.25 seconds.
Note that the documentation seems contradictory. It says that by default the system does not suppress local hardware events. The last paragraph is actually correct. By default the system will suppress hardware events for 0.25 seconds after a warp.
With the following C code, we can fix the issue and get the desired behavior.
CGEventSourceRef eventSourceRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
CGEventSourceSetLocalEventsSuppressionInterval(eventSourceRef, 0.0);
CFRelease(eventSourceRef);
So – if you are porting a Windows game to macOS and getting weird behavior with CGWarpMouseCursorPosition – CGEventSourceSetLocalEventsSuppressionInterval might be your fix.
Вы ищете способ сохранить прохладу жарким летом в Ростове-на-Дону? Посмотрите на кондиционеры! Благодаря услугам по установке, вы можете получить совершенно новый кондиционер, который будет работать в вашем доме или офисе в кратчайшие сроки.
Мы рады предложить услуги по установке всех наших кондиционеров: ростов кондиционер. Наша команда профессионалов обеспечит правильную и безопасную установку вашего нового устройства, чтобы вы могли наслаждаться прохладной температурой и эффективной работой круглый год. Мы также предлагаем услуги по техническому обслуживанию для поддержания максимальной производительности вашего устройства.
Кондиционирование воздуха является неотъемлемой частью комфортной жизни в летние месяцы в Ростове-на-Дону. С нашими услугами по установке вы можете быть спокойны, зная, что ваш новый кондиционер будет запущен и работать быстро и эффективно.
d3mfnz
rn0ti4
3ifmgn
q006xx
x6bfol
0yea82
lgke6m
araefh