Skip to content

Commit

Permalink
screen_utils: convert screen_getch() to awaitable
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxKellermann committed Sep 11, 2024
1 parent a8f90d3 commit 37b17be
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 78 deletions.
21 changes: 11 additions & 10 deletions src/KeyDefPage.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
#include "ConfigFile.hxx"
#include "Bindings.hxx"
#include "GlobalBindings.hxx"
#include "screen_utils.hxx"
#include "Options.hxx"
#include "page/ListPage.hxx"
#include "page/ProxyPage.hxx"
#include "dialogs/KeyDialog.hxx"
#include "ui/Bell.hxx"
#include "ui/ListText.hxx"
#include "ui/TextListRenderer.hxx"
Expand Down Expand Up @@ -99,7 +99,8 @@ class CommandKeysPage final : public ListPage, ListText {
/**
* Assigns a new key to a key slot.
*/
void OverwriteKey(int key_index);
[[nodiscard]]
Co::InvokeTask OverwriteKey(int key_index) noexcept;

/**
* Assign a new key to a new slot.
Expand Down Expand Up @@ -153,23 +154,23 @@ CommandKeysPage::DeleteKey(int key_index)
bindings->Check(nullptr, 0);
}

void
CommandKeysPage::OverwriteKey(int key_index)
Co::InvokeTask
CommandKeysPage::OverwriteKey(int key_index) noexcept
{
assert(key_index < MAX_COMMAND_KEYS);

const auto prompt = fmt::format(fmt::runtime(_("Enter new key for {}")),
get_key_command_name(Command(subcmd)));
const int key = screen_getch(screen, prompt.c_str());
const int key = co_await KeyDialog{screen, prompt};

if (key == ERR) {
Alert(_("Aborted"));
return;
co_return;
}

if (key == '\0') {
Alert(_("Ctrl-Space can't be used"));
return;
co_return;
}

const Command cmd = bindings->FindKey(key);
Expand All @@ -178,7 +179,7 @@ CommandKeysPage::OverwriteKey(int key_index)
GetLocalizedKeyName(key),
get_key_command_name(cmd));
Bell();
return;
co_return;
}

binding->keys[key_index] = key;
Expand All @@ -200,7 +201,7 @@ void
CommandKeysPage::AddKey()
{
if (n_keys < MAX_COMMAND_KEYS)
OverwriteKey(n_keys);
CoStart(OverwriteKey(n_keys));
}

std::string_view
Expand Down Expand Up @@ -258,7 +259,7 @@ CommandKeysPage::OnCommand(struct mpdclient &c, Command cmd)
} else {
/* just to be sure ;-) */
assert(IsKeyPosition(lw.GetCursorIndex()));
OverwriteKey(PositionToKeyIndex(lw.GetCursorIndex()));
CoStart(OverwriteKey(PositionToKeyIndex(lw.GetCursorIndex())));
}
return true;
case Command::DELETE:
Expand Down
62 changes: 62 additions & 0 deletions src/dialogs/KeyDialog.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project

#include "KeyDialog.hxx"
#include "ui/Keys.hxx"
#include "ui/Options.hxx"
#include "ui/Window.hxx"
#include "Styles.hxx"

using std::string_view_literals::operator""sv;

void
KeyDialog::OnLeave(const Window window) noexcept
{
curs_set(0);

if (ui_options.enable_colors)
window.SetBackgroundStyle(Style::STATUS);
}

void
KeyDialog::OnCancel() noexcept
{
SetResult(-1);
}

void
KeyDialog::Paint(const Window window) const noexcept
{
if (ui_options.enable_colors)
window.SetBackgroundStyle(Style::INPUT);

SelectStyle(window, Style::STATUS_ALERT);
window.String({0, 0}, prompt);
window.String(": "sv);

SelectStyle(window, Style::INPUT);
window.ClearToEol();

curs_set(1);
}

static constexpr bool
IsCancelKey(int key) noexcept
{
return key == KEY_CANCEL || key == KEY_SCANCEL ||
key == KEY_CLOSE ||
key == KEY_CTL('C') ||
key == KEY_CTL('G') ||
key == KEY_ESCAPE;
}

