Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: janbraun 12. 11. 2016, 09:52:32

Název: Java: událost stisknutí tlačítka v okně
Přispěvatel: janbraun 12. 11. 2016, 09:52:32
Potřeboval bych provést nějakou akci, když se stiskne klávesa kdykoliv a kdekoliv v okně. Chci aby se zareagovalo pouze na uvolnění klávesy. Bohužel když je klávesa držená zmáčknutá, tak tak se akce neustále opakuje. Co dělám špatně?


Kód: [Vybrat]
KeyboardFocusManager.getCurrentKeyboardFocusManager()
                .addKeyEventDispatcher(new KeyEventDispatcher() {
                    @Override
                    public boolean dispatchKeyEvent(KeyEvent e) {
                       
                        if (e.getID() == KeyEvent.KEY_RELEASED) {
                           
                            System.out.println("akce");

                        }
                    }
                });
Název: Re:Java-událost stisknutí tlačítka
Přispěvatel: Filip Jirsák 12. 11. 2016, 11:39:22
Chování při držení klávesy je podle mne závislé na platformě (operačním systému). Java ty události jenom přebírá ze systému.
Název: Re:Java-událost stisknutí tlačítka
Přispěvatel: . 12. 11. 2016, 11:52:12
Javu neznám, ale evidentně jsi se chytil na "klávesnici" a ne na fyzickou klávesu. Zatímco fyzická klávesa dá jeden event down a jeden up, "klávesnice" jich může vygenerovat mraky (viz třeba autorepeat, pokud klávesu držím).
Název: Re:Java-událost stisknutí tlačítka
Přispěvatel: Lol Phirae 12. 11. 2016, 12:08:53
Co dělám špatně?

Používáš Javu.  ;D
Název: Re:Java-událost stisknutí tlačítka
Přispěvatel: Ondra Satai Nekola 14. 11. 2016, 08:41:19
Co dělám špatně?

Používáš Javu.  ;D

Tak urcite.
Název: Re:Java: událost stisknutí tlačítka v okně
Přispěvatel: linuxtardis 14. 11. 2016, 09:28:21
Už jsem se s tím setkal. Prostě Xorg posílá takové eventy.
http://bugs.java.com/view_bug.do?bug_id=4153069
http://stackoverflow.com/questions/1736828/how-to-stop-repeated-keypressed-keyreleased-events-in-swing
Tam někdo odkazuje na workaround: http://tech.stolsvik.com/2010/05/linux-java-repeats-released-keyevents.html
Název: Re:Java: událost stisknutí tlačítka v okně
Přispěvatel: gll 14. 11. 2016, 09:37:01
Na linuxu lze číst eventy z xinput, ale určitě existují i lepší řešení.
Název: Re:Java: událost stisknutí tlačítka v okně
Přispěvatel: linuxtardis 14. 11. 2016, 10:03:11
Když se ti povede nějak z Javy vydobýt X11 handle, tak můžeš zkusit přes JNI zavolat XkbSetDetectableAutoRepeat (https://linux.die.net/man/3/xkbsetdetectableautorepeat).
Název: Re:Java: událost stisknutí tlačítka v okně
Přispěvatel: linuxtardis 14. 11. 2016, 11:27:14
Vyrobil jsem nějaký hack. JNI wrapper je už v knihovně ve formě XlibWrapper, ale to je non-public API.

Kód: (Test.java) [Vybrat]
import java.lang.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Test {
    public static void main(String[] args) throws Exception {
        Program prog = new Program();
        SwingUtilities.invokeAndWait(prog);
    }
   
    public static class Program implements Runnable {

        public Program() {}

        @Override
        public void run() {
            JFrame myFrame = new JFrame("Main frame");

            XHack.SetDetectableAutoRepeat(true);

            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            myFrame.setSize(750, 500);
            myFrame.setLocationRelativeTo(null);
            myFrame.setVisible(true);

            KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new Dispatcher());

        }
    }

    public static class Dispatcher implements KeyEventDispatcher {

        public Dispatcher() {}

        @Override
        public boolean dispatchKeyEvent(KeyEvent e) {

            if (e.getID() == KeyEvent.KEY_RELEASED)
                System.out.println("akce");

            return false;

        }
    }
}
Kód: (XHack.java) [Vybrat]
import java.lang.reflect.Method;
import java.lang.Class;


public class XHack {
    public static void SetDetectableAutoRepeat(boolean enabled) {

        try {
            Class<?> xToolkit = Class.forName("sun.awt.X11.XToolkit");
            Class<?> xWrapper = Class.forName("sun.awt.X11.XlibWrapper");

            Method getDisplay = xToolkit.getDeclaredMethod("getDisplay");
            Method setRepeat  = xWrapper.getDeclaredMethod("XkbSetDetectableAutoRepeat", Long.TYPE, Boolean.TYPE);

            getDisplay.setAccessible(true);
            setRepeat .setAccessible(true);

            long display = (long) getDisplay.invoke(null);
            setRepeat.invoke(null, display, enabled);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
U mě to ale nemá žádný efekt, protože se to chová správně i bez toho.
Název: Re:Java: událost stisknutí tlačítka v okně
Přispěvatel: linuxtardis 14. 11. 2016, 11:42:37
Moje domněnka, proč mi to funguje i bez toho:
XlibWrapper.XkbSetDetectableAutoRepeat(..., true) je volán z XToolkit.tryXKB()
XToolkit.tryXKB() je volán z XToolkit.init()
XToolkit.init() je volán z XToolkit.XToolkit()
XToolkit.XToolkit() je volán nepřímo z Toolkit.getDefaultToolkit() pomocí reflexe.
A Toolkit.getDefaultToolkit() je volán např. z EventQueue.invokeAndWait(...)
a ten je konečně volán z SwingUtilities.invokeAndWait(...)
Název: Re:Java: událost stisknutí tlačítka v okně
Přispěvatel: linuxtardis 14. 11. 2016, 13:10:30
Chjo, někdy mám dlouhé vedení... :D
Funguje to, jde to otestovat tak, že místo true použiješ false. Pak u sebe mám stejnou situaci jako ty ve výchozím stavu (tzn. jak to nepotřebuješ).
Kód: [Vybrat]
XHack.SetDetectableAutoRepeat(false);
Název: Re:Java: událost stisknutí tlačítka v okně
Přispěvatel: phi 14. 11. 2016, 14:48:42
To je proste problem na Xkach, pokud je mi znamo. beznej workarround je hlidat si rozestupy mezi key release a nasledujicim key pressed.