diff --git a/README.md b/README.md
index c7c91c4..f723f09 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,15 @@
-# Mouse-Keyboard
-An onscreen keyboard for the mouse.
-
-### Requirements
-* PyGObject
-* python-xlib
-
-# TODO
-
Get save and execute of custom commands working.
-
-# Images
-![1 GUI of the alphabet. ](images/pic1.png)
-![2 GUI of the symbols. ](images/pic2.png)
+# Mouse-Keyboard
+An onscreen keyboard for the mouse.
+
+### Requirements
+* PyGObject
+* python-xlib
+* pyautogui
+
+# TODO
+Get save and execute of custom commands working.
+
+# Images
+![1 image of the alphabet. ](images/pic1.png)
+![2 Image of the symbols. ](images/pic2.png)
+![3 Image of the emoji. ](images/pic3.png)
\ No newline at end of file
diff --git a/debugger.sh b/debugger.sh
deleted file mode 100755
index 42162e7..0000000
--- a/debugger.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-
-# . CONFIG.sh
-
-# set -o xtrace ## To debug scripts
-# set -o errexit ## To exit on error
-# set -o errunset ## To exit if a variable is referenced but not set
-
-
-function main() {
- SCRIPTPATH="$( cd "$(dirname "")" >/dev/null 2>&1 ; pwd -P )"
- cd "${SCRIPTPATH}"
- echo "Working Dir: " $(pwd)
-
- source '/home/abaddon/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/activate'
- python -m pudb $(pwd)/src/__main__.py; bash
-}
-main "$@";
diff --git a/images/pic1.png b/images/pic1.png
index 30f945c..e75bce1 100644
Binary files a/images/pic1.png and b/images/pic1.png differ
diff --git a/images/pic2.png b/images/pic2.png
index d921d40..11ea5f3 100644
Binary files a/images/pic2.png and b/images/pic2.png differ
diff --git a/images/pic3.png b/images/pic3.png
new file mode 100644
index 0000000..a6ee757
Binary files /dev/null and b/images/pic3.png differ
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..92aa60d
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+PyGObject
+pyautogui
+python-xlib
+setproctitle
\ No newline at end of file
diff --git a/src/__builtins__.py b/src/__builtins__.py
index 728a2a6..86c482d 100644
--- a/src/__builtins__.py
+++ b/src/__builtins__.py
@@ -6,9 +6,9 @@ import threading
# Lib imports
# Application imports
-from utils.pyautogui_control import ControlMixin
-from utils.endpoint_registry import EndpointRegistry
-from utils.event_system import EventSystem
+from libs.pyautogui_control import ControlMixin
+from libs.endpoint_registry import EndpointRegistry
+from libs.event_system import EventSystem
diff --git a/src/__main__.py b/src/__main__.py
index 6188fed..7d9554f 100644
--- a/src/__main__.py
+++ b/src/__main__.py
@@ -1,6 +1,5 @@
#!/usr/bin/python3
-
# Python imports
import argparse
import faulthandler
@@ -10,36 +9,38 @@ from setproctitle import setproctitle
import tracemalloc
tracemalloc.start()
-
# Lib imports
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
# Application imports
+from __builtins__ import *
from app import Application
+def main(args, unknownargs):
+ setproctitle(f'{app_name}')
+ Application(args, unknownargs)
+
+
+
if __name__ == "__main__":
- """ Set process title, get arguments, and create GTK main thread. """
+ ''' Set process title, get arguments, and create GTK main thread. '''
+
+ parser = argparse.ArgumentParser()
+ # Add long and short arguments
+ parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
+ parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
+ parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.")
+
+ parser.add_argument("--new-tab", "-nt", default="false", help="Opens a 'New Tab' if a handler is set for it.")
+ parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.")
+
+ # Read arguments (If any...)
+ args, unknownargs = parser.parse_known_args()
try:
- # import web_pdb
- # web_pdb.set_trace()
-
- setproctitle('Mouse Keyboard')
faulthandler.enable() # For better debug info
- parser = argparse.ArgumentParser()
- # Add long and short arguments
- parser.add_argument("--new-tab", "-t", default="", help="Open a file into new tab.")
- parser.add_argument("--new-window", "-w", default="", help="Open a file into a new window.")
-
- # Read arguments (If any...)
- args, unknownargs = parser.parse_known_args()
-
- Application(args, unknownargs)
- Gtk.main()
+ main(args, unknownargs)
except Exception as e:
traceback.print_exc()
- quit()
+ quit()
\ No newline at end of file
diff --git a/src/app.py b/src/app.py
index d80a172..6bcc0bb 100644
--- a/src/app.py
+++ b/src/app.py
@@ -1,20 +1,37 @@
# Python imports
-import inspect
-
-
-# Gtk imports
+import signal
+import os
+# Lib imports
# Application imports
-from __builtins__ import *
+from libs.debugging import debug_signal_handler
from core.window import Window
+class AppLaunchException(Exception):
+ ...
+
+
+
+class Application:
+ """ docstring for Application. """
-class Application(object):
def __init__(self, args, unknownargs):
+ super(Application, self).__init__()
+
+ self.setup_debug_hook()
+ Window(args, unknownargs).main()
+
+
+ def setup_debug_hook(self):
try:
- Window(args, unknownargs)
- except Exception as e:
- raise
+ # kill -SIGUSR2 from Linux/Unix or SIGBREAK signal from Windows
+ signal.signal(
+ vars(signal).get("SIGBREAK") or vars(signal).get("SIGUSR1"),
+ debug_signal_handler
+ )
+ except ValueError:
+ # Typically: ValueError: signal only works in main thread
+ ...
\ No newline at end of file
diff --git a/src/core/columns/keys_column.py b/src/core/columns/keys_column.py
index fd67536..5f7f482 100644
--- a/src/core/columns/keys_column.py
+++ b/src/core/columns/keys_column.py
@@ -21,6 +21,8 @@ class Keys_Column(Gtk.Box):
super(Keys_Column, self).__init__()
self.setup_styling()
+ self.setup_signals()
+ self.setup_custom_signals()
self.setup_key_buttons()
self.show_all()
@@ -31,6 +33,16 @@ class Keys_Column(Gtk.Box):
self.set_property("homogeneous", True)
self.set_hexpand(True)
+ def setup_signals(self):
+ self.connect("button-release-event", self._on_button_release_event)
+
+ def setup_custom_signals(self):
+ event_system.subscribe("itterate_mode", self.itterate_mode)
+
+ def _on_button_release_event(self, widget = None, eve = None):
+ if eve.button == 3: # NOTE: right-click
+ event_system.emit_and_await("itterate_mode")
+
def setup_key_buttons(self):
keys = keys_set["keys"]
children = keys.keys()
@@ -57,3 +69,15 @@ class Keys_Column(Gtk.Box):
self.add(row_box)
return row_box
+
+ def itterate_mode(self):
+ emoji_view_shown = event_system.emit_and_await("is_emoji_view_shown")
+ is_symbols_enabled = event_system.emit_and_await("is_symbols_enabled")
+
+ if not is_symbols_enabled and not emoji_view_shown:
+ event_system.emit("toggle_symbol_keys")
+ elif is_symbols_enabled and not emoji_view_shown:
+ event_system.emit("show_emoji_view")
+ elif is_symbols_enabled and emoji_view_shown:
+ event_system.emit("hide_emoji_view")
+ event_system.emit("toggle_symbol_keys")
diff --git a/src/core/widgets/defined_keys.py b/src/core/widgets/defined_keys.py
index 4689c5e..6a8b688 100644
--- a/src/core/widgets/defined_keys.py
+++ b/src/core/widgets/defined_keys.py
@@ -15,26 +15,14 @@ from .key import Key
class Esc_Key(Key):
def __init__(self):
- super(Esc_Key, self).__init__("Esc", "Esc", iscontrol=True)
+ super(Esc_Key, self).__init__("Esc", "Esc", iscontrol = True)
def setup_signals(self):
self.connect("released", self._do_press_special_key)
-class Symbols_Key(Key):
- def __init__(self):
- super(Symbols_Key, self).__init__("Symbols", "Symbols", iscontrol=True)
-
- def setup_signals(self):
- self.connect("released", self._clicked)
-
- def _clicked(self, widget = None):
- ctx = widget.get_style_context()
- ctx.remove_class("toggled_bttn") if ctx.has_class("toggled_bttn") else ctx.add_class("toggled_bttn")
- event_system.emit("toggle_symbol_keys")
-
class CAPS_Key(Key):
def __init__(self):
- super(CAPS_Key, self).__init__("Caps", "Caps", iscontrol=True)
+ super(CAPS_Key, self).__init__("Caps", "Caps", iscontrol = True)
self.setup_styling()
self.show_all()
@@ -80,8 +68,26 @@ class Emoji_Key(Key):
def unset_selected(self, widget = None):
self._ctx.remove_class("toggled_bttn")
+class Symbols_Key(Key):
+ def __init__(self):
+ super(Symbols_Key, self).__init__("Symbols", "Symbols", iscontrol = True)
+ self.setup_custom_signals()
+ def setup_signals(self):
+ self.connect("released", self._clicked)
+ def setup_custom_signals(self):
+ event_system.subscribe("is_symbols_enabled", self.is_symbols_enabled)
+ event_system.subscribe("toggle_symbol_keys", self.toggle_symbol_keys)
+
+ def _clicked(self, widget = None):
+ ctx = widget.get_style_context()
+ ctx.remove_class("toggled_bttn") if ctx.has_class("toggled_bttn") else ctx.add_class("toggled_bttn")
+ event_system.emit("toggle_symbol_keys")
+
+ def is_symbols_enabled(self):
+ ctx = self.get_style_context()
+ return True if ctx.has_class("toggled_bttn") else False
class Enter_Key(Key):
def __init__(self):
diff --git a/src/core/widgets/emoji_popover.py b/src/core/widgets/emoji_popover.py
index 89caa57..69e2251 100644
--- a/src/core/widgets/emoji_popover.py
+++ b/src/core/widgets/emoji_popover.py
@@ -49,12 +49,16 @@ class Emoji_Notebook(Gtk.Notebook):
for group in emoji_grouping:
tab_widget = Gtk.Label(label=group)
scroll, grid = self.create_scroll_and_grid()
+ self.append_page(scroll, tab_widget)
+ self.set_tab_reorderable(scroll, False)
+ self.set_tab_detachable(scroll, False)
top = 0
left = 0
for emoji in emoji_grouping[group]:
key = Key(emoji["emoji"], emoji["emoji"])
key._is_emoji = True
+ key.show()
grid.attach(key, left, top, width, height)
left += 1
@@ -62,9 +66,6 @@ class Emoji_Notebook(Gtk.Notebook):
left = 0
top += 1
- self.append_page(scroll, tab_widget)
- self.set_tab_reorderable(scroll, False)
- self.set_tab_detachable(scroll, False)
def create_scroll_and_grid(self):
scroll = Gtk.ScrolledWindow()
@@ -83,8 +84,9 @@ class Emoji_Popover(Gtk.Popover):
emoji_notebook = Emoji_Notebook()
self.add(emoji_notebook)
- self.set_default_widget(emoji_notebook)
self.setup_styling()
+ self.setup_signals()
+ self.setup_custom_signals()
self._emoji_key = None
@@ -94,8 +96,22 @@ class Emoji_Popover(Gtk.Popover):
self.set_size_request(480, 280)
def setup_signals(self):
- self.connect("closed", self._emoji_key.unset_selected)
+ ...
+
+ def setup_custom_signals(self):
+ event_system.subscribe("is_emoji_view_shown", self.is_emoji_view_shown)
+ event_system.subscribe("show_emoji_view", self.show_emoji_view)
+ event_system.subscribe("hide_emoji_view", self.hide_emoji_view)
def set_parent_key(self, emoji_key):
self._emoji_key = emoji_key
self.setup_signals()
+
+ def is_emoji_view_shown(self):
+ return self.is_visible()
+
+ def show_emoji_view(self):
+ self.popup()
+
+ def hide_emoji_view(self):
+ self.popdown()
diff --git a/src/core/window.py b/src/core/window.py
index b3d6111..72bc7d5 100644
--- a/src/core/window.py
+++ b/src/core/window.py
@@ -67,3 +67,6 @@ class Window(SignalsMixin, Gtk.ApplicationWindow):
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.paint()
cr.set_operator(cairo.OPERATOR_OVER)
+
+ def main(self):
+ Gtk.main()
\ No newline at end of file
diff --git a/src/utils/__init__.py b/src/libs/__init__.py
similarity index 100%
rename from src/utils/__init__.py
rename to src/libs/__init__.py
diff --git a/src/libs/debugging.py b/src/libs/debugging.py
new file mode 100644
index 0000000..b84193a
--- /dev/null
+++ b/src/libs/debugging.py
@@ -0,0 +1,52 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+
+
+
+# Break into a Python console upon SIGUSR1 (Linux) or SIGBREAK (Windows:
+# CTRL+Pause/Break). To be included in all production code, just in case.
+def debug_signal_handler(signal, frame):
+ del signal
+ del frame
+
+ try:
+ import rpdb2
+ logger.debug("\n\nStarting embedded RPDB2 debugger. Password is 'foobar'\n\n")
+ rpdb2.start_embedded_debugger("foobar", True, True)
+ rpdb2.setbreak(depth=1)
+ return
+ except StandardError:
+ ...
+
+ try:
+ from rfoo.utils import rconsole
+ logger.debug("\n\nStarting embedded rconsole debugger...\n\n")
+ rconsole.spawn_server()
+ return
+ except StandardError as ex:
+ ...
+
+ try:
+ from pudb import set_trace
+ logger.debug("\n\nStarting PuDB debugger...\n\n")
+ set_trace(paused = True)
+ return
+ except StandardError as ex:
+ ...
+
+ try:
+ import pdb
+ logger.debug("\n\nStarting embedded PDB debugger...\n\n")
+ pdb.Pdb(skip=['gi.*']).set_trace()
+ return
+ except StandardError as ex:
+ ...
+
+ try:
+ import code
+ code.interact()
+ except StandardError as ex:
+ logger.debug(f"{ex}, returning to normal program flow...")
diff --git a/src/utils/endpoint_registry.py b/src/libs/endpoint_registry.py
similarity index 100%
rename from src/utils/endpoint_registry.py
rename to src/libs/endpoint_registry.py
diff --git a/src/libs/event_system.py b/src/libs/event_system.py
new file mode 100644
index 0000000..cd6975f
--- /dev/null
+++ b/src/libs/event_system.py
@@ -0,0 +1,73 @@
+# Python imports
+from collections import defaultdict
+
+# Lib imports
+
+# Application imports
+from .singleton import Singleton
+
+
+
+class EventSystem(Singleton):
+ """ Create event system. """
+
+ def __init__(self):
+ self.subscribers = defaultdict(list)
+ self._is_paused = False
+
+ self._subscribe_to_events()
+
+
+ def _subscribe_to_events(self):
+ self.subscribe("pause_event_processing", self._pause_processing_events)
+ self.subscribe("resume_event_processing", self._resume_processing_events)
+
+ def _pause_processing_events(self):
+ self._is_paused = True
+
+ def _resume_processing_events(self):
+ self._is_paused = False
+
+ def subscribe(self, event_type, fn):
+ self.subscribers[event_type].append(fn)
+
+ def unsubscribe(self, event_type, fn):
+ self.subscribers[event_type].remove(fn)
+
+ def unsubscribe_all(self, event_type):
+ self.subscribers.pop(event_type, None)
+
+ def emit(self, event_type, data = None):
+ if self._is_paused and event_type != "resume_event_processing":
+ return
+
+ if event_type in self.subscribers:
+ for fn in self.subscribers[event_type]:
+ if data:
+ if hasattr(data, '__iter__') and not type(data) is str:
+ fn(*data)
+ else:
+ fn(data)
+ else:
+ fn()
+
+ def emit_and_await(self, event_type, data = None):
+ if self._is_paused and event_type != "resume_event_processing":
+ return
+
+ """ NOTE: Should be used when signal has only one listener and vis-a-vis """
+ if event_type in self.subscribers:
+ response = None
+ for fn in self.subscribers[event_type]:
+ if data:
+ if hasattr(data, '__iter__') and not type(data) is str:
+ response = fn(*data)
+ else:
+ response = fn(data)
+ else:
+ response = fn()
+
+ if not response in (None, ''):
+ break
+
+ return response
diff --git a/src/utils/logger.py b/src/libs/logger.py
similarity index 100%
rename from src/utils/logger.py
rename to src/libs/logger.py
diff --git a/src/utils/pyautogui/__init__.py b/src/libs/pyautogui/__init__.py
similarity index 100%
rename from src/utils/pyautogui/__init__.py
rename to src/libs/pyautogui/__init__.py
diff --git a/src/utils/pyautogui/__main__.py b/src/libs/pyautogui/__main__.py
similarity index 100%
rename from src/utils/pyautogui/__main__.py
rename to src/libs/pyautogui/__main__.py
diff --git a/src/utils/pyautogui/_pyautogui_java.py b/src/libs/pyautogui/_pyautogui_java.py
similarity index 100%
rename from src/utils/pyautogui/_pyautogui_java.py
rename to src/libs/pyautogui/_pyautogui_java.py
diff --git a/src/utils/pyautogui/_pyautogui_osx.py b/src/libs/pyautogui/_pyautogui_osx.py
similarity index 100%
rename from src/utils/pyautogui/_pyautogui_osx.py
rename to src/libs/pyautogui/_pyautogui_osx.py
diff --git a/src/utils/pyautogui/_pyautogui_win.py b/src/libs/pyautogui/_pyautogui_win.py
similarity index 100%
rename from src/utils/pyautogui/_pyautogui_win.py
rename to src/libs/pyautogui/_pyautogui_win.py
diff --git a/src/utils/pyautogui/_pyautogui_x11.py b/src/libs/pyautogui/_pyautogui_x11.py
similarity index 100%
rename from src/utils/pyautogui/_pyautogui_x11.py
rename to src/libs/pyautogui/_pyautogui_x11.py
diff --git a/src/utils/pyautogui_control.py b/src/libs/pyautogui_control.py
similarity index 98%
rename from src/utils/pyautogui_control.py
rename to src/libs/pyautogui_control.py
index 26538ad..1ad933c 100644
--- a/src/utils/pyautogui_control.py
+++ b/src/libs/pyautogui_control.py
@@ -48,7 +48,7 @@ class ControlMixin:
def enter(self, widget = None, data = None):
pyautogui.press("enter")
- def backspace(self, widget = None, data=None):
+ def backspace(self, widget = None, data = None):
pyautogui.press("backspace")
def press_special_keys(self, key):
diff --git a/src/libs/singleton.py b/src/libs/singleton.py
new file mode 100644
index 0000000..23b7191
--- /dev/null
+++ b/src/libs/singleton.py
@@ -0,0 +1,24 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+
+
+
+class SingletonError(Exception):
+ pass
+
+
+
+class Singleton:
+ ccount = 0
+
+ def __new__(cls, *args, **kwargs):
+ obj = super(Singleton, cls).__new__(cls)
+ cls.ccount += 1
+
+ if cls.ccount == 2:
+ raise SingletonError(f"Exceeded {cls.__name__} instantiation limit...")
+
+ return obj
diff --git a/src/utils/event_system.py b/src/utils/event_system.py
deleted file mode 100644
index 25c96fc..0000000
--- a/src/utils/event_system.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Python imports
-from collections import defaultdict
-
-# Lib imports
-
-# Application imports
-
-
-
-
-class EventSystem:
- """ Create event system. """
-
- def __init__(self):
- self.subscribers = defaultdict(list)
-
-
- def subscribe(self, event_type, fn):
- self.subscribers[event_type].append(fn)
-
- def emit(self, event_type, data = None):
- if event_type in self.subscribers:
- for fn in self.subscribers[event_type]:
- if data:
- if hasattr(data, '__iter__') and not type(data) is str:
- fn(*data)
- else:
- fn(data)
- else:
- fn()