bool
KeyDialog::OnKey(Window, int key)
{
if (IsCancelKey(key))
Cancel();
else
SetResult(key);

return true;
}
77 changes: 77 additions & 0 deletions src/dialogs/KeyDialog.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project

#pragma once

#include "ModalDialog.hxx"
#include "co/AwaitableHelper.hxx"

#include <cstdint>
#include <string_view>

/**
* A #ModalDialog that asks the user to press a key.
*
* This dialog is supposed to be awaited from a coroutine using
* co_await. It suspends the caller while waiting for user input.
*/
class KeyDialog final : public ModalDialog {
const std::string_view prompt;

std::coroutine_handle<> continuation;

int result = -2;

using Awaitable = Co::AwaitableHelper<KeyDialog, false>;
friend Awaitable;

public:
/**
* @param _prompt the human-readable prompt to be displayed
* (including question mark if desired); the pointed-by memory
* is owned by the caller and must remain valid during the
* lifetime of this dialog
*/
KeyDialog(ModalDock &_dock, std::string_view _prompt) noexcept
:ModalDialog(_dock), prompt(_prompt)
{
Show();
}

~KeyDialog() noexcept {
Hide();
}

/**
* Await completion of this dialog.
*
* @return an ncurses key code or -1 if the dialog was
* canceled
*/
Awaitable operator co_await() noexcept {
return *this;
}

private:
void SetResult(int _result) noexcept {
result = _result;

if (continuation)
continuation.resume();
}

bool IsReady() const noexcept {
return result != -2;
}

int TakeValue() noexcept {
return result;
}

public:
/* virtual methodds from Modal */
void OnLeave(Window window) noexcept override;
void OnCancel() noexcept override;
void Paint(Window window) const noexcept override;
bool OnKey(Window window, int key) override;
};
1 change: 1 addition & 0 deletions src/dialogs/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ dialogs = static_library(
'dialogs',
'ModalDialog.cxx',
'YesNoDialog.cxx',
'KeyDialog.cxx',
'TextInputDialog.cxx',
include_directories: inc,
dependencies: [
Expand Down
64 changes: 0 additions & 64 deletions src/screen_utils.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -4,75 +4,11 @@
#include "screen_utils.hxx"
#include "screen.hxx"
#include "Styles.hxx"
#include "ui/Options.hxx"
#include "util/ScopeExit.hxx"
#include "config.h"

#ifndef _WIN32
#include "WaitUserInput.hxx"
#include <cerrno>
#endif

#include <string.h>

using std::string_view_literals::operator""sv;

static constexpr bool
ignore_key(int key) noexcept
{
return
#ifdef HAVE_GETMOUSE
/* ignore mouse events */
key == KEY_MOUSE ||
#endif
key == ERR;
}

int
screen_getch(ScreenManager &screen, const char *prompt) noexcept
{
const auto &window = screen.status_bar.GetWindow();

if (ui_options.enable_colors)
window.SetBackgroundStyle(Style::INPUT);

AtScopeExit(&window) {
if (ui_options.enable_colors)
window.SetBackgroundStyle(Style::STATUS);
};

SelectStyle(window, Style::STATUS_ALERT);
window.String({0, 0}, prompt);
window.String(": "sv);
window.ClearToEol();

echo();
curs_set(1);

#ifndef _WIN32
WaitUserInput wui;
#endif

int key;
do {
key = window.GetChar();

#ifndef _WIN32
if (key == ERR && errno == EAGAIN) {
if (wui.Wait())
continue;
else
break;
}
#endif
} while (ignore_key(key));

noecho();
curs_set(0);

return key;
}

static const char *
CompletionDisplayString(const char *value) noexcept
{
Expand Down
4 changes: 0 additions & 4 deletions src/screen_utils.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@

class ScreenManager;

/* read a character from the status window */
int
screen_getch(ScreenManager &screen, const char *prompt) noexcept;

void
screen_display_completion_list(ScreenManager &screen,
Completion::Range range) noexcept;
Expand Down

0 comments on commit 37b17be

Please sign in to comment.