diff --git a/README.md b/README.md
index e35fd2a..eb75fb6 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
-# Mouse_Keyboard
-
# Mouse_Keyboard
An onscreen keyboard for the mouse.
+
+# TODO
+
Get save and execute of custom commands working.
+Get button case toggle working.
+
+# Images
+![1 GUI of the keyboard. ](images/pic1.png)
diff --git a/images/pic1.png b/images/pic1.png
new file mode 100644
index 0000000..f737500
Binary files /dev/null and b/images/pic1.png differ
diff --git a/src/__builtins__.py b/src/__builtins__.py
new file mode 100644
index 0000000..ece2dd4
--- /dev/null
+++ b/src/__builtins__.py
@@ -0,0 +1,7 @@
+import builtins
+
+class Builtins:
+ def __init__(self):
+ pass
+
+builtins.app_name = "Mouse_Keyboard"
diff --git a/src/__init__.py b/src/__init__.py
new file mode 100644
index 0000000..3f5db84
--- /dev/null
+++ b/src/__init__.py
@@ -0,0 +1,34 @@
+# Python imports
+import inspect
+
+
+# Gtk imports
+
+
+# Application imports
+from utils.settings import Settings
+from signal_classes.signals import Signals
+from __builtins__ import Builtins
+
+
+class Main(Builtins):
+ def __init__(self, args):
+ settings = Settings()
+ builder = settings.returnBuilder()
+
+ # Gets the methods from the classes and sets to handler.
+ # Then, builder connects to any signals it needs.
+ classes = [Signals(settings)]
+
+ handlers = {}
+ for c in classes:
+ methods = None
+ try:
+ methods = inspect.getmembers(c, predicate=inspect.ismethod)
+ handlers.update(methods)
+ except Exception as e:
+ pass
+
+ builder.connect_signals(handlers)
+ window = settings.createWindow()
+ window.show()
diff --git a/src/__main__.py b/src/__main__.py
new file mode 100644
index 0000000..1065eb3
--- /dev/null
+++ b/src/__main__.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python3
+
+
+# Python imports
+import argparse
+from setproctitle import setproctitle
+
+# Gtk imports
+import gi, faulthandler, signal
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+from gi.repository import GLib
+
+# Application imports
+from __init__ import Main
+
+
+if __name__ == "__main__":
+ try:
+ setproctitle('Mouse Keyboard')
+ GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
+ faulthandler.enable() # For better debug info
+ parser = argparse.ArgumentParser()
+ # Add long and short arguments
+ parser.add_argument("--file", "-f", default="default", help="JUST SOME FILE ARG.")
+
+ # Read arguments (If any...)
+ args = parser.parse_args()
+ main = Main(args)
+ Gtk.main()
+ except Exception as e:
+ print( repr(e) )
diff --git a/src/resources/Main_Window.glade b/src/resources/Main_Window.glade
new file mode 100644
index 0000000..7931fad
--- /dev/null
+++ b/src/resources/Main_Window.glade
@@ -0,0 +1,436 @@
+
+
+
+
+
+
+
diff --git a/src/resources/icon.png b/src/resources/icon.png
new file mode 100644
index 0000000..bcf986e
Binary files /dev/null and b/src/resources/icon.png differ
diff --git a/src/resources/stylesheet.css b/src/resources/stylesheet.css
new file mode 100644
index 0000000..f219141
--- /dev/null
+++ b/src/resources/stylesheet.css
@@ -0,0 +1,8 @@
+/* * {
+ background: rgba(0, 0, 0, 0.14);
+ color: rgba(255, 255, 255, 1);
+}
+
+#popoutkeyboard {
+ background-color: rgba(0, 65, 125, 1);
+} */
diff --git a/src/signal_classes/__init__.py b/src/signal_classes/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/signal_classes/mixins/__init__.py b/src/signal_classes/mixins/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/signal_classes/mixins/keyboardmixin.py b/src/signal_classes/mixins/keyboardmixin.py
new file mode 100644
index 0000000..9a96478
--- /dev/null
+++ b/src/signal_classes/mixins/keyboardmixin.py
@@ -0,0 +1,94 @@
+# Python imports
+import pyautogui
+
+# Gtk imports
+
+# Application imports
+
+
+# Let piautogui make updates as quick as it can...
+pyautogui.FAILSAFE = False # If we hit corner, that's ok
+pyautogui.MINIMUM_DURATION = 0
+pyautogui.PAUSE = 0
+
+
+class KeyboardMixin:
+
+ def typeString(self, widget = None, data = None):
+ text = self.autoTypeField.get_text()
+ for char in text:
+ self.do_insert(char)
+
+ def insert(self, widget = None, data = None, key = None):
+ if not key:
+ key = widget.get_label().strip()
+
+ if self.is_keypress_type(key):
+ return
+
+ if self.isCapsLockOn:
+ key = key.upper()
+
+ self.do_insert(key)
+
+
+ def do_insert(self, key):
+ if self.isCtrlOn or self.isShiftOn or self.isAltOn:
+ self.set_hotkeys()
+
+ pyautogui.typewrite(key)
+
+ if self.isCtrlOn or self.isShiftOn or self.isAltOn:
+ self.unset_hotkeys()
+
+
+ def is_keypress_type(self, key):
+ if key in ["Esc", "Tab", "Space", "Del", "Up", "Down", "Left", "Right", "PrtSc"]:
+ pyautogui.press(key.lower())
+ return True
+
+ for i in range(1, 13):
+ fkey = 'F' + str(i)
+ if key == fkey:
+ pyautogui.press(key.lower())
+ return True
+
+ return False
+
+
+ def set_hotkeys(self):
+ if self.isCtrlOn:
+ pyautogui.keyDown('ctrl')
+ if self.isShiftOn:
+ pyautogui.keyDown('shiftleft')
+ pyautogui.keyDown('shiftright')
+ if self.isAltOn:
+ pyautogui.keyDown('alt')
+
+
+ def unset_hotkeys(self):
+ pyautogui.keyUp('ctrl')
+ pyautogui.keyUp('shiftleft')
+ pyautogui.keyUp('shiftright')
+ pyautogui.keyUp('alt')
+
+
+ def toggleCaps(self, widget, data=None):
+ self.isCapsLockOn = False if self.isCapsLockOn else True
+
+ def tgglCtrl(self, widget, data=None):
+ self.isCtrlOn = False if self.isCtrlOn else True
+
+ def tgglShift(self, widget, data=None):
+ self.isShiftOn = False if self.isShiftOn else True
+
+ def tgglAlt(self, widget, data=None):
+ self.isAltOn = False if self.isAltOn else True
+
+
+ def enter(self, widget, data=None):
+ pyautogui.press("enter")
+
+
+ def backspace(self, widget, data=None):
+ pyautogui.press("backspace")
diff --git a/src/signal_classes/signals.py b/src/signal_classes/signals.py
new file mode 100644
index 0000000..0751a68
--- /dev/null
+++ b/src/signal_classes/signals.py
@@ -0,0 +1,107 @@
+# Python imports
+import threading, subprocess, os
+
+# Gtk imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# Application imports
+from .mixins.keyboardmixin import KeyboardMixin
+
+
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs).start()
+ return wrapper
+
+
+class Signals(KeyboardMixin):
+ def __init__(self, settings):
+ self.settings = settings
+ self.builder = self.settings.returnBuilder()
+ self.autoTypeField = self.builder.get_object("autoTypeField")
+ self.commandsStore = self.builder.get_object("commands")
+ self.specialsStore = self.builder.get_object("specials")
+ self.specialsTree = self.builder.get_object("specialsTree")
+ main_keys = self.builder.get_object("main_keys")
+
+ self.isCapsLockOn = False
+ self.isCtrlOn = False
+ self.isShiftOn = False
+ self.isAltOn = False
+
+ special_characters = "<>()[]{}/\!?#$%&@*:^|'\"-_=+~`"
+ self.generate_keys(special_characters, self.specialsStore)
+ self.specialsStore.show_all()
+ main_keys.show_all()
+
+ row1_characters = "1234567890"
+ row1 = Gtk.Box()
+ self.generate_keys(["Esc",], row1)
+ self.generate_keys(row1_characters, row1)
+ self.generate_keys(["Backspace",], row1)
+ row1.set_homogeneous(True)
+ row1.show_all()
+ main_keys.add(row1)
+
+ row2_characters = "qwertyuiop"
+ row2 = Gtk.Box()
+ self.generate_keys(["Tab",], row2)
+ self.generate_keys(row2_characters, row2)
+ row2.set_homogeneous(True)
+ row2.show_all()
+ main_keys.add(row2)
+
+ row3_characters = "asdfghjkl"
+ row3 = Gtk.Box()
+ self.generate_keys(["Caps Lock",], row3)
+ self.generate_keys(row3_characters, row3)
+ self.generate_keys(["Enter",], row3)
+ row3.set_homogeneous(True)
+ row3.show_all()
+ main_keys.add(row3)
+
+ row4_characters = "zxcvbnm,.:"
+ row4 = Gtk.Box()
+ self.generate_keys(row4_characters, row4)
+ row4.set_homogeneous(True)
+ row4.show_all()
+ main_keys.add(row4)
+
+ row5_characters = "Space"
+ row5 = Gtk.Box()
+ self.generate_keys([row5_characters,], row5)
+ row5.set_homogeneous(True)
+ row5.show_all()
+ main_keys.add(row5)
+
+
+ def generate_keys(self, labels, target):
+ for label in labels:
+ button = Gtk.Button.new_with_label(label)
+ if label not in ["Enter", "Backspace", "Caps Lock"]:
+ button.connect("clicked", self.insert)
+ else:
+ if label == "Enter":
+ button.connect("clicked", self.enter)
+ if label == "Backspace":
+ button.connect("clicked", self.backspace)
+ if label == "Caps Lock":
+ button = Gtk.ToggleButton.new_with_label(label)
+ button.connect("toggled", self.toggleCaps)
+
+ target.add(button)
+
+
+ def getClipboardData(self):
+ proc = subprocess.Popen(['xclip','-selection', 'clipboard', '-o'], stdout=subprocess.PIPE)
+ retcode = proc.wait()
+ data = proc.stdout.read()
+ return data.decode("utf-8").strip()
+
+ def setClipboardData(self, data):
+ proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE)
+ proc.stdin.write(data)
+ proc.stdin.close()
+ retcode = proc.wait()
diff --git a/src/utils/__init__.py b/src/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/utils/logger.py b/src/utils/logger.py
new file mode 100644
index 0000000..c7f294e
--- /dev/null
+++ b/src/utils/logger.py
@@ -0,0 +1,56 @@
+# Python imports
+import os, logging
+
+# Application imports
+
+
+class Logger:
+ def __init__(self):
+ pass
+
+ def get_logger(self, loggerName = "NO_LOGGER_NAME_PASSED", createFile = True):
+ """
+ Create a new logging object and return it.
+ :note:
+ NOSET # Don't know the actual log level of this... (defaulting or literally none?)
+ Log Levels (From least to most)
+ Type Value
+ CRITICAL 50
+ ERROR 40
+ WARNING 30
+ INFO 20
+ DEBUG 10
+ :param loggerName: Sets the name of the logger object. (Used in log lines)
+ :param createFile: Whether we create a log file or just pump to terminal
+
+ :return: the logging object we created
+ """
+
+ globalLogLvl = logging.DEBUG # Keep this at highest so that handlers can filter to their desired levels
+ chLogLevel = logging.CRITICAL # Prety musch the only one we change ever
+ fhLogLevel = logging.DEBUG
+ log = logging.getLogger(loggerName)
+ log.setLevel(globalLogLvl)
+
+ # Set our log output styles
+ fFormatter = logging.Formatter('[%(asctime)s] %(pathname)s:%(lineno)d %(levelname)s - %(message)s', '%m-%d %H:%M:%S')
+ cFormatter = logging.Formatter('%(pathname)s:%(lineno)d] %(levelname)s - %(message)s')
+
+ ch = logging.StreamHandler()
+ ch.setLevel(level=chLogLevel)
+ ch.setFormatter(cFormatter)
+ log.addHandler(ch)
+
+ if createFile:
+ folder = "logs"
+ file = folder + "/application.log"
+
+ if not os.path.exists(folder):
+ os.mkdir(folder)
+
+ fh = logging.FileHandler(file)
+ fh.setLevel(level=fhLogLevel)
+ fh.setFormatter(fFormatter)
+ log.addHandler(fh)
+
+ return log
diff --git a/src/utils/settings.py b/src/utils/settings.py
new file mode 100644
index 0000000..081eaed
--- /dev/null
+++ b/src/utils/settings.py
@@ -0,0 +1,97 @@
+# Python imports
+import os
+
+# Gtk imports
+import gi, cairo
+gi.require_version('Gtk', '3.0')
+gi.require_version('Gdk', '3.0')
+
+from gi.repository import Gtk
+from gi.repository import Gdk
+
+
+# Application imports
+
+
+class Settings:
+ def __init__(self):
+ self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__))
+ self._USER_HOME = os.path.expanduser('~')
+ self._CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}"
+ self._GLADE_FILE = f"{self._CONFIG_PATH}/Main_Window.glade"
+ self._CSS_FILE = f"{self._CONFIG_PATH}/stylesheet.css"
+ self._USR_PATH = f"/usr/share/{app_name.lower()}"
+
+ if not os.path.exists(self._CONFIG_PATH):
+ os.mkdir(self._CONFIG_PATH)
+ if not os.path.exists(self._GLADE_FILE):
+ self._GLADE_FILE = f"{self._USR_PATH}/Main_Window.glade"
+ if not os.path.exists(self._CSS_FILE):
+ self._CSS_FILE = f"{self._USR_PATH}/stylesheet.css"
+
+ # 'Filters'
+ self.office = ('.doc', '.docx', '.xls', '.xlsx', '.xlt', '.xltx', '.xlm',
+ '.ppt', 'pptx', '.pps', '.ppsx', '.odt', '.rtf')
+ self.vids = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv',
+ '.mpeg', '.mp4', '.webm')
+ self.txt = ('.txt', '.text', '.sh', '.cfg', '.conf')
+ self.music = ('.psf', '.mp3', '.ogg' , '.flac')
+ self.images = ('.png', '.jpg', '.jpeg', '.gif')
+ self.pdf = ('.pdf')
+
+ self.builder = Gtk.Builder()
+ self.builder.add_from_file(self._GLADE_FILE)
+
+
+ def createWindow(self):
+ # Get window and connect signals
+ window = self.builder.get_object("Main_Window")
+ window.connect("delete-event", Gtk.main_quit)
+ self.setWindowData(window, False)
+ return window
+
+ def setWindowData(self, window, paintable):
+ screen = window.get_screen()
+ visual = screen.get_rgba_visual()
+
+ if visual != None and screen.is_composited():
+ window.set_visual(visual)
+
+ # bind css file
+ cssProvider = Gtk.CssProvider()
+ cssProvider.load_from_path(self._CSS_FILE)
+ screen = Gdk.Screen.get_default()
+ styleContext = Gtk.StyleContext()
+ styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
+
+ window.set_app_paintable(paintable)
+ if paintable:
+ window.connect("draw", self.area_draw)
+
+ def getMonitorData(self):
+ screen = self.builder.get_object("Main_Window").get_screen()
+ monitors = []
+ for m in range(screen.get_n_monitors()):
+ monitors.append(screen.get_monitor_geometry(m))
+
+ for monitor in monitors:
+ print("{}x{}+{}+{}".format(monitor.width, monitor.height, monitor.x, monitor.y))
+
+ return monitors
+
+ def area_draw(self, widget, cr):
+ cr.set_source_rgba(0, 0, 0, 0.54)
+ cr.set_operator(cairo.OPERATOR_SOURCE)
+ cr.paint()
+ cr.set_operator(cairo.OPERATOR_OVER)
+
+
+ def returnBuilder(self): return self.builder
+
+ # Filter returns
+ def returnOfficeFilter(self): return self.office
+ def returnVidsFilter(self): return self.vids
+ def returnTextFilter(self): return self.txt
+ def returnMusicFilter(self): return self.music
+ def returnImagesFilter(self): return self.images
+ def returnPdfFilter(self): return self.pdf
diff --git a/user_config/usr/share/mouse_keyboard/Main_Window.glade b/user_config/usr/share/mouse_keyboard/Main_Window.glade
new file mode 100644
index 0000000..7931fad
--- /dev/null
+++ b/user_config/usr/share/mouse_keyboard/Main_Window.glade
@@ -0,0 +1,436 @@
+
+
+
+
+
+
+
+
+
+
+
+ False
+ Mouse Board
+ center
+ 260
+ icon.png
+ toolbar
+ True
+ True
+ False
+ center
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+
+
+ True
+ False
+
+
+ True
+ True
+ 0
+
+
+
+
+ 520
+ True
+ False
+
+
+ True
+ True
+ gtk-go-forward
+ False
+ False
+ Autotype Field...
+
+
+ True
+ True
+ 0
+
+
+
+
+ Type
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+
+
+ True
+ True
+ 2
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ 10
+ 10
+ Special Characters
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ in
+
+
+ True
+ False
+
+
+ True
+ False
+ vertical
+ 10
+ start
+
+
+
+
+
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ popoutkeyboard
+ True
+ False
+ 5
+ vertical
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ 10
+ 10
+ vertical
+
+
+ True
+ False
+ start
+
+
+ Del
+ True
+ True
+ True
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ Ctrl
+ True
+ True
+ True
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ Shift
+ True
+ True
+ True
+
+
+
+ True
+ True
+ 2
+
+
+
+
+ Alt
+ True
+ True
+ True
+
+
+
+ True
+ True
+ 3
+
+
+
+
+ PrtSc
+ True
+ True
+ False
+
+
+
+ True
+ True
+ 4
+
+
+
+
+ gtk-add
+ True
+ True
+ True
+ True
+
+
+ True
+ True
+ 5
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ in
+
+
+ True
+ False
+
+
+ True
+ True
+ commands
+
+
+
+
+
+ Commands
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+
+ True
+ False
+ True
+
+
+ Up
+ True
+ True
+ True
+
+
+
+ 1
+ 0
+
+
+
+
+ Down
+ True
+ True
+ True
+
+
+
+ 1
+ 2
+
+
+
+
+ Left
+ True
+ True
+ True
+
+
+
+ 0
+ 1
+
+
+
+
+ Right
+ True
+ True
+ True
+
+
+
+ 2
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+
+
diff --git a/user_config/usr/share/mouse_keyboard/mouse-keyboard.png b/user_config/usr/share/mouse_keyboard/mouse-keyboard.png
new file mode 100644
index 0000000..bcf986e
Binary files /dev/null and b/user_config/usr/share/mouse_keyboard/mouse-keyboard.png differ
diff --git a/user_config/usr/share/mouse_keyboard/stylesheet.css b/user_config/usr/share/mouse_keyboard/stylesheet.css
new file mode 100644
index 0000000..f219141
--- /dev/null
+++ b/user_config/usr/share/mouse_keyboard/stylesheet.css
@@ -0,0 +1,8 @@
+/* * {
+ background: rgba(0, 0, 0, 0.14);
+ color: rgba(255, 255, 255, 1);
+}
+
+#popoutkeyboard {
+ background-color: rgba(0, 65, 125, 1);
+} */