autoclicker reimplemented

This commit is contained in:
Radek Davidek 2026-01-23 16:04:46 +01:00
parent 37f49c5f33
commit 0998ba3762
4 changed files with 162 additions and 141 deletions

View File

@ -1,12 +1,13 @@
#Pokémon 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

View File

@ -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 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) {

View File

@ -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);
}
}

View File

@ -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)