You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
7.8 KiB
198 lines
7.8 KiB
#!/usr/bin/env python3 |
|
|
|
# Copyright 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
|
# Copyright 2020 Aristocratos (https://github.com/aristocratos/bpytop) |
|
# |
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
# you may not use this file except in compliance with the License. |
|
# You may obtain a copy of the License at |
|
# |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
# |
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS IS" BASIS, |
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
# See the License for the specific language governing permissions and |
|
# limitations under the License. |
|
|
|
import os, sys, io, threading, signal, re, subprocess, logging, logging.handlers, argparse |
|
import re |
|
import queue |
|
from select import select |
|
from time import time, sleep, strftime, localtime |
|
from typing import TextIO, List, Set, Dict, Tuple, Optional, Union, Any, Callable, ContextManager, Iterable, Type, NamedTuple |
|
|
|
try: import fcntl, termios, tty, pwd |
|
except Exception as e: |
|
print(f'ERROR: {e}') |
|
exit(1) |
|
|
|
class MouseEvent: |
|
# Keys |
|
NoButton = 0x00000000 # The button state does not refer to any button (see QMouseEvent::button()). |
|
AllButtons = 0x07ffffff # This value corresponds to a mask of all possible mouse buttons. Use to set the 'acceptedButtons' property of a MouseArea to accept ALL mouse buttons. |
|
LeftButton = 0x00000001 # The left button is pressed, or an event refers to the left button. (The left button may be the right button on left-handed mice.) |
|
RightButton = 0x00000002 # The right button. |
|
MidButton = 0x00000004 # The middle button. |
|
MiddleButton = MidButton # The middle button. |
|
Wheel = 0x00000008 |
|
|
|
# Events |
|
NoEvent = 0x00000000 |
|
Press = 0x00010000 |
|
Release = 0x00020000 |
|
Drag = 0x00040000 |
|
Move = 0x00080000 |
|
Up = 0x00100000 # Wheel Up |
|
Down = 0x00200000 # Wheel Down |
|
|
|
__slots__ = ('x','y','key','evt','raw') |
|
def __init__(self, x: int, y: int, key: int, evt: int, raw: str): |
|
self.x = x |
|
self.y = y |
|
self.key = key |
|
self.evt = evt |
|
self.raw = raw |
|
|
|
def key2str(self): |
|
return { |
|
MouseEvent.NoButton : "NoButton", |
|
MouseEvent.AllButtons : "AllButtons", |
|
MouseEvent.LeftButton : "LeftButton", |
|
MouseEvent.RightButton : "RightButton", |
|
MouseEvent.MidButton : "MidButton", |
|
MouseEvent.MiddleButton : "MiddleButton", |
|
MouseEvent.Wheel : "Wheel", |
|
}.get(self.key, "Undefined") |
|
|
|
def evt2str(self): |
|
return { |
|
MouseEvent.NoEvent : "NoEvent", |
|
MouseEvent.Press : "Press", |
|
MouseEvent.Release : "Release", |
|
MouseEvent.Drag : "Drag", |
|
MouseEvent.Move : "Move", |
|
MouseEvent.Up : "Up", |
|
MouseEvent.Down : "Down", |
|
}.get(self.evt, "Undefined") |
|
|
|
def __str__(self): |
|
return f"MouseEvent ({self.x},{self.y}) {self.key2str()} {self.evt2str()} - {self.raw}" |
|
|
|
class KeyEvent: |
|
__slots__ = ('id', 'key') |
|
def __init__(self, id:int, key: str): |
|
self.id = id |
|
self.key = key |
|
def __str__(self): |
|
return f"KeyEvent: {self.key}" |
|
|
|
class Input: |
|
class _nonblocking(object): |
|
"""Set nonblocking mode for device""" |
|
def __init__(self, stream: TextIO): |
|
self.stream = stream |
|
self.fd = self.stream.fileno() |
|
def __enter__(self): |
|
self.orig_fl = fcntl.fcntl(self.fd, fcntl.F_GETFL) |
|
fcntl.fcntl(self.fd, fcntl.F_SETFL, self.orig_fl | os.O_NONBLOCK) |
|
def __exit__(self, *args): |
|
fcntl.fcntl(self.fd, fcntl.F_SETFL, self.orig_fl) |
|
|
|
class _raw(object): |
|
"""Set raw input mode for device""" |
|
def __init__(self, stream: TextIO): |
|
self.stream = stream |
|
self.fd = self.stream.fileno() |
|
def __enter__(self): |
|
self.original_stty = termios.tcgetattr(self.stream) |
|
tty.setcbreak(self.stream) |
|
def __exit__(self, type, value, traceback): |
|
termios.tcsetattr(self.stream, termios.TCSANOW, self.original_stty) |
|
|
|
@staticmethod |
|
def get_key(callback=None): |
|
input_key: str = "" |
|
mouse_re = re.compile(r"\033\[<(\d+);(\d+);(\d+)([mM])") |
|
|
|
while not False: |
|
with Input._raw(sys.stdin): |
|
#if not select([sys.stdin], [], [], 0.1)[0]: #* Wait 100ms for input on stdin then restart loop to check for stop flag |
|
# continue |
|
input_key += sys.stdin.read(1) #* Read 1 key safely with blocking on |
|
if input_key == "\033": #* If first character is a escape sequence keep reading |
|
with Input._nonblocking(sys.stdin): #* Set non blocking to prevent read stall |
|
input_key += sys.stdin.read(20) |
|
if input_key.startswith("\033[<"): |
|
_ = sys.stdin.read(1000) |
|
# ttk.debug("INPUT: "+input_key.replace("\033","<ESC>")) |
|
mevt = None |
|
kevt = None |
|
if input_key == "\033" or input_key == "q": #* Key is "escape" key if only containing \033 |
|
break |
|
elif len(input_key) == 1: |
|
# Key Pressed |
|
kevt = KeyEvent(0, input_key) |
|
elif input_key.startswith("\033[<"): |
|
# Mouse Event |
|
m = mouse_re.match(input_key) |
|
if not m: |
|
# TODO: Return Error |
|
continue |
|
code = int(m.group(1)) |
|
x = int(m.group(2)) |
|
y = int(m.group(3)) |
|
state = m.group(4) |
|
key = MouseEvent.NoButton |
|
evt = MouseEvent.NoEvent |
|
if code == 0x00: |
|
key = MouseEvent.LeftButton |
|
evt = MouseEvent.Press if state=="M" else MouseEvent.Release |
|
elif code == 0x01: |
|
key = MouseEvent.MidButton |
|
evt = MouseEvent.Press if state=="M" else MouseEvent.Release |
|
elif code == 0x02: |
|
key = MouseEvent.RightButton |
|
evt = MouseEvent.Press if state=="M" else MouseEvent.Release |
|
elif code == 0x20: |
|
key = MouseEvent.LeftButton |
|
evt = MouseEvent.Drag |
|
elif code == 0x21: |
|
key = MouseEvent.MidButton |
|
evt = MouseEvent.Drag |
|
elif code == 0x22: |
|
key = MouseEvent.RightButton |
|
evt = MouseEvent.Drag |
|
elif code == 0x40: |
|
key = MouseEvent.Wheel |
|
evt = MouseEvent.Up |
|
elif code == 0x41: |
|
key = MouseEvent.Wheel |
|
evt = MouseEvent.Down |
|
mevt = MouseEvent(x, y, key, evt, m.group(0).replace("\033", "<ESC>")) |
|
|
|
input_key = "" |
|
if callback is not None: |
|
callback(kevt, mevt) |
|
|
|
def main(): |
|
print("Retrieve Keyboard, Mouse press/drag/wheel Events") |
|
print("Press q or <ESC> to exit") |
|
import term as t |
|
|
|
t.Term.push(t.Term.mouse_on) |
|
t.Term.echo(False) |
|
|
|
def callback(kevt=None, mevt=None): |
|
if kevt is not None: |
|
print(f"Key Event: {kevt}") |
|
if mevt is not None: |
|
print(f"Mouse Event: {mevt}") |
|
|
|
Input.get_key(callback) |
|
|
|
t.Term.push(t.Term.mouse_off, t.Term.mouse_direct_off) |
|
t.Term.echo(True) |
|
|
|
if __name__ == "__main__": |
|
main() |