Search code examples
javalinuxdebugginglwjglfreeze

LWJGL grabbed mouse - debug if application hangs or when breakpoint hits with grabbed mouse


I have a LWJGL program (LWJGL 2.9.0) that sometimes randomly hangs. The problem with debugging it is that mouse is always grabbed. On Windows it's possible to get mouse back without any issues, but on linux (I use linux Kubuntu) the only way I know to get mouse back is to stop the application. The same issue happens when a breakpoint hits when mouse is grabbed.

Using netbeans debug mode I can pause application and get some information at any time, but when the application hangs mouse no longer works (there is no cursor). Is it possible to get the mouse back without stopping the application or debug using only keyboard?


Solution

  • So far I have found 5 solutions to this problem:

    1. This may or may not work depending on your IDE and operating system - if you are able to switch into the IDE window you can try to use keyboard shortcuts to pause execution and then evaluate expression to ungrab the mouse. The expression you need to evaluate in that case is Mouse.setGrabbed(false). This is also useful when breakpoint hits and your mouse is stuck within LWJGL window. Since I first asked this question I switched to IntelliJ IDEA so here is how to do it in that IDE: alt+u to open "run" menu, then select "pause", then step through the code one line further using F7 or F8, and then press alt+u again and select "evaluate expression".

    2. Configure breakpoint to evaluate Mouse.setGrabbed(false).

      Alternatively you can set a breakpoint and apply a condition with code that ungrabs the mouse, for example: package com.acne;

      import org.lwjgl.input.Mouse;
      
      public class DebugHelper {
          public static boolean restoreMouse() {
              Mouse.setGrabbed(false);
              return true;
          }
      }
      

      Then set your breakpoint condition to com.acne.DebugHelper.restoreMouse()

    3. Remote debugging - good solution if you have access to a second machine and know that you will need remote debugging before starting your program.

      On the first computer start it in debug mode and attach the debugger on the second computer.

    4. [linux only] By starting second X session

      Switch to tty1/2/... using ctrl+alt+Fn (for example ctr+alt+F1 for tty1), login and run command startx. This should start new X session, eighter in the tty you are in or in tty8. The you can switch between graphical environments using ctrl+alt+Fn (usially F7 and F8).

      Unfortunately this is not a good solution if your application takes so much memory that you can't run second X session.

    5. [linux only] You can add a second mouse pointer. Your LWJGL (or OpenGL) application will grab only one mouse pointer and you will have the second one for you.

      Unfortunately most window managers don't officially support multiple mouse pointers, but it doesn't mean that it doesn't work. It does work, but there are some annoying glitches.

      You can add a second mouse pointer using xinput:

      • Run xinput create-master pointer-name. A second mouse pointer should appear on the screen. This creates keyboard/pointer pair, you don't need to do anything with the second added keyboard. It won't be attached to any physical device.
      • Run xinput list to list all your devices

        On my laptop it looks like this:

        ⎡ Virtual core pointer                      id=2    [master pointer  (3)]
        ⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
        ⎜   ↳ ETPS/2 Elantech Touchpad                  id=14   [slave  pointer  (2)]
        ⎜   ↳ A4Tech USB Mouse                          id=11   [slave  pointer  (2)]
        ⎣ Virtual core keyboard                     id=3    [master keyboard (2)]
            ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
            ↳ Power Button                              id=6    [slave  keyboard (3)]
            ↳ Video Bus                                 id=7    [slave  keyboard (3)]
            ↳ Video Bus                                 id=8    [slave  keyboard (3)]
            ↳ Power Button                              id=9    [slave  keyboard (3)]
            ↳ Lenovo EasyCamera                         id=10   [slave  keyboard (3)]
            ↳ Ideapad extra buttons                     id=12   [slave  keyboard (3)]
            ↳ AT Translated Set 2 keyboard              id=13   [slave  keyboard (3)]
        ⎡ new-mouse pointer                         id=15   [master pointer  (16)]
        ⎜   ↳ new-mouse XTEST pointer                   id=17   [slave  pointer  (15)]
        ⎣ new-mouse keyboard                        id=16   [master keyboard (15)]
            ↳ new-mouse XTEST keyboard                  id=18   [slave  keyboard (16)]
        

        The newly added mouse pointer (master device) has id=15. I have a touchpad and an external mouse so I can attach one of them to the new cursor and leave the other attached to the old cursor. If you don't have 2 physical devices - you can leave the old pointer with no physical device attached.

      • Now run xinput reattach slave-device-id master-device-id. For example if I want to attach my touchpad to the new pointer: xinput reattach 14 15

        After this you should be able to control the newly added pointer.

      • When you no longer want the second mouse pointer use xinput remove-master master-device-id, in my case it would be xinput remove-master 15

      • sometimes you may need to reattach the device to the previous master device.

        Note: It's better to add the new pointer before you start debugging. I also noticed that some window managers have some issues with multiple cursors that cause all kinds of unexpected bugs - for example "typing stops working", or typing works but in the wrong window. So leaving multiple cursors enabled normally may not be a good option.