diff options
author | Bardur Arantsson <bardur@scientician.net> | 2019-02-15 19:20:26 +0100 |
---|---|---|
committer | Bardur Arantsson <bardur@scientician.net> | 2019-02-15 19:20:26 +0100 |
commit | 83032ffafaf3eb1fb6cd9408a7af479b5f5a5f78 (patch) | |
tree | c999bb1c000e2a4ab459c90654364e7cb4b15e08 | |
parent | 231f73171c21acf419d93cf0c505028bd5f223ca (diff) |
Change from ui_hooks_t to a pure virtual interface Frontend
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/frontend.cc | 1 | ||||
-rw-r--r-- | src/frontend.hpp | 112 | ||||
-rw-r--r-- | src/frontend_fwd.hpp | 3 | ||||
-rw-r--r-- | src/main-gcu.cc | 349 | ||||
-rw-r--r-- | src/main-gtk2.cc | 294 | ||||
-rw-r--r-- | src/main-x11.cc | 269 | ||||
-rw-r--r-- | src/modules.cc | 2 | ||||
-rw-r--r-- | src/z-term.cc | 170 | ||||
-rw-r--r-- | src/z-term.hpp | 73 |
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); |