Skip to content

Commit 42536a7

Browse files
feat: Window tiling (#322)
Developer utility that auto tiles launched windows when using the multi-run instance feature in Godot. --------- Co-authored-by: Tamás Gálffy <[email protected]>
1 parent 95d7298 commit 42536a7

File tree

10 files changed

+188
-5
lines changed

10 files changed

+188
-5
lines changed

addons/netfox.extras/netfox-extras.gd

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,52 @@
11
@tool
22
extends EditorPlugin
33

4+
const ROOT = "res://addons/netfox.extras"
5+
46
var SETTINGS = [
5-
_NetfoxLogger.make_setting("netfox/logging/netfox_extras_log_level")
7+
_NetfoxLogger.make_setting("netfox/logging/netfox_extras_log_level"),
8+
9+
#Window Tiler Settings
10+
{
11+
"name": "netfox/extras/auto_tile_windows",
12+
"value": false,
13+
"type": TYPE_BOOL
14+
},
15+
{
16+
"name": "netfox/extras/screen",
17+
"value": 0,
18+
"type": TYPE_INT
19+
},
20+
{
21+
"name": "netfox/extras/borderless",
22+
"value": false,
23+
"type": TYPE_BOOL
24+
},
25+
]
26+
27+
const AUTOLOADS = [
28+
{
29+
"name": "WindowTiler",
30+
"path": ROOT + "/window-tiler.gd"
31+
}
632
]
733

834
func _enter_tree():
935
for setting in SETTINGS:
1036
add_setting(setting)
1137

38+
for autoload in AUTOLOADS:
39+
add_autoload_singleton(autoload.name, autoload.path)
40+
41+
1242
func _exit_tree():
1343
if ProjectSettings.get_setting("netfox/general/clear_settings", false):
1444
for setting in SETTINGS:
1545
remove_setting(setting)
1646

47+
for autoload in AUTOLOADS:
48+
remove_autoload_singleton(autoload.name)
49+
1750
func add_setting(setting: Dictionary):
1851
if ProjectSettings.has_setting(setting.name):
1952
return

addons/netfox.extras/plugin.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
name="netfox.extras"
44
description="Game-specific utilities for Netfox"
55
author="Tamas Galffy"
6-
version="1.10.0"
6+
version="1.11.0"
77
script="netfox-extras.gd"
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
extends Node
2+
3+
# Hash the game name, so we always get a valid filename
4+
var _prefix: String = "netfox-window-tiler-%x" % [ProjectSettings.get("application/config/name").hash()]
5+
6+
var _sid: String = "%x" % [hash(int(Time.get_unix_time_from_system() / 2.))]
7+
var _uid: String = "%d" % [Time.get_unix_time_from_system() * 1000_0000.]
8+
9+
static var _logger: _NetfoxLogger = _NetfoxLogger.for_extras("WindowTiler")
10+
11+
func _ready() -> void:
12+
# Running on a non-editor (export template) build
13+
if OS.has_feature("template"):
14+
return
15+
16+
# Cleanup in case some files were left
17+
_cleanup()
18+
19+
# Don't tile if disabled
20+
if not ProjectSettings.get_setting("netfox/extras/auto_tile_windows", false):
21+
return
22+
23+
_logger.debug("Tiling with sid: %s, uid: %s" % [_sid, _uid])
24+
25+
var err = _make_lock(_sid, _uid)
26+
if err != Error.OK:
27+
_logger.warning("Failed to create lock for tiling, reason: %s" % [error_string(err)])
28+
return
29+
30+
# Search for locks, stop once no new locks are found
31+
var locks = []
32+
await get_tree().create_timer(0.25).timeout
33+
for i in range(20):
34+
await get_tree().create_timer(0.1).timeout
35+
var new_locks = _list_lock_ids()
36+
37+
if locks == new_locks:
38+
break
39+
40+
locks = new_locks
41+
42+
var tile_count = locks.size()
43+
var idx = locks.find(_uid)
44+
45+
_logger.debug("Tiling as idx %d / %d - %s in %s" % [idx, tile_count, _uid, locks])
46+
_tile_window(idx, tile_count)
47+
48+
func _make_lock(sid: String, uid: String) -> Error:
49+
var path = "%s/%s-%s-%s" % [OS.get_cache_dir(), _prefix, sid, uid]
50+
var file := FileAccess.open(path, FileAccess.WRITE)
51+
52+
if file == null:
53+
return FileAccess.get_open_error()
54+
55+
file.close()
56+
return Error.OK
57+
58+
func _list_lock_ids() -> Array[String]:
59+
var result: Array[String] = []
60+
var dir := DirAccess.open(OS.get_cache_dir())
61+
62+
if dir:
63+
for f in dir.get_files():
64+
if f.begins_with(_prefix):
65+
result.append(_get_uid(f))
66+
67+
return result
68+
69+
func _cleanup():
70+
var result: Array[String] = []
71+
var dir := DirAccess.open(OS.get_cache_dir())
72+
73+
if dir:
74+
for f in dir.get_files():
75+
if f.begins_with(_prefix) and _get_sid(f) != _sid:
76+
_logger.trace("Cleaned up lock: %s" % [f])
77+
dir.remove(OS.get_cache_dir() + "/" + f)
78+
79+
func _get_sid(filename: String) -> String:
80+
return filename.substr(_prefix.length() + 1).get_slice("-", 0)
81+
82+
func _get_uid(filename: String) -> String:
83+
return filename.substr(_prefix.length() + 1).get_slice("-", 1)
84+
85+
func _tile_window(i: int, total: int) -> void:
86+
var screen = ProjectSettings.get_setting("netfox/extras/screen", 0)
87+
var screen_rect = DisplayServer.screen_get_usable_rect(screen)
88+
89+
var window: Window = get_tree().get_root()
90+
window.set_current_screen(screen)
91+
92+
if total == 1:
93+
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MAXIMIZED)
94+
return
95+
96+
window.borderless = ProjectSettings.get_setting("netfox/extras/borderless", false)
97+
98+
# Divide up the screen
99+
var windows_per_row = int(ceil(sqrt(total)))
100+
var windows_per_col = int(ceil(total / float(windows_per_row)))
101+
var window_size = Vector2(
102+
screen_rect.size.x / windows_per_row, screen_rect.size.y / windows_per_col
103+
)
104+
105+
window.set_size(window_size)
106+
107+
# Position of the window based on index.
108+
var row = i / windows_per_row
109+
var col = i % windows_per_row
110+
111+
var x = screen_rect.position.x + col * window_size.x
112+
var y = screen_rect.position.y + row * window_size.y
113+
114+
window.set_position(Vector2(x, y))

addons/netfox.internals/plugin.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
name="netfox.internals"
44
description="Shared internals for netfox addons"
55
author="Tamas Galffy"
6-
version="1.10.0"
6+
version="1.11.0"
77
script="plugin.gd"

addons/netfox.noray/plugin.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
name="netfox.noray"
44
description="Bulletproof your connectivity with noray integration for netfox"
55
author="Tamas Galffy"
6-
version="1.10.0"
6+
version="1.11.0"
77
script="netfox-noray.gd"

addons/netfox/plugin.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
name="netfox"
44
description="Shared internals for netfox addons"
55
author="Tamas Galffy"
6-
version="1.10.0"
6+
version="1.11.0"
77
script="netfox.gd"
162 KB
Loading
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# WindowTiler
2+
3+
A developer convenience feature that automatically tiles the launched windows
4+
when working from the editor.
5+
6+
![Window Tiler](../assets/window-tiler.gif)
7+
8+
## Limitations
9+
10+
### Borderless mode on Linux
11+
12+
Setting window position and size works inconsistently under Linux at the time
13+
of writing. Your mileage may vary based on your desktop environment and
14+
distribution.
15+
16+
In case the windows don't tile properly with *Borderless* enabled, disabling it
17+
is a fallback.
18+
19+
### Window decorations
20+
21+
At the time of writing, there is no known and consistent way to compensate for
22+
window decoration size and offset. In practice, this means that windows may
23+
slightly overlap.
24+
25+
## Configuration
26+
27+
*Auto Tile Windows* Enables auto tiling from editor launches.
28+
29+
*Screen* Which screen number to move and tile the windows to.
30+
31+
*Borderless* Enable borderless mode to make the most out of the screen real
32+
estate.
33+

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ nav:
3333
- 'netfox.extras/guides/base-net-input.md'
3434
- 'netfox.extras/guides/network-weapon.md'
3535
- 'netfox.extras/guides/rewindable-state-machine.md'
36+
- 'netfox.extras/guides/window-tiler.md'
3637

project.godot

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ NetworkTimeSynchronizer="*res://addons/netfox/network-time-synchronizer.gd"
2828
NetworkRollback="*res://addons/netfox/rollback/network-rollback.gd"
2929
NetworkEvents="*res://addons/netfox/network-events.gd"
3030
NetworkPerformance="*res://addons/netfox/network-performance.gd"
31+
WindowTiler="*res://addons/netfox.extras/window-tiler.gd"
3132

3233
[display]
3334

@@ -112,6 +113,7 @@ aim_south={
112113

113114
general/clear_settings=false
114115
time/tickrate=24
116+
extras/auto_tile_windows=true
115117

116118
[rendering]
117119

0 commit comments

Comments
 (0)