Implement automatic discovery of terminal adapters

This commit is contained in:
Charles Milette 2018-01-06 19:51:42 -05:00
parent 3312d54809
commit 71a341ca1c
14 changed files with 150 additions and 126 deletions

View file

@ -178,7 +178,7 @@ Alternatively, you can delete images from this folder and it will not break the
$ sudo apt-get update $ sudo apt-get update
$ sudo apt install terminology $ sudo apt install terminology
``` ```
* If you get the error `39:46: syntax error: Expected end of line but found identifier. (-2741)`: Locate the file `ITerm.py` in `pokemonterminal/adapter/implementations` and on line 7, change `iTerm` to `iTerm2`. If you still experience the error, try changing it to `iTerm 2`. * If you get the error `39:46: syntax error: Expected end of line but found identifier. (-2741)`: Locate the file `ITerm.py` in `pokemonterminal/terminal/adapters` and on line 9, change `iTerm` to `iTerm2`. If you still experience the error, try changing it to `iTerm 2`.
## Saving ## Saving

View file

@ -1,24 +0,0 @@
from pokemonterminal.adapter.implementations.ITerm import ITerm
from pokemonterminal.adapter.implementations.NullAdapter import NullAdapter
from pokemonterminal.adapter.implementations.Terminology import Terminology
from pokemonterminal.adapter.implementations.Tilix import Tilix
available_terminals = [
Terminology,
Tilix,
ITerm
]
def identify():
"""
Identify the terminal we are using based on env vars.
:return: A terminal adapter interface or a NullAdapter.
:rtype: TerminalAdapterInterface
"""
for terminal in available_terminals:
if terminal.is_available():
return terminal()
return NullAdapter()

View file

@ -1,22 +0,0 @@
class TerminalAdapterInterface(object):
@staticmethod
def is_available():
"""
:return: True if the environment implies we are using this terminal.
:rtype bool
"""
raise NotImplementedError()
def set_image_file_path(self, image_file_path):
"""
Set the background image of the terminal.
:param image_file_path: Path to an image file.
:rtype str
"""
raise NotImplementedError()
def clear(self):
"""
Clear the terminal's background image.
"""
raise NotImplementedError()

View file

@ -1,33 +0,0 @@
import os
import subprocess
from pokemonterminal.adapter.base import TerminalAdapterInterface
# OSA script that will change the terminal background image
osa_script_fmt = """tell application "iTerm"
\ttell current session of current window
\t\tset background image to "{}"
\tend tell
end tell"""
class ITerm(TerminalAdapterInterface):
@staticmethod
def is_available():
return os.environ.get("ITERM_PROFILE")
def __run_osascript(self, stream):
p = subprocess.Popen(['osascript'], stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
p.stdin.write(stream)
p.communicate()
p.stdin.close()
def set_image_file_path(self, image_file_path):
stdin = osa_script_fmt.format(image_file_path)
self.__run_osascript(str.encode(stdin))
def clear(self):
stdin = osa_script_fmt.format("")
self.__run_osascript(str.encode(stdin))

View file

@ -1,15 +0,0 @@
from pokemonterminal.adapter.base import TerminalAdapterInterface
class NullAdapter(TerminalAdapterInterface):
err = "This terminal emulator is not supported."
@staticmethod
def is_available():
return True
def set_image_file_path(self, image_file_path):
print(self.err)
def clear(self):
print(self.err)

View file

@ -1,15 +0,0 @@
import os
from pokemonterminal.adapter.base import TerminalAdapterInterface
class Terminology(TerminalAdapterInterface):
@staticmethod
def is_available():
return os.environ.get("TERMINOLOGY") == '1'
def set_image_file_path(self, image_file_path):
os.system('tybg "{}"'.format(image_file_path))
def clear(self):
os.system("tybg")

View file

@ -1,17 +1,47 @@
# Used for creating, running and analyzing applescript and bash scripts. # Used for creating, running and analyzing applescript and bash scripts.
import sys import sys
from pokemonterminal.adapter import identify from .terminal import get_current_terminal_adapters
from .wallpaper import get_current_adapters from .wallpaper import get_current_wallpaper_adapters
TERMINAL_PROVIDER = None
WALLPAPER_PROVIDER = None WALLPAPER_PROVIDER = None
def __init_terminal_provider():
global TERMINAL_PROVIDER
if TERMINAL_PROVIDER is not None:
return
providers = get_current_terminal_adapters()
if len(providers) > 1:
# All this if is really not supposed to happen at all whatsoever
# really what kind of person has 2 simultaneous D.E???
print("Multiple providers found select the appropriate one:")
[print(str(x)) for x in providers]
print("If some of these make no sense or are irrelevant please file" +
"an issue in https://github.com/LazoCoder/Pokemon-Terminal")
print("=> ", end='')
inp = None
while inp is None:
try:
inp = int(input())
if inp >= len(providers):
raise ValueError()
except ValueError as _:
print("Invalid number, try again!")
TERMINAL_PROVIDER = providers[inp]
elif len(providers) <= 0:
print("Your terminal emulator isn't supported at this time.")
sys.exit()
else:
TERMINAL_PROVIDER = providers[0]
def __init_wallpaper_provider(): def __init_wallpaper_provider():
global WALLPAPER_PROVIDER global WALLPAPER_PROVIDER
if WALLPAPER_PROVIDER is not None: if WALLPAPER_PROVIDER is not None:
return return
providers = get_current_adapters() providers = get_current_wallpaper_adapters()
if len(providers) > 1: if len(providers) > 1:
# All this if is really not supposed to happen at all whatsoever # All this if is really not supposed to happen at all whatsoever
# really what kind of person has 2 simultaneous D.E??? # really what kind of person has 2 simultaneous D.E???
@ -37,18 +67,16 @@ def __init_wallpaper_provider():
def clear_terminal(): def clear_terminal():
adapter = identify() __init_terminal_provider()
adapter.clear() TERMINAL_PROVIDER.clear()
def change_terminal(image_file_path): def change_terminal(image_file_path):
if not isinstance(image_file_path, str): if not isinstance(image_file_path, str):
print("A image path must be passed to the change terminal function.") print("A image path must be passed to the change terminal function.")
return return
adapter = identify() __init_terminal_provider()
if adapter is None: TERMINAL_PROVIDER.change_terminal(image_file_path)
print("Terminal not supported")
adapter.set_image_file_path(image_file_path)
def change_wallpaper(image_file_path): def change_wallpaper(image_file_path):

View file

@ -0,0 +1,34 @@
import os
import importlib
import inspect
from .adapters import TerminalProvider
def _is_adapter(member) -> bool:
return (inspect.isclass(member)
and issubclass(member, TerminalProvider)
and member != TerminalProvider)
def _get_adapter_classes() -> [TerminalProvider]:
"""
This methods reads all the modules in the adapters folder searching for
all the implementing wallpaper adapter classes
thanks for/adapted from https://github.com/cclauss/adapter_pattern/
"""
dirname = os.path.join(os.path.dirname(
os.path.abspath(__file__)), 'adapters')
adapter_classes = []
for file_name in sorted(os.listdir(dirname)):
root, ext = os.path.splitext(file_name)
if ext.lower() == '.py' and not root.startswith('__'):
module = importlib.import_module(
'.' + root, 'pokemonterminal.terminal.adapters')
for _, c in inspect.getmembers(module, _is_adapter):
adapter_classes.append(c)
return adapter_classes
def get_current_terminal_adapters() -> [TerminalProvider]:
arr = _get_adapter_classes()
return [x for x in arr if x.is_compatible()]

View file

@ -0,0 +1,31 @@
import os
import subprocess
from . import TerminalProvider as _TProv
class ITerm(_TProv):
# OSA script that will change the terminal background image
osa_script_fmt = """tell application "iTerm"
\ttell current session of current window
\t\tset background image to "{}"
\tend tell
end tell"""
def is_compatible() -> bool:
return os.environ.get("ITERM_PROFILE")
def __run_osascript(stream):
p = subprocess.Popen(['osascript'], stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
p.stdin.write(stream)
p.communicate()
p.stdin.close()
def change_terminal(self, path: str):
stdin = self.osa_script_fmt.format(path)
self.__run_osascript(str.encode(stdin))
def clear(self):
stdin = self.osa_script_fmt.format("")
self.__run_osascript(str.encode(stdin))

View file

@ -0,0 +1,14 @@
import os
from . import TerminalProvider as _TProv
class Terminology(_TProv):
def is_compatible() -> bool:
return os.environ.get("TERMINOLOGY") == '1'
def change_terminal(path: str):
os.system('tybg "{}"'.format(path))
def clear():
os.system("tybg")

View file

@ -1,21 +1,20 @@
import os import os
from pokemonterminal.adapter.base import TerminalAdapterInterface from . import TerminalProvider as _TProv
class Tilix(TerminalAdapterInterface): class Tilix(_TProv):
setting_key = "com.gexperts.Tilix.Settings" setting_key = "com.gexperts.Tilix.Settings"
setting_field = "background-image" setting_field = "background-image"
@staticmethod def is_compatible() -> bool:
def is_available():
return "TILIX_ID" in os.environ return "TILIX_ID" in os.environ
def set_image_file_path(self, image_file_path): def change_terminal(self, path: str):
command = 'gsettings set {} {} "{}"' command = 'gsettings set {} {} "{}"'
os.system(command.format(self.setting_key, os.system(command.format(self.setting_key,
self.setting_field, self.setting_field,
image_file_path)) path))
def clear(self): def clear(self):
command = 'gsettings set {} {}' command = 'gsettings set {} {}'

View file

@ -0,0 +1,27 @@
class TerminalProvider:
"""
Interface representing all the different terminal emulators supported
by pokemon-terminal if you want to implement a TE, create a module in this
folder that implements this interface, reflection will do the rest.
"""
def change_terminal(path: str):
"""
This sets the wallpaper of the corresponding TE of this adapter.
:param path The full path of the required pokemon image
"""
raise NotImplementedError()
def is_compatible() -> bool:
"""
checks for compatibility
:return a boolean saying whether or not the current adaptor is
compatible with the running TE
"""
raise NotImplementedError()
def clear(self):
"""
Clear the terminal's background image.
"""
raise NotImplementedError()

View file

@ -29,6 +29,6 @@ def _get_adapter_classes() -> [WallpaperProvider]:
return adapter_classes return adapter_classes
def get_current_adapters() -> [WallpaperProvider]: def get_current_wallpaper_adapters() -> [WallpaperProvider]:
arr = _get_adapter_classes() arr = _get_adapter_classes()
return [x for x in arr if x.is_compatible()] return [x for x in arr if x.is_compatible()]