Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions FoxDot/BootManager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
"""
Boot manager

Examples

# With the environment variable or the config file

# With a script using the Go() function:

```python
# `my_file.py` content
from FoxDot import *
p1 >> pads([0, 1, 2, 3])
d1 >> play("x-o-")
Go()
```

$ python my_file.py -b
$ python my_file.py --boot

# With a python module:

$ python -m FoxDot -b
$ python -m FoxDot --boot

# With the CLI:

$ FoxDot -b
$ FoxDot --boot
"""

import os
import os.path
import platform
import sys
from subprocess import Popen
from functools import cached_property

import psutil

FOXDOT_ROOT = os.path.dirname(__file__)
FOXDOT_STARTUP_FILE = os.path.join(FOXDOT_ROOT, "osc/Startup.scd")
FOXDOT_CONFIG_FILE = os.path.join(FOXDOT_ROOT, "lib/Settings/conf.txt")


class BaseBooter:
process_cmd = ["sclang", FOXDOT_STARTUP_FILE]
process_name = "sclang"
process = None
running = False

@cached_property
def BOOT_ON_STARTUP(self):
"""Boot on startup via CLI/Env/Config when import."""
# Loading from cli
for arg in sys.argv[1:]:
if arg in ['-b', '--boot']:
return True
if arg.startswith('-') and 'b' in arg.split('=')[0]:
# Example
# FoxDot -pbs startup.py
# python -m FoxDot -pbs=startup.py
# python my_file.py -pbs=startup.py
return True

# Loading from env
env = os.getenv("BOOT_ON_STARTUP")
if env is not None:
return env

# Loading from configfile
try:
with open(FOXDOT_CONFIG_FILE) as file:
contents = file.read()
code = compile(contents, "FoxDot", "exec")
exec(code, globals())
except FileNotFoundError:
return False
return bool(globals().get("BOOT_ON_STARTUP"))

def start(self):
"""Start SuperCollider."""
print("Operating system unrecognised")

def stop(self):
"""Stop SuperCollider."""
if self.running and self.process:
return self.kill()

def kill(self):
"""Killl process."""
if not self.process:
return

process = psutil.Process(self.process.pid)
for children in process.children(recursive=True):
children.kill()
process.kill()


class WindowsBooter(BaseBooter):
def start(self):
if not (not self.process and not self.running and not self.is_running()):
return

sclangloc = os.popen('where /R "C:\\Program Files" sclang.exe').read()
thiscwd = str(sclangloc)
ourcwd = thiscwd.replace("\\sclang.exe\n", "")

self.process_cmd = [sclangloc, FOXDOT_STARTUP_FILE]
self.process = Popen(self.process_cmd, cwd=ourcwd)
self.running = True

def is_running(self):
for p in psutil.process_iter(attrs=["name", "exe", "cmdline"]):
procname = (
p.info["name"]
or p.info["exe"]
and os.path.basename(p.info["exe"]) == self.process_name
or p.info["cmdline"]
and p.info["cmdline"][0] == self.process_name
)
if procname.startswith(self.process_name):
self.running = True
return self.running


class LinuxBooter(BaseBooter):
def start(self):
if not (not self.process and not self.running and not self.is_running()):
return

self.process = Popen(self.process_cmd, cwd=os.getcwd())
self.running = True

def is_running(self):
for p in psutil.process_iter(attrs=["name", "cmdline"]):
# print(p);
procname = (
p.info["name"]
or p.info["cmdline"]
and p.info["cmdline"][0] == self.process_name
)
if procname.startswith(self.process_name):
self.running = True
return self.running


OS = platform.system()

if OS == "Windows":
Boot = WindowsBooter()
elif OS == "Linux":
Boot = LinuxBooter()
else:
Boot = BaseBooter()
77 changes: 77 additions & 0 deletions FoxDot/Cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from __future__ import absolute_import, division, print_function

import argparse
import sys
import traceback

from .BootManager import Boot

parser = argparse.ArgumentParser(
prog="FoxDot",
description="Live coding with Python and SuperCollider",
epilog="More information: https://foxdot.org/")

parser.add_argument('-p', '--pipe', action='store_true', help="run FoxDot from the command line interface")
parser.add_argument('-d', '--dir', action='store', help="use an alternate directory for looking up samples")
parser.add_argument('-s', '--startup', action='store', help="use an alternate startup file")
parser.add_argument('-S', '--simple', action='store_true', help="run FoxDot in simple (accessible) mode")
parser.add_argument('-n', '--no-startup', action='store_true', help="does not load startup.py on boot")
parser.add_argument('-b', '--boot', action='store_true', help="Boot SuperCollider from the command line")

args = parser.parse_args()


def main():
"""Main function to be executed from the command line."""
if args.boot and not Boot.running:
Boot.start()

from .lib import FoxDotCode, handle_stdin

if args.dir:

try:

# Use given directory

FoxDotCode.use_sample_directory(args.dir)

except OSError as e:

# Exit with last error

sys.exit(traceback.print_exc(limit=1))

if args.startup:

try:

FoxDotCode.use_startup_file(args.startup)

except OSError as e:

sys.exit(traceback.print_exc(limit=1))

if args.no_startup:

FoxDotCode.no_startup()

if args.pipe:

# Just take commands from the CLI

handle_stdin()

else:

# Open the GUI

if args.simple:

from .lib.Workspace.Simple import workspace

else:

from .lib.Workspace.Editor import workspace

workspace(FoxDotCode).run()
95 changes: 7 additions & 88 deletions FoxDot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,97 +19,14 @@

from __future__ import absolute_import, division, print_function

def boot_supercollider():
""" Uses subprocesses to boot supercollider from the cli """
from .BootManager import Boot
from .Cli import main

import time
import platform
import os
import subprocess
import getpass

try:
import psutil
except ImportError:
os.system("pip install psutil")
import sys
sys.exit("Installed psutil, please start FoxDot again.")

sclangpath = "" #find path to sclang

thispath = "" #find this path

thisdir = os.getcwd()

OS = platform.system()

username = getpass.getuser()

if(OS == "Windows"):

sclangloc = os.popen('where /R "C:\\Program Files" sclang.exe').read()

thiscwd = str(sclangloc)

ourcwd = thiscwd.replace('\\sclang.exe\n', '')

def is_proc_running(name):
for p in psutil.process_iter(attrs=["name", "exe", "cmdline"]):
#print(p);
procname = p.info['name'] or \
p.info['exe'] and os.path.basename(p.info['exe']) == name or \
p.info['cmdline'] and p.info['cmdline'][0] == name
if(procname.startswith(name)):
return True
return False


running = (is_proc_running("sclang"))

if(running == False):
startup = thisdir+"/FoxDot/startup.scd"
#os.system("sclang"+startup+" &")
subprocess.Popen([sclangloc, startup], cwd=ourcwd, shell=True)

elif(OS == "Linux"):

def is_proc_running(name):
for p in psutil.process_iter(attrs=["name","cmdline"]):
#print(p);
procname = p.info['name'] or \
p.info['cmdline'] and p.info['cmdline'][0] == name
if(procname.startswith(name)):
return True


running = (is_proc_running("sclang"))

if(running == False):
startup = thisdir+"/FoxDot/startup.scd"
#os.system('sclang "/home/foxdot/Desktop/FoxDot-Cross-Platform/FoxDot/startup.scd" &') #fuctional
os.system("sclang "+startup+" &")


else:
print("Operating system unrecognised")
#Potentially get the user to choose their OS from a list?
#Then run the corresponding functions

import sys

if "--boot" in sys.argv:

boot_supercollider()

sys.argv.remove("--boot")
if not Boot.running and Boot.BOOT_ON_STARTUP:
Boot.start()

from .lib import *

def main():
""" Function for starting the GUI when importing the library """
from .lib.Workspace.Editor import workspace
FoxDot = workspace(FoxDotCode).run()

def Go():
""" Function to be called at the end of Python files with FoxDot code in to keep
the TempoClock thread alive. """
Expand All @@ -118,4 +35,6 @@ def Go():
while 1:
time.sleep(100)
except KeyboardInterrupt:
return
execute("Clock.stop()")
execute("Server.quit()")
execute("Boot.stop()")
Loading