mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-12-19 17:37:42 -05:00
Likely with the change to SDL's 'captured mouse' mode, mouse movements during fullscreen became deltas *from the last movement*, not from the last sampled position or frame. In order to correctly measure the mouse movement, the deltas must be summed up and then reset when 'read' at input-processing time. Moving to summing the deltas threw many other mouse-related calibration factors out of whack, so these factors are re-determined empirically across a range of mice, from 400dpi 125hz to 30,000dpi 8khz. Reasonable defaults are chosen targeting an 'average' mouse of 1000dpi, and the mouse sensitivity precision is increased to allow finer user tuning.
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 "log.h"
|
|
#include "pserror.h"
|
|
#include "renderer.h"
|
|
#include "Macros.h"
|
|
|
|
constexpr float kDefaultMouseScale = 20;
|
|
|
|
#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 / kDefaultMouseScale;
|
|
UI_input.my = my / kDefaultMouseScale;
|
|
} 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) {
|
|
LOG_DEBUG.printf("%d UI windows left open. Closed them!", 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;
|
|
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) {
|
|
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_MouseSetVCoords(UI_screen_width * kDefaultMouseScale, UI_screen_height * kDefaultMouseScale);
|
|
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; }
|