mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
565 lines
16 KiB
C++
565 lines
16 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
--- HISTORICAL COMMENTS FOLLOW ---
|
|
|
|
* $Logfile: /DescentIII/Main/ui/UISystem.cpp $
|
|
* $Revision: 44 $
|
|
* $Date: 3/20/00 12:30p $
|
|
* $Author: Matt $
|
|
*
|
|
* UI system code.
|
|
*
|
|
* $Log: /DescentIII/Main/ui/UISystem.cpp $
|
|
*
|
|
* 44 3/20/00 12:30p Matt
|
|
* Merge of Duane's post-1.3 changes.
|
|
* Mac-only debugging change.
|
|
*
|
|
* 43 10/21/99 3:33p Kevin
|
|
* Mac merge!
|
|
*
|
|
* 42 7/28/99 2:03p Kevin
|
|
* Macintosh Changes
|
|
*
|
|
* 41 5/05/99 6:48p Samir
|
|
* hack to get pause key to properly register in UI system.
|
|
*
|
|
* 40 5/01/99 1:14a Samir
|
|
* fixed mouse sequencing probs with selections being canceled out by
|
|
* UISystem.
|
|
*
|
|
* 39 4/14/99 1:54a Jeff
|
|
* fixed case mismatched #includes
|
|
*
|
|
* 38 4/01/99 11:28a Samir
|
|
* never flush mouse during ui_Flush. This will screw up emulated mouse
|
|
* input and maybe mess up some hardware mouse support from the ddio
|
|
* library.
|
|
*
|
|
* 37 3/23/99 9:02p Samir
|
|
* added mouse flush for ui flush. also made keyboard flush too.
|
|
*
|
|
* 36 2/21/99 6:38p Samir
|
|
* mouse and key input better. buffered mouse.
|
|
*
|
|
* 35 1/28/99 6:49p Samir
|
|
* frame limit ui system.
|
|
*
|
|
* 34 1/25/99 11:02a Samir
|
|
* revamped mouse and key controls.
|
|
*
|
|
* 33 10/19/98 2:50p Samir
|
|
* added function to cleanup ui windows.
|
|
*
|
|
* 32 10/16/98 5:54p Samir
|
|
* to prevent infinite loops, error check.
|
|
*
|
|
* 31 10/16/98 5:53p Samir
|
|
* redid add and remove window mechanisms.
|
|
*
|
|
* 30 10/13/98 8:23p Samir
|
|
* added more keyboard input options.
|
|
*
|
|
* 29 10/13/98 3:47p Samir
|
|
* added function to check if a cursor is visible
|
|
*
|
|
* 28 10/06/98 7:31p Samir
|
|
* changed behavior of mouse b1_count calculation.
|
|
*
|
|
* 27 10/02/98 5:47p Samir
|
|
* changed behavior of mouse cursor.
|
|
*
|
|
* 26 9/30/98 4:32p Samir
|
|
* added frametime value.
|
|
*
|
|
* 25 9/27/98 3:52p Samir
|
|
* made mouse input delay 25 fps.
|
|
*
|
|
* 24 9/01/98 4:12p Samir
|
|
* added screenshot caparbilities.
|
|
*
|
|
* 23 8/31/98 4:08p Samir
|
|
* don't reset mouse position when flushing ui.
|
|
*
|
|
* 22 8/24/98 3:13p Samir
|
|
* fixed swimming of mouse.
|
|
*
|
|
* 21 6/30/98 6:13p Samir
|
|
* call ddio_MouseReset on ui_SetScreenMode.
|
|
*
|
|
* 20 6/29/98 6:44p Samir
|
|
* redid ui input polling.
|
|
*
|
|
* 19 6/05/98 5:35p Samir
|
|
* massive improvement in UI keyboard interface.
|
|
*
|
|
* 18 5/06/98 4:34p Samir
|
|
* added function to set ui screen resolution.
|
|
*
|
|
* 17 5/05/98 5:15p Samir
|
|
* ui system now takes a width and height too
|
|
*
|
|
* 16 4/10/98 7:51p Samir
|
|
* added ui_Flush
|
|
*
|
|
* 15 3/24/98 4:27p Samir
|
|
* To check key states in ui_Poll, calls ddio_GetAdjKeyValue.
|
|
*
|
|
* 14 3/18/98 6:25p Samir
|
|
* Don't turn Z-buffering back on.
|
|
*
|
|
* 13 3/10/98 12:50p Samir
|
|
* Made UI_screen_width & height accessable.
|
|
*
|
|
* 12 2/18/98 2:39p Samir
|
|
* fixed mouse cursor clipping.
|
|
*
|
|
* 11 2/17/98 2:22p Jason
|
|
* fixed some potential z sorting problems
|
|
*
|
|
* 10 2/13/98 6:36p Samir
|
|
* Added ability to prevent input polling and mouse input.
|
|
*
|
|
* 9 2/11/98 5:55p Samir
|
|
* Hide and Show cursor aren't cumulative now.
|
|
*
|
|
* 8 2/02/98 7:35p Samir
|
|
* Added show/hide cursor functions.
|
|
*
|
|
* 7 1/30/98 7:07p Samir
|
|
* Cleaned up Window handling code.
|
|
*
|
|
* 6 1/18/98 4:22p Samir
|
|
* Changed some UI initialization stuff.
|
|
*
|
|
* 5 1/16/98 1:27p Jeff
|
|
* Poll always at least once.
|
|
*
|
|
* 4 1/13/98 4:32p Samir
|
|
* Made tUIInput a shared structure to be used by ui_DoFrame to return UI
|
|
* input values.
|
|
*
|
|
* 3 1/08/98 12:17p Samir
|
|
* Added TitledWindow and modified the UI interface for polling.
|
|
*
|
|
* 2 1/02/98 12:48p Samir
|
|
* Better keyboard support and full mouse cursor support.
|
|
*
|
|
* 1 12/30/97 4:36p Samir
|
|
* Initial revision
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include "UIlib.h"
|
|
#include "application.h"
|
|
#include "bitmap.h"
|
|
#include "ddvid.h"
|
|
#include "renderer.h"
|
|
#include "Macros.h"
|
|
|
|
#define UI_MOUSE_HOTX 2
|
|
#define UI_MOUSE_HOTY 2
|
|
constexpr int ui_FrameTimeMS = 50;
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// VARIABLES
|
|
struct tUIWindowNode {
|
|
UIWindow *wnd;
|
|
struct tUIWindowNode *next;
|
|
struct tUIWindowNode *prev;
|
|
};
|
|
// user input structure
|
|
tUIInput UI_input;
|
|
// user output structure
|
|
tUIOutput UI_output;
|
|
int UI_screen_width, UI_screen_height;
|
|
float UI_aspect_x = 1.0f;
|
|
float UI_aspect_y = 1.0f;
|
|
// application object
|
|
static bool UI_init = false;
|
|
static oeApplication *UI_app = NULL;
|
|
static int UI_cursor_bm = -1;
|
|
static tUIWindowNode *UIWindowList = NULL;
|
|
static tUIWindowNode *UIWindowTail = NULL;
|
|
static UIWindow *UIWindowFocus = NULL;
|
|
static int UI_cursor_show = 0;
|
|
float UIFrameTime = 0.0f;
|
|
bool ui_MousePoll(bool buttons);
|
|
bool ui_KeyPoll();
|
|
void ui_UpdateWindows();
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTIONS
|
|
// call this function to initialize the UI system. pass a surface where all UI will occur
|
|
void ui_Init(oeApplication *app, tUIInitInfo *init_info) {
|
|
// initialize variables
|
|
if (UI_app) {
|
|
ui_Close();
|
|
}
|
|
// clear out input poller.
|
|
ui_Flush();
|
|
UI_cursor_bm = -1;
|
|
UI_cursor_show = 0;
|
|
UI_app = app;
|
|
UIWindowList = NULL;
|
|
UIWindowTail = NULL;
|
|
UITextItem::SetDefaultFont(init_info->window_font);
|
|
ui_SetScreenMode(init_info->w, init_info->h);
|
|
UI_init = true;
|
|
}
|
|
// frees ui input cache
|
|
void ui_Flush() {
|
|
// UI_input.mx = 0;
|
|
// UI_input.last_mx = 0;
|
|
// UI_input.my = 0;
|
|
// UI_input.last_my = 0;
|
|
UI_input.b1_status = 0;
|
|
UI_input.b1_last_status = 0;
|
|
UI_input.b1_count = 0;
|
|
UIFrameTime = 0.0f;
|
|
UI_input.cur_time = timer_GetTime();
|
|
ui_KeyFlush();
|
|
}
|
|
// flushes out key input info currently in ui.
|
|
void ui_KeyFlush() {
|
|
UI_input.key = 0;
|
|
UI_input.key_first_press = false;
|
|
UI_input.key_status = 0;
|
|
UI_input.last_key = 0;
|
|
UI_input.last_key_status = 0;
|
|
UI_input.printscreen = false;
|
|
ddio_KeyFlush();
|
|
}
|
|
// closes UI system. do this when setting a new surface.
|
|
void ui_Close() {
|
|
if (UI_cursor_bm > -1) {
|
|
bm_FreeBitmap(UI_cursor_bm);
|
|
UI_cursor_bm = -1;
|
|
}
|
|
// delete window node list.
|
|
ui_RemoveAllWindows();
|
|
UI_app = NULL;
|
|
UI_init = false;
|
|
}
|
|
// retrieves input for user interface
|
|
// mouse
|
|
// keyboard
|
|
// possibly joystick?
|
|
bool ui_MousePoll(bool buttons) {
|
|
int mx, my;
|
|
static int btn_mask = 0;
|
|
int msebtn;
|
|
bool state;
|
|
if (!buttons) {
|
|
// get all input, mouse maintains persistent button info. key doesn't.
|
|
btn_mask = ddio_MouseGetState(&mx, &my, NULL, NULL);
|
|
UI_input.last_mx = UI_input.mx;
|
|
UI_input.last_my = UI_input.my;
|
|
UI_input.mx = mx;
|
|
UI_input.my = my;
|
|
} else if (UI_cursor_show) {
|
|
// if bX_count is 0, then repeat processing can occur, otherwise only real mouse events are processed.
|
|
if (ddio_MouseGetEvent(&msebtn, &state)) {
|
|
// mprintf(2, "mouse #%d state %d at %04d %04d\n", msebtn, UI_input.b1_status, UI_input.mx, UI_input.my);
|
|
if (msebtn == 0) {
|
|
UI_input.b1_last_status = UI_input.b1_status;
|
|
UI_input.b1_status = state ? UIMSEBTN_PRESSED : UIMSEBTN_RELEASED;
|
|
UI_input.b1_count = 1;
|
|
// mprintf(0, "M");
|
|
return true;
|
|
} else {
|
|
UI_input.b1_status = 0;
|
|
}
|
|
} else {
|
|
if (UI_input.b1_count == 0) {
|
|
// this frame, there was no down or up event, so now we can check if we should
|
|
// report button 1's state.
|
|
UI_input.b1_last_status = UI_input.b1_status;
|
|
UI_input.b1_count = 1;
|
|
if (CHECK_FLAG(btn_mask, MOUSE_LB)) {
|
|
// process repeat key events.
|
|
UI_input.b1_status = UIMSEBTN_PRESSED;
|
|
// mprintf(0, "m");
|
|
return true;
|
|
}
|
|
UI_input.b1_status = 0;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
bool ui_KeyPoll() {
|
|
int key;
|
|
key = ddio_KeyInKey();
|
|
if (key == KEY_PRINT_SCREEN)
|
|
UI_input.printscreen = true;
|
|
UI_input.last_key = UI_input.key;
|
|
UI_input.last_key_status = UI_input.key_status;
|
|
// messy hack for pause key
|
|
if ((key & 0x00ff) == KEY_PAUSE) {
|
|
key = key & 0xff;
|
|
}
|
|
UI_input.key = key;
|
|
if (UI_input.key) {
|
|
if (ddio_GetAdjKeyState(UI_input.key)) {
|
|
UI_input.key_status = UIKEY_PRESSED;
|
|
UI_input.key_first_press = true;
|
|
} else {
|
|
UI_input.key_status = UIKEY_CLICKED;
|
|
UI_input.key_first_press = true;
|
|
}
|
|
} else if (UI_input.last_key && UI_input.last_key_status == UIKEY_PRESSED) {
|
|
if (ddio_GetAdjKeyState(UI_input.last_key)) {
|
|
UI_input.key = UI_input.last_key;
|
|
UI_input.key_status = UIKEY_PRESSED;
|
|
UI_input.key_first_press = false;
|
|
} else {
|
|
UI_input.key_status = UIKEY_RELEASED;
|
|
UI_input.key = UI_input.last_key;
|
|
UI_input.key_first_press = false;
|
|
}
|
|
} else {
|
|
UI_input.key_status = 0;
|
|
UI_input.key_first_press = false;
|
|
}
|
|
// mprintf(0, "key=%d status=%d.\n", UI_input.key, UI_input.key_status);
|
|
return UI_input.key ? true : false;
|
|
}
|
|
// ability to load/use mouse cursors
|
|
void ui_UseCursor(const char *fname) {
|
|
if (UI_cursor_bm > -1)
|
|
bm_FreeBitmap(UI_cursor_bm);
|
|
UI_cursor_bm = bm_AllocLoadFileBitmap(fname, 0);
|
|
ASSERT(UI_cursor_bm > -1);
|
|
}
|
|
// adds a window to the ui list.
|
|
void ui_AddWindow(UIWindow *wnd) {
|
|
tUIWindowNode *newnode = new tUIWindowNode;
|
|
|
|
if (!UIWindowList) {
|
|
// first item to add to list.
|
|
UIWindowList = newnode;
|
|
newnode->prev = NULL;
|
|
} else {
|
|
// add to existing list.
|
|
tUIWindowNode *node = UIWindowList;
|
|
while (node->next) {
|
|
node = node->next;
|
|
}
|
|
node->next = newnode;
|
|
newnode->prev = node;
|
|
}
|
|
newnode->next = NULL;
|
|
newnode->wnd = wnd;
|
|
UIWindowTail = newnode;
|
|
}
|
|
// removes a window from ui list.if topmost, the next topmost window has focus.
|
|
void ui_RemoveAllWindows() {
|
|
int count = 0;
|
|
while (UIWindowTail) {
|
|
ui_RemoveWindow(UIWindowTail->wnd);
|
|
count++;
|
|
ASSERT(count < 50);
|
|
}
|
|
if (count) {
|
|
mprintf(0, "%d UI windows left open. Closed them!\n", count);
|
|
}
|
|
}
|
|
// removes a window from ui list.
|
|
void ui_RemoveWindow(UIWindow *wnd) {
|
|
tUIWindowNode *node = UIWindowList;
|
|
bool found_wnd = false;
|
|
while (node) {
|
|
if (node->wnd == wnd) {
|
|
// we found a node.
|
|
if (node->prev) {
|
|
node->prev->next = node->next;
|
|
}
|
|
if (node->next) {
|
|
node->next->prev = node->prev;
|
|
}
|
|
if (node == UIWindowList) {
|
|
UIWindowList = NULL;
|
|
UIWindowList = node->next;
|
|
}
|
|
if (node == UIWindowTail) {
|
|
UIWindowTail = NULL;
|
|
UIWindowTail = node->prev;
|
|
}
|
|
delete node;
|
|
found_wnd = true;
|
|
break;
|
|
}
|
|
node = node->next;
|
|
}
|
|
ASSERT(found_wnd);
|
|
}
|
|
// Procedure to find which window should gain focus in the
|
|
// first check what window contains mouse pointer and save that window
|
|
// check if keystroke,
|
|
// then adjust focus and save as current focus
|
|
// else
|
|
// set current focus = saved mouse focus.
|
|
void ui_DoWindowFocus() {
|
|
// right now, the topmost window always has focus!
|
|
tUIWindowNode *wndnode = UIWindowTail;
|
|
if (wndnode)
|
|
UIWindowFocus = wndnode->wnd;
|
|
else
|
|
UIWindowFocus = NULL;
|
|
}
|
|
// returns a result and processes input of a window in focus
|
|
int ui_ProcessFocusedWindow() {
|
|
int res = -1;
|
|
int res2;
|
|
// no input processing, just render frame
|
|
// process the window in focus.
|
|
// process at least once regardless of input (for user processes.)
|
|
// get keys. get mouse while real events (non ui manuipulated) are there.
|
|
ui_MousePoll(false);
|
|
if (UIWindowFocus) {
|
|
UI_input.b1_count = 0; // button one state reset.
|
|
ui_KeyPoll();
|
|
ui_MousePoll(true);
|
|
do {
|
|
res2 = UIWindowFocus->Process();
|
|
if (res2 != -1)
|
|
res = res2;
|
|
} while (ui_MousePoll(true));
|
|
}
|
|
return res;
|
|
}
|
|
// updates all visible windows.
|
|
void ui_UpdateWindows() {
|
|
tUIWindowNode *wndnode = UIWindowList;
|
|
while (wndnode) {
|
|
if (wndnode->next == NULL) {
|
|
}
|
|
wndnode->wnd->Render();
|
|
wndnode = wndnode->next;
|
|
}
|
|
}
|
|
// do cursor display and update
|
|
void ui_DoCursor() {
|
|
ui_StartDraw(0, 0, UI_screen_width, UI_screen_height);
|
|
|
|
rend_SetOverlayType(OT_NONE);
|
|
rend_SetLighting(LS_NONE);
|
|
rend_SetColorModel(CM_MONO);
|
|
rend_SetZBufferState(0);
|
|
rend_SetAlphaType(AT_CONSTANT + AT_TEXTURE);
|
|
rend_SetAlphaValue(255);
|
|
|
|
// This function needs to get called do if there are no windows, stuff gets drawn using
|
|
// the ui_StartDraw and ui_EndDraw
|
|
if (UI_cursor_show && UI_cursor_bm > -1) {
|
|
ui_MousePoll(false); // DAJ
|
|
|
|
float u0 = 0.0f, v0 = 0.0f, u1 = 1.0f, v1 = 1.0f;
|
|
int cur_w = bm_w(UI_cursor_bm, 0);
|
|
int cur_h = bm_h(UI_cursor_bm, 0);
|
|
if (UI_input.mx > (UI_screen_width - cur_w))
|
|
u1 = ((float)(UI_screen_width - UI_input.mx)) / ((float)cur_w);
|
|
if (UI_input.my > (UI_screen_height - cur_h))
|
|
v1 = ((float)(UI_screen_height - UI_input.my)) / ((float)cur_h);
|
|
if (UI_input.mx < UI_MOUSE_HOTX)
|
|
u0 = (float)(UI_MOUSE_HOTX - UI_input.mx) / (float)cur_w;
|
|
if (UI_input.my < UI_MOUSE_HOTY)
|
|
v0 = (float)(UI_MOUSE_HOTY - UI_input.my) / (float)cur_h;
|
|
// mprintf_at(1,5,30,"mx=%d my=%d ", UI_input.mx,UI_input.my);
|
|
rend_DrawScaledBitmap(UI_input.mx - UI_MOUSE_HOTX, UI_input.my - UI_MOUSE_HOTY,
|
|
UI_input.mx + (int)((float)cur_w * u1) - UI_MOUSE_HOTX,
|
|
UI_input.my + (int)((float)cur_h * v1) - UI_MOUSE_HOTY, UI_cursor_bm, u0, v0, u1, v1);
|
|
}
|
|
ui_EndDraw();
|
|
}
|
|
// ui_DoFrame
|
|
// polls input
|
|
// does a ui frame given a list of windows
|
|
int ui_DoFrame(bool input) {
|
|
int res = -1;
|
|
if (!UI_init)
|
|
return res;
|
|
|
|
// reset this flag here, so that print screen is valid only until next call to ui_DoFrame
|
|
if (input)
|
|
UI_input.printscreen = false;
|
|
if (input) {
|
|
ui_DoWindowFocus(); // determine window with current input focus.
|
|
res = ui_ProcessFocusedWindow(); // process focused window
|
|
|
|
const float elapsedTimeS = timer_GetTime() - UI_input.cur_time;
|
|
const int waitTimeMS = ui_FrameTimeMS - static_cast<int>(elapsedTimeS * 1000.0f);
|
|
D3::ChronoTimer::SleepMS(waitTimeMS);
|
|
|
|
float temp_time = timer_GetTime();
|
|
UIFrameTime = temp_time - UI_input.cur_time;
|
|
UI_input.cur_time = temp_time;
|
|
}
|
|
ui_UpdateWindows();
|
|
ui_DoCursor(); // update mouse cursor position
|
|
|
|
return res;
|
|
}
|
|
// does a ui frame and gets mouse and key information.
|
|
int ui_DoFrame(tUIInput *input, bool doinput) {
|
|
int res;
|
|
res = ui_DoFrame(doinput);
|
|
*input = UI_input;
|
|
return res;
|
|
}
|
|
// hide and show cursor. effects are cumulative
|
|
bool ui_ShowCursor() {
|
|
if (!UI_cursor_show) {
|
|
UI_cursor_show = 1;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
bool ui_HideCursor() {
|
|
if (UI_cursor_show) {
|
|
UI_cursor_show = 0;
|
|
return false;
|
|
}
|
|
return true; // cursor was hidden before.
|
|
}
|
|
// set user interface screen resolution
|
|
void ui_SetScreenMode(int w, int h) {
|
|
int old_w = UI_screen_width;
|
|
int old_h = UI_screen_height;
|
|
UI_screen_width = w;
|
|
UI_screen_height = h;
|
|
UI_aspect_x = (float)w / (float)FIXED_SCREEN_WIDTH;
|
|
UI_aspect_y = (float)h / (float)FIXED_SCREEN_HEIGHT;
|
|
ddio_MouseReset();
|
|
// ddio_MouseSetLimits(0,0, UI_screen_width, UI_screen_height);
|
|
ddio_MouseSetVCoords(UI_screen_width, UI_screen_height);
|
|
UI_input.cur_time = timer_GetTime();
|
|
// reposition all active open windows to their correct locations in this new resolution
|
|
//@@ tUIWindowNode *wndnode = UIWindowList;
|
|
//@@ float scalar_x = -1.0f + (float)w/(float)old_w;
|
|
//@@ float scalar_y = -1.0f + (float)h/(float)old_h;
|
|
//@@
|
|
//@@ while (wndnode)
|
|
//@@ {
|
|
//@@ wndnode->wnd->Move(wndnode->wnd->X() + (int)(scalar_x*old_w), wndnode->wnd->Y() + (int)(scalar_y*old_h),
|
|
//@@ wndnode->wnd->W(), wndnode->wnd->H());
|
|
//@@ wndnode = wndnode->next;
|
|
//@@ }
|
|
}
|
|
bool ui_IsCursorVisible() { return UI_cursor_show ? true : false; }
|