0 Replies - 2109 Views - Last Post: 19 November 2018 - 05:31 PM

#1 german-one   User is offline

  • D.I.C Head
  • member icon

Reputation: 22
  • View blog
  • Posts: 57
  • Joined: 05-August 18

MORE, PAUSE, CLS functionality for Windows console applications

Posted 19 November 2018 - 05:31 PM

Recently a PAUSE-like functionality was enquired in this thread:
How to implement "Press any key to exit..."
Well, not every key is an "any" key in terms of the Windows PAUSE command that prompts us to press any key. The _getwch() function is able to react pretty much to the same keys as PAUSE does. But that would not have been worth to create a new topic. I wrote a little library that wraps Windows-specific code in a class. Implemented in another translation unit in order to lock out all of the API stuff from your nice clean C++ code. The header file contains a few comments that tells you what the member functions are for and how to use them. Just add more.h and more.cpp to your project.

more.h
/** \file more.h */

/*
Copyright (c) 2018 Steffen Illhardt

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef WINC_MORE_H_INCLUDED__
#define WINC_MORE_H_INCLUDED__

#include <iostream>
#include <string>
#ifndef _WIN32
# error Windows only!
#endif


/** \namespace winc
* \brief  Windows console wrapping */
namespace winc
{
  /** \class more
  * \brief  emulate the Windows commands MORE, PAUSE and CLS */
  class more
  {
  public:

    /** \brief  Get the reference of the singleton more instance.
    *  \param  prompt  Prompt to be displayed.
    *  \param  key     (optional) Unicode code point of the character that is represented by the key pressed or (0x10000 + scancode) for keys that don't represent a character.
    *                   Any key by default.
    *  \return Reference of the more instance. */
    static more& Instance(const std::string& prompt, const unsigned key = 0u);

    /** \brief  Change prompt and key.
    *  \param  prompt  Prompt to be displayed.
    *  \param  key     (optional) Unicode code point of the character that is represented by the key pressed or (0x10000 + scancode) for keys that don't represent a character.
    *                   Any key by default.
    *  \return void */
    void Mutate(const std::string& prompt, const unsigned key = 0u);

    /** \brief  Suspend the program and prompt the user.
    *           After the key was pressed:
    *           - Overwrite the prompt, set cursor to the beginning of the line and reset the line counter.
    *           - Alternatively clear the window if cls = true (see Cls())
    *  \param  cls  (optional) Clear the window if true. Set to false by default.
    *  \return void */
    void Pause(const bool cls = false);

    /** \brief  Clear the window, set cursor to the upper left corner and reset the line counter.
    *  \return void */
    void Cls();

    /** \brief  Get the value of the line counter.
    *  \return Number of lines already written. */
    unsigned Counter() const;

    /** \brief  Set the value of the line counter.
    *  \param  num  New value for the line conter.
    *  \return void */
    void Counter(const unsigned num);

    /** \brief  Get the number of lines that fit into the window.
    *  \return Number of lines minus one line that is needed for the prompt. */
    unsigned ConsoleHeight();

    friend std::ostream& operator<< (std::ostream& out, more& more_ref);

    ~more() noexcept;

  private:
    unsigned    m_linecnt; // line counter
    std::string m_prompt;  // prompt
    unsigned    m_key;     // Unicode code point or (65536 + scancode)
    void*       m_conout;  // output handle of the console window (HANDLE and void* are compatible types)

    more(const std::string& prompt, const unsigned key); // constructor is private (only one console window and thus, only one class instance).

    // Delete the copy constructor and the assignment operator to make sure only one instance exists at runtime.
    more(const more&) = delete;
    more& operator= (const more&) = delete;
  };

  /** \brief  Overload of the ostream << operator, use the more instance to wrap the line (instead of '\n' or std::endl).
  *           Wrap the line, flush the output buffer, increment the line counter, if the window height is reached then prompt the user.
  *  \param  out       An ostream reference (cout).
  *  \param  more_ref  Reference of the singleton more instance.
  *  \return out */
  std::ostream& operator<< (std::ostream& out, more& more_ref);
}
#endif // WINC_MORE_H_INCLUDED__



