Compare commits

...

7 Commits

Author SHA1 Message Date
ZavGaro
b7f559698c
Merge be6c00d1b4d0fe630371939396c9e077db294223 into 9a1501ae89ffe79c38dbd6756c9e7ed647dd7dc1 2024-06-27 21:11:54 +03:00
grorp
9a1501ae89
CIrrDeviceSDL: Fix numpad key events not having correct KeyInput.Char (#14780)
Allows you to change viewing range using numpad +/- again. This fix also works with the current unreleased version of SDL 3.

The keycodes for numpad keys (both SDL keycodes and Irrlicht keycodes) are not the same as the keycodes for the equivalent non-numpad keys and don't correspond to chars, so I mapped them to chars manually.

Since I think the resolution of https://github.com/minetest/minetest/issues/13770 was "just disable numlock", I made sure to only do this for the numpad number keys if numlock is enabled.
2024-06-27 14:44:44 +02:00
ZavGaro
be6c00d1b4 Grammatical corrections in Profiler and ProfilerGraph documentation 2024-06-25 16:51:12 +03:00
ZavGaro
31337a0486 Correct ProfilerGraph documentation 2024-06-25 16:25:17 +03:00
ZavGaro
4dc3fd2fcc Improve Profiler documentation 2024-06-25 16:15:11 +03:00
ZavGaro
e41e2c43e8 Add documentation to Profiler and ProfilerGraph in Doxygen format 2024-06-24 21:23:58 +03:00
ZavGaro
7be7d15a02 Add documentation to profiler and profiler graph 2024-06-15 13:37:06 +03:00
4 changed files with 198 additions and 23 deletions

@ -129,9 +129,9 @@ EM_BOOL CIrrDeviceSDL::MouseLeaveCallback(int eventType, const EmscriptenMouseEv
}
#endif
bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE irrlichtKey)
{
switch (key) {
switch (irrlichtKey) {
// keys which are known to have safe special character interpretation
// could need changes over time (removals and additions!)
case KEY_RETURN:
@ -189,24 +189,68 @@ bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
}
}
int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
int CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock)
{
switch (irrlichtKey) {
// special cases that always return a char regardless of how the SDL keycode
// looks
switch (key) {
case KEY_RETURN:
case KEY_ESCAPE:
return (int)key;
return (int)irrlichtKey;
// This is necessary for keys on the numpad because they don't use the same
// keycodes as their non-numpad versions (whose keycodes correspond to chars),
// but have their own SDL keycodes and their own Irrlicht keycodes (which
// don't correspond to chars).
case KEY_MULTIPLY:
return '*';
case KEY_ADD:
return '+';
case KEY_SUBTRACT:
return '-';
case KEY_DIVIDE:
return '/';
default:
break;
}
if (numlock) {
// Number keys on the numpad are also affected, but we only want them
// to produce number chars when numlock is enabled.
switch (irrlichtKey) {
case KEY_NUMPAD0:
return '0';
case KEY_NUMPAD1:
return '1';
case KEY_NUMPAD2:
return '2';
case KEY_NUMPAD3:
return '3';
case KEY_NUMPAD4:
return '4';
case KEY_NUMPAD5:
return '5';
case KEY_NUMPAD6:
return '6';
case KEY_NUMPAD7:
return '7';
case KEY_NUMPAD8:
return '8';
case KEY_NUMPAD9:
return '9';
default:
break;
}
}
// SDL in-place ORs values with no character representation with 1<<30
// https://wiki.libsdl.org/SDL2/SDLKeycodeLookup
if (assumedChar & (1 << 30))
// This also affects the numpad keys btw.
if (sdlKey & (1 << 30))
return 0;
switch (key) {
switch (irrlichtKey) {
case KEY_PRIOR:
case KEY_NEXT:
case KEY_HOME:
@ -218,7 +262,7 @@ int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
case KEY_NUMLOCK:
return 0;
default:
return assumedChar;
return sdlKey;
}
}
@ -825,7 +869,8 @@ bool CIrrDeviceSDL::run()
irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_KEYDOWN);
irrevent.KeyInput.Shift = (SDL_event.key.keysym.mod & KMOD_SHIFT) != 0;
irrevent.KeyInput.Control = (SDL_event.key.keysym.mod & KMOD_CTRL) != 0;
irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key);
irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key,
(SDL_event.key.keysym.mod & KMOD_NUM) != 0);
postEventFromUser(irrevent);
} break;

@ -273,10 +273,10 @@ class CIrrDeviceSDL : public CIrrDeviceStub
#endif
// Check if a key is a known special character with no side effects on text boxes.
static bool keyIsKnownSpecial(EKEY_CODE key);
static bool keyIsKnownSpecial(EKEY_CODE irrlichtKey);
// Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0).
static int findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key);
static int findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock);
// Check if a text box is in focus. Enable or disable SDL_TEXTINPUT events only if in focus.
void resetReceiveTextInputEvents();

@ -26,15 +26,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IVideoDriver.h>
#include "profiler.h"
/* Profiler display */
/** \brief Draws graph every frame using data given by Profiler.
It displays only entries that are recorded for profiler graph (via Profiler::graphAdd,
ScopeProfilerType::SPT_GRAPH_ADD and other) and Profiler itself doesn't show them.
Each profiling entry described with upper bound value, entry name and lower bound
value on its right. Graph is displayed as \e line if it is relative (lower value
is not 0) and \e filled if it is absolute.
\ingroup Profiling
*/
class ProfilerGraph
{
private:
/// One-frame slice of graph values.
struct Piece
{
Piece(Profiler::GraphValues v) : values(std::move(v)) {}
Profiler::GraphValues values;
};
/// Data for drawing one graph. Updates every frame.
struct Meta
{
float min;
@ -54,6 +67,8 @@ class ProfilerGraph
ProfilerGraph() = default;
/// Adds graph values to the end of graph (rendered at right side) and
/// removes the oldest ones (beyond the `m_log_max_size`).
void put(const Profiler::GraphValues &values);
void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,

@ -29,22 +29,63 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/timetaker.h"
#include "util/numeric.h" // paging()
// Global profiler
class Profiler;
extern Profiler *g_profiler;
/*
Time profiler
/** \defgroup Profiling Profiling tools
\brief Embedded tools to profile CPU time or anything else that is recorded
by certain functions.
An example with global profiler (#g_profiler), that is available in any file
with profiler.h included:
\code{.c++}
void functionToProfie(int count) {
// Record (add) time till function (scope) end
ScopeProfiler scope_profiler(g_profiler, "functionToProfie()"); // SPT_ADD by default
// Record (add) `count`
g_profiler->add("functionToProfie() count", count)
}
\endcode
In this example "add" method is used, so if function is called several times during
g_profiler cycle, sum of all recorded values will be shown at cycle end. See
#g_profiler for details about cycle and info showing.
TimeTaker can be used instead of ScopeProfiler.
\{
*/
/** \brief Collects values (time intervals, counts and other) in named entries.
Can be used to get sum, average or maximum of recorded values for each entry. Gives
result in text form via print().
Can collect values for #ProfilerGraph and give them via graphPop().
For usage, see \ref Profiling.
*/
class Profiler
{
public:
Profiler();
/** \brief Records the \p value in entry with given \p name to get sum of recorded values.
\param[in] name: entry name; can be same for several function calls
\param[in] value: value to record
*/
void add(const std::string &name, float value);
/** \brief Records the \p value in entry with given \p name to get average of recorded values.
\param[in] name: entry name; can be same for several function calls
\param[in] value: value to record
*/
void avg(const std::string &name, float value);
/** \brief Records the \p value in entry with given \p name to get maximum of recorded values.
\param[in] name: entry name; can be same for several function calls
\param[in] value: value to record
*/
void max(const std::string &name, float value);
/// Clears all profiling entries (#m_data). Does not affect #m_graphvalues.
void clear();
float getValue(const std::string &name) const;
@ -53,16 +94,46 @@ class Profiler
typedef std::map<std::string, float> GraphValues;
// Returns the line count
/** \brief Prints collected data formatted as table.
Prints each entry as one line which contains name, avgCount and value.
Meaning of printed numbers depends on used recording method (add/avg/max).
avgCount makes sense only for "avg" method and shows number of records;
always shows 1 for other methods.
Breaks all entries into \p pagecount pages and print only given \p page.
\param[out] o: stream to print into
\param[in] page: number of page to print
\param[in] pagecount: count of pages to break data into
\returns printed line count
*/
int print(std::ostream &o, u32 page = 1, u32 pagecount = 1);
/** \brief Breaks #m_data (entries) into \p pagecount pages (GraphValues) and
writes values on given \p page into \p o.
\param[out] o: variable to write page into
\param[in] page: page to get
\param[in] pagecount: number of page to break data into
*/
void getPage(GraphValues &o, u32 page, u32 pagecount);
/** \brief Records the \p value in graph entry with given \p id (name) to get
exactly this value.
\param[in] id: entry name
\param[in] value: value to record
*/
void graphSet(const std::string &id, float value)
{
MutexAutoLock lock(m_mutex);
m_graphvalues[id] = value;
}
/** \brief Records the \p value in graph entry with given \p id (name) to get
sum of recorded values.
\param[in] id: entry name
\param[in] value: value to record
*/
void graphAdd(const std::string &id, float value)
{
MutexAutoLock lock(m_mutex);
@ -72,6 +143,9 @@ class Profiler
else
it->second += value;
}
/// \brief Gives m_graphvalues into \p result.
/// \param[out] result: Variable to record values into
void graphPop(GraphValues &result)
{
MutexAutoLock lock(m_mutex);
@ -79,6 +153,8 @@ class Profiler
std::swap(result, m_graphvalues);
}
/// \brief Removes entry with given \p name.
/// \param[in] name: Name of entry to remove
void remove(const std::string& name)
{
MutexAutoLock lock(m_mutex);
@ -87,7 +163,10 @@ class Profiler
private:
struct DataPair {
/// Recorded value. Recording method depends on used functions (add/avg/max)
/// of profiler.
float value = 0;
/// Count of records if "avg" method is used.
int avgcount = 0;
inline void reset() {
@ -102,33 +181,69 @@ class Profiler
};
std::mutex m_mutex;
/// All the profiler entries, stored until `clear()` call.
std::map<std::string, DataPair> m_data;
/// Values for profiler graph collected until next frame draw. Value history
/// is stored in `ProfilerGraph`.
std::map<std::string, float> m_graphvalues;
u64 m_start_time;
};
/** \brief Global profiler. User can view its data via in-game profiler menu,
graph or "/profiler" command.
Profiling cycles are managed in Game class:
- cycle for profiler menu (GameUI::updateProfiler()) is managed in
Game::updateProfilers() and lasts for \c profiler_print_interval seconds (user
setting) or for 3 seconds if setting is set to 0;
- cycle for ProfilerGraph is managed in Game::updateProfilerGraphs() and lasts
for one frame;
*/
extern Profiler* g_profiler;
enum ScopeProfilerType : u8
{
/// Record time with Profiler::add() (to get sum of recorded values) at scope end.
SPT_ADD = 1,
/// Record time with Profiler::avg() (to get average of recorded values) at scope end.
SPT_AVG,
/// Record time with Profiler::graphAdd() (to get sum of recorded values on
/// ProfilerGraph) at scope end.
SPT_GRAPH_ADD,
/// Record time with Profiler::max() (to get maximum of recorded values) at scope end.
SPT_MAX
};
// Note: this class should be kept lightweight.
/** \brief A class for time measurements. Each created object records time from its
construction till scope end.
Note: this class should be kept lightweight.
*/
class ScopeProfiler
{
public:
/** \brief Begins measurement until scope end.Result will be recorded to
\p name
profiling entry in \p profiler (profiling entries are created/deleted automatically).
*/
ScopeProfiler(Profiler *profiler, const std::string &name,
ScopeProfilerType type = SPT_ADD,
TimePrecision precision = PRECISION_MILLI);
/// Ends measurement and record result in profiler.
~ScopeProfiler();
private:
Profiler *m_profiler = nullptr;
std::string m_name;
u64 m_time1;
Profiler *m_profiler = nullptr; /// Profiler to record in
std::string m_name; /// Name of profiler entry to record in
u64 m_time1; /// Record start time
ScopeProfilerType m_type;
TimePrecision m_precision;
};
/** \} */