Skip to content

Commit

Permalink
Merge pull request #20 from MrEliptik/global_shortcut
Browse files Browse the repository at this point in the history
  • Loading branch information
MrEliptik authored Sep 4, 2024
2 parents 187d221 + 456e21f commit e766c2c
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 1 deletion.
1 change: 1 addition & 0 deletions autoload/focus_checker.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ func _ready():
process_mode = Node.PROCESS_MODE_ALWAYS

func _notification(what: int) -> void:
return
if what == NOTIFICATION_WM_WINDOW_FOCUS_IN:
print("Focus enter")
get_tree().paused = false
Expand Down
3 changes: 3 additions & 0 deletions autoload/signal_bus.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
extends Node

signal shortcut_close_game_pressed()
51 changes: 51 additions & 0 deletions global_shortcut/global_shortcut.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
extends Node

class_name GlobalShortcut

var ws_socket: WebSocketPeer = WebSocketPeer.new()
var websocket_url = "ws://localhost:65432"

func _init() -> void:
set_process(false)
if ws_socket.connect_to_url(websocket_url) != OK:
print("Could not connect.")
return

print("Connected to: ", websocket_url)

var timer: Timer = Timer.new()
timer.one_shot = false
timer.timeout.connect(process_ws)
Engine.get_main_loop().root.add_child.call_deferred(timer)
timer.start.call_deferred(0.5)

func _ready():
set_process(false)
if ws_socket.connect_to_url(websocket_url) != OK:
print("Could not connect.")
return

print("Connected to: ", websocket_url)

var timer: Timer = Timer.new()
timer.one_shot = false
timer.timeout.connect(process_ws)
add_child(timer)
timer.start(0.5)

func process_ws() -> void:
ws_socket.poll()

if ws_socket.get_ready_state() == WebSocketPeer.STATE_OPEN:
while ws_socket.get_available_packet_count():
print("WS received: ", ws_socket.get_packet().get_string_from_ascii())
# No need to check what the message is, we know we receive a signal only
# when the shortcut we want is pressed
SignalBus.shortcut_close_game_pressed.emit()
elif ws_socket.get_ready_state() == WebSocketPeer.STATE_CLOSING:
print("WS closing")
elif ws_socket.get_ready_state() == WebSocketPeer.STATE_CLOSED:
print("WS closed")

func _exit_tree():
ws_socket.close()
3 changes: 3 additions & 0 deletions launcher_config.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[SETTINGS]
shortcut_kill_game = "ctrl+q"
fullscreen = false
1 change: 1 addition & 0 deletions project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ config/icon="res://icon.svg"

[autoload]

SignalBus="*res://autoload/signal_bus.gd"
FocusChecker="*res://autoload/focus_checker.gd"

[display]
Expand Down
30 changes: 29 additions & 1 deletion scenes/app/scripts/app.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ extends Control
@export var default_bg: Texture

var pid_watching: int = -1
var pid_python: int = -1
var games: Dictionary
var launcher_settings: Dictionary
var curr_game_btn: Button = null

@onready var bg: TextureRect = $BG
Expand All @@ -19,6 +21,7 @@ var curr_game_btn: Button = null
@onready var qr_label: Label = $PanelContainer/MarginContainer/HBoxContainer/QRContainer/QRLabel

@onready var update_checker := UpdateChecker.new()
@onready var global_shortcut: GlobalShortcut

var qr_generator: QrCode = null

Expand All @@ -29,9 +32,17 @@ func _ready() -> void:

configure_timer()
var base_dir: String = ProjectSettings.globalize_path("res://") if OS.has_feature("editor") else OS.get_executable_path().get_base_dir()
read_launcher_config(base_dir.path_join("launcher_config.ini"), launcher_settings)
if launcher_settings["shortcut_kill_game"] != null and launcher_settings["shortcut_kill_game"] != "":
# Launch python script
start_python_shortcut_listener(base_dir.path_join("shortcut_listener.py"))
global_shortcut = GlobalShortcut.new()

create_game_folder(base_dir)
parse_games(base_dir.path_join("games"))

SignalBus.shortcut_close_game_pressed.connect(func(): stop_game(pid_watching))