more.cpp
/* more.cpp */

/*
Copyright (c) 2018 Steffen Illhardt

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "more.h"
#include <stdexcept>
#include <conio.h>

#ifdef _WIN32_WINNT
# undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x501
#define WIN32_LEAN_AND_MEAN
#define NOUSER
#define NOSERVICE
#define NOMCX
#define NOIME
#define NOKERNEL
#ifndef NOMINMAX
# define NOMINMAX
#endif
#include <windows.h>

winc::more::more(const std::string& prompt, const unsigned key)
  : m_linecnt{}, m_prompt{ prompt }, m_key{ key }, m_conout{ GetStdHandle(STD_OUTPUT_HANDLE) }
{
  if (m_conout == INVALID_HANDLE_VALUE)
    throw std::runtime_error{ "invalid output handle" };
}

winc::more::~more() noexcept
{
}

winc::more& winc::more::Instance(const std::string& prompt, const unsigned key)
{
  static more singleton{ prompt, key };
  return singleton;
}

void winc::more::Mutate(const std::string& prompt, const unsigned key)
{
  m_prompt = prompt;
  m_key = key;
}

void winc::more::Pause(const bool cls)
{
  CONSOLE_SCREEN_BUFFER_INFO csbi{};
  if (!cls)
    GetConsoleScreenBufferInfo(m_conout, &csbi);

  std::cout << m_prompt << std::flush;
  unsigned ch{};
  do
  {
    if ((ch = _getwch()) == 0u || ch == 224u)
      ch = 0x10000u + _getwch();
  } while (m_key != 0u && ch != m_key);

  if (cls)
    Cls();
  else
  {
    DWORD written{};
    FillConsoleOutputCharacterA(m_conout, ' ', static_cast<DWORD>(m_prompt.length()), csbi.dwCursorPosition, &written);
    SetConsoleCursorPosition(m_conout, csbi.dwCursorPosition);
    m_linecnt = 0u;
  }
}

void winc::more::Cls()
{
  const COORD coord_zero{};
  CONSOLE_SCREEN_BUFFER_INFO csbi{};
  DWORD scrn_size{}, written{};

  GetConsoleScreenBufferInfo(m_conout, &csbi);
  FillConsoleOutputCharacterA(m_conout, ' ', (scrn_size = csbi.dwSize.X * csbi.dwSize.Y), coord_zero, &written);
  FillConsoleOutputAttribute(m_conout, csbi.wAttributes, scrn_size, coord_zero, &written);
  SetConsoleCursorPosition(m_conout, coord_zero);

  m_linecnt = 0u;
}

unsigned winc::more::Counter() const
{
  return m_linecnt;
}

void winc::more::Counter(const unsigned num)
{
  m_linecnt = num;
}

unsigned winc::more::ConsoleHeight()
{
  CONSOLE_SCREEN_BUFFER_INFO csbi{};
  GetConsoleScreenBufferInfo(m_conout, &csbi);

  return csbi.srwindow.Bottom - csbi.srwindow.Top;
}

namespace winc
{
  std::ostream& operator<< (std::ostream& out, more& more_ref)
  {
    out << '\n' << std::flush; // For whatever reason 64 bit MinGW builts won't work using std::endl at that point.

    if (++more_ref.m_linecnt >= more_ref.ConsoleHeight())
      more_ref.Pause();

    return out;
  }
}



You want to see a little example how to use it? Sure.
#include <iostream>
#include "more.h"

int main()
{
  winc::more& more_ref = winc::more::Instance("--- Hit the space bar to show more --- ", ' ');

  for (unsigned i = 1u; i < 111u; ++i)
    std::cout << i << " - " << i * i << more_ref;

  more_ref.Mutate("Press any key to continue . . . ");
  more_ref.Pause(true);

  for (unsigned i = 1u; i < 111u; ++i)
    std::cout << i << more_ref << " - " << i * i << more_ref;

  more_ref.Pause();

  return 0;
}



Is This A Good Question/Topic? 0
  • +

Page 1 of 1