fs = fs .. "style[label_button2;border=false]" ..
"button[0.1,6;5.3,1;label_button2;" ..
fgettext("Active renderer:") .. "\n" ..
- core.formspec_escape(core.get_screen_info().render_info) .. "]"
+ core.formspec_escape(core.get_active_renderer()) .. "]"
if PLATFORM == "Android" then
fs = fs .. "button[0.5,5.1;4.5,0.8;share_debug;" .. fgettext("Share debug log") .. "]"
protocol_version = 32, -- protocol version used by client
formspec_version = 2, -- supported formspec version
lang_code = "fr" -- Language code used for translation
+
-- the following keys can be missing if no stats have been collected yet
min_rtt = 0.01, -- minimum round trip time
max_rtt = 0.2, -- maximum round trip time
--vers_string = "0.4.9-git", -- full version string
--state = "Active" -- current client state
}
+* `minetest.get_player_window_information(player_name)`:
+
+ -- Will only be present if the client sent this information (requires v5.7+)
+ --
+ -- Note that none of these things are constant, they are likely to change during a client
+ -- connection as the player resizes the window and moves it between monitors
+ --
+ -- real_gui_scaling and real_hud_scaling can be used instead of DPI.
+ -- OSes don't necessarily give the physical DPI, as they may allow user configuration.
+ -- real_*_scaling is just OS DPI / 96 but with another level of user configuration.
+ {
+ -- Current size of the in-game render target (pixels).
+ --
+ -- This is usually the window size, but may be smaller in certain situations,
+ -- such as side-by-side mode.
+ size = {
+ x = 1308,
+ y = 577,
+ },
+
+ -- Estimated maximum formspec size before Minetest will start shrinking the
+ -- formspec to fit. For a fullscreen formspec, use a size 10-20% larger than
+ -- this and `padding[-0.01,-0.01]`.
+ max_formspec_size = {
+ x = 20,
+ y = 11.25
+ },
+
+ -- GUI Scaling multiplier
+ -- Equal to the setting `gui_scaling` multiplied by `dpi / 96`
+ real_gui_scaling = 1,
+
+ -- HUD Scaling multiplier
+ -- Equal to the setting `hud_scaling` multiplied by `dpi / 96`
+ real_hud_scaling = 1,
+ }
* `minetest.mkdir(path)`: returns success.
* Creates a directory specified by `path`, creating parent directories
will be added to fieldname value is set to formname itself
* if `is_file_select` is `true`, a file and not a folder will be selected
* returns nil or selected file/folder
-* `core.get_screen_info()`
- * returns
+* `core.get_active_renderer()`: Ex: "OpenGL 4.6".
+* `core.get_window_info()`: Same as server-side `get_player_window_information` API.
+
+ -- Note that none of these things are constant, they are likely to change
+ -- as the player resizes the window and moves it between monitors
+ --
+ -- real_gui_scaling and real_hud_scaling can be used instead of DPI.
+ -- OSes don't necessarily give the physical DPI, as they may allow user configuration.
+ -- real_*_scaling is just OS DPI / 96 but with another level of user configuration.
+ {
+ -- Current size of the in-game render target.
+ --
+ -- This is usually the window size, but may be smaller in certain situations,
+ -- such as side-by-side mode.
+ size = {
+ x = 1308,
+ y = 577,
+ },
+
+ -- Estimated maximum formspec size before Minetest will start shrinking the
+ -- formspec to fit. For a fullscreen formspec, use a size 10-20% larger than
+ -- this and `padding[-0.01,-0.01]`.
+ max_formspec_size = {
+ x = 20,
+ y = 11.25
+ },
+
+ -- GUI Scaling multiplier
+ -- Equal to the setting `gui_scaling` multiplied by `dpi / 96`
+ real_gui_scaling = 1,
+
+ -- HUD Scaling multiplier
+ -- Equal to the setting `hud_scaling` multiplied by `dpi / 96`
+ real_hud_scaling = 1,
+ }
- {
- density = <screen density 0.75,1.0,2.0,3.0 ... (dpi)>,
- display_width = <width of display>,
- display_height = <height of display>,
- window_width = <current window width>,
- window_height = <current window height>,
- render_info = <active render information>
- }
Content and Packages
--- /dev/null
+local function show_fullscreen_fs(name)
+ local window = minetest.get_player_window_information(name)
+ if not window then
+ return false, "Unable to get window info"
+ end
+
+ print(dump(window))
+
+ local size = { x = window.max_formspec_size.x * 1.1, y = window.max_formspec_size.y * 1.1 }
+ local fs = {
+ "formspec_version[4]",
+ ("size[%f,%f]"):format(size.x, size.y),
+ "padding[-0.01,-0.01]",
+ ("button[%f,%f;1,1;%s;%s]"):format(0, 0, "tl", "TL"),
+ ("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, 0, "tr", "TR"),
+ ("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, size.y - 1, "br", "BR"),
+ ("button[%f,%f;1,1;%s;%s]"):format(0, size.y - 1, "bl", "BL"),
+
+ ("label[%f,%f;%s]"):format(size.x / 2, size.y / 2, "Fullscreen")
+ }
+
+ minetest.show_formspec(name, "testfullscreenfs:fs", table.concat(fs))
+ return true, ("Calculated size of %f, %f"):format(size.x, size.y)
+end
+
+
+minetest.register_chatcommand("testfullscreenfs", {
+ func = show_fullscreen_fs,
+})
--- /dev/null
+name = testfullscreenfs
+description = Test mod to use minetest.get_player_window_information()
Send(&pkt);
}
+void Client::sendUpdateClientInfo(const ClientDynamicInfo& info)
+{
+ NetworkPacket pkt(TOSERVER_UPDATE_CLIENT_INFO, 4*2 + 4 + 4 + 4*2);
+ pkt << (u32)info.render_target_size.X << (u32)info.render_target_size.Y;
+ pkt << info.real_gui_scaling;
+ pkt << info.real_hud_scaling;
+ pkt << (f32)info.max_fs_size.X << (f32)info.max_fs_size.Y;
+
+ Send(&pkt);
+}
+
void Client::removeNode(v3s16 p)
{
std::map<v3s16, MapBlock*> modified_blocks;
#include "network/address.h"
#include "network/peerhandler.h"
#include "gameparams.h"
+#include "clientdynamicinfo.h"
#include <fstream>
#include "util/numeric.h"
void sendRespawn();
void sendReady();
void sendHaveMedia(const std::vector<u32> &tokens);
+ void sendUpdateClientInfo(const ClientDynamicInfo &info);
ClientEnvironment& getEnv() { return m_env; }
ITextureSource *tsrc() { return getTextureSource(); }
#include "version.h"
#include "script/scripting_client.h"
#include "hud.h"
+#include "clientdynamicinfo.h"
#if USE_SOUND
#include "client/sound_openal.h"
static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX];
f32 getSensitivityScaleFactor() const;
+ ClientDynamicInfo getCurrentDynamicInfo() const;
InputHandler *input = nullptr;
Client *client = nullptr;
Server *server = nullptr;
+ ClientDynamicInfo client_display_info{};
+ float dynamic_info_send_timer = 0;
+
IWritableTextureSource *texture_src = nullptr;
IWritableShaderSource *shader_src = nullptr;
&& client->checkPrivilege("fast");
#endif
- irr::core::dimension2d<u32> previous_screen_size(g_settings->getU16("screen_w"),
+ v2u32 previous_screen_size(g_settings->getU16("screen_w"),
g_settings->getU16("screen_h"));
while (m_rendering_engine->run()
&& !(*kill || g_gamecallback->shutdown_requested
|| (server && server->isShutdownRequested()))) {
- const irr::core::dimension2d<u32> ¤t_screen_size =
- m_rendering_engine->get_video_driver()->getScreenSize();
+ // Calculate dtime =
+ // m_rendering_engine->run() from this iteration
+ // + Sleep time until the wanted FPS are reached
+ draw_times.limit(device, &dtime);
+
+ const auto current_dynamic_info = getCurrentDynamicInfo();
+ if (!current_dynamic_info.equal(client_display_info)) {
+ client_display_info = current_dynamic_info;
+ dynamic_info_send_timer = 0.2f;
+ }
+
+ if (dynamic_info_send_timer > 0) {
+ dynamic_info_send_timer -= dtime;
+ if (dynamic_info_send_timer <= 0) {
+ client->sendUpdateClientInfo(current_dynamic_info);
+ }
+ }
+
+ const auto ¤t_screen_size = current_dynamic_info.render_target_size;
+
// Verify if window size has changed and save it if it's the case
// Ensure evaluating settings->getBool after verifying screensize
// First condition is cheaper
if (previous_screen_size != current_screen_size &&
current_screen_size != irr::core::dimension2d<u32>(0,0) &&
g_settings->getBool("autosave_screensize")) {
- g_settings->setU16("screen_w", current_screen_size.Width);
- g_settings->setU16("screen_h", current_screen_size.Height);
+ g_settings->setU16("screen_w", current_screen_size.X);
+ g_settings->setU16("screen_h", current_screen_size.Y);
previous_screen_size = current_screen_size;
}
- // Calculate dtime =
- // m_rendering_engine->run() from this iteration
- // + Sleep time until the wanted FPS are reached
- draw_times.limit(device, &dtime);
-
// Prepare render data for next iteration
updateStats(&stats, draw_times, dtime);
return tan(fov_y / 2.0f) * 1.3763818698f;
}
+ClientDynamicInfo Game::getCurrentDynamicInfo() const
+{
+ v2u32 screen_size = RenderingEngine::getWindowSize();
+ f32 density = RenderingEngine::getDisplayDensity();
+ f32 gui_scaling = g_settings->getFloat("gui_scaling") * density;
+ f32 hud_scaling = g_settings->getFloat("hud_scaling") * density;
+
+ return {
+ screen_size, gui_scaling, hud_scaling,
+ ClientDynamicInfo::calculateMaxFSSize(screen_size)
+ };
+}
+
void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
{
#ifdef HAVE_TOUCHSCREENGUI
void removeMesh(const scene::IMesh* mesh);
+ /**
+ * This takes 3d_mode into account - side-by-side will return a
+ * halved horizontal size.
+ *
+ * @return "window" size
+ */
static v2u32 getWindowSize()
{
sanity_check(s_singleton);
--- /dev/null
+/*
+Minetest
+Copyright (C) 2022-3 rubenwardy <rw@rubenwardy.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+
+#include "irrTypes.h"
+
+
+struct ClientDynamicInfo
+{
+ v2u32 render_target_size;
+ f32 real_gui_scaling;
+ f32 real_hud_scaling;
+ v2f32 max_fs_size;
+
+ bool equal(const ClientDynamicInfo &other) const {
+ return render_target_size == other.render_target_size &&
+ abs(real_gui_scaling - other.real_gui_scaling) < 0.001f &&
+ abs(real_hud_scaling - other.real_hud_scaling) < 0.001f;
+ }
+
+ static v2f32 calculateMaxFSSize(v2u32 render_target_size) {
+ f32 factor =
+#ifdef HAVE_TOUCHSCREENGUI
+ 10;
+#else
+ 15;
+#endif
+ f32 ratio = (f32)render_target_size.X / (f32)render_target_size.Y;
+ if (ratio < 1)
+ return { factor, factor / ratio };
+ else
+ return { factor * ratio, factor };
+ }
+};
#include "network/address.h"
#include "porting.h"
#include "threading/mutex_auto_lock.h"
+#include "clientdynamicinfo.h"
#include <list>
#include <vector>
void setCachedAddress(const Address &addr) { m_addr = addr; }
const Address &getAddress() const { return m_addr; }
+ void setDynamicInfo(const ClientDynamicInfo &info) { m_dynamic_info = info; }
+ const ClientDynamicInfo &getDynamicInfo() const { return m_dynamic_info; }
+
private:
// Version is stored in here after INIT before INIT2
u8 m_pending_serialization_version = SER_FMT_VER_INVALID;
// Cached here so retrieval doesn't have to go to connection API
Address m_addr;
- // Client sent language code
+ // Client-sent language code
std::string m_lang_code;
+ // Client-sent dynamic info
+ ClientDynamicInfo m_dynamic_info{};
+
/*
Blocks that have been sent to client.
- These don't have to be sent again.
{ "TOSERVER_FIRST_SRP", 1, true }, // 0x50
{ "TOSERVER_SRP_BYTES_A", 1, true }, // 0x51
{ "TOSERVER_SRP_BYTES_M", 1, true }, // 0x52
+ { "TOSERVER_UPDATE_CLIENT_INFO", 1, true }, // 0x53
};
std::string bytes_M
*/
- TOSERVER_NUM_MSG_TYPES = 0x53,
+ TOSERVER_UPDATE_CLIENT_INFO = 0x53,
+ /*
+ v2s16 render_target_size
+ f32 gui_scaling
+ f32 hud_scaling
+ v2f32 max_fs_info
+ */
+
+ TOSERVER_NUM_MSG_TYPES = 0x54,
};
enum AuthMechanism
{ "TOSERVER_FIRST_SRP", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_FirstSrp }, // 0x50
{ "TOSERVER_SRP_BYTES_A", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesA }, // 0x51
{ "TOSERVER_SRP_BYTES_M", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesM }, // 0x52
+ { "TOSERVER_UPDATE_CLIENT_INFO", TOSERVER_STATE_INGAME, &Server::handleCommand_UpdateClientInfo }, // 0x53
};
const static ClientCommandFactory null_command_factory = { "TOCLIENT_NULL", 0, false };
#include "util/pointedthing.h"
#include "util/serialize.h"
#include "util/srp.h"
+#include "clientdynamicinfo.h"
void Server::handleCommand_Deprecated(NetworkPacket* pkt)
{
}
}
}
+
+void Server::handleCommand_UpdateClientInfo(NetworkPacket *pkt)
+{
+ ClientDynamicInfo info;
+ *pkt >> info.render_target_size.X;
+ *pkt >> info.render_target_size.Y;
+ *pkt >> info.real_gui_scaling;
+ *pkt >> info.real_hud_scaling;
+ *pkt >> info.max_fs_size.X;
+ *pkt >> info.max_fs_size.Y;
+
+ session_t peer_id = pkt->getPeerId();
+ RemoteClient *client = getClient(peer_id, CS_Invalid);
+ client->setDynamicInfo(info);
+}
lua_setfield(L, -2, "y");
}
+void push_v2u32(lua_State *L, v2u32 p)
+{
+ lua_createtable(L, 0, 2);
+ lua_pushinteger(L, p.X);
+ lua_setfield(L, -2, "x");
+ lua_pushinteger(L, p.Y);
+ lua_setfield(L, -2, "y");
+}
+
v2s32 read_v2s32(lua_State *L, int index)
{
v2s32 p;
void push_v2s16 (lua_State *L, v2s16 p);
void push_v2s32 (lua_State *L, v2s32 p);
+void push_v2u32 (lua_State *L, v2u32 p);
void push_v3s16 (lua_State *L, v3s16 p);
void push_aabb3f (lua_State *L, aabb3f box);
void push_ARGB8 (lua_State *L, video::SColor color);
#include "network/networkprotocol.h"
#include "content/mod_configuration.h"
#include "threading/mutex_auto_lock.h"
+#include "common/c_converter.h"
/******************************************************************************/
std::string ModApiMainMenu::getTextData(lua_State *L, std::string name)
}
/******************************************************************************/
-int ModApiMainMenu::l_get_screen_info(lua_State *L)
+int ModApiMainMenu::l_get_window_info(lua_State *L)
{
lua_newtable(L);
int top = lua_gettop(L);
- lua_pushstring(L,"density");
- lua_pushnumber(L,RenderingEngine::getDisplayDensity());
- lua_settable(L, top);
const v2u32 &window_size = RenderingEngine::getWindowSize();
- lua_pushstring(L,"window_width");
- lua_pushnumber(L, window_size.X);
+ f32 density = RenderingEngine::getDisplayDensity();
+ f32 gui_scaling = g_settings->getFloat("gui_scaling") * density;
+ f32 hud_scaling = g_settings->getFloat("hud_scaling") * density;
+
+ lua_pushstring(L, "size");
+ push_v2u32(L, window_size);
lua_settable(L, top);
- lua_pushstring(L,"window_height");
- lua_pushnumber(L, window_size.Y);
+ lua_pushstring(L, "max_formspec_size");
+ push_v2f(L, ClientDynamicInfo::calculateMaxFSSize(window_size));
lua_settable(L, top);
- lua_pushstring(L, "render_info");
- lua_pushstring(L, wide_to_utf8(RenderingEngine::get_video_driver()->getName()).c_str());
+ lua_pushstring(L, "real_gui_scaling");
+ lua_pushnumber(L, gui_scaling);
lua_settable(L, top);
+
+ lua_pushstring(L, "real_hud_scaling");
+ lua_pushnumber(L, hud_scaling);
+ lua_settable(L, top);
+
+ return 1;
+}
+
+/******************************************************************************/
+
+int ModApiMainMenu::l_get_active_renderer(lua_State *L)
+{
+ lua_pushstring(L, wide_to_utf8(RenderingEngine::get_video_driver()->getName()).c_str());
return 1;
}
API_FCT(download_file);
API_FCT(gettext);
API_FCT(get_video_drivers);
- API_FCT(get_screen_info);
+ API_FCT(get_window_info);
+ API_FCT(get_active_renderer);
API_FCT(get_min_supp_proto);
API_FCT(get_max_supp_proto);
API_FCT(open_url);
static int l_set_formspec_prepend(lua_State *L);
- static int l_get_screen_info(lua_State *L);
+ static int l_get_window_info(lua_State *L);
+
+ static int l_get_active_renderer(lua_State *L);
//filesystem
return 1;
}
+// get_player_window_information(name)
+int ModApiServer::l_get_player_window_information(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ Server *server = getServer(L);
+
+ const char *name = luaL_checkstring(L, 1);
+ RemotePlayer *player = server->getEnv().getPlayer(name);
+ if (!player)
+ return 0;
+
+ auto dynamic = server->getClientDynamicInfo(player->getPeerId());
+
+ if (!dynamic || dynamic->render_target_size == v2u32())
+ return 0;
+
+ lua_newtable(L);
+ int dyn_table = lua_gettop(L);
+
+ lua_pushstring(L, "size");
+ push_v2u32(L, dynamic->render_target_size);
+ lua_settable(L, dyn_table);
+
+ lua_pushstring(L, "max_formspec_size");
+ push_v2f(L, dynamic->max_fs_size);
+ lua_settable(L, dyn_table);
+
+ lua_pushstring(L, "real_gui_scaling");
+ lua_pushnumber(L, dynamic->real_gui_scaling);
+ lua_settable(L, dyn_table);
+
+ lua_pushstring(L, "real_hud_scaling");
+ lua_pushnumber(L, dynamic->real_hud_scaling);
+ lua_settable(L, dyn_table);
+ return 1;
+}
+
// get_ban_list()
int ModApiServer::l_get_ban_list(lua_State *L)
{
API_FCT(dynamic_add_media);
API_FCT(get_player_information);
+ API_FCT(get_player_window_information);
API_FCT(get_player_privs);
API_FCT(get_player_ip);
API_FCT(get_ban_list);
// get_player_information(name)
static int l_get_player_information(lua_State *L);
+ // get_player_window_information(name)
+ static int l_get_player_window_information(lua_State *L);
+
// get_ban_list()
static int l_get_ban_list(lua_State *L);
return true;
}
+const ClientDynamicInfo *Server::getClientDynamicInfo(session_t peer_id)
+{
+ ClientInterface::AutoLock clientlock(m_clients);
+ RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
+
+ if (!client)
+ return nullptr;
+
+ return &client->getDynamicInfo();
+}
+
void Server::handlePeerChanges()
{
while(!m_peer_change_queue.empty())
void handleCommand_SrpBytesA(NetworkPacket* pkt);
void handleCommand_SrpBytesM(NetworkPacket* pkt);
void handleCommand_HaveMedia(NetworkPacket *pkt);
+ void handleCommand_UpdateClientInfo(NetworkPacket *pkt);
void ProcessData(NetworkPacket *pkt);
void DisconnectPeer(session_t peer_id);
bool getClientConInfo(session_t peer_id, con::rtt_stat_type type, float *retval);
bool getClientInfo(session_t peer_id, ClientInfo &ret);
+ const ClientDynamicInfo *getClientDynamicInfo(session_t peer_id);
void printToConsoleOnly(const std::string &text);