1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
4
# You can run this .tac file directly with:
5
# twistd -ny demo_insults.tac
7
"""Various simple terminal manipulations using the insults module.
9
This demo sets up two listening ports: one on 6022 which accepts ssh
10
connections; one on 6023 which accepts telnet connections. No login
11
for the telnet server is required; for the ssh server, \"username\" is
12
the username and \"password\" is the password.
14
The TerminalProtocol subclass defined here ignores most user input
15
(except to print it out to the server log) and spends the duration of
16
the connection drawing (the author's humble approximation of)
17
raindrops at random locations on the client's terminal. +, -, *, and
18
/ are respected and each adjusts an aspect of the timing of the
24
from twisted.python import log
25
from twisted.internet import protocol, task
26
from twisted.application import internet, service
27
from twisted.cred import checkers, portal
29
from twisted.conch.insults import insults
30
from twisted.conch.telnet import TelnetTransport, TelnetBootstrapProtocol
31
from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm
33
class DrawingFinished(Exception):
34
"""Sentinel exception, raised when no \"frames\" for a particular
35
\"animation\" remain to be drawn.
39
"""Representation of an animation.
41
Constructed with a protocol instance and a coordinate on the
42
screen, waits for invocations of iterate() at which point it
43
erases the previous frame of the animation and draws the next one,
44
using its protocol instance and always placing the upper left hand
45
corner of the frame at the given coordinates.
47
Frames are defined with draw_ prefixed methods. Erasure is
48
performed by erase_ prefixed methods.
52
def __init__(self, proto, col, line):
57
def drawLines(self, s):
58
lines = s.splitlines()
62
self.proto.cursorPosition(c - len(lines) / 2, line)
67
getattr(self, 'erase_' + str(self.n))()
69
f = getattr(self, 'draw_' + str(self.n), None)
71
raise DrawingFinished()
78
class Splat(Drawable):
86
self.drawLines(' . .\n. . .\n . .')
89
self.drawLines(' \n \n ')
97
self.drawLines(' . . . .\n . o o o .\n. o o o o .\n . o o o .\n . . . .')
100
self.drawLines(' \n \n \n \n ')
108
self.drawLines(' o o o o\n o O O O o\no O O O O o\n o O O O o\n o o o o')
118
self.drawLines(' O O O O\n O . . . O\nO . . . . O\n O . . . O\n O O O O')
128
self.drawLines(' . . . .\n . .\n. .\n . .\n . . . .')
132
class Drop(Drawable):
147
self.drawLines(' _ \n/ \\\n\\./')
150
self.drawLines(' \n \n ')
159
class DemoProtocol(insults.TerminalProtocol):
160
"""Draws random things at random places on the screen.
168
def connectionMade(self):
171
def connectionLost(self, reason):
176
# Clear the screen, matey
177
self.terminal.eraseDisplay()
179
self._call = task.LoopingCall(self._iterate)
180
self._call.start(self.interval)
183
cls = random.choice((Splat, Drop))
185
# Move to a random location on the screen
186
col = random.randrange(self.width - cls.WIDTH) + cls.WIDTH
187
line = random.randrange(self.height - cls.HEIGHT) + cls.HEIGHT
189
s = cls(self.terminal, col, line)
191
c = task.LoopingCall(s.iterate)
192
c.start(self.rate).addErrback(lambda f: f.trap(DrawingFinished)).addErrback(log.err)
195
def terminalSize(self, width, height):
199
def unhandledControlSequence(self, seq):
200
log.msg("Client sent something weird: %r" % (seq,))
202
def keystrokeReceived(self, keyID, modifier):
212
log.msg("Client sent: %r" % (keyID,))
216
self._call = task.LoopingCall(self._iterate)
217
self._call.start(self.interval)
220
def makeService(args):
221
checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username="password")
223
f = protocol.ServerFactory()
224
f.protocol = lambda: TelnetTransport(TelnetBootstrapProtocol,
225
insults.ServerProtocol,
226
args['protocolFactory'],
227
*args.get('protocolArgs', ()),
228
**args.get('protocolKwArgs', {}))
229
tsvc = internet.TCPServer(args['telnet'], f)
231
def chainProtocolFactory():
232
return insults.ServerProtocol(
233
args['protocolFactory'],
234
*args.get('protocolArgs', ()),
235
**args.get('protocolKwArgs', {}))
237
rlm = TerminalRealm()
238
rlm.chainedProtocolFactory = chainProtocolFactory
239
ptl = portal.Portal(rlm, [checker])
240
f = ConchFactory(ptl)
241
csvc = internet.TCPServer(args['ssh'], f)
243
m = service.MultiService()
244
tsvc.setServiceParent(m)
245
csvc.setServiceParent(m)
248
application = service.Application("Insults Demo App")
250
makeService({'protocolFactory': DemoProtocol,
252
'ssh': 6022}).setServiceParent(application)