[splash] Add the obligatory splash screen on startup

Also fix a bunch of asyncio nonsense going on.
master
Daniel Edgecumbe 7 years ago
parent a4dea8dcdd
commit 0709fb00e2

@ -131,7 +131,7 @@ class BlockView(object):
self._window_size = MIN_WINDOW_SIZE
def _draw(self, block, bestblockhash):
async def _draw(self, block, bestblockhash):
# TODO: figure out window width etc.
if self._pad is not None:
@ -170,9 +170,9 @@ class BlockView(object):
self._pad.addstr(4, 31, "Root {}".format(block["merkleroot"]), CBOLD)
self._draw_pad_to_screen()
await self._draw_pad_to_screen()
def _draw_pad_to_screen(self):
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 8 or maxx < 3:
return # Can't do it
@ -244,7 +244,7 @@ class BlockView(object):
block = await self._blockstore.get_block(self._hash)
bestblockhash = await self._blockstore.get_bestblockhash()
self._draw(block, bestblockhash)
await self._draw(block, bestblockhash)
async def on_bestblockhash(self, key, obj):
try:

@ -16,7 +16,9 @@ class FooterView(object):
self._window_size = MIN_WINDOW_SIZE
def draw(self):
self._visible = False
async def _draw(self):
# TODO: figure out window width etc.
if self._pad is None:
self._pad = curses.newpad(2, 100)
@ -41,9 +43,13 @@ class FooterView(object):
if self._dt:
self._pad.addstr(0, 81, self._dt.isoformat(timespec="seconds")[:19], CYELLOW + CBOLD)
self._draw_pad_to_screen()
await self._draw_pad_to_screen()
async def draw(self):
if self._mode is not None and self._mode != "splash":
await self._draw()
def _draw_pad_to_screen(self):
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 5 or maxx < 3:
# Can't do it
@ -56,17 +62,17 @@ class FooterView(object):
return
self._mode = newmode
self.draw()
await self.draw()
async def on_tick(self, dt):
self._dt = dt
self.draw()
await self.draw()
async def on_window_resize(self, y, x):
# At the moment we ignore the x size and limit to 100.
if y > self._window_size[0] and self._pad:
self._pad.clear()
self._draw_pad_to_screen()
await self._draw_pad_to_screen()
self._window_size = (y, x)
self.draw()
await self.draw()

@ -20,6 +20,8 @@ class HeaderView(object):
platform.machine(),
)
self._mode = None
self._subversion = None
self._chain = None
self._connectioncount = None
@ -28,7 +30,9 @@ class HeaderView(object):
self._window_size = MIN_WINDOW_SIZE
def draw(self):
self._visible = False
async def _draw(self):
# TODO: figure out window width etc.
self._pad.clear()
@ -105,9 +109,20 @@ class HeaderView(object):
else:
self._pad.addstr(0, 85, "wallet disabled", CBOLD + CRED)
self._draw_pad_to_screen()
await self._draw_pad_to_screen()
async def draw(self):
if self._mode is not None and self._mode != "splash":
await self._draw()
async def on_mode_change(self, newmode):
if self._mode == newmode:
return
self._mode = newmode
await self.draw()
def _draw_pad_to_screen(self):
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 3 or maxx < 3:
# can't do it
@ -121,7 +136,7 @@ class HeaderView(object):
except KeyError:
pass
self.draw()
await self.draw()
async def on_blockchaininfo(self, key, obj):
try:
@ -129,7 +144,7 @@ class HeaderView(object):
except KeyError:
pass
self.draw()
await self.draw()
async def on_peerinfo(self, key, obj):
try:
@ -137,7 +152,7 @@ class HeaderView(object):
except KeyError:
pass
self.draw()
await self.draw()
async def on_nettotals(self, key, obj):
try:
@ -147,7 +162,7 @@ class HeaderView(object):
except KeyError:
pass
self.draw()
await self.draw()
async def on_walletinfo(self, key, obj):
try:
@ -158,9 +173,9 @@ class HeaderView(object):
except KeyError:
pass
self.draw()
await self.draw()
async def on_window_resize(self, y, x):
# At the moment we ignore the x size and limit to 100.
self._window_size = (y, x)
self.draw()
await self.draw()

@ -9,6 +9,6 @@ VERSION_STRING = "bitcoind-ncurses v0.2.0-dev"
# "tx", "console", "net", "forks",
# ]
MODES = ["monitor", "peers", "block"]
DEFAULT_MODE = "monitor"
# DEFAULT_MODE = "monitor"
MIN_WINDOW_SIZE = (10, 20)

@ -10,13 +10,14 @@ import datetime
import rpc
import interface
import modes
import splash
import header
import footer
import monitor
import peers
import block
from macros import DEFAULT_MODE
# from macros import DEFAULT_MODE
async def keypress_loop(window, callback, resize_callback):
@ -34,7 +35,7 @@ async def keypress_loop(window, callback, resize_callback):
# Unhandled key. Don't care.
pass
first = True
# first = True
while True:
# This is basically spinning which is really annoying.
# TODO: find a way of having async blocking getch/getkey.
@ -42,9 +43,9 @@ async def keypress_loop(window, callback, resize_callback):
key = window.getkey()
except Exception:
# This is bonkers and I don't understand it.
if first:
await callback(DEFAULT_MODE[0]) # hackery!
first = False
# if first:
# await callback(DEFAULT_MODE[0]) # hackery!
# first = False
await asyncio.sleep(0.05)
continue
@ -78,13 +79,18 @@ def initialize():
parser.add_argument("--datadir",
help="path to bitcoin datadir [~/.bitcoin/]",
default=os.path.expanduser("~/.bitcoin/"))
parser.add_argument("--no-splash",
help="whether to disable the splash screen [False]",
action='store_true',
dest="nosplash",
default=False)
args = parser.parse_args()
url = rpc.get_url_from_datadir(args.datadir)
auth = rpc.get_auth_from_datadir(args.datadir)
client = rpc.BitcoinRPCClient(url, auth)
return client
return client, args.nosplash
def check_disablewallet(client):
@ -103,11 +109,15 @@ def check_disablewallet(client):
return False
def create_tasks(client, window):
def create_tasks(client, window, nosplash):
headerview = header.HeaderView()
footerview = footer.FooterView()
modehandler = modes.ModeHandler(footerview.on_mode_change)
modehandler = modes.ModeHandler(
(headerview.on_mode_change, footerview.on_mode_change, ),
)
splashview = splash.SplashView(modehandler.set_mode)
monitorview = monitor.MonitorView(client)
peerview = peers.PeersView()
@ -136,18 +146,14 @@ def create_tasks(client, window):
async def on_window_resize(y, x):
interface.check_min_window_size(y, x)
await splashview.on_window_resize(y, x)
await headerview.on_window_resize(y, x)
await footerview.on_window_resize(y, x)
await monitorview.on_window_resize(y, x)
await peerview.on_window_resize(y, x)
await blockview.on_window_resize(y, x)
# Set the initial window sizes
ty, tx = window.getmaxyx()
loop2 = asyncio.new_event_loop()
loop2.run_until_complete(on_window_resize(ty, tx))
loop2.close()
tasks = [
poll_client(client, "getbestblockhash",
on_bestblockhash, 1.0),
@ -171,7 +177,9 @@ def create_tasks(client, window):
poll_client(client, "uptime",
monitorview.on_uptime, 5.0, params=[10]),
tick(on_tick, 1.0),
keypress_loop(window, modehandler.handle_keypress, on_window_resize)
keypress_loop(window, modehandler.handle_keypress, on_window_resize),
on_window_resize(ty, tx),
splashview.draw(nosplash),
]
if not check_disablewallet(client):
@ -183,13 +191,13 @@ def create_tasks(client, window):
def mainfn():
client = initialize()
client, nosplash = initialize()
try:
window = interface.init_curses()
tasks = create_tasks(client, window)
tasks = create_tasks(client, window, nosplash)
loop = asyncio.get_event_loop()
t = asyncio.gather(*tasks)

@ -6,11 +6,11 @@ from macros import MODES
class ModeHandler(object):
def __init__(self, base_callback):
def __init__(self, base_callbacks):
self._mode = None
self._callbacks = {} # mode -> callback, one per mode.
self._base_callback = base_callback
self._base_callbacks = base_callbacks
self._keypress_handlers = {} # mode -> keypress handler.
@ -39,8 +39,9 @@ class ModeHandler(object):
if cb2 is not None:
await cb2(newmode)
# Base callback (generally FooterView)
await self._base_callback(newmode)
# Base callbacks (FooterView, HeaderView)
for bcb in self._base_callbacks:
await bcb(newmode)
async def set_mode(self, newmode):
if self._mode == newmode:

@ -31,7 +31,7 @@ class MonitorView(object):
self._window_size = MIN_WINDOW_SIZE
def _draw(self):
async def _draw(self):
# TODO: figure out window width etc.
if self._pad is not None:
@ -132,9 +132,9 @@ class MonitorView(object):
if self._uptime:
self._pad.addstr(13, 1, "uptime: {}".format(datetime.timedelta(seconds=self._uptime)))
self._draw_pad_to_screen()
await self._draw_pad_to_screen()
def _draw_pad_to_screen(self):
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 8 or maxx < 3:
return # Can't do it
@ -143,7 +143,8 @@ class MonitorView(object):
async def draw(self):
with await self._lock:
self._draw()
if self._visible:
await self._draw()
async def on_bestblockhash(self, key, obj):
try:
@ -166,7 +167,7 @@ class MonitorView(object):
j = await self._client.request("getrawtransaction", [j["result"]["tx"][0], 1])
self._bestcoinbase = j["result"]
if draw and self._visible:
if draw:
await self.draw()
async def on_mempoolinfo(self, key, obj):
@ -175,8 +176,7 @@ class MonitorView(object):
except KeyError:
return
if self._visible:
await self.draw()
await self.draw()
async def on_estimatesmartfee(self, key, obj):
try:
@ -190,15 +190,13 @@ class MonitorView(object):
except KeyError:
self._estimatesmartfee = None
if self._visible:
await self.draw()
await self.draw()
async def on_tick(self, dt):
with await self._lock:
self._dt = dt
if self._visible:
await self.draw()
await self.draw()
async def on_uptime(self, key, obj):
try:
@ -206,8 +204,7 @@ class MonitorView(object):
except KeyError:
return
if self._visible:
await self.draw()
await self.draw()
async def on_mode_change(self, newmode):
if newmode != "monitor":
@ -220,5 +217,4 @@ class MonitorView(object):
async def on_window_resize(self, y, x):
# At the moment we ignore the x size and limit to 100.
self._window_size = (y, x)
if self._visible:
await self.draw()
await self.draw()

@ -18,7 +18,7 @@ class PeersView(object):
self._window_size = MIN_WINDOW_SIZE
def draw(self):
async def _draw(self):
# TODO: figure out window width etc.
if self._pad is not None:
@ -88,9 +88,13 @@ class PeersView(object):
if 'synced_headers' in peer:
self._pad.addstr(1+index-offset, 93, str(peer['synced_headers']).rjust(7) )
self._draw_pad_to_screen()
await self._draw_pad_to_screen()
def _draw_pad_to_screen(self):
async def draw(self):
if self._visible:
await self._draw()
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < 8 or maxx < 3:
return # Can't do it
@ -103,8 +107,7 @@ class PeersView(object):
except KeyError:
return
if self._visible:
self.draw()
await self.draw()
async def on_mode_change(self, newmode):
if newmode != "peers":
@ -112,10 +115,9 @@ class PeersView(object):
return
self._visible = True
self.draw()
await self.draw()
async def on_window_resize(self, y, x):
# At the moment we ignore the x size and limit to 100.
self._window_size = (y, x)
if self._visible:
await self.draw()
await self.draw()

@ -0,0 +1,86 @@
# Copyright (c) 2014-2017 esotericnonsense (Daniel Edgecumbe)
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://opensource.org/licenses/mit-license.php
import curses
import asyncio
from macros import MIN_WINDOW_SIZE
import time
splash_array = [
" BB BB BB ",
" BB BB BB BBBB BBBB BB BB BB BB ",
" BBBBB BBBB BB BB BB BBB BB BBBBB ",
" BB BB BB BB BB BB BB BB BB BB BB BB ",
" BBB BB BB BB BB BB BB BB BB BB BB BB ",
" BB BBB BB BB BBBB BBBB BB BB BB BBBB ",
" ",
" ---------------------------",
" n c u r s e s ",
" ---------------------------",
]
width = len(splash_array[0])
height = len(splash_array)
class SplashView(object):
def __init__(self, set_mode_callback):
self._set_mode_callback = set_mode_callback # ModeHandler
self._pad = None
self._window_size = MIN_WINDOW_SIZE
async def draw(self, nosplash):
if nosplash:
await self._end_splash(nosplash)
return
if self._pad is not None:
self._pad.clear()
else:
self._pad = curses.newpad(20, 100)
CGREEN = curses.color_pair(1)
CRED = curses.color_pair(3)
CBOLD = curses.A_BOLD
CREVERSE = curses.A_REVERSE
for x in range(len(splash_array[0])):
for y in range(len(splash_array)):
if splash_array[y][x] == "B":
if y < 7:
self._pad.addstr(y+1, x, " ", CGREEN + CREVERSE)
else:
self._pad.addstr(y+1, x, " ", CRED + CREVERSE)
elif splash_array[y][x] != " ":
self._pad.addstr(y+1, x, splash_array[y][x], CRED + CBOLD)
y += 1
await self._draw_pad_to_screen()
time.sleep(0.01)
await asyncio.sleep(0.5)
time.sleep(0.5)
await self._end_splash(nosplash)
async def _end_splash(self, nosplash):
if not nosplash:
self._pad.clear()
await self._draw_pad_to_screen()
await self._set_mode_callback("monitor")
async def _draw_pad_to_screen(self):
maxy, maxx = self._window_size
if maxy < height+1 or maxx < width+1:
return # Can't do it
t = (maxy-height)//2
l = (maxx-width)//2
self._pad.refresh(0, 0, t, l, t+height, l+width)
async def on_window_resize(self, y, x):
# This should prevent the splash from crashing
# if there's a resize during the draw operations.
self._window_size = (y, x)
Loading…
Cancel
Save