From 0998ba37626fed1bbe0febfbe44d043286f03513 Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Fri, 23 Jan 2026 16:04:46 +0100 Subject: [PATCH] autoclicker reimplemented --- pgo-automat-settings.properties | 15 +- src/main/java/com/pokemongo/GlobalHotkey.java | 53 ++++- src/main/java/com/pokemongo/PokemonGoGUI.java | 196 ++++++------------ src/main/java/com/pokemongo/WindowFinder.java | 39 ++++ 4 files changed, 162 insertions(+), 141 deletions(-) diff --git a/pgo-automat-settings.properties b/pgo-automat-settings.properties index 994f7d1..db01edb 100644 --- a/pgo-automat-settings.properties +++ b/pgo-automat-settings.properties @@ -1,12 +1,13 @@ #Pokmon GO Automatizace - Nastaven -#Sat Dec 20 18:52:36 CET 2025 -autoklik.count=1 +#Fri Jan 23 16:03:46 CET 2026 +autoklik.count=1000 window.width=807 transfer.delay=0 +autoklik.x=2123 window.height=743 -autoklik.x=2093 -autoklik.y=670 +autoklik.downArrow=true +autoklik.y=1129 +autoklik.interval=150 transfer.count=6 -window.x=1143 -autoklik.interval=100 -window.y=595 +window.x=887 +window.y=505 diff --git a/src/main/java/com/pokemongo/GlobalHotkey.java b/src/main/java/com/pokemongo/GlobalHotkey.java index c367840..caa49eb 100644 --- a/src/main/java/com/pokemongo/GlobalHotkey.java +++ b/src/main/java/com/pokemongo/GlobalHotkey.java @@ -17,9 +17,39 @@ public class GlobalHotkey extends JLabel { public GlobalHotkey(Runnable onHotkey) { this.globalKeyListener = new GlobalKeyListener(onHotkey); - // Registrace globálního key listeneru + // Registrace globálního key listeneru pro případy, kdy má aplikace fokus KeyboardFocusManager.getCurrentKeyboardFocusManager() .addKeyEventDispatcher(globalKeyListener); + + // Spustit nativní polling pro globální STOP hotkey na Linuxu (funguje i bez fokusu) + if (System.getProperty("os.name").toLowerCase().contains("linux")) { + Thread pollingThread = new Thread(() -> { + while (true) { + try { + Thread.sleep(150); + // Kontrola CTRL+ALT+X globálně + // XK_Control_L/R: 0xFFE3/4, XK_Alt_L/R: 0xFFE9/A, XK_x: 0x78, XK_X: 0x58 + boolean ctrl = WindowFinder.isKeyPressedGlobally(0xFFE3) || WindowFinder.isKeyPressedGlobally(0xFFE4); + boolean alt = WindowFinder.isKeyPressedGlobally(0xFFE9) || WindowFinder.isKeyPressedGlobally(0xFFEA); + boolean xPressed = WindowFinder.isKeyPressedGlobally(0x78) || WindowFinder.isKeyPressedGlobally(0x58); + + if (ctrl && alt && xPressed) { + if (onHotkey != null) { + System.out.println("🔌 Globální NATIVNÍ hotkey detekován: CTRL+ALT+X"); + onHotkey.run(); + Thread.sleep(1000); // Prevence vícenásobného spuštění + } + } + } catch (InterruptedException e) { + break; + } catch (Exception e) { + // Ignorovat chyby v pollingu + } + } + }, "GlobalHotkeyPolling"); + pollingThread.setDaemon(true); + pollingThread.start(); + } } public static GlobalHotkey create(Runnable onHotkey) { @@ -28,6 +58,23 @@ public class GlobalHotkey extends JLabel { } return instance; } + + public boolean isKeyPressed(int keyCode) { + // Na Linuxu pro šipky používáme výhradně nativní globální detekci. + // Standardní KeyEventDispatcher se může "zaseknout", pokud okno ztratí fokus během stisku. + if (System.getProperty("os.name").toLowerCase().contains("linux")) { + if (keyCode == KeyEvent.VK_DOWN) { + try { + return WindowFinder.isKeyPressedGlobally(0xFF54); // XK_Down + } catch (Exception e) {} + } else if (keyCode == KeyEvent.VK_UP) { + try { + return WindowFinder.isKeyPressedGlobally(0xFF52); // XK_Up + } catch (Exception e) {} + } + } + return globalKeyListener.isKeyPressed(keyCode); + } /** * Interní třída pro globální naslouchání na klávesnici @@ -39,6 +86,10 @@ public class GlobalHotkey extends JLabel { public GlobalKeyListener(Runnable callback) { this.callback = callback; } + + public boolean isKeyPressed(int keyCode) { + return pressedKeys.contains(keyCode); + } @Override public boolean dispatchKeyEvent(KeyEvent e) { diff --git a/src/main/java/com/pokemongo/PokemonGoGUI.java b/src/main/java/com/pokemongo/PokemonGoGUI.java index bb8f1d3..0f57fde 100644 --- a/src/main/java/com/pokemongo/PokemonGoGUI.java +++ b/src/main/java/com/pokemongo/PokemonGoGUI.java @@ -363,92 +363,24 @@ public class PokemonGoGUI extends JFrame { centerPanel.add(createFieldPanel("Počet:", countComboBox)); centerPanel.add(createFieldPanel("Čekání (s):", delaySpinner)); } else if (title.contains("AUTOKLIK")) { - JSpinner xSpinner = new JSpinner(new SpinnerNumberModel( - Integer.parseInt(settings.getProperty("autoklik.x", "960")), - 0, 9999, 1 - )); - xSpinner.setPreferredSize(new Dimension(70, 26)); - card.putClientProperty("xSpinner", xSpinner); - - JSpinner ySpinner = new JSpinner(new SpinnerNumberModel( - Integer.parseInt(settings.getProperty("autoklik.y", "540")), - 0, 9999, 1 - )); - ySpinner.setPreferredSize(new Dimension(70, 26)); - card.putClientProperty("ySpinner", ySpinner); - JSpinner intervalSpinner = new JSpinner(new SpinnerNumberModel( Integer.parseInt(settings.getProperty("autoklik.interval", "100")), 10, 10000, 10 )); - intervalSpinner.setPreferredSize(new Dimension(70, 26)); + intervalSpinner.setPreferredSize(new Dimension(80, 26)); card.putClientProperty("intervalSpinner", intervalSpinner); - JSpinner clickCountSpinner = new JSpinner(new SpinnerNumberModel( - Integer.parseInt(settings.getProperty("autoklik.count", "10")), - 1, 10000, 1 - )); - clickCountSpinner.setPreferredSize(new Dimension(70, 26)); - card.putClientProperty("clickCountSpinner", clickCountSpinner); - - centerPanel.add(createFieldPanel("X:", xSpinner)); - centerPanel.add(createFieldPanel("Y:", ySpinner)); centerPanel.add(createFieldPanel("Interval (ms):", intervalSpinner)); - centerPanel.add(createFieldPanel("Počet kliknutí:", clickCountSpinner)); } - // Pravý panel - START a Nastavit (pro AUTOKLIK) tlačítka + // Pravý panel - START tlačítko (a u Autokliku i STOP v rámci tlačítka) JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 0)); rightPanel.setBackground(CARD_BACKGROUND); rightPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); - // Tlačítko Nastavit pro AUTOKLIK - if (title.contains("AUTOKLIK")) { - JSpinner xSpinner = (JSpinner) card.getClientProperty("xSpinner"); - JSpinner ySpinner = (JSpinner) card.getClientProperty("ySpinner"); - - JButton settingsBtn = new JButton("⚙ Nastavit"); - settingsBtn.setPreferredSize(new Dimension(100, 32)); - styleButton(settingsBtn, ACCENT_BLUE, Color.WHITE, SMALL_FONT); - settingsBtn.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - PositionPicker picker = new PositionPicker(null); - - new Thread(new Runnable() { - @Override - public void run() { - picker.setVisible(true); - - while (picker.isVisible()) { - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - break; - } - } - - if (picker.isPositionSelected()) { - Point pos = picker.getSelectedPosition(); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - xSpinner.setValue(pos.x); - ySpinner.setValue(pos.y); - logArea.append("✅ Pozice nastavena: (" + pos.x + ", " + pos.y + ")\n"); - } - }); - } - } - }).start(); - } - }); - rightPanel.add(settingsBtn); - } - - // Tlačítko START + // Tlačítko START / VYPNOUT JButton startBtn = new JButton("▶ START"); - startBtn.setPreferredSize(new Dimension(90, 32)); + startBtn.setPreferredSize(new Dimension(100, 32)); styleButton(startBtn, PRIMARY_GREEN, Color.WHITE, SMALL_FONT); startBtn.addActionListener(startAction); @@ -669,51 +601,42 @@ public class PokemonGoGUI extends JFrame { * Spustí autoklik */ private void startAutoClick(JPanel autoClickCard) { + if (autoClickRunning) { + stopAutomation(); + return; + } + if (isRunning) { - JOptionPane.showMessageDialog(this, "Automatizace již běží!", "Upozornění", JOptionPane.WARNING_MESSAGE); + JOptionPane.showMessageDialog(this, "Jiná automatizace již běží!", "Upozornění", JOptionPane.WARNING_MESSAGE); return; } // Načtení nastavení - JSpinner xSpinner = (JSpinner) autoClickCard.getClientProperty("xSpinner"); - JSpinner ySpinner = (JSpinner) autoClickCard.getClientProperty("ySpinner"); JSpinner intervalSpinner = (JSpinner) autoClickCard.getClientProperty("intervalSpinner"); - JSpinner clickCountSpinner = (JSpinner) autoClickCard.getClientProperty("clickCountSpinner"); - - int x = (Integer) xSpinner.getValue(); - int y = (Integer) ySpinner.getValue(); int interval = (Integer) intervalSpinner.getValue(); - int clickCount = (Integer) clickCountSpinner.getValue(); // Uložení nastavení - settings.setProperty("autoklik.x", String.valueOf(x)); - settings.setProperty("autoklik.y", String.valueOf(y)); settings.setProperty("autoklik.interval", String.valueOf(interval)); - settings.setProperty("autoklik.count", String.valueOf(clickCount)); saveSettings(); - // Uložit pozici myši před spuštěním - PointerInfo pointerInfo = MouseInfo.getPointerInfo(); - Point originalMousePosition = pointerInfo != null ? pointerInfo.getLocation() : null; - stopButton.setEnabled(true); - statusLabel.setText("Autoklik běží... (" + x + ", " + y + ")"); + statusLabel.setText("Autokliker aktivní (Šipka dolů)..."); statusLabel.setForeground(new Color(255, 152, 0)); isRunning = true; autoClickRunning = true; shouldStop = false; - // Změnit barvu START tlačítka na červenou + // Změnit tlačítko na VYPNOUT JButton startBtn = (JButton) autoClickCard.getClientProperty("startButton"); if (startBtn != null) { - startBtn.setEnabled(false); + startBtn.setText("⏹ VYPNOUT"); styleButton(startBtn, PRIMARY_RED, Color.WHITE, SMALL_FONT); } logArea.append("\n========================================\n"); - logArea.append("SPOUŠTĚNÍ AUTOKLIKERU\n"); - logArea.append("Pozice: (" + x + ", " + y + ")\n"); - logArea.append("Interval: " + interval + "ms, Počet kliknutí: " + clickCount + "\n"); + logArea.append("AUTOKLIKER AKTIVNÍ\n"); + logArea.append("Interval: " + interval + "ms\n"); + logArea.append("Ovládání: Stiskněte ŠIPKU NAHORU nebo DOLŮ pro zapnutí/vypnutí\n"); logArea.append("========================================\n\n"); automationThread = new Thread(new Runnable() { @@ -721,42 +644,61 @@ public class PokemonGoGUI extends JFrame { public void run() { try { java.awt.Robot robot = new java.awt.Robot(); + int clicksDone = 0; + boolean isClickingActive = false; + boolean wasKeyPressed = false; + long lastClickTime = 0; + long lastToggleTime = 0; - logArea.append("Spouštím autoklik...\n"); - - for (int i = 0; i < clickCount && autoClickRunning && !shouldStop; i++) { - // Přesunutí kurzoru na pozici - robot.mouseMove(x, y); - Thread.sleep(10); + while (autoClickRunning && !shouldStop) { + // Kontrola obou šipek + boolean isKeyPressed = globalHotkey.isKeyPressed(KeyEvent.VK_DOWN) || + globalHotkey.isKeyPressed(KeyEvent.VK_UP); + long currentTime = System.currentTimeMillis(); - // Klik - robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); - robot.mouseRelease(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); - - // Čekání mezi kliknutími - if (i < clickCount - 1 && !shouldStop) { - Thread.sleep(interval); - } - - // Update progress každých 10 kliknutí - if ((i + 1) % 10 == 0 && !shouldStop) { - int progress = i + 1; - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - logArea.append(" Kliknutí: " + progress + "/" + clickCount + "\n"); - } + // Detekce stisku (náběžná hrana) pro přepnutí stavu + if (isKeyPressed && !wasKeyPressed && (currentTime - lastToggleTime > 200)) { + isClickingActive = !isClickingActive; + lastToggleTime = currentTime; + + final boolean active = isClickingActive; + SwingUtilities.invokeLater(() -> { + logArea.append(active ? "▶ Klikání AKTIVOVÁNO šipkou\n" : "⏸ Klikání POZASTAVENO šipkou\n"); + statusLabel.setText(active ? "Autokliker: KLIKÁM..." : "Autokliker: POZASTAVENO"); }); + + if (isClickingActive) { + lastClickTime = 0; // Spustit klikání hned + } } + wasKeyPressed = isKeyPressed; + + if (isClickingActive) { + if (currentTime - lastClickTime >= interval) { + // Klik na aktuální pozici + robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(java.awt.event.InputEvent.BUTTON1_DOWN_MASK); + + clicksDone++; + if (clicksDone % 20 == 0) { + final int count = clicksDone; + SwingUtilities.invokeLater(() -> logArea.append(" Kliknutí: " + count + "\n")); + } + lastClickTime = currentTime; + } + } + + // Rychlá smyčka pro detekci kláves (10ms) + Thread.sleep(10); } if (!shouldStop) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - statusLabel.setText("Autoklik úspěšně dokončen!"); + statusLabel.setText("Autokliker zastaven."); statusLabel.setForeground(new Color(76, 175, 80)); - logArea.append("\n✅ Autoklik úspěšně dokončen! Celkem kliknutí: " + clickCount + "\n"); + logArea.append("\n✅ Autokliker byl vypnut.\n"); } }); } @@ -767,25 +709,13 @@ public class PokemonGoGUI extends JFrame { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - statusLabel.setText("Chyba!"); + statusLabel.setText("Chyba autoklikeru!"); statusLabel.setForeground(new Color(244, 67, 54)); logArea.append("\n❌ Chyba: " + e.getMessage() + "\n"); - e.printStackTrace(); } }); } } finally { - // Vrátit myš na původní pozici - if (originalMousePosition != null) { - try { - java.awt.Robot robot = new java.awt.Robot(); - robot.mouseMove(originalMousePosition.x, originalMousePosition.y); - logArea.append("🖱️ Myš vrácena na původní pozici: (" + originalMousePosition.x + ", " + originalMousePosition.y + ")\n"); - } catch (Exception e) { - System.err.println("Chyba při vracení myši: " + e.getMessage()); - } - } - SwingUtilities.invokeLater(new Runnable() { @Override public void run() { @@ -793,10 +723,10 @@ public class PokemonGoGUI extends JFrame { autoClickRunning = false; stopButton.setEnabled(false); - // Vrátit barvu START tlačítka na zelenou + // Vrátit tlačítko na START JButton btn = (JButton) autoClickCard.getClientProperty("startButton"); if (btn != null) { - btn.setEnabled(true); + btn.setText("▶ START"); styleButton(btn, PRIMARY_GREEN, Color.WHITE, SMALL_FONT); } } diff --git a/src/main/java/com/pokemongo/WindowFinder.java b/src/main/java/com/pokemongo/WindowFinder.java index bbd139c..64067fb 100644 --- a/src/main/java/com/pokemongo/WindowFinder.java +++ b/src/main/java/com/pokemongo/WindowFinder.java @@ -84,10 +84,49 @@ public class WindowFinder { int src_x, int src_y, IntByReference dest_x_return, IntByReference dest_y_return, PointerByReference child_return); + + // Globální klávesy + int XQueryKeymap(Pointer display, byte[] keys_return); + int XKeysymToKeycode(Pointer display, long keysym); Pointer XFree(Pointer ptr); } + /** + * Zjistí, zda je klávesa stisknuta globálně (i mimo fokus aplikace) + * @param keysym X11 Keysym (např. 0xFF54 pro Down Arrow) + * @return true pokud je klávesa držena + */ + public static synchronized boolean isKeyPressedGlobally(long keysym) { + Pointer display = null; + try { + if (cachedDisplay != null) { + display = cachedDisplay; + } else { + display = X11.INSTANCE.XOpenDisplay(null); + if (display == null) return false; + cachedDisplay = display; + } + + int keycode = X11.INSTANCE.XKeysymToKeycode(display, keysym); + if (keycode == 0) return false; + + byte[] keys = new byte[32]; + X11.INSTANCE.XQueryKeymap(display, keys); + + // bitmask v XQueryKeymap: keycode / 8 je index bajtu, (keycode % 8) je pozice bitu + int byteIdx = keycode / 8; + int bitIdx = keycode % 8; + + if (byteIdx >= 0 && byteIdx < 32) { + return (keys[byteIdx] & (1 << bitIdx)) != 0; + } + return false; + } catch (Exception e) { + return false; + } + } + /** * Najde okno podle jeho názvu a vrátí jeho bounds * @param searchPattern Pattern pro hledání (substring názvu okna)