summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBardur Arantsson <bardur@scientician.net>2019-02-15 19:20:26 +0100
committerBardur Arantsson <bardur@scientician.net>2019-02-15 19:20:26 +0100
commit83032ffafaf3eb1fb6cd9408a7af479b5f5a5f78 (patch)
treec999bb1c000e2a4ab459c90654364e7cb4b15e08
parent231f73171c21acf419d93cf0c505028bd5f223ca (diff)
Change from ui_hooks_t to a pure virtual interface Frontend
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/frontend.cc1
-rw-r--r--src/frontend.hpp112
-rw-r--r--src/frontend_fwd.hpp3
-rw-r--r--src/main-gcu.cc349
-rw-r--r--src/main-gtk2.cc294
-rw-r--r--src/main-x11.cc269
-rw-r--r--src/modules.cc2
-rw-r--r--src/z-term.cc170
-rw-r--r--src/z-term.hpp73
10 files changed, 625 insertions, 649 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2aa2f7c5..6c9ca9d6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -26,6 +26,7 @@ SET(SRCS_COMMON
dice.cc
dungeon.cc
files.cc
+ frontend.cc
format_ext.cc
game.cc
game_edit_data.cc
diff --git a/src/frontend.cc b/src/frontend.cc
new file mode 100644
index 00000000..2194ff21
--- /dev/null
+++ b/src/frontend.cc
@@ -0,0 +1 @@
+#include "frontend.hpp"
diff --git a/src/frontend.hpp b/src/frontend.hpp
new file mode 100644
index 00000000..7a2d95aa
--- /dev/null
+++ b/src/frontend.hpp
@@ -0,0 +1,112 @@
+#pragma once
+
+#include "frontend_fwd.hpp"
+
+#include <string_view>
+
+#include "h-basic.hpp"
+
+/**
+ * User interface operations supported by the front end.
+ */
+class Frontend {
+
+public:
+ /**
+ * Result polling/waiting for an event.
+ */
+ enum class event_result_t {
+ SUCCESS,
+ FAILURE,
+ };
+
+ /**
+ * Initialize the underlying user interface element.
+ */
+ virtual void init() = 0;
+
+ /**
+ * Destroy the underlying user interface element.
+ */
+ virtual void nuke() = 0;
+
+ /**
+ * Poll or wait for event and process it.
+ */
+ virtual void process_event(bool wait) = 0;
+
+ /**
+ * Does this user interface draw its own cursor?
+ *
+ * The return value is assumed to be constant over the
+ * lifetime of the user interface.
+ */
+ virtual bool soft_cursor() const = 0;
+
+ /**
+ * Does this user interface have an "icky" lower right corner,
+ * i.e. does it _not_ support putting the cursor in that corner?
+ *
+ * The return value is assumed to be constant over the
+ * lifetime of the user interface.
+ */
+ virtual bool icky_corner() const = 0;
+
+ /**
+ * Flush any pending events.
+ */
+ virtual void flush_events() = 0;
+
+ /**
+ * Process any pending events.
+ */
+ virtual void process_queued_events() = 0;
+
+ /**
+ * Clear contents of the user interface.
+ */
+ virtual void clear() = 0;
+
+ /**
+ * Flush pending output.
+ */
+ virtual void flush_output() = 0;
+
+ /**
+ * Make a noise, e.g. a system beep or similar.
+ */
+ virtual void noise() = 0;
+
+ /**
+ * React to changes generated by the game, e.g. window
+ * size, or colors.
+ */
+ virtual void react() = 0;
+
+ /**
+ * Signal the activation/deactivation of the user interface.
+ */
+ virtual void activate_deactivate(bool resume) = 0;
+
+ /**
+ * Rename the main window.
+ */
+ virtual void rename_main_window(std::string_view) = 0;
+
+ /**
+ * Draw the cursor at position x, y.
+ */
+ virtual void draw_cursor(int x, int y) = 0;
+
+ /**
+ * Draw the given text string s at position x, y
+ * using attributes a.
+ */
+ virtual void draw_text(int x, int y, int n, byte a, const char *s) = 0;
+
+ /**
+ * Destroy.
+ */
+ virtual ~Frontend() = default;
+
+};
diff --git a/src/frontend_fwd.hpp b/src/frontend_fwd.hpp
new file mode 100644
index 00000000..925cfa5e
--- /dev/null
+++ b/src/frontend_fwd.hpp
@@ -0,0 +1,3 @@
+ #pragma once
+
+class Frontend;
diff --git a/src/main-gcu.cc b/src/main-gcu.cc
index 9fb7724f..bc6b9ab1 100644
--- a/src/main-gcu.cc
+++ b/src/main-gcu.cc
@@ -39,6 +39,7 @@
*/
#include "main.hpp"
+#include "frontend.hpp"
#include "variable.hpp"
#include "z-util.hpp"
@@ -205,12 +206,6 @@ static term_data data[MAX_TERM_DATA];
/*
- * Hack -- Number of initialized "term" structures
- */
-static int active = 0;
-
-
-/*
* Hack -- define "A_BRIGHT" to be "A_BOLD", because on many
* machines, "A_BRIGHT" produces ugly "inverse" video.
*/
@@ -430,219 +425,240 @@ static void keymap_game_prepare()
/*
- * Init the "curses" system
+ * React to changes
*/
-static void Term_init_gcu(void *data)
+static void Term_xtra_gcu_react()
{
- term_data *td = (term_data *) data;
- /* Count init's, handle first */
- if (active++ != 0) return;
-
- /* Erase the window */
- (void)wclear(td->win);
+ int i;
- /* Reset the cursor */
- (void)wmove(td->win, 0, 0);
+ /* Cannot handle color redefinition */
+ if (!can_fix_color)
+ {
+ return;
+ }
- /* Flush changes */
- (void)wrefresh(td->win);
+ /* Set the colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Set one color (note scaling) */
+ init_color(i,
+ angband_color_table[i][1] * 1000 / 255,
+ angband_color_table[i][2] * 1000 / 255,
+ angband_color_table[i][3] * 1000 / 255);
+ }
- /* Game keymap */
- keymap_game();
}
/*
- * Nuke the "curses" system
+ * User Interface for GCU.
*/
-static void Term_nuke_gcu(void *data)
-{
- int x, y;
- term_data *td = (term_data *) data;
+class CursesFrontend final : public Frontend {
- /* Delete this window */
- delwin(td->win);
+ /*
+ * Hack -- Number of initialized "term" structures
+ */
+ static int m_active;
- /* Count nuke's, handle last */
- if (--active != 0) return;
+private:
+ term_data *m_term_data;
- /* Reset colors to defaults */
- start_color();
+ bool event_aux(bool wait)
+ {
+ int i, k;
- /* Get current cursor position */
- getyx(curscr, y, x);
+ char buf[2];
- /* Move the cursor to bottom right corner */
- mvcur(y, x, LINES - 1, 0);
+ /* Wait */
+ if (wait)
+ {
+ /* Wait for one byte */
+ i = read(0, buf, 1);
- /* Flush the curses buffer */
- (void)refresh();
+ /* Hack -- Handle bizarre "errors" */
+ if ((i <= 0) && (errno != EINTR)) abort();
+ }
- /* Exit curses */
- endwin();
+ /* Do not wait */
+ else
+ {
+ /* Get the current flags for stdin */
+ k = fcntl(0, F_GETFL, 0);
- /* Flush the output */
- (void)fflush(stdout);
+ /* Oops */
+ if (k < 0)
+ {
+ return false;
+ }
- /* Normal keymap */
- keymap_norm();
-}
+ /* Tell stdin not to block */
+ if (fcntl(0, F_SETFL, k | O_NDELAY) < 0)
+ {
+ return false;
+ }
+ /* Read one byte, if possible */
+ i = read(0, buf, 1);
-/*
-* Process events (with optional wait)
-*/
-static errr Term_xtra_gcu_event(int v)
-{
- int i, k;
+ /* Replace the flags for stdin */
+ if (fcntl(0, F_SETFL, k))
+ {
+ return false;
+ }
+ }
- char buf[2];
+ /* Ignore "invalid" keys */
+ if ((i != 1) || (!buf[0]))
+ {
+ return false;
+ }
- /* Wait */
- if (v)
- {
- /* Wait for one byte */
- i = read(0, buf, 1);
+ /* Enqueue the keypress */
+ Term_keypress(buf[0]);
- /* Hack -- Handle bizarre "errors" */
- if ((i <= 0) && (errno != EINTR)) abort();
+ /* Success */
+ return true;
}
- /* Do not wait */
- else
+public:
+ explicit CursesFrontend(term_data *term_data)
+ : m_term_data(term_data)
{
- /* Get the current flags for stdin */
- k = fcntl(0, F_GETFL, 0);
+ }
- /* Oops */
- if (k < 0) return (1);
+ void init() final
+ {
+ /* Count init's, handle first */
+ if (m_active++ != 0) return;
- /* Tell stdin not to block */
- if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) return (1);
+ /* Clear */
+ wclear(m_term_data->win);
+ wmove(m_term_data->win, 0, 0);
+ wrefresh(m_term_data->win);
- /* Read one byte, if possible */
- i = read(0, buf, 1);
+ /* Game keymap */
+ keymap_game();
+ };
- /* Replace the flags for stdin */
- if (fcntl(0, F_SETFL, k)) return (1);
+ bool icky_corner() const final
+ {
+ return true;
}
- /* Ignore "invalid" keys */
- if ((i != 1) || (!buf[0])) return (1);
+ bool soft_cursor() const final
+ {
+ return false;
+ }
- /* Enqueue the keypress */
- Term_keypress(buf[0]);
+ void nuke() final
+ {
+ /* Delete this window */
+ delwin(m_term_data->win);
- /* Success */
- return (0);
-}
+ /* Count nuke's, handle last */
+ if (--m_active != 0) return;
+ /* Reset colors to defaults */
+ start_color();
-/*
- * React to changes
- */
-static void Term_xtra_gcu_react()
-{
+ /* Get current cursor position */
+ int x, y;
+ getyx(curscr, y, x);
- int i;
+ /* Move the cursor to bottom right corner */
+ mvcur(y, x, LINES - 1, 0);
- /* Cannot handle color redefinition */
- if (!can_fix_color)
- {
- return;
- }
+ /* Flush the curses buffer */
+ refresh();
- /* Set the colors */
- for (i = 0; i < 16; i++)
- {
- /* Set one color (note scaling) */
- init_color(i,
- angband_color_table[i][1] * 1000 / 255,
- angband_color_table[i][2] * 1000 / 255,
- angband_color_table[i][3] * 1000 / 255);
- }
+ /* Exit curses */
+ endwin();
-}
+ /* Flush the output */
+ fflush(stdout);
+ /* Normal keymap */
+ keymap_norm();
+ }
-/*
- * Handle a "special request"
- */
-static void Term_xtra_gcu(void *data, int n, int v)
-{
- term_data *td = (term_data *) data;
+ void process_event(bool wait) final
+ {
+ event_aux(wait);
+ }
- /* Analyze the request */
- switch (n)
+ void flush_events() final
{
- /* Clear screen */
- case TERM_XTRA_CLEAR:
- touchwin(td->win);
- (void)wclear(td->win);
- return;
+ while (event_aux(false))
+ {
+ // Keep flushing
+ }
+ }
- /* Make a noise */
- case TERM_XTRA_NOISE:
- (void)write(1, "\007", 1);
- return;
+ void clear() final
+ {
+ touchwin(m_term_data->win);
+ wclear(m_term_data->win);
+ }
- /* Flush the Curses buffer */
- case TERM_XTRA_FRESH:
- (void)wrefresh(td->win);
- return;
+ void flush_output() final
+ {
+ wrefresh(m_term_data->win);
+ }
- /* Process events */
- case TERM_XTRA_EVENT:
- Term_xtra_gcu_event(v);
- return;
+ void noise() final
+ {
+ write(1, "\007", 1);
+ }
- /* Flush events */
- case TERM_XTRA_FLUSH:
- while (!Term_xtra_gcu_event(false));
- return;
+ void process_queued_events() final
+ {
+ // No action necessary
+ }
- /* React to events */
- case TERM_XTRA_REACT:
+ void react() final
+ {
Term_xtra_gcu_react();
- return;
}
-}
+ void activate_deactivate(bool) final
+ {
+ // No action necessary
+ }
-/*
- * Actually MOVE the hardware cursor
- */
-static void Term_curs_gcu(void *data, int x, int y)
-{
- term_data *td = (term_data *) data;
+ void rename_main_window(std::string_view) final
+ {
+ // Don't have window titles
+ }
- /* Literally move the cursor */
- wmove(td->win, y, x);
-}
+ void draw_cursor(int x, int y) final
+ {
+ wmove(m_term_data->win, y, x);
+ }
+ void draw_text(int x, int y, int n, byte a, const char *s) final
+ {
+ /* Set the color */
+ if (can_use_color)
+ {
+ wattrset(m_term_data->win, colortable[a & 0x0F]);
+ }
-/*
- * Place some text on the screen using an attribute
- */
-static void Term_text_gcu(void *data, int x, int y, int n, byte a, const char *s)
-{
- term_data *td = (term_data *) data;
+ /* Move the cursor */
+ wmove(m_term_data->win, y, x);
- /* Set the color */
- if (can_use_color) wattrset(td->win, colortable[a & 0x0F]);
+ /* Draw each character */
+ for (int i = 0; i < n; i++)
+ {
- /* Move the cursor */
- wmove(td->win, y, x);
+ /* Draw a normal character */
+ waddch(m_term_data->win, (byte)s[i]);
+ }
+ }
- /* Draw each character */
- for (int i = 0; i < n; i++)
- {
+};
- /* Draw a normal character */
- waddch(td->win, (byte)s[i]);
- }
-}
+int CursesFrontend::m_active = 0;
/*
@@ -662,19 +678,8 @@ static errr term_data_init_gcu(term_data *td, int rows, int cols, int y, int x)
quit("Failed to setup curses window.");
}
- /* Hooks */
- struct term_ui_hooks_t ui_hooks = {
- Term_init_gcu,
- Term_nuke_gcu,
- Term_xtra_gcu,
- Term_curs_gcu,
- Term_text_gcu,
- };
-
/* Initialize the term */
- td->term_ptr = term_init(td, cols, rows, 256);
- term_init_icky_corner(td->term_ptr);
- term_init_ui_hooks(td->term_ptr, ui_hooks);
+ td->term_ptr = term_init(cols, rows, 256, std::make_shared<CursesFrontend>(td));
/* Activate it */
Term_activate(td->term_ptr);
@@ -684,12 +689,8 @@ static errr term_data_init_gcu(term_data *td, int rows, int cols, int y, int x)
}
-static void hook_quit(const char *str)
+static void hook_quit(const char *)
{
- /* Unused */
- (void)str;
-
- /* Exit curses */
endwin();
}
diff --git a/src/main-gtk2.cc b/src/main-gtk2.cc
index 510c72fd..d75109f3 100644
--- a/src/main-gtk2.cc
+++ b/src/main-gtk2.cc
@@ -32,6 +32,7 @@
#include "config.hpp"
#include "files.hpp"
+#include "frontend.hpp"
#include "main.hpp"
#include "util.hpp"
#include "variable.hpp"
@@ -261,34 +262,6 @@ static void term_data_set_fg(term_data *td, byte attr)
/*
- * Free data used by a term
- */
-static void Term_nuke_gtk(void *data)
-{
- term_data *td = (term_data *) data;
-
- /* Free name */
- if (td->name) free(td->name);
-
- /* Forget it */
- td->name = NULL;
-
- /* Free font */
- if (td->font) gdk_font_unref(td->font);
-
- /* Forget it */
- td->font = NULL;
-
- /* Free backing store */
- if (td->backing_store) gdk_pixmap_unref(td->backing_store);
-
- /* Forget it too */
- td->backing_store = NULL;
-
-}
-
-
-/*
* Erase the whole term.
*/
static void Term_clear_gtk(term_data *td)
@@ -347,81 +320,6 @@ static errr Term_wipe_gtk(term_data *td, int x, int y, int n)
/*
- * Draw some textual characters.
- */
-static void Term_text_gtk(void *data, int x, int y, int n, byte a, const char *s)
-{
- term_data *td = (term_data *) data;
-
- /* Don't draw to hidden windows */
- if (!td->shown)
- {
- return;
- }
-
- /* Paranoia */
- g_assert(td->drawing_area->window != 0);
-
- /* Set foreground colour */
- term_data_set_fg(td, a);
-
- /* Clear the line */
- Term_wipe_gtk(td, x, y, n);
-
- /* Draw the text to the window */
- gdk_draw_text(
- TERM_DATA_DRAWABLE(td),
- td->font,
- td->gc,
- x * td->font_wid,
- td->font->ascent + y * td->font_hgt,
- s,
- n);
-
- /* Copy image from backing store if present */
- TERM_DATA_REFRESH(td, x, y, n, 1);
-}
-
-
-/*
- * Draw software cursor at (x, y)
- */
-static void Term_curs_gtk(void *data, int x, int y)
-{
- term_data *td = (term_data *) data;
- int cells = 1;
-
-
- /* Don't draw to hidden windows */
- if (!td->shown)
- {
- return;
- }
-
- /* Paranoia */
- g_assert(td->drawing_area->window != 0);
-
- /* Set foreground colour */
- term_data_set_fg(td, TERM_YELLOW);
-
- /* Draw the software cursor */
- gdk_draw_rectangle(
- TERM_DATA_DRAWABLE(td),
- td->gc,
- false,
- x * td->font_wid,
- y * td->font_hgt,
- td->font_wid * cells - 1,
- td->font_hgt - 1);
-
- /* Copy image from backing store if present */
- TERM_DATA_REFRESH(td, x, y, cells, 1);
-}
-
-
-
-
-/*
* Process an event, if there's none block when wait is set true,
* return immediately otherwise.
*/
@@ -442,70 +340,165 @@ static void DrainEvents()
}
-/*
- * Handle a "special request"
+
+/**
+ * GTK2 implementation of a UserInterface
*/
-static void Term_xtra_gtk(void *term_data_ctx, int n, int v)
-{
- term_data *td = (term_data *) term_data_ctx;
+class Gtk2Frontend final : public Frontend {
- /* Handle a subset of the legal requests */
- switch (n)
+private:
+ term_data *m_term_data;
+
+public:
+ Gtk2Frontend(term_data *term_data)
+ : m_term_data(term_data)
{
- /* Make a noise */
- case TERM_XTRA_NOISE:
- {
- gdk_beep();
- return;
- }
+ }
- /* Flush the output */
- case TERM_XTRA_FRESH:
+ void init() final
+ {
+ }
+
+ void nuke() final
+ {
+ /* Free name */
+ if (m_term_data->name)
{
- gdk_flush();
- return;
+ free(m_term_data->name);
}
+ m_term_data->name = NULL;
- /* Process random events */
- case TERM_XTRA_BORED:
+ /* Free font */
+ if (m_term_data->font)
{
- CheckEvent(false);
- return;
+ gdk_font_unref(m_term_data->font);
}
+ m_term_data->font = NULL;
- /* Process Events */
- case TERM_XTRA_EVENT:
+ /* Free backing store */
+ if (m_term_data->backing_store)
{
- CheckEvent(v);
- return;
+ gdk_pixmap_unref(m_term_data->backing_store);
}
+ m_term_data->backing_store = NULL;
+ }
- /* Flush the events */
- case TERM_XTRA_FLUSH:
+ void process_event(bool wait) final
+ {
+ CheckEvent(wait);
+ }
+
+ bool soft_cursor() const final
+ {
+ return true;
+ }
+
+ bool icky_corner() const final
+ {
+ return false;
+ }
+
+ void flush_events() final
+ {
+ DrainEvents();
+ }
+
+ void process_queued_events() final
+ {
+ CheckEvent(false);
+ }
+
+ void clear() final
+ {
+ Term_clear_gtk(m_term_data);
+ }
+
+ void flush_output() final
+ {
+ gdk_flush();
+ }
+
+ void noise() final
+ {
+ gdk_beep();
+ }
+
+ void react() final
+ {
+ init_colours();
+ }
+
+ void activate_deactivate(bool resume) final
+ {
+ // No action necessary
+ };
+
+ void rename_main_window(std::string_view name_sv) final
+ {
+ gtk_window_set_title(GTK_WINDOW(data[0].window), std::string(angband_term_name[0]).c_str());
+ }
+
+ void draw_cursor(int x, int y) final
+ {
+ int cells = 1;
+
+ /* Don't draw to hidden windows */
+ if (!m_term_data->shown)
{
- DrainEvents();
return;
}
- /* Clear the screen */
- case TERM_XTRA_CLEAR:
- Term_clear_gtk(td);
- return;
+ /* Paranoia */
+ g_assert(m_term_data->drawing_area->window != 0);
- /* Rename main window */
- case TERM_XTRA_RENAME_MAIN_WIN:
- gtk_window_set_title(GTK_WINDOW(data[0].window), angband_term_name[0]);
- return;
+ /* Set foreground colour */
+ term_data_set_fg(m_term_data, TERM_YELLOW);
+
+ /* Draw the software cursor */
+ gdk_draw_rectangle(
+ TERM_DATA_DRAWABLE(m_term_data),
+ m_term_data->gc,
+ false,
+ x * m_term_data->font_wid,
+ y * m_term_data->font_hgt,
+ m_term_data->font_wid * cells - 1,
+ m_term_data->font_hgt - 1);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(m_term_data, x, y, cells, 1);
+ }
- /* React to changes */
- case TERM_XTRA_REACT:
+ void draw_text(int x, int y, int n, byte a, const char *s) final
+ {
+ /* Don't draw to hidden windows */
+ if (!m_term_data->shown)
{
- init_colours();
return;
}
- }
-}
+ /* Paranoia */
+ g_assert(m_term_data->drawing_area->window != 0);
+
+ /* Set foreground colour */
+ term_data_set_fg(m_term_data, a);
+
+ /* Clear the line */
+ Term_wipe_gtk(m_term_data, x, y, n);
+
+ /* Draw the text to the window */
+ gdk_draw_text(
+ TERM_DATA_DRAWABLE(m_term_data),
+ m_term_data->font,
+ m_term_data->gc,
+ x * m_term_data->font_wid,
+ m_term_data->font->ascent + y * m_term_data->font_hgt,
+ s,
+ n);
+
+ /* Copy image from backing store if present */
+ TERM_DATA_REFRESH(m_term_data, x, y, n, 1);
+ }
+};
@@ -1215,19 +1208,8 @@ static term *term_data_init(term_data *td, int i)
td->cols = 80;
td->rows = 24;
- /* Hooks */
- struct term_ui_hooks_t ui_hooks = {
- NULL /* init */,
- Term_nuke_gtk,
- Term_xtra_gtk,
- Term_curs_gtk,
- Term_text_gtk,
- };
-
/* Initialize the term */
- td->term_ptr = term_init(td, td->cols, td->rows, 1024);
- term_init_soft_cursor(td->term_ptr);
- term_init_ui_hooks(td->term_ptr, ui_hooks);
+ td->term_ptr = term_init(td->cols, td->rows, 1024, std::make_shared<Gtk2Frontend>(td));
/* Store the name of the term */
assert(angband_term_name[i] != NULL);
diff --git a/src/main-x11.cc b/src/main-x11.cc
index 97e1b212..aacbf3a0 100644
--- a/src/main-x11.cc
+++ b/src/main-x11.cc
@@ -94,6 +94,7 @@
#include "config.hpp"
#include "defines.hpp"
+#include "frontend.hpp"
#include "loadsave.hpp"
#include "main.hpp"
#include "util.hpp"
@@ -553,28 +554,27 @@ static errr Metadpy_new(const char *name)
/*
- * Make a simple beep
+ * Set the name (in the title bar) of Infowin
*/
-static void Metadpy_do_beep()
+static void Infowin_set_name(std::string_view name_sv)
{
- /* Make a simple beep */
- XBell(Metadpy->dpy, 100);
-}
+ char buf[128];
+ // Trim to the size of the buffer - 1 so that
+ // strncpy is guaranteed to NUL-terminate.
+ auto name = name_sv.substr(0, sizeof(buf) - 1);
+ // Copy
+ strncpy(buf, name.begin(), name.size());
-/*
- * Set the name (in the title bar) of Infowin
- */
-static void Infowin_set_name(const char *name)
-{
- Status st;
- XTextProperty tp;
- char buf[128];
char *bp = buf;
- strcpy(buf, name);
- st = XStringListToTextProperty(&bp, 1, &tp);
- if (st) XSetWMName(Metadpy->dpy, Infowin->win, &tp);
+
+ // Set
+ XTextProperty tp;
+ Status st = XStringListToTextProperty(&bp, 1, &tp);
+ if (st) {
+ XSetWMName(Metadpy->dpy, Infowin->win, &tp);
+ }
}
@@ -1368,150 +1368,134 @@ static errr CheckEvent(term_data *old_td, bool wait)
}
-/*
- * Handle "activation" of a term
+/**
+ * UserInterace for X11
*/
-static void Term_xtra_x11_level(term_data *td, int v)
-{
- /* Handle "activate" */
- if (v)
- {
- /* Activate the window */
- Infowin_set(td->win);
-
- /* Activate the font */
- Infofnt_set(td->fnt);
- }
-}
+class X11Frontend final : public Frontend {
+private:
+ term_data *m_term_data;
-/*
- * React to changes
- */
-static void Term_xtra_x11_react()
-{
- int i;
-
- if (Metadpy->color)
+public:
+ explicit X11Frontend(term_data *term_data)
+ : m_term_data(term_data)
{
- /* Check the colors */
- for (i = 0; i < 256; i++)
- {
- if ((color_table[i][0] != angband_color_table[i][0]) ||
- (color_table[i][1] != angband_color_table[i][1]) ||
- (color_table[i][2] != angband_color_table[i][2]) ||
- (color_table[i][3] != angband_color_table[i][3]))
- {
- Pixell pixel;
-
- /* Save new values */
- color_table[i][0] = angband_color_table[i][0];
- color_table[i][1] = angband_color_table[i][1];
- color_table[i][2] = angband_color_table[i][2];
- color_table[i][3] = angband_color_table[i][3];
-
- /* Create pixel */
- pixel = create_pixel(Metadpy->dpy,
- color_table[i][1],
- color_table[i][2],
- color_table[i][3]);
-
- /* Change the foreground */
- Infoclr_set(clr[i]);
- Infoclr_change_fg(pixel);
- }
- }
}
-}
-
-
-/*
- * Handle a "special request"
- */
-static void Term_xtra_x11(void *data, int n, int v)
-{
- term_data *td = (term_data*) data;
- /* Handle a subset of the legal requests */
- switch (n)
+ void init() final
{
- /* Make a noise */
- case TERM_XTRA_NOISE:
- Metadpy_do_beep();
- return;
+ // No action necessary
+ }
- /* Flush the output XXX XXX */
- case TERM_XTRA_FRESH:
- XFlush(Metadpy->dpy);
- return;
+ bool soft_cursor() const final
+ {
+ return true;
+ }
- /* Process random events XXX */
- case TERM_XTRA_BORED:
- CheckEvent(td, false);
- return;
+ bool icky_corner() const final
+ {
+ return false;
+ }
- /* Process Events XXX */
- case TERM_XTRA_EVENT:
- CheckEvent(td, v);
- return;
+ void nuke() final
+ {
+ // No action necessary
+ }
- /* Flush the events XXX */
- case TERM_XTRA_FLUSH:
- while (!CheckEvent(td, false));
- return;
+ void process_event(bool wait) final
+ {
+ CheckEvent(m_term_data, wait);
+ }
- /* Handle change in the "level" */
- case TERM_XTRA_LEVEL:
- Term_xtra_x11_level(td, v);
- return;
+ void flush_events() final
+ {
+ while (!CheckEvent(m_term_data, false))
+ {
+ // Keep flushing
+ }
+ }
- /* Clear the screen */
- case TERM_XTRA_CLEAR:
+ void clear() final
+ {
Infowin_wipe();
- return;
-
- /* React to changes */
- case TERM_XTRA_REACT:
- Term_xtra_x11_react();
- return;
-
- /* Rename main window */
- case TERM_XTRA_RENAME_MAIN_WIN:
- Infowin_set_name(angband_term_name[0]);
- return;
}
-}
+ void flush_output() final
+ {
+ XFlush(Metadpy->dpy);
+ }
-/*
- * Draw the cursor as an inverted rectangle.
- *
- * Consider a rectangular outline like "main-mac.c". XXX XXX
- */
-static void Term_curs_x11(void *data, int x, int y)
-{
- /* Draw the cursor */
- Infoclr_set(cursor_clr);
+ void noise() final
+ {
+ XBell(Metadpy->dpy, 100);
+ }
- /* Hilite the cursor character */
- Infofnt_text_non(x, y, " ", 1);
-}
+ void process_queued_events() final
+ {
+ CheckEvent(m_term_data, false);
+ }
+ void react() final
+ {
+ if (Metadpy->color)
+ {
+ /* Check the colors */
+ for (int i = 0; i < 256; i++)
+ {
+ if ((color_table[i][0] != angband_color_table[i][0]) ||
+ (color_table[i][1] != angband_color_table[i][1]) ||
+ (color_table[i][2] != angband_color_table[i][2]) ||
+ (color_table[i][3] != angband_color_table[i][3]))
+ {
+ Pixell pixel;
+
+ /* Save new values */
+ color_table[i][0] = angband_color_table[i][0];
+ color_table[i][1] = angband_color_table[i][1];
+ color_table[i][2] = angband_color_table[i][2];
+ color_table[i][3] = angband_color_table[i][3];
+
+ /* Create pixel */
+ pixel = create_pixel(Metadpy->dpy,
+ color_table[i][1],
+ color_table[i][2],
+ color_table[i][3]);
+
+ /* Change the foreground */
+ Infoclr_set(clr[i]);
+ Infoclr_change_fg(pixel);
+ }
+ }
+ }
+ }
-/*
- * Draw some textual characters.
- */
-static void Term_text_x11(void *data, int x, int y, int n, byte a, const char *s)
-{
- /* Draw the text */
- Infoclr_set(clr[a]);
+ void activate_deactivate(bool resume) final
+ {
+ if (resume)
+ {
+ Infowin_set(m_term_data->win);
+ Infofnt_set(m_term_data->fnt);
+ }
- /* Draw the text */
- Infofnt_text_std(x, y, s, n);
-}
+ }
+ void rename_main_window(std::string_view sv) final
+ {
+ Infowin_set_name(sv);
+ }
+ void draw_cursor(int x, int y) final
+ {
+ Infoclr_set(cursor_clr);
+ Infofnt_text_non(x, y, " ", 1);
+ }
+ void draw_text(int x, int y, int n, byte a, const char *s) final
+ {
+ Infoclr_set(clr[a]);
+ Infofnt_text_std(x, y, s, n);
+ }
+};
/*
@@ -1745,19 +1729,8 @@ static term *term_data_init(term_data *td, int i)
/* Move the window to requested location */
if ((x >= 0) && (y >= 0)) Infowin_impell(x, y);
- /* Hooks */
- struct term_ui_hooks_t ui_hooks = {
- NULL /* init */,
- NULL /* nuke */,
- Term_xtra_x11,
- Term_curs_x11,
- Term_text_x11,
- };
-
/* Initialize the term */
- td->term_ptr = term_init(td, cols, rows, num);
- term_init_soft_cursor(td->term_ptr);
- term_init_ui_hooks(td->term_ptr, ui_hooks);
+ td->term_ptr = term_init(cols, rows, num, std::make_shared<X11Frontend>(td));
/* Activate (important) */
Term_activate(td->term_ptr);
diff --git a/src/modules.cc b/src/modules.cc
index fe5ff2e9..08c16a93 100644
--- a/src/modules.cc
+++ b/src/modules.cc
@@ -187,7 +187,7 @@ static void activate_module(int module_idx)
if (equals(game_module, "ToME"))
{
strnfmt(angband_term_name[0], 79, "T-Engine: %s", game_module);
- Term_xtra(TERM_XTRA_RENAME_MAIN_WIN, 0);
+ Term_rename_main_win(angband_term_name[0]);
}
}
diff --git a/src/z-term.cc b/src/z-term.cc
index ca658bb4..764997c3 100644
--- a/src/z-term.cc
+++ b/src/z-term.cc
@@ -11,6 +11,7 @@
#include "z-term.hpp"
#include "key_queue.hpp"
+#include "frontend.hpp"
#include "tome/unique_handle.hpp"
#include <cassert>
@@ -415,13 +416,9 @@ static errr push_result_to_errr(key_queue::push_result_t r)
struct term
{
- void *data;
-
bool active_flag = false;
bool mapped_flag = false;
bool total_erase = true;
- bool icky_corner = false;
- bool soft_cursor = false;
key_queue m_key_queue;
@@ -438,24 +435,24 @@ struct term
std::unique_ptr<term_win> scr;
std::unique_ptr<term_win> mem;
- init_hook_t *init_hook = nullptr;
- nuke_hook_t *nuke_hook = nullptr;
- xtra_hook_t *xtra_hook = nullptr;
- curs_hook_t *curs_hook = nullptr;
- text_hook_t *text_hook = nullptr;
+ std::shared_ptr<Frontend> m_frontend;
+ std::function<void ()> m_resize_hook;
- resize_hook_t *resize_hook = nullptr;
+ bool icky_corner;
+ bool soft_cursor;
/**
* Ctor
*/
- term(int w, int h, int k, void *data_)
- : data(data_)
- , m_key_queue(k)
+ term(int w, int h, int k, std::shared_ptr<Frontend> user_interface)
+ : m_key_queue(k)
, wid(w)
, hgt(h)
, x1(h)
, x2(h)
+ , m_frontend(std::move(user_interface))
+ , icky_corner(m_frontend->icky_corner())
+ , soft_cursor(m_frontend->soft_cursor())
{
/* Allocate "displayed" */
old = std::make_unique<term_win>(w, h);
@@ -485,10 +482,7 @@ struct term
if (active_flag)
{
/* Call the "nuke" hook */
- if (nuke_hook)
- {
- (*nuke_hook)(data);
- }
+ m_frontend->nuke();
/* Remember */
active_flag = false;
@@ -498,31 +492,29 @@ struct term
}
}
-};
+ void text(int x, int y, int n, byte a, const char *s)
+ {
+ m_frontend->draw_text(x, y, n, a, s);
+ }
+ void curs(int x, int y)
+ {
+ m_frontend->draw_cursor(x, y);
+ }
+ void fresh()
+ {
+ m_frontend->flush_output();
+ }
-/*
- * The current "term"
- */
-term *Term = nullptr;
+};
-/*** External hooks ***/
/*
- * Execute the "Term->xtra_hook" hook, if any.
+ * The current "term"
*/
-void Term_xtra(int n, int v)
-{
- if (Term->xtra_hook)
- {
- (*Term->xtra_hook)(Term->data, n, v);
- }
-}
-
-
-/*** Efficient routines ***/
+term *Term = nullptr;
/*
@@ -609,25 +601,6 @@ static const byte ATTR_BLANK = TERM_WHITE;
static const char CHAR_BLANK = ' ';
/*
- * Call the text hook
- */
-static void do_text_hook(int x, int y, int n, byte a, const char *s)
-{
- assert(Term->text_hook);
- (*Term->text_hook)(Term->data, x, y, n, a, s);
-}
-
-/*
- * Call the curs hook
- */
-static void do_curs_hook(int x, int y)
-{
- assert(Term->curs_hook);
- (*Term->curs_hook)(Term->data, x, y);
-}
-
-
-/*
* Flush a row of the current window (see "Term_fresh")
*
* Display text using "Term_text()" and "Term_wipe()"
@@ -674,7 +647,7 @@ static void Term_fresh_row_text(int y, int x1, int x2)
/* Flush */
if (fn)
{
- do_text_hook(fx, y, fn, fa, &scr_cc[fx]);
+ Term->text(fx, y, fn, fa, &scr_cc[fx]);
/* Forget */
fn = 0;
@@ -695,7 +668,7 @@ static void Term_fresh_row_text(int y, int x1, int x2)
if (fn)
{
/* Draw the pending chars */
- do_text_hook(fx, y, fn, fa, &scr_cc[fx]);
+ Term->text(fx, y, fn, fa, &scr_cc[fx]);
/* Forget */
fn = 0;
@@ -712,7 +685,7 @@ static void Term_fresh_row_text(int y, int x1, int x2)
/* Flush */
if (fn)
{
- do_text_hook(fx, y, fn, fa, &scr_cc[fx]);
+ Term->text(fx, y, fn, fa, &scr_cc[fx]);
}
}
@@ -838,7 +811,7 @@ void Term_fresh()
char nc = CHAR_BLANK;
/* Physically erase the entire window */
- Term_xtra(TERM_XTRA_CLEAR, 0);
+ Term->m_frontend->clear();
/* Hack -- clear all "cursor" data */
old->cv = false;
@@ -893,7 +866,7 @@ void Term_fresh()
char oc = old_cc[tx];
/* Hack -- restore the actual character */
- do_text_hook(tx, ty, 1, oa, &oc);
+ Term->text(tx, ty, 1, oa, &oc);
}
}
@@ -947,7 +920,7 @@ void Term_fresh()
if (!scr->cu && scr->cv)
{
/* Call the cursor display routine */
- do_curs_hook(scr->cx, scr->cy);
+ Term->curs(scr->cx, scr->cy);
}
}
@@ -958,7 +931,7 @@ void Term_fresh()
if (scr->cu)
{
/* Paranoia -- Put the cursor NEAR where it belongs */
- do_curs_hook(w - 1, scr->cy);
+ Term->curs(w - 1, scr->cy);
/* Make the cursor invisible */
/* Term_xtra(TERM_XTRA_SHAPE, 0); */
@@ -968,7 +941,7 @@ void Term_fresh()
else if (!scr->cv)
{
/* Paranoia -- Put the cursor where it belongs */
- do_curs_hook(scr->cx, scr->cy);
+ Term->curs(scr->cx, scr->cy);
/* Make the cursor invisible */
/* Term_xtra(TERM_XTRA_SHAPE, 0); */
@@ -978,7 +951,7 @@ void Term_fresh()
else
{
/* Put the cursor where it belongs */
- do_curs_hook(scr->cx, scr->cy);
+ Term->curs(scr->cx, scr->cy);
}
}
@@ -991,7 +964,7 @@ void Term_fresh()
/* Actually flush the output */
- Term_xtra(TERM_XTRA_FRESH, 0);
+ Term->fresh();
}
@@ -1353,7 +1326,7 @@ void Term_redraw_section(int x1, int y1, int x2, int y2)
void Term_bell()
{
- Term_xtra(TERM_XTRA_NOISE, 0);
+ Term->m_frontend->noise();
}
/*** Access routines ***/
@@ -1554,7 +1527,7 @@ void Term_show_cursor()
void Term_flush()
{
/* Hack -- Flush all events */
- Term_xtra(TERM_XTRA_FLUSH, 0);
+ Term->m_frontend->flush_events();
/* Forget all keypresses */
Term->m_key_queue.clear();
@@ -1606,7 +1579,7 @@ errr Term_inkey(char *ch, bool wait, bool take)
(*ch) = '\0';
/* Process queued UI events */
- Term_xtra(TERM_XTRA_BORED, 0);
+ Term->m_frontend->process_queued_events();
/* Wait */
if (wait)
@@ -1615,7 +1588,7 @@ errr Term_inkey(char *ch, bool wait, bool take)
while (key_queue.empty())
{
/* Process events (wait for one) */
- Term_xtra(TERM_XTRA_EVENT, true);
+ Term->m_frontend->process_event(true);
}
}
@@ -1626,7 +1599,7 @@ errr Term_inkey(char *ch, bool wait, bool take)
if (key_queue.empty())
{
/* Process events (do not wait) */
- Term_xtra(TERM_XTRA_EVENT, false);
+ Term->m_frontend->process_event(false);
}
}
@@ -1837,9 +1810,9 @@ void Term_resize(int w, int h)
Term->y2 = h - 1;
/* Execute the "resize_hook" hook, if available */
- if (Term->resize_hook)
+ if (Term->m_resize_hook)
{
- Term->resize_hook();
+ Term->m_resize_hook();
}
}
@@ -1863,16 +1836,15 @@ void Term_activate(term *t)
}
/* Deactivate the old Term */
- if (Term) Term_xtra(TERM_XTRA_LEVEL, 0);
+ if (Term)
+ {
+ Term->m_frontend->activate_deactivate(false);
+ }
/* Hack -- Call the special "init" hook */
if (t && !t->active_flag)
{
- /* Call the "init" hook */
- if (t->init_hook)
- {
- (*t->init_hook)(t->data);
- }
+ t->m_frontend->init();
/* Remember */
t->active_flag = true;
@@ -1885,13 +1857,16 @@ void Term_activate(term *t)
Term = t;
/* Activate the new Term */
- if (Term) Term_xtra(TERM_XTRA_LEVEL, 1);
+ if (Term)
+ {
+ Term->m_frontend->activate_deactivate(true);
+ }
}
void Term_xtra_react()
{
- Term_xtra(TERM_XTRA_REACT, 0);
+ Term->m_frontend->react();
}
/**
@@ -1912,12 +1887,9 @@ void Term_unmapped()
}
-/*
- * Nuke a term
- */
-void term_nuke(term *t)
+void Term_rename_main_win(std::string_view name)
{
- delete t;
+ Term->m_frontend->rename_main_window(name);
}
@@ -1927,31 +1899,23 @@ void term_nuke(term *t)
* By default, the cursor starts out "invisible"
* By default, we "erase" using "black spaces"
*/
-term *term_init(void *data, int w, int h, int k)
-{
- return new term(w, h, k, data);
-}
-
-void term_init_icky_corner(term *t)
-{
- t->icky_corner = true;
-}
-
-void term_init_soft_cursor(term *t)
+term *term_init(int w, int h, int k, std::shared_ptr<Frontend> user_interface)
{
- t->soft_cursor = true;
+ return new term(w, h, k, user_interface);
}
-void term_init_ui_hooks(term *t, term_ui_hooks_t hooks)
+/*
+ * Nuke a term
+ */
+void term_nuke(term *t)
{
- t->init_hook = hooks.init_hook;
- t->nuke_hook = hooks.nuke_hook;
- t->xtra_hook = hooks.xtra_hook;
- t->curs_hook = hooks.curs_hook;
- t->text_hook = hooks.text_hook;
+ delete t;
}
-void term_set_resize_hook(term *t, resize_hook_t *hook)
+/**
+ * Set the function to call when terminal is resized.
+ */
+void term_set_resize_hook(term *t, std::function<void ()> f)
{
- t->resize_hook = hook;
+ t->m_resize_hook = f;
}
diff --git a/src/z-term.hpp b/src/z-term.hpp
index 83c903cc..24077c16 100644
--- a/src/z-term.hpp
+++ b/src/z-term.hpp
@@ -9,8 +9,11 @@
#pragma once
#include "h-basic.hpp"
+#include "frontend_fwd.hpp"
#include <functional>
+#include <memory>
+#include <string_view>
typedef struct term_win term_win;
@@ -20,38 +23,6 @@ typedef struct term term;
struct term; // Opaque
-/*
- * UI hooks
- */
-
-typedef void(init_hook_t)(void *data);
-typedef void(nuke_hook_t)(void *data);
-typedef void(xtra_hook_t)(void *data, int n, int v);
-typedef void(curs_hook_t)(void *data, int x, int y);
-typedef void(text_hook_t)(void *data, int x, int y, int n, byte a, const char *s);
-
-typedef struct term_ui_hooks_t term_ui_hooks_t;
-
-struct term_ui_hooks_t {
-
- init_hook_t *init_hook;
-
- nuke_hook_t *nuke_hook;
-
- xtra_hook_t *xtra_hook;
-
- curs_hook_t *curs_hook;
-
- text_hook_t *text_hook;
-
-};
-
-/*
- * Resize hook type
- */
-typedef void(resize_hook_t)();
-
-
/*** Color constants ***/
@@ -80,42 +51,12 @@ typedef void(resize_hook_t)();
-/**** Available Constants ****/
-
-
-/*
- * Definitions for the "actions" of "Term_xtra()"
- *
- * These values may be used as the first parameter of "Term_xtra()",
- * with the second parameter depending on the "action" itself. Many
- * of the actions shown below are optional on at least one platform.
- *
- * The "TERM_XTRA_EVENT" action uses "v" to "wait" for an event
- * The "TERM_XTRA_SHAPE" action uses "v" to "show" the cursor
- * The "TERM_XTRA_ALIVE" action uses "v" to "activate" (or "close")
- * The "TERM_XTRA_LEVEL" action uses "v" to "resume" (or "suspend")
- *
- * The other actions do not need a "v" code, so "zero" is used.
- */
-#define TERM_XTRA_EVENT 1 /* Process some pending events */
-#define TERM_XTRA_FLUSH 2 /* Flush all pending events */
-#define TERM_XTRA_CLEAR 3 /* Clear the entire window */
-#define TERM_XTRA_FRESH 6 /* Flush all rows (optional) */
-#define TERM_XTRA_NOISE 7 /* Make a noise (optional) */
-#define TERM_XTRA_BORED 9 /* Handle stuff when bored (optional) */
-#define TERM_XTRA_REACT 10 /* React to global changes (optional) */
-#define TERM_XTRA_LEVEL 12 /* Change the "soft" level (optional) */
-#define TERM_XTRA_RENAME_MAIN_WIN 16 /* Rename the main game window */
-
-
/**** Available Variables ****/
extern term *Term;
/**** Available Functions ****/
-void Term_xtra(int n, int v);
-
void Term_queue_char(int x, int y, byte a, char c);
void Term_fresh();
@@ -160,10 +101,8 @@ void Term_xtra_react();
void Term_mapped();
void Term_unmapped();
+void Term_rename_main_win(std::string_view);
+term *term_init(int w, int h, int k, std::shared_ptr<Frontend> user_interface);
void term_nuke(term *t);
-term *term_init(void *data, int w, int h, int k);
-void term_init_icky_corner(term *t);
-void term_init_soft_cursor(term *t);
-void term_init_ui_hooks(term *t, term_ui_hooks_t hooks);
-void term_set_resize_hook(term *t, resize_hook_t *hook);
+void term_set_resize_hook(term *t, std::function<void ()> f);