Highlights:
- Borderless<->non-borderless window changes are handled properly.
- Fullscreen window moves are handled properly.
- Added requested window positioning option to the VideoOptions dialog. Useful for borderless windows and multi-monitor setups.
- Moving around/resizing the window while the video options dialog is open will update the dialog appropriately.
With syntax highlighting
Direct download
Relevant development history:
Code: Select all
Date: Sun Dec 22 12:31:53 2013 -0500
Small tweaks/cleanup.
Main.c/.h:
Remove displayRestartRequired hint. While it is a valid hint, nothing uses
it at the moment (calls that would truly need it check beforehand anyway).
src/core_lua.c:
Remove unused core.display binding (see main.c changes)
Rename core.display.setWindowSizeRequiresRestart C function to
"sdl_set_window_size_restart_check"
DisplayResolution.lua:
Swap the only restart / restart+reset options in the confirmation dialog.
Tweak confirmation dialog text.
Date: Sun Dec 22 11:59:48 2013 -0500
Fix 'é' character encoding strangeness.
Date: Sun Dec 22 11:41:26 2013 -0500
Lots of changes related to borderless windowed mode. Changes attempt to
preserve the existing code style where applicable.
main.c/.h:
Added a few functions/globals for determining if an engine should restart
due to the display system changing.
Added do_move function to help better handle fullscreen window moves
(for multimonitor systems).
The do_resize function handles borderless->non-borderless window transitions.
core_lua.c:
Add some core.display lua bindings related to the changes made in main.c/.h.
default/engine/Game.lua
Continue borderless windowed mode changes.
Expand onWindowMoved method, add updateVideoDialogs method for
communicating event changes to existing dialogs.
default/engine/dialogs/DisplayResolution.lua:
Add "requires restart" check for resolution changes.
default/engine/dialogs/VideoOptions.lua:
Added "Requested Window Position" option. Allows fine-tuning of
the requested window position. Includes a failsafe timer in case the option
is set with a bogus value.
default/engine/ui/Numberbox.lua:
Allow a negative sign to be entered.
Code: Select all
diff --git a/game/engines/default/engine/Game.lua b/game/engines/default/engine/Game.lua
index 2a292a2..c07b45d 100644
--- a/game/engines/default/engine/Game.lua
+++ b/game/engines/default/engine/Game.lua
@@ -457,14 +457,14 @@ function _M:setResolution(res, force)
-- Change the window size
print("setResolution: switching resolution to", res, r[1], r[2], r[3], r[4], force and "(forced)")
- local old_w, old_h, old_f = self.w, self.h, self.fullscreen
+ local old_w, old_h, old_f, old_b = self.w, self.h, self.fullscreen, self.borderless
core.display.setWindowSize(r[1], r[2], r[3], r[4])
-- Don't write self.w/h/fullscreen yet
- local new_w, new_h, new_f = core.display.size()
+ local new_w, new_h, new_f, new_b = core.display.size()
-- Check if a resolution change actually happened
- if new_w ~= old_w or new_h ~= old_h or new_f ~= old_f then
+ if new_w ~= old_w or new_h ~= old_h or new_f ~= old_f or new_b ~= old_b then
print("setResolution: performing onResolutionChange...\n")
self:onResolutionChange()
-- onResolutionChange saves settings...
@@ -486,7 +486,8 @@ function _M:onResolutionChange()
-- Get new resolution and save
self.w, self.h, self.fullscreen, self.borderless = core.display.size()
- config.settings.window.size = ("%dx%d%s"):format(self.w, self.h, self.fullscreen and " Fullscreen" or (self.borderless and " Borderless" or " Windowed"))
+ config.settings.window.size = ("%dx%d%s"):format(self.w, self.h, self.fullscreen and " Fullscreen" or (self.borderless and " Borderless" or " Windowed"))
+
self:saveSettings("resolution", ("window.size = '%s'\n"):format(config.settings.window.size))
print("onResolutionChange: resolution changed to ", self.w, self.h, "from", ow, oh)
@@ -495,9 +496,13 @@ function _M:onResolutionChange()
print("onResolutionChange: no game yet!")
return
end
+
+ -- Redraw existing dialogs
+ self:updateVideoDialogs()
-- No actual resize
- if ow == self.w and oh == self.h then
+ if ow == self.w and oh == self.h
+ and of == self.fullscreen and ob == self.borderless then
print("onResolutionChange: no actual resize, no confirm dialog.")
return
end
@@ -543,7 +548,26 @@ end
--- Called when the game window is moved around
function _M:onWindowMoved(x, y)
+ config.settings.window.pos.x = x
+ config.settings.window.pos.y = y
self:saveSettings("window_pos", ("window.pos = {x=%d, y=%d}\n"):format(x, y))
+
+ -- Redraw existing dialogs
+ self:updateVideoDialogs()
+end
+
+--- Update any registered video options dialogs with the latest changes.
+function _M:updateVideoDialogs()
+ -- Update the video settings dialogs if any are registered.
+ -- We don't know which dialog (if any) is VideoOptions, so iterate through.
+ --
+ -- Note: If the title of the video options dialog changes, this
+ -- functionality will break.
+ for i, v in ipairs(self.dialogs) do
+ if v.title == "Video Options" then
+ v.c_list:drawTree()
+ end
+ end
end
--- Sets the gamma of the window
diff --git a/game/engines/default/engine/dialogs/DisplayResolution.lua b/game/engines/default/engine/dialogs/DisplayResolution.lua
index 65775f5..45087d2 100644
--- a/game/engines/default/engine/dialogs/DisplayResolution.lua
+++ b/game/engines/default/engine/dialogs/DisplayResolution.lua
@@ -61,7 +61,35 @@ function _M:use(item)
elseif self.c_bl.checked then mode = " Borderless"
end
local r = item.r..mode
- game:setResolution(r, true)
+ local _, _, w, h = r:find("^([0-9]+)x([0-9]+)")
+
+ -- See if we need a restart (confirm).
+ if core.display.setWindowSizeRequiresRestart(w, h, self.c_fs.checked
+ , self.c_bl.checked) then
+ Dialog:yesnoPopup("Engine Restart Required"
+ , "Continue?" .. (game.creating_player and "" or " (progress will be saved)")
+ , function(restart)
+ if restart then
+ local resetPos = Dialog:yesnoPopup("Reset Window Position?"
+ , "Simply restart or restart+reset window position?"
+ , function(simplyRestart)
+ if not simplyRestart then
+ core.display.setWindowPos(0, 0)
+ game:onWindowMoved(0, 0)
+ end
+ game:setResolution(r, true)
+ -- Save game and reboot
+ if not game.creating_player then game:saveGame() end
+ util.showMainMenu(false, nil, nil
+ , game.__mod_info.short_name, game.save_name
+ , false)
+ end, "Restart", "Restart with reset")
+ end
+ end, "Yes", "No")
+ else
+ game:setResolution(r, true)
+ end
+
game:unregisterDialog(self)
if self.on_change then self.on_change(r) end
end
diff --git a/game/engines/default/engine/dialogs/VideoOptions.lua b/game/engines/default/engine/dialogs/VideoOptions.lua
index f3bf46d..0a31d26 100644
--- a/game/engines/default/engine/dialogs/VideoOptions.lua
+++ b/game/engines/default/engine/dialogs/VideoOptions.lua
@@ -66,7 +66,7 @@ function _M:generateList()
list[#list+1] = { zone=zone, name=string.toTString"#GOLD##{bold}#Resolution#WHITE##{normal}#", status=function(item)
return config.settings.window.size
end, fct=function(item)
- local menu = require("engine.dialogs.DisplayResolution").new(function() self.c_list:drawItem(item) end)
+ local menu = require("engine.dialogs.DisplayResolution").new(function() self.c_list:drawItem(item) end)
game:registerDialog(menu)
end,}
@@ -180,6 +180,59 @@ function _M:generateList()
game:saveSettings("censor_boot", ("censor_boot = %s\n"):format(tostring(config.settings.censor_boot)))
self.c_list:drawItem(item)
end,}
-
+
+ -- *Requested* Window Position
+ -- SDL tends to lie about where windows are positioned in fullscreen mode,
+ -- so always store the position requests, not the actual positions.
+ local zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text="Request a specific origin point for the game window.\nThis point corresponds to where the upper left corner of the window will be located.\nUseful when dealing with multiple monitors and borderless windows.\n\nThe default origin is (0,0).\n\nNote: This value will automatically revert after ten seconds if not confirmed by the user.#WHITE#"}
+ list[#list+1] = { zone=zone, name=string.toTString"#GOLD##{bold}#Requested Window Position#WHITE##{normal}#", status=function(item)
+ local curX, curY = config.settings.window.pos.x, config.settings.window.pos.y
+ return table.concat({"(", curX, ",", curY, ")"})
+ end, fct=function(item)
+ local itemRef = item
+ local oldX, oldY = config.settings.window.pos.x, config.settings.window.pos.y
+ local newX, newY
+ local function revertMove()
+ core.display.setWindowPos(oldX, oldY)
+ config.settings.window.pos.x = oldX
+ config.settings.window.pos.y = oldY
+ self.c_list:drawItem(itemRef)
+ end
+ -- TODO: Maybe change this to a GetText and parse?
+ game:registerDialog(GetQuantity.new("Window Origin: X-Coordinate", "Enter the x-coordinate", oldX, 99999
+ , function(qty)
+ newX=util.bound(qty, -99999, 99999)
+ game:registerDialog(GetQuantity.new("Window Origin: Y-Coordinate", "Enter the y-coordinate", oldY, 99999
+ , function(qty)
+ newY = util.bound(qty, -99999, 99999)
+ core.display.setWindowPos(newX, newY)
+ config.settings.window.pos.x = newX
+ config.settings.window.pos.y = newY
+ self.c_list:drawItem(itemRef)
+ local userAnswered = false
+ local confirmDialog = Dialog:yesnoPopup("Position changed.", "Save position?"
+ , function(ret)
+ userAnswered = true
+ if ret then
+ -- Write out settings
+ game:onWindowMoved(newX, newY)
+ else
+ -- Revert
+ revertMove()
+ end
+ end
+ , "Accept", "Revert")
+ game:registerTimer(10
+ , function()
+ -- Blast out changes if no response
+ if not userAnswered then
+ game:unregisterDialog(confirmDialog)
+ revertMove()
+ end
+ end )
+ end, -99999))
+ end, -99999))
+ end,}
+
self.list = list
end
diff --git a/game/engines/default/engine/ui/Numberbox.lua b/game/engines/default/engine/ui/Numberbox.lua
index 436e0d5..68f3397 100644
--- a/game/engines/default/engine/ui/Numberbox.lua
+++ b/game/engines/default/engine/ui/Numberbox.lua
@@ -125,7 +125,7 @@ function _M:generate()
end,
__TEXTINPUT = function(c)
if self.first then self.first = false self.tmp = {} self.cursor = 1 end
- if #self.tmp and (c == '0' or c == '1' or c == '2' or c == '3' or c == '4' or c == '5' or c == '6' or c == '7' or c == '8' or c == '9') then
+ if #self.tmp and (c == '-' or c == '0' or c == '1' or c == '2' or c == '3' or c == '4' or c == '5' or c == '6' or c == '7' or c == '8' or c == '9') then
table.insert(self.tmp, self.cursor, c)
self.cursor = self.cursor + 1
self.scroll = util.scroll(self.cursor, self.scroll, self.max_display)
diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua
index 8572143..e1b699e 100644
--- a/game/engines/default/engine/utils.lua
+++ b/game/engines/default/engine/utils.lua
@@ -1888,7 +1888,7 @@ function util.showMainMenu(no_reboot, reboot_engine, reboot_engine_version, rebo
core.game.setRealtime(0)
-- Save any remaining files
- savefile_pipe:forceWait()
+ if savefile_pipe then savefile_pipe:forceWait() end
if game and type(game) == "table" and game.__session_time_played_start then
if game.onDealloc then game:onDealloc() end
diff --git a/src/core_lua.c b/src/core_lua.c
index 6565850..2f67c1a 100644
--- a/src/core_lua.c
+++ b/src/core_lua.c
@@ -267,7 +267,7 @@ static GLenum sdl_gl_texture_format(SDL_Surface *s) {
static char *largest_black = NULL;
static int largest_size = 0;
void make_texture_for_surface(SDL_Surface *s, int *fw, int *fh, bool clamp) {
- // Paramétrage de la texture.
+ // Param?©trage de la texture.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp ? GL_CLAMP_TO_BORDER : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp ? GL_CLAMP_TO_BORDER : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -574,6 +574,15 @@ static int sdl_screen_size(lua_State *L)
return 4;
}
+static int sdl_window_pos(lua_State *L)
+{
+ int x, y;
+ SDL_GetWindowPosition(window, &x, &y);
+ lua_pushnumber(L, x);
+ lua_pushnumber(L, y);
+ return 2;
+}
+
static int sdl_new_font(lua_State *L)
{
const char *name = luaL_checkstring(L, 1);
@@ -2327,12 +2336,23 @@ static int sdl_set_window_size(lua_State *L)
return 1;
}
-static int sdl_set_window_pos(lua_State *L)
+static int sdl_set_window_size_restart_check(lua_State *L)
{
int w = luaL_checknumber(L, 1);
int h = luaL_checknumber(L, 2);
+ bool fullscreen = lua_toboolean(L, 3);
+ bool borderless = lua_toboolean(L, 4);
+
+ lua_pushboolean(L, resizeNeedsNewWindow(w, h, fullscreen, borderless));
+ return 1;
+}
+
+static int sdl_set_window_pos(lua_State *L)
+{
+ int x = luaL_checknumber(L, 1);
+ int y = luaL_checknumber(L, 2);
- SDL_SetWindowPosition(window, w, h);
+ do_move(x, y);
lua_pushboolean(L, TRUE);
return 1;
@@ -2923,6 +2943,7 @@ static const struct luaL_Reg displaylib[] =
{"getTextBlended", get_text_aa},
{"forceRedraw", sdl_redraw_screen},
{"size", sdl_screen_size},
+ {"windowPos", sdl_window_pos},
{"newFont", sdl_new_font},
{"newSurface", sdl_new_surface},
{"newTile", sdl_new_tile},
@@ -2940,6 +2961,7 @@ static const struct luaL_Reg displaylib[] =
{"loadImageMemory", sdl_load_image_mem},
{"setWindowTitle", sdl_set_window_title},
{"setWindowSize", sdl_set_window_size},
+ {"setWindowSizeRequiresRestart", sdl_set_window_size_restart_check},
{"setWindowPos", sdl_set_window_pos},
{"getModesList", sdl_get_modes_list},
{"setMouseCursor", sdl_set_mouse_cursor},
diff --git a/src/main.c b/src/main.c
index b34352f..56b201a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -50,12 +50,14 @@
#define WIDTH 800
#define HEIGHT 600
#define DEFAULT_IDLE_FPS (2)
+#define WINDOW_ICON_PATH ("/engines/default/data/gfx/te4-icon.png")
int start_xpos = -1, start_ypos = -1;
char *override_home = NULL;
int g_argc = 0;
char **g_argv;
SDL_Window *window = NULL;
+SDL_Surface *windowIconSurface = NULL;
SDL_GLContext maincontext; /* Our opengl context handle */
bool is_fullscreen = FALSE;
bool is_borderless = FALSE;
@@ -805,21 +807,81 @@ int resizeWindow(int width, int height)
return( TRUE );
}
+/* @see main.h#resizeNeedsNewWindow */
+extern bool resizeNeedsNewWindow(int w, int h, bool fullscreen, bool borderless)
+{
+ /* Note: w and h currently not a factor */
+ bool newWindowNeeded = window && ( (is_borderless && !borderless)
+ || (!is_borderless && borderless) );
+ return (newWindowNeeded);
+}
+
+/* @see main.h#do_move */
+void do_move(int w, int h) {
+ /* Save the origin in case a window needs to be remade later. */
+ start_xpos = w;
+ start_ypos = h;
+
+ /* Can't move a fullscreen SDL window in one go.*/
+ if (is_fullscreen) {
+ /* Drop out of fullscreen so we can move the window. */
+ SDL_SetWindowFullscreen(window, SDL_FALSE);
+
+ }
+
+ /* Move the window */
+ SDL_SetWindowPosition(window, w, h);
+
+ /* Jump back into fullscreen if necessary */
+ if (is_fullscreen) {
+ if (!SDL_SetWindowFullscreen(window, SDL_TRUE)) {
+ /* Fullscreen change successful */
+ is_fullscreen = SDL_TRUE;
+
+ } else {
+ /* Error switching fullscreen mode */
+ printf("[DO MOVE] Unable to return window"
+ " to fullscreen mode: %s\n", SDL_GetError());
+ SDL_ClearError();
+ }
+
+ }
+
+}
+
+/* @see main.h#do_resize */
void do_resize(int w, int h, bool fullscreen, bool borderless)
{
/* Temporary width, height (since SDL might reject our resize) */
int aw, ah;
int mustPushEvent = 0;
+ int mustCreateIconSurface = 0;
SDL_Event fsEvent;
printf("[DO RESIZE] Requested: %dx%d (%d, %d)\n", w, h, fullscreen, borderless);
+ /* See if we need to reinitialize the window */
+ if (resizeNeedsNewWindow(w, h, fullscreen, borderless)) {
+ /* Destroy the current window */
+ SDL_GL_DeleteContext(maincontext);
+ SDL_DestroyWindow(window);
+ maincontext = 0;
+ window = 0;
+ screen = 0;
+ /* Clean up the old window icon */
+ SDL_FreeSurface(windowIconSurface);
+ windowIconSurface = 0;
+ /* Signal a new icon needs to be created. */
+ mustCreateIconSurface = 1;
+ }
+
/* If there is no current window, we have to make one and initialize */
if (!window) {
window = SDL_CreateWindow("TE4",
(start_xpos == -1) ? SDL_WINDOWPOS_CENTERED : start_xpos,
(start_ypos == -1) ? SDL_WINDOWPOS_CENTERED : start_ypos, w, h,
- SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
+ SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL
+ | (!borderless ? SDL_WINDOW_RESIZABLE : 0)
| (fullscreen ? SDL_WINDOW_FULLSCREEN : 0)
| (borderless ? SDL_WINDOW_BORDERLESS : 0)
);
@@ -834,7 +896,13 @@ void do_resize(int w, int h, bool fullscreen, bool borderless)
SDL_GL_MakeCurrent(window, maincontext);
glewInit();
+ /* Set the window icon. */
+ windowIconSurface = IMG_Load_RW(PHYSFSRWOPS_openRead(WINDOW_ICON_PATH)
+ , TRUE);
+ SDL_SetWindowIcon(window, windowIconSurface);
+
} else {
+
/* SDL won't allow a fullscreen resolution change in one go. Check. */
if (is_fullscreen) {
/* Drop out of fullscreen so we can change resolution. */
@@ -846,8 +914,6 @@ void do_resize(int w, int h, bool fullscreen, bool borderless)
/* Update window size */
SDL_SetWindowSize(window, w, h);
- SDL_SetWindowBordered(window, !borderless);
- is_borderless = borderless;
/* Jump [back] into fullscreen if requested */
if (fullscreen) {
@@ -863,11 +929,12 @@ void do_resize(int w, int h, bool fullscreen, bool borderless)
}
} else if (mustPushEvent) {
+ /* Handle fullscreen -> nonfullscreen transition */
/*
* Our changes will get clobbered by an automatic event from
* setWindowFullscreen. Push an event to the event loop to make
- * sure these changes are applied after whatever that other event
- * throws.
+ * sure these changes are applied after whatever that other
+ * event throws.
*/
/* Create an event to push */
fsEvent.type = SDL_WINDOWEVENT;
@@ -887,12 +954,8 @@ void do_resize(int w, int h, bool fullscreen, bool borderless)
/* Finally, update the screen info */
screen = SDL_GetWindowSurface(window);
-
}
-
- if (is_borderless) SDL_SetWindowPosition(window, 0, 0);
-
/* Check and see if SDL honored our resize request */
SDL_GetWindowSize(window, &aw, &ah);
printf("[DO RESIZE] Got: %dx%d (%d, %d)\n", aw, ah, is_fullscreen, borderless);
@@ -1209,7 +1272,7 @@ int main(int argc, char *argv[])
printf("error opening screen: %s\n", SDL_GetError());
return 3;
}
- SDL_SetWindowIcon(window, IMG_Load_RW(PHYSFSRWOPS_openRead("/engines/default/data/gfx/te4-icon.png"), TRUE));
+
SDL_SetWindowTitle(window, "T4Engine");
TTF_Init();
@@ -1277,18 +1340,23 @@ int main(int argc, char *argv[])
break;
case SDL_WINDOWEVENT_MOVED: {
int x, y;
- SDL_GetWindowPosition(window, &x, &y);
- printf("move %d x %d\n", x, y);
- if (current_game != LUA_NOREF)
- {
- lua_rawgeti(L, LUA_REGISTRYINDEX, current_game);
- lua_pushstring(L, "onWindowMoved");
- lua_gettable(L, -2);
- lua_remove(L, -2);
- lua_rawgeti(L, LUA_REGISTRYINDEX, current_game);
- lua_pushnumber(L, x);
- lua_pushnumber(L, y);
- docall(L, 3, 0);
+ /* Note: SDL can't resize a fullscreen window, so don't bother! */
+ if (!is_fullscreen) {
+ SDL_GetWindowPosition(window, &x, &y);
+ printf("move %d x %d\n", x, y);
+ if (current_game != LUA_NOREF)
+ {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, current_game);
+ lua_pushstring(L, "onWindowMoved");
+ lua_gettable(L, -2);
+ lua_remove(L, -2);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, current_game);
+ lua_pushnumber(L, x);
+ lua_pushnumber(L, y);
+ docall(L, 3, 0);
+ }
+ } else {
+ printf("SDL_WINDOWEVENT_MOVED: ignored due to fullscreen\n");
}
break;
}
diff --git a/src/main.h b/src/main.h
index 65d19ca..0c71ec9 100644
--- a/src/main.h
+++ b/src/main.h
@@ -34,7 +34,26 @@
#endif
extern int resizeWindow(int width, int height);
+
+/**
+ * Will the requested do_resize call require a new window?
+ */
+extern bool resizeNeedsNewWindow(int w, int h, bool fullscreen, bool borderless);
+
+/**
+ * Move the window. Handles both windowed and fullscreen window moves.
+ */
+void do_move(int w, int h);
+
+/**
+ * Handle a resolution change request.
+ *
+ * The three window modes supported are windowed, borderless windowed,
+ * and fullscreen. These three modes are mutually exclusive, with the
+ * fullscreen flag taking priority over the borderless flag.
+ */
extern void do_resize(int w, int h, bool fullscreen, bool borderless);
+
extern void setupRealtime(float freq);
extern void setupDisplayTimer(int fps);
extern int docall (lua_State *L, int narg, int nret);