#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022 , Nils Hilbricht , Germany ( https : / / www . hilbricht . net )
This file is part of the Laborejo Software Suite ( https : / / www . laborejo . org ) ,
This application is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : / / www . gnu . org / licenses / > .
"""
import logging ; logger = logging . getLogger ( __name__ ) ; logger . info ( " import " )
import os , socket , asyncio
from signal import signal , SIGTERM , SIGUSR1
from threading import Thread
from sys import argv
from . nsmclient import _IncomingMessage , _OutgoingMessage
class NSMProtocol ( asyncio . DatagramProtocol ) :
directory = None
addr = None #a cache
def __init__ ( self ) :
super ( ) . __init__ ( )
def connection_made ( self , transport ) :
self . transport = transport
def datagram_received ( self , data , addr ) :
NSMProtocol . addr = addr
msg = _IncomingMessage ( data )
if msg . oscpath == " /nsm/server/announce " :
application_name , capabilities , executable_name , api_version_major , api_version_minor , pid = msg . params
NSMProtocol . pid = pid
reply = _OutgoingMessage ( " /reply " )
reply . add_arg ( " /nsm/server/announce " )
reply . add_arg ( " Welcome! " )
reply . add_arg ( " Fake Save Server " )
reply . add_arg ( " server-control: " )
self . send ( reply , addr )
#['/home/user/NSM Sessions/dev-example/QtCboxNsm Exämple ツ.nXDBM', 'dev-example', 'QtCboxNsm Exämple ツ.nXDBM']
openMsg = reply = _OutgoingMessage ( " /nsm/client/open " )
openMsg . add_arg ( NSMProtocol . directory )
openMsg . add_arg ( " NOT-A-SESSION " )
openMsg . add_arg ( application_name )
self . send ( openMsg , addr )
self . send ( _OutgoingMessage ( " /nsm/client/show_optional_gui " ) , addr )
elif msg . oscpath == " /nsm/gui/client/save " :
self . send ( _OutgoingMessage ( " /nsm/client/save " ) , addr )
elif msg . oscpath == " /nsm/client/gui_is_hidden " :
os . kill ( NSMProtocol . pid , SIGTERM )
elif msg . oscpath == " /nsm/server/stop " :
os . kill ( NSMProtocol . pid , SIGTERM )
#else:
# print (msg.oscpath, msg.params)
def send ( self , message , addr ) :
sock = socket . socket ( socket . AF_INET , socket . SOCK_DGRAM ) # UDP
sock . sendto ( message . build ( ) , addr )
@staticmethod
def staticSave ( * args ) :
NSMProtocol . send ( None , _OutgoingMessage ( " /nsm/client/save " ) , NSMProtocol . addr )
def startSingleNSMServer ( directory ) :
""" Set all paths like NSM would receive them and nsmclient.py expects them. """
serverSock = socket . socket ( socket . AF_INET , socket . SOCK_DGRAM )
serverSock . bind ( ( ' ' , 0 ) ) # Bind to a free port provided by the host.
SERVER_PORT = serverSock . getsockname ( ) [ 1 ]
NSMProtocol . directory = directory
serverSock . close ( )
os . environ [ " NSM_URL " ] = f " osc.udp://localhost: { SERVER_PORT } / "
executableName = os . path . basename ( argv [ 0 ] )
executableDir = os . path . abspath ( os . path . join ( os . path . dirname ( __file__ ) , " .. " , " .. " ) )
##assert os.path.exists(os.path.join(executableDir, executableName)) not valid anymore with zipapp. But it worked for years, so I guess the code is ok.
argv [ 0 ] = os . path . join ( executableDir , executableName ) #NSM speciality. nsmclient exlicitely checks for this
os . environ [ " PATH " ] = os . environ [ " PATH " ] + " : " + executableDir
#print (argv[0])
#print (executableName)
#print (executableDir)
#print(os.environ["PATH"])
#print(os.environ["NSM_URL"])
#loop = asyncio.get_event_loop()
#loop.create_task(asyncio.start_server(handle_client, 'localhost', SERVER_PORT))
#loop.run_forever()
#asyncio.run(asyncio.start_server(handle_client, 'localhost', SERVER_PORT))
logger . info ( f " Starting fake NSM server on port { SERVER_PORT } " )
#For Carla:
signal ( SIGUSR1 , NSMProtocol . staticSave )
loop = asyncio . get_event_loop ( )
def run_loop ( loop ) :
asyncio . set_event_loop ( loop )
t = loop . create_datagram_endpoint ( NSMProtocol , local_addr = ( ' 127.0.0.1 ' , SERVER_PORT ) , family = socket . AF_INET )
loop . run_until_complete ( t )
loop . run_forever ( )
Thread ( target = lambda : run_loop ( loop ) , daemon = True ) . start ( ) #Daemon makes the thread just stop when main thread ends.