DUDS
Distributed Update of Data from Something
TextDisplayStream.hpp
Go to the documentation of this file.
1 /*
2  * This file is part of the DUDS project. It is subject to the BSD-style
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at https://github.com/jjackowski/duds/blob/master/LICENSE.
5  * No part of DUDS, including this file, may be copied, modified, propagated,
6  * or distributed except according to the terms contained in the LICENSE file.
7  *
8  * Copyright (C) 2017 Jeff Jackowski
9  */
16 #include <iostream>
17 #include <cstring>
18 
19 namespace duds { namespace hardware { namespace display {
20 
32 template <class Char, class Traits = std::char_traits<Char> >
33 class TextDisplayBasicStreambuf : public std::basic_streambuf<Char, Traits> {
37  std::shared_ptr<TextDisplay> disp;
38 protected:
42  virtual typename Traits::int_type overflow(
43  typename Traits::int_type c = Traits::eof()
44  ) {
45  // nowhere to go; return an error
46  if (!disp) {
47  return Traits::eof();
48  }
49  // there is no end to the data the display can take
50  if (c == Traits::eof()) {
51  return Traits::not_eof(c);
52  }
53  switch (c) {
54  // carriage return
55  case '\r':
56  disp->move(0, disp->rowPos());
57  break;
58  // new line
59  case '\n':
60  disp->clearTo(disp->columns() - 1, disp->rowPos());
61  break;
62  // something else
63  default:
64  // write out the character
65  disp->write((int)c);
66  }
67  return c;
68  }
69 public:
75  TextDisplayBasicStreambuf(const std::shared_ptr<TextDisplay> &d) : disp(d) {
76  assert(d);
77  }
81  const std::shared_ptr<TextDisplay> &display() const {
82  return disp;
83  }
87  virtual unsigned int column() const {
88  return disp->columnPos();
89  }
93  virtual unsigned int row() const {
94  return disp->rowPos();
95  }
103  virtual void moveCursor(unsigned int c, unsigned int r) {
104  disp->move(c, r);
105  }
110  virtual void clearDisplay() {
111  disp->clear();
112  }
122  virtual void clearTo(unsigned int c, unsigned int r) {
123  disp->clearTo(c, r);
124  }
130  virtual void startLine() {
131  TextDisplay *td = disp.get();
132  if (td) {
133  if (td->columnPos() > 0) {
134  td->clearTo(td->columns() - 1, td->rowPos());
135  }
136  }
137  }
138 };
139 
154 template <class Char, class Traits = std::char_traits<Char> >
156 public TextDisplayBasicStreambuf<Char, Traits> {
161  std::vector<Char> shown;
165  std::vector<Char> update;
169  std::vector<Char> working;
180  std::uint8_t columnsize;
187  std::uint8_t rowsize;
191  std::uint8_t cpos;
195  std::uint8_t rpos;
199  void write(Char c) {
200  // write out the character
201  *base::pptr() = c;
202  // advance to the next character
203  if (++cpos >= columnsize) {
204  cpos = 0;
205  if (++rpos >= rowsize) {
206  rpos = 0;
207  // reposition to start of buffer
208  base::setp(base::pbase(), base::epptr());
209  return;
210  }
211  }
212  base::pbump(1);
213  }
222  void clearToImpl(unsigned int c, unsigned int r) {
223  while ((cpos != c) || (rpos != r)) {
224  write(' ');
225  }
226  write(' ');
227  }
232  void bufWrite(Char c) {
233  // no locking; must already be locked
234  switch (c) {
235  // carriage return
236  case '\r':
237  base::pbump(-(int)cpos);
238  cpos = 0;
239  break;
240  // new line
241  case '\n':
242  clearToImpl(columnsize - 1, rpos);
243  break;
244  // something else
245  default:
246  // write out the character
247  write((int)c);
248  }
249  }
250 protected:
255  virtual typename Traits::int_type overflow(
256  typename Traits::int_type c = Traits::eof()
257  ) {
258  // ignore EOF
259  if (c != Traits::eof()) {
260  duds::general::SpinLockGuard lock(wblock);
261  bufWrite((Char)c);
262  }
263  return Traits::not_eof(c);
264  }
265  virtual std::streamsize xsputn(const Char* s, std::streamsize count) {
266  duds::general::SpinLockGuard lock(wblock);
267  for (std::streamsize cnt = count; cnt > 0; ++s, --cnt) {
268  bufWrite(*s);
269  }
270  return count;
271  }
272  virtual int sync() {
273  // copy data to write to the display; allows another thread to keep
274  { // writing more text
275  duds::general::SpinLockGuard lock(wblock);
276  update = working;
277  }
278  // compare update and shown buffer to output what is needed
279  typename std::vector<Char>::iterator siter = shown.begin();
280  typename std::vector<Char>::iterator uiter = update.begin();
281  bool cont = false; // contiguous flag
282  for (int r = 0; r < rowsize; ++r) {
283  for (int c = 0; c < columnsize; ++siter, ++uiter, ++c) {
284  // difference?
285  if (*siter != *uiter) {
286  // non-contiguous?
287  if (!cont) {
288  // reposition cursor
289  base::display()->move(c, r);
290  cont = true;
291  }
292  assert(base::display()->columnPos() == c);
293  assert(base::display()->rowPos() == r);
294  // write data
295  base::display()->write((int)*uiter);
296  // update shown buffer
297  *siter = *uiter;
298  } else {
299  // no longer contiguos
300  cont = false;
301  }
302  }
303  }
304  return 0; // success code
305  }
306  virtual typename base::pos_type seekoff(
307  typename base::off_type off,
308  std::ios_base::seekdir dir,
309  std::ios_base::openmode which = std::ios_base::out
310  ) {
311  // only output seeking is supported
312  if (which & std::ios_base::out) {
313  duds::general::SpinLockGuard lock(wblock);
314  // figure out new absolute position
315  typename base::pos_type cp = base::pptr() - base::pbase();
316  typename base::pos_type op;
317  switch (dir) {
318  case std::ios_base::beg:
319  op = off;
320  break;
321  case std::ios_base::cur:
322  op = cp + off;
323  break;
324  case std::ios_base::end:
325  op = working.size() + off;
326  break;
327  default:
328  return typename base::pos_type(-1);
329  }
330  // do not seek out of bounds
331  if ((op > 0) && (op < working.size())) {
332  // reposition to start
333  base::setp(base::pbase(), base::epptr());
334  // seek to requested position
335  base::pbump(op);
336  // compute row & column positions
337  rpos = op / columnsize;
338  cpos = op % columnsize;
339  return op;
340  }
341  }
342  return typename base::pos_type(-1);
343  }
344  virtual typename base::pos_type seekpos(
345  typename base::pos_type pos,
346  std::ios_base::openmode which = std::ios_base::out
347  ) {
348  return seekoff(typename base::off_type(pos), std::ios_base::beg, which);
349  }
350 public:
356  TextDisplayBasicBufferedStreambuf(const std::shared_ptr<TextDisplay> &d) :
357  TextDisplayBasicStreambuf<Char, Traits>(d),
358  columnsize(d->columns()),
359  rowsize(d->rows()),
360  cpos(0),
361  rpos(0),
362  shown(d->columns() * d->rows(), ' '),
363  update(d->columns() * d->rows(), ' '),
364  working(d->columns() * d->rows(), ' ') {
365  base::setp(&(working[0]), &(working[0]) + (columnsize * rowsize));
366  }
367  virtual unsigned int column() const {
368  return cpos;
369  }
370  virtual unsigned int row() const {
371  return rpos;
372  }
373  virtual void moveCursor(unsigned int c, unsigned int r) {
374  // range check
375  if ((c >= columnsize) || (r >= rowsize)) {
378  TextDisplaySizeInfo(Info_DisplayColRow(columnsize, rowsize))
379  );
380  }
381  duds::general::SpinLockGuard lock(wblock);
382  base::setp(base::pbase(), base::epptr());
383  cpos = c;
384  rpos = r;
385  base::pbump(r * columnsize + c);
386  }
387  virtual void clearDisplay() {
388  duds::general::SpinLockGuard lock(wblock);
389  // make the work-in-progress all spaces
390  std::memset(&(working[0]), ' ', working.size());
391  // reposition to start of buffer
392  base::setp(base::pbase(), base::epptr());
393  cpos = rpos = 0;
394  }
395  virtual void clearTo(unsigned int c, unsigned int r) {
396  // range check
397  if ((c >= columnsize) || (r >= rowsize)) {
400  TextDisplaySizeInfo(Info_DisplayColRow(columnsize, rowsize))
401  );
402  }
403  duds::general::SpinLockGuard lock(wblock);
404  clearToImpl(c, r);
405  }
406  virtual void startLine() {
407  duds::general::SpinLockGuard lock(wblock);
408  if (cpos > 0) {
409  clearToImpl(columnsize - 1, rpos);
410  }
411  }
412 };
413 
423 template <class Char = char, class Traits = std::char_traits<Char> >
424 class TextDisplayBaseStream : public std::basic_ostream<Char, Traits> {
425  using base = std::basic_ostream<Char, Traits>;
434  static const int xidx;
435 public:
442  tdbb(tbuf) {
443  base::init(tdbb);
444  this->pword(xidx) = this;
445  }
449  static int xallocIndex() {
450  return xidx;
451  }
455  const std::shared_ptr<TextDisplay> &display() const {
456  return tdbb->display();
457  }
465  void moveCursor(unsigned int c, unsigned int r) {
466  tdbb->moveCursor(c, r);
467  }
472  void clearDisplay() {
473  tdbb->clearDisplay();
474  }
484  void clearTo(unsigned int c, unsigned int r) {
485  tdbb->clearTo(c, r);
486  }
492  void startLine() {
493  tdbb->startLine();
494  }
495 };
496 
497 
498 // convineince classes; could use TextDisplayBasicBuffer or
499 // TextDisplayStoredBuffer without them
507 template <class Char, class Traits = std::char_traits<Char> >
508 class TextDisplayBasicStream : public TextDisplayBaseStream<Char, Traits> {
513 public:
519  TextDisplayBasicStream(const std::shared_ptr<TextDisplay> &d) :
520  buff(d), TextDisplayBaseStream<Char, Traits>(&buff) { }
521 };
522 
541 template <class Char, class Traits = std::char_traits<Char> >
547 public:
554  TextDisplayBasicBufferedStream(const std::shared_ptr<TextDisplay> &d) :
555  buff(d), TextDisplayBaseStream<Char, Traits>(&buff) { }
556 };
557 
562 template <class Char, class Traits>
563 const int TextDisplayBaseStream<Char, Traits>::xidx = std::ios_base::xalloc();
564 
571 template <class Char, class Traits>
572 std::basic_ostream<Char, Traits> &clear(std::basic_ostream<Char, Traits> &os) {
573  if (os.pword(TextDisplayBaseStream<Char, Traits>::xallocIndex()) == &os) {
575  }
576  return os;
577 }
578 
584 template <class Char, class Traits>
585 std::basic_ostream<Char, Traits> &startLine(std::basic_ostream<Char, Traits> &os) {
586  if (os.pword(TextDisplayBaseStream<Char, Traits>::xallocIndex()) == &os) {
588  }
589  return os;
590 }
591 
597 struct move_impl {
598  unsigned int col;
599  unsigned int row;
600  move_impl(unsigned int c, unsigned int r) : col(c), row(r) { }
601  template <class Char, class Traits>
602  friend std::basic_ostream<Char, Traits> &operator << (
603  std::basic_ostream<Char, Traits> &os, const move_impl &mi
604  ) {
605  if (os.pword(TextDisplayBaseStream<Char, Traits>::xallocIndex()) == &os) {
607  mi.col, mi.row
608  );
609  }
610  return os;
611  }
612 };
613 
623 inline move_impl move(unsigned int c, unsigned int r) {
624  return move_impl(c, r);
625 }
626 
632 struct clearTo_impl {
633  unsigned int col;
634  unsigned int row;
635  clearTo_impl(unsigned int c, unsigned int r) : col(c), row(r) { }
636  template <class Char, class Traits>
637  friend std::basic_ostream<Char, Traits> &operator << (
638  std::basic_ostream<Char, Traits> &os, const clearTo_impl &mi
639  ) {
640  if (os.pword(TextDisplayBaseStream<Char, Traits>::xallocIndex()) == &os) {
642  mi.col, mi.row
643  );
644  }
645  return os;
646  }
647 };
648 
659 inline clearTo_impl clearTo(unsigned int c, unsigned int r) {
660  return clearTo_impl(c, r);
661 }
662 
679 
680 } } }
Part of the move() display stream manipulator.
boost::error_info< struct Info_DisplayPosition, Info_DisplayColRow > TextDisplayPositionInfo
Column and row of a display position as part of an error.
TextDisplayBasicBufferedStreambuf< char > TextDisplayBufferedStreambuf
Most common type for the TextDisplayBasicBufferedStreambuf.
void startLine()
Moves the cursor to the start of a line clearing text along the way.
const std::shared_ptr< TextDisplay > & display() const
Returns the output display.
An output stream for buffering writes to TextDisplay objects.
virtual unsigned int row() const
Returns the cursos&#39;s row position.
virtual unsigned int row() const
Returns the cursos&#39;s row position.
unsigned int columnPos() const
The current column position of the cursor.
static int xallocIndex()
Returns the index from xalloc(); needed by the stream manipulators.
The specified location is beyond the bounds of the display.
Part of the clearTo() display stream manipulator.
static const int xidx
The value from xalloc() used for stream manipulators to identify this stream type.
std::vector< Char > update
The buffer that is being written to the display.
void clearTo(unsigned int c, unsigned int r)
Clear text from the current cursor position to the given position, inclusive.
virtual void startLine()
Moves the cursor to the start of a line clearing text along the way.
TextDisplayBasicStream< char > TextDisplayStream
Most common type for the TextDisplayBasicStream.
move_impl move(unsigned int c, unsigned int r)
Display stream manipulator that moves the display cursor to the given location.
virtual Traits::int_type overflow(typename Traits::int_type c=Traits::eof())
Writes a character to the start of the display after wrapping around from the end.
virtual unsigned int column() const
Returns the cursos&#39;s column position.
virtual void startLine()
Moves the cursor to the start of a line clearing text along the way.
TextDisplayBaseStream(TextDisplayBasicStreambuf< Char, Traits > *tbuf)
Makes an output stream that writes to the given display.
move_impl(unsigned int c, unsigned int r)
virtual void clearTo(unsigned int c, unsigned int r)
Clear text from the current cursor position to the given position, inclusive.
TextDisplayBasicStreambuf(const std::shared_ptr< TextDisplay > &d)
Makes the stream buffer with a display for output.
unsigned int columns() const
Returns the number of columns on the display.
std::uint8_t columnsize
Number of columns on the display.
virtual unsigned int column() const
Returns the cursos&#39;s column position.
virtual void moveCursor(unsigned int c, unsigned int r)
Moves the display&#39;s cursor to the given location.
virtual void clearTo(unsigned int c, unsigned int r)
Clear text from the current cursor position to the given position, inclusive.
Stores column and row data for display errors.
The base class for output streams that write to TextDisplay objects.
TextDisplayBasicStreambuf< char > TextDisplayStreambuf
Most common type for the TextDisplayBasicStreambuf.
std::vector< Char > shown
The buffer currently shown by the display.
std::basic_ostream< Char, Traits > & clear(std::basic_ostream< Char, Traits > &os)
Display stream manipulator that clears all text from the display and places the cursor in the upper l...
void moveCursor(unsigned int c, unsigned int r)
Moves the display&#39;s cursor to the given location.
TextDisplayBasicStream(const std::shared_ptr< TextDisplay > &d)
Makes an output stream that immediately writes to the given display.
TextDisplayBasicStreambuf< Char, Traits > * tdbb
The buffer handling the output.
Writes output from a stream into an internal buffer, and writes that buffer to the display when sync(...
virtual base::pos_type seekpos(typename base::pos_type pos, std::ios_base::openmode which=std::ios_base::out)
A simple spinlock following the lockable and timed lockable concepts so that it can be used with std:...
Definition: Spinlock.hpp:52
ConversationVector & operator<<(ConversationVector &cv, const Int &i)
Insertion operator to add an integer to a ConversationVector object.
virtual Traits::int_type overflow(typename Traits::int_type c=Traits::eof())
Writes a character to the display.
TextDisplayBasicBufferedStream< char > TextDisplayBufferedStream
Most common type for the TextDisplayBasicBufferedStream.
virtual void moveCursor(unsigned int c, unsigned int r)
Moves the display&#39;s cursor to the given location.
std::shared_ptr< TextDisplay > disp
The display that will receive the output.
void clearToImpl(unsigned int c, unsigned int r)
Clears a portion of the buffer with spaces from the current cursor position to the indicated position...
TextDisplayBasicBufferedStreambuf(const std::shared_ptr< TextDisplay > &d)
Makes the stream buffer with a display for output.
void clearDisplay()
Remove all text from the display and place the cursor in the upper left corner.
TextDisplayBasicStreambuf< Char, Traits > buff
The buffer handling the output.
virtual void clearDisplay()
Remove all text from the display and place the cursor in the upper left corner.
void bufWrite(Char c)
Handles writing a character into the buffer or moving the cursor for new lines and carriage returns...
clearTo_impl(unsigned int c, unsigned int r)
TextDisplayBasicBufferedStream(const std::shared_ptr< TextDisplay > &d)
Makes an output stream that writes to a buffer, and writes that buffer to the given display when flus...
Moves output from an output stream to a TextDisplay one character at a time.
virtual void clearDisplay()
Remove all text from the display and place the cursor in the upper left corner.
std::uint8_t rowsize
Number of rows on the display.
An output stream for immediately writing data to TextDisplay objects.
std::vector< Char > working
The buffer that accepts new data.
virtual base::pos_type seekoff(typename base::off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which=std::ios_base::out)
void write(Char c)
Internal function to write a printable character to the buffer.
A fairly generic interface to a character based display that lacks color.
Definition: TextDisplay.hpp:31
TextDisplayBasicBufferedStreambuf< Char, Traits > buff
The buffer handling the output.
const std::shared_ptr< TextDisplay > & display() const
Returns the output display.
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48
void clearTo(unsigned int c, unsigned int r)
Clear text from the current cursor position to the given position, inclusive.
unsigned int rowPos() const
The current row position of the cursor.
std::lock_guard< duds::general::Spinlock > SpinLockGuard
A convenience typedef for a std::lock_guard using the Spinlock object.
Definition: Spinlock.hpp:216
virtual std::streamsize xsputn(const Char *s, std::streamsize count)
boost::error_info< struct Info_DisplaySize, Info_DisplayColRow > TextDisplaySizeInfo
Column and row size of a display as part of an error.