love icon indicating copy to clipboard operation
love copied to clipboard

Mouse is not updating on right / bottom screen edge (DPI scale > 100%)

Open damil42 opened this issue 1 year ago • 7 comments

The mouse does not update on the right and bottom edges of the screen.

demo

function love.draw()
  love.graphics.clear(0.1, 0.2, 0.3)

  local sw, sh = love.graphics.getDimensions()
  local mx, my = love.mouse.getPosition()
  local time = love.timer.getTime()

  love.graphics.setColor(0.3, 0.4, 0.5)
  love.graphics.setLineWidth(5)
  love.graphics.line(0, my, sw, my)
  love.graphics.line(mx, 0, mx, sh)
  love.graphics.circle("fill", mx, my, 20 + math.cos(time * 5) * 5)
  love.graphics.circle("line", mx, my, 40 + math.sin(time * 5) * 10)

  love.graphics.setColor(1, 1, 1)
  love.graphics.print("Press F11 to toggle fullscreen.", 10, 10)
  love.graphics.print("In fullscreen, move the mouse to each edge of the screen.", 10, 30)
  love.graphics.print("Bug: The mouse is not updated on the right and bottom edges.", 10, 50)
  love.graphics.print("Tested on Windows 11.", 10, 70)
end

function love.keypressed(key)
  if key == "escape" then love.event.quit() end
  if key == "f11" then love.window.setFullscreen(not love.window.getFullscreen()) end
end

Version 11.5 (2023-12-03) Windows 11

damil42 avatar Apr 01 '24 12:04 damil42

It's working as expected for me in Windows 10 with love 11.5 - in windowed mode, the mouse position isn't updated when the cursor is outside of the window (no matter what edge). In fullscreen it behaves the same, I have two monitors and if my mouse cursor goes past the right edge of the primary one where love is into my secondary monitor, then the mouse position in love doesn't update.

Additionally, if I hold my left mouse button down and move the cursor around, it updates the mouse position past the bounds of the window no matter the edge but still clamps it (this is new intended behaviour as of the latest SDL version).

Do you have a multi-monitor setup that extends past the bottom and right sides of the display where you made that recording, maybe?

love also has APIs like love.mouse.setRelativeMode and love.mouse.setGrabbed if you want to constrain the user's cursor to the window in a particular way.

slime73 avatar Apr 01 '24 13:04 slime73

I only have one monitor. The mouse is within the screen. In the gif video, you can see a bit of the cursor when moving on the right edge. I know that the mouse is not updating when moved outside. This is ok. But in this case, the mouse is still within the window. You can just detect this bug in fullscreen where you can't move outside.

One use case I'm thinking is an RTS/city building game where you can move the mouse on the edge to scroll the camera view. No mouse event (click or position) is working on the edge right or bottom.

function love.draw()
  love.graphics.clear(0.1, 0.2, 0.3, 0.1)

  local sw, sh = love.graphics.getDimensions()
  local mx, my = love.mouse.getPosition()

  love.graphics.setColor(0.3, 0.4, 0.5)
  love.graphics.setLineWidth(5)
  love.graphics.line(0, my, sw, my)
  love.graphics.line(mx, 0, mx, sh)
  love.graphics.circle("fill", mx, my, love.mouse.isDown(1) and 30 or 10)
end

function love.keypressed(key)
  if key == "escape" then love.event.quit() end
  if key == "f11" then love.window.setFullscreen(not love.window.getFullscreen()) end
end

As you can see in the code. You can now click the (left) mouse button to change the circle size. Try it on the edge. Unplug your second monitor and test it.

I don't know the source code but it looks like (pseudo code):

mosue_pos >= 0 and mouse_pos < screen_size
-- instead of
mosue_pos >= 0 and mouse_pos <= screen_size

You know what I mean? Or the game area is 1 pixel smaller that the screen. Or the window invisible border blocks the event. I don't know. But it's definitly a bug.

damil42 avatar Apr 01 '24 13:04 damil42

Like I said, it's working as expected on my system, I can't reproduce the problem you're describing - and also love doesn't have code like that itself (it just does what SDL and the OS do under the hood). So there's probably a difference between our two systems causing it. Maybe the OS itself considering you have windows 11 and I have windows 10, or maybe something else.

Do you use a DPI scale / display scale set to something other than 100%, for your monitor? Windows will handle a bunch of scaling itself and upscale love in that situation.

slime73 avatar Apr 01 '24 13:04 slime73

Indeed, I use 200% UI scaling for my 4k TV. For testing I changed it back to 100% and the problem no longer occurs. So it seems it's related to the UI scaling. Can someone reproduce this or explain what happened here? So, it's a bug. The question is, can we fix this in love2d? Any workaround?

damil42 avatar Apr 01 '24 14:04 damil42

Btw. t.window.highdpi = true does not work on Windows. I would expect an option to disable scaling for the application. I also tried variations with t.window.usedpiscale. It seems this is not supported in love2d. These options are for mobile, right? In e.g. Godot this option also works on Windows. Btw. this is one reason why the screen looks pixelated.

damil42 avatar Apr 01 '24 15:04 damil42

Btw. t.window.highdpi = true does not work on Windows.

That's correct, the version of SDL love uses doesn't support it on Windows with love 11's api.

It does work on macOS, iOS, Android, and Wayland (Linux). Microsoft ended up implementing app DPI scaling in a manner that's incompatible with how love sets it dynamically for the window though.

love 12 has a global per-app API for it instead, to accommodate Windows.

slime73 avatar Apr 01 '24 15:04 slime73

I just played around again and found this... (for the sake of completeness and possible bug fixes)

  • It has nothing to do with the fullscreen. I was able to reproduce the same behavior with mouse.setGrabbed.
  • If isGrabbed then the mouse also hangs on right / bottom edges.
  • If I click and hold the mouse button while moving the cursor around the edges and outside, it works without any problems.

In both cases (grabbed and pressing) the mouse position on edge is the same. But only pressing works as expected (mouse also works on edges). But you can also move the mouse outside the game (if windowed). So I guess it's a global mouse event. Only the last pixel on the right/bottom doesn't seem to be a part of the game view. So setGrabbed does also not work. ...

demo.webm

function love.load()
  love.mouse.setGrabbed(true)
end

function love.draw()
  love.graphics.clear(0.1, 0.2, 0.3, 0.1)

  local sw, sh = love.graphics.getDimensions()
  local mx, my = love.mouse.getPosition()

  love.graphics.setColor(0.3, 0.4, 0.5)
  love.graphics.setLineWidth(5)
  love.graphics.line(0, my, sw, my)
  love.graphics.line(mx, 0, mx, sh)
  love.graphics.circle("fill", mx, my, love.mouse.isDown(1) and 30 or 10)

  love.graphics.setColor(1, 1, 1)
  love.graphics.print("ESC: Quit; F11: Fullscreen; F1: Grab/Free mouse", 10, 10)
  love.graphics.print("Screen size: " .. sw .. ", " .. sh, 10, 30)
  love.graphics.print("Mouse pos: " .. mx .. ", " .. my, 10, 50)
end

function love.keypressed(key)
  if key == "escape" then love.event.quit() end
  if key == "f11" then love.window.setFullscreen(not love.window.getFullscreen()) end
  if key == "f1" then love.mouse.setGrabbed(not love.mouse.isGrabbed()) end
end

damil42 avatar Apr 01 '24 18:04 damil42