#configure QR generator
qr_generator = QrCode.new()
qr_generator.error_correct_level = QrCode.ErrorCorrectionLevel.LOW
Expand All @@ -51,6 +62,7 @@ func _notification(what: int) -> void:
if what == NOTIFICATION_WM_CLOSE_REQUEST:
print("About to quit, killing process")
stop_game(pid_watching)
stop_game(pid_python)

# Maybe use a softer method, by sending a WM_CLOSE message first
# windows only
Expand All @@ -75,6 +87,22 @@ func configure_timer() -> void:
timer.wait_time = 1.0
timer.timeout.connect(on_timer_timeout)

func read_launcher_config(path: String, dict: Dictionary):
var config = ConfigFile.new()
# Load data from a file.
var err = config.load(path)
# If the file didn't load, ignore it.
if err != OK:
return
dict["fullscreen"] = config.get_value("SETTINGS", "fullscreen")
dict["shortcut_kill_game"] = config.get_value("SETTINGS", "shortcut_kill_game")
if dict["fullscreen"]:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)

func start_python_shortcut_listener(path: String) -> void:
pid_python = OS.create_process("python", [path])
print("Python running at: ", pid_python)

func create_game_folder(base_dir: String) -> void:
var dir = DirAccess.open(base_dir)
if dir.dir_exists(base_dir.path_join("games")): return
Expand Down Expand Up @@ -182,7 +210,7 @@ func launch_game(game_name: String) -> void:
timer.start()

func stop_game(pid: int) -> void:
if pid_watching < 0: return
if pid < 0: return
games_container.can_move = true
OS.kill(pid)

Expand Down
80 changes: 80 additions & 0 deletions shortcut_listener.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import asyncio
import websockets
import keyboard
import threading
import configparser

# List to hold connected WebSocket clients
connected_clients = set()
current_shortcut = None

def read_config():
config = configparser.ConfigParser()
config.read('launcher_config.ini')
shortcut_kill_game = config.get('SETTINGS', 'shortcut_kill_game', fallback="ctrl+q")
if shortcut_kill_game:
# Strip any surrounding quotes
shortcut_kill_game = shortcut_kill_game.strip('"\'')

return shortcut_kill_game

async def handle_client(websocket, path):
# Register the client
connected_clients.add(websocket)
print(f"New client connected. Total clients: {len(connected_clients)}")
try:
# Keep the connection open and listen for messages (though we won't use them)
async for _ in websocket:
pass
except websockets.exceptions.ConnectionClosed:
print("Client disconnected")
finally:
# Unregister the client
connected_clients.remove(websocket)
print(f"Client disconnected. Total clients: {len(connected_clients)}")

async def broadcast_key_press(key_name):
if connected_clients: # Check if there are any connected clients
message = f"KEY_PRESSED:{key_name}"
print(f"Broadcasting message: {message}")
await asyncio.gather(*[client.send(message) for client in connected_clients])
else:
print("No clients connected; message not sent.")

def on_shortcut_combination():
print(f"Shortcut {current_shortcut} triggered!")
if connected_clients:
asyncio.run_coroutine_threadsafe(broadcast_key_press(f"KEY_COMBINATION:{current_shortcut}"), loop)
else:
print("No clients to send the key combination to.")

def start_websocket_server():
global loop
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

# Start the WebSocket server
start_server = websockets.serve(handle_client, "localhost", 65432)

# Run the server in the event loop
loop.run_until_complete(start_server)
print("WebSocket server started on ws://localhost:65432")
loop.run_forever()

def initialize_shortcut():
global current_shortcut
current_shortcut = read_config()
if current_shortcut:
keyboard.add_hotkey(current_shortcut, on_shortcut_combination)
print(f"Listening for the shortcut from config: {current_shortcut}")
else:
print("No shortcut found in config.")

# Start the WebSocket server in a background thread
server_thread = threading.Thread(target=start_websocket_server)
server_thread.start()

# Initialize the shortcut from config
# Start listening for key presses
initialize_shortcut()
print("Listening for key presses...")

0 comments on commit e766c2c

Please sign in to comment.