Initial commit.

This commit is contained in:
2026-07-01 07:19:52 +02:00
commit b870b7a165
4 changed files with 362 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
Copyright (C) 2026 Nicolás A. Ortega Froysa <nicolas@ortegas.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
+120
View File
@@ -0,0 +1,120 @@
# Keyboard Input Blocker
A graphical application for Linux that toggles keyboard input blocking with a
single button. Perfect if your toddler likes to press a lot of buttons during
video calls.
## Features
- Simple one-button toggle interface built with PyQt6
- Disables/enables all keyboard input at the system level
- Status indicator showing current state
- Works on Wayland and X11
## Requirements
- Python 3.7+
- PyQt6
- evdev (Linux kernel input device library)
## Installation
### 1. Install dependencies
**On Ubuntu/Debian:**
```bash
sudo apt-get install python3 python3-pip libevdev-dev
pip install -r requirements.txt
```
**On Fedora:**
```bash
sudo dnf install python3 python3-pip libevdev-devel
pip install -r requirements.txt
```
**On Arch:**
```bash
sudo pacman -S python3 libevdev python-pyqt6 python-evdev
```
### 2. Run the application
The application requires elevated privileges to access input devices:
```bash
sudo -E python3 kb-block.py
```
Or make it executable:
```bash
chmod +x kb-block.py
sudo -E ./kb-block.py
```
## Usage
1. Launch the application with `sudo -E`
2. Click **"Block Keyboard"** to disable all keyboard input
- The button turns red and shows **"Unblock Keyboard"**
- Status shows "Keyboard BLOCKED ⚠"
3. Click **"Unblock Keyboard"** to re-enable keyboard input
- The button returns to normal
- Status shows "Keyboard enabled ✓"
**Note:** When keyboard is blocked, you'll need to use:
- Mouse to unblock via the button
- External input devices (if available)
- Or kill the process from another terminal: `sudo killall -9 python3` (or the
specific PID)
## How it Works
The application uses the `evdev` library to grab input devices at the kernel level:
- `grab()` - Prevents the keyboard from sending any events
- `ungrab()` - Restores keyboard input
This works with both X11 and Wayland display servers.
## Permissions Note
The application requires `sudo` because it needs to directly access
`/dev/input/event*` devices.
If you want to run without `sudo`, you can add your user to the `input` group:
```bash
sudo usermod -a -G input $USER
# Log out and back in for the change to take effect
```
However, this is less secure as it grants all users in the input group broad
input device access.
## Troubleshooting
### No keyboard device found
- Ensure you're running with `sudo -E`
- Check that keyboard input devices exist: `ls -la /dev/input/event*`
### Permission denied"
- Run with `sudo -E`
- Or add your user to the input group (see Permissions Note above)
### Keyboard still responds
- Multiple keyboard devices may exist; the app grabs the primary one
- Try unblocking and re-blocking
## License
This project is licensed under the Zlib license (see [LICENSE](LICENSE) file for
more information).
Executable
+223
View File
@@ -0,0 +1,223 @@
#!/usr/bin/env python3
# Copyright (C) 2026 Nicolás Ortega Froysa <nicolas@ortegas.org> All rights reserved.
# Author: Nicolás Ortega Froysa <nicolas@ortegas.org>
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
#
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
#
# 3. This notice may not be removed or altered from any source
# distribution.
"""
Keyboard Input Blocker for X11/Wayland
A graphical application that toggles keyboard input on/off.
Requires: PyQt6, evdev
"""
import sys
import os
from pathlib import Path
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QLabel, QMessageBox
from PyQt6.QtCore import Qt, QThread, pyqtSignal
from PyQt6.QtGui import QFont
import evdev
from evdev import InputDevice, list_devices
class KeyboardBlockerThread(QThread):
"""Thread to handle keyboard grabbing in the background."""
error_occurred = pyqtSignal(str)
grabbed = pyqtSignal()
released = pyqtSignal()
def __init__(self):
super().__init__()
self.keyboard_device = None
self.is_blocking = False
self.should_stop = False
def find_keyboard_device(self):
"""Find the keyboard input device."""
devices = [InputDevice(path) for path in list_devices()]
# Look for keyboard devices
keyboard_devices = []
for device in devices:
try:
caps = device.capabilities()
# Check if device has KEY capability (keyboards have this)
if evdev.ecodes.EV_KEY in caps:
# Prefer devices with common keyboard names
if any(name in device.name.lower() for name in ['keyboard', 'input', 'wacom']):
keyboard_devices.append(device)
except:
continue
if keyboard_devices:
return keyboard_devices[0]
# Fallback: return first device with KEY capability
for device in devices:
try:
caps = device.capabilities()
if evdev.ecodes.EV_KEY in caps:
return device
except:
continue
return None
def grab_keyboard(self):
"""Grab the keyboard device to block input."""
try:
self.keyboard_device = self.find_keyboard_device()
if not self.keyboard_device:
self.error_occurred.emit(
"No keyboard device found. You may need to run with sudo."
)
return False
# Grab the device (this requires appropriate permissions)
self.keyboard_device.grab()
self.is_blocking = True
self.grabbed.emit()
return True
except PermissionError:
self.error_occurred.emit(
"Permission denied. Please run with elevated privileges (sudo)."
)
return False
except Exception as e:
self.error_occurred.emit(f"Error grabbing keyboard: {str(e)}")
return False
def release_keyboard(self):
"""Release the keyboard device to allow input."""
try:
if self.keyboard_device:
self.keyboard_device.ungrab()
self.is_blocking = False
self.released.emit()
return True
except Exception as e:
self.error_occurred.emit(f"Error releasing keyboard: {str(e)}")
return False
class KeyboardBlockerApp(QMainWindow):
"""Main application window for keyboard blocker."""
def __init__(self):
super().__init__()
self.blocker_thread = KeyboardBlockerThread()
self.is_keyboard_blocked = False
# Connect thread signals
self.blocker_thread.grabbed.connect(self.on_keyboard_grabbed)
self.blocker_thread.released.connect(self.on_keyboard_released)
self.blocker_thread.error_occurred.connect(self.on_error)
self.init_ui()
def init_ui(self):
"""Initialize the user interface."""
self.setWindowTitle("Keyboard Input Blocker")
self.setGeometry(100, 100, 400, 200)
# Create central widget and layout
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
# Title label
title_label = QLabel("Keyboard Input Blocker")
title_font = QFont()
title_font.setPointSize(14)
title_font.setBold(True)
title_label.setFont(title_font)
layout.addWidget(title_label)
# Status label
self.status_label = QLabel("Status: Keyboard enabled")
layout.addWidget(self.status_label)
# Toggle button
self.toggle_button = QPushButton("Block Keyboard")
self.toggle_button.setMinimumHeight(50)
self.toggle_button.setFont(QFont("Times", 12))
self.toggle_button.clicked.connect(self.toggle_keyboard)
layout.addWidget(self.toggle_button)
# Info label
info_label = QLabel(
"Click the button to toggle keyboard input blocking.\n"
"Note: This application may require elevated privileges (sudo)."
)
info_font = QFont()
info_font.setPointSize(9)
info_label.setFont(info_font)
layout.addWidget(info_label)
layout.addStretch()
def toggle_keyboard(self):
"""Toggle keyboard blocking on/off."""
if self.is_keyboard_blocked:
self.blocker_thread.release_keyboard()
else:
self.blocker_thread.grab_keyboard()
def on_keyboard_grabbed(self):
"""Handle keyboard grabbed signal."""
self.is_keyboard_blocked = True
self.toggle_button.setText("Unblock Keyboard")
self.toggle_button.setStyleSheet("background-color: #ff6b6b;")
self.status_label.setText("Status: Keyboard BLOCKED ⚠")
def on_keyboard_released(self):
"""Handle keyboard released signal."""
self.is_keyboard_blocked = False
self.toggle_button.setText("Block Keyboard")
self.toggle_button.setStyleSheet("")
self.status_label.setText("Status: Keyboard enabled ✓")
def on_error(self, message):
"""Handle error signal."""
QMessageBox.critical(self, "Error", message)
def closeEvent(self, a0):
"""Ensure keyboard is released when closing."""
if self.is_keyboard_blocked:
self.blocker_thread.release_keyboard()
self.blocker_thread.quit()
self.blocker_thread.wait()
a0.accept()
def main():
"""Main entry point."""
app = QApplication(sys.argv)
window = KeyboardBlockerApp()
window.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
+2
View File
@@ -0,0 +1,2 @@
PyQt6>=6.0.0
evdev>=1.6.0