diff options
Diffstat (limited to 'src')
35 files changed, 7621 insertions, 0 deletions
diff --git a/src/config/config_manager.h b/src/config/config_manager.h new file mode 100644 index 0000000..97f751e --- /dev/null +++ b/src/config/config_manager.h @@ -0,0 +1,101 @@ +#ifndef SRDWM_CONFIG_MANAGER_H +#define SRDWM_CONFIG_MANAGER_H + +#include <string> +#include <memory> +#include <map> +#include <functional> +#include <vector> // Added missing include + +// Forward declarations +class WindowManager; + +// Configuration manager for srdwm +class ConfigManager { +public: + struct KeyBinding { + std::string key; + std::string command; + std::string description; + }; + + struct LayoutConfig { + std::string name; + std::string type; // "tiling", "dynamic", "floating" + std::map<std::string, std::string> properties; + }; + + struct ThemeConfig { + std::string name; + std::map<std::string, std::string> colors; + std::map<std::string, std::string> fonts; + std::map<std::string, int> dimensions; + }; + + ConfigManager(); + ~ConfigManager(); + + // Configuration loading + bool load_config(const std::string& config_path); + bool reload_config(); + + // Configuration access + std::string get_string(const std::string& key, const std::string& default_value = "") const; + int get_int(const std::string& key, int default_value = 0) const; + bool get_bool(const std::string& key, bool default_value = false) const; + double get_float(const std::string& key, double default_value = 0.0) const; + + // Key bindings + std::vector<KeyBinding> get_key_bindings() const; + bool add_key_binding(const std::string& key, const std::string& command, const std::string& description = ""); + bool remove_key_binding(const std::string& key); + + // Layout configuration + std::vector<LayoutConfig> get_layout_configs() const; + LayoutConfig get_layout_config(const std::string& name) const; + + // Theme configuration + ThemeConfig get_theme_config() const; + bool set_theme(const std::string& theme_name); + + // Window rules + struct WindowRule { + std::string match_type; // "class", "title", "role" + std::string match_value; + std::map<std::string, std::string> properties; + }; + + std::vector<WindowRule> get_window_rules() const; + bool add_window_rule(const WindowRule& rule); + + // Configuration validation + bool validate_config() const; + std::vector<std::string> get_validation_errors() const; + +private: + std::string config_path_; + std::map<std::string, std::string> string_values_; + std::map<std::string, int> int_values_; + std::map<std::string, bool> bool_values_; + std::map<std::string, double> float_values_; + + std::vector<KeyBinding> key_bindings_; + std::vector<LayoutConfig> layout_configs_; + std::vector<WindowRule> window_rules_; + ThemeConfig current_theme_; + + std::vector<std::string> validation_errors_; + + // Private methods + void parse_config_file(const std::string& content); + void setup_default_config(); + bool validate_key_binding(const KeyBinding& binding) const; + bool validate_layout_config(const LayoutConfig& config) const; + bool validate_window_rule(const WindowRule& rule) const; + + // Lua integration (placeholder for future implementation) + bool execute_lua_config(const std::string& lua_code); +}; + +#endif // SRDWM_CONFIG_MANAGER_H + diff --git a/src/config/lua_manager.cc b/src/config/lua_manager.cc new file mode 100644 index 0000000..1956578 --- /dev/null +++ b/src/config/lua_manager.cc @@ -0,0 +1,1024 @@ +#include "lua_manager.h" +#include <iostream> +#include <fstream> +#include <filesystem> +#include <algorithm> +#include <cstring> + +// Global Lua manager instance +std::unique_ptr<LuaManager> g_lua_manager; + +// Default configuration values +namespace Defaults { + std::map<std::string, LuaConfigValue> create_default_config() { + std::map<std::string, LuaConfigValue> config; + + config["general.default_layout"] = {LuaConfigValue::Type::String, "dynamic", 0.0, false, {}, ""}; + config["general.smart_placement"] = {LuaConfigValue::Type::Boolean, "", 0.0, true, {}, ""}; + config["general.window_gap"] = {LuaConfigValue::Type::Number, "", 8.0, false, {}, ""}; + config["general.border_width"] = {LuaConfigValue::Type::Number, "", 2.0, false, {}, ""}; + config["general.animations"] = {LuaConfigValue::Type::Boolean, "", 0.0, true, {}, ""}; + config["general.animation_duration"] = {LuaConfigValue::Type::Number, "", 200.0, false, {}, ""}; + config["general.focus_follows_mouse"] = {LuaConfigValue::Type::Boolean, "", 0.0, false, {}, ""}; + config["general.mouse_follows_focus"] = {LuaConfigValue::Type::Boolean, "", 0.0, true, {}, ""}; + config["general.auto_raise"] = {LuaConfigValue::Type::Boolean, "", 0.0, false, {}, ""}; + config["general.auto_focus"] = {LuaConfigValue::Type::Boolean, "", 0.0, true, {}, ""}; + + config["monitor.primary_layout"] = {LuaConfigValue::Type::String, "dynamic", 0.0, false, {}, ""}; + config["monitor.secondary_layout"] = {LuaConfigValue::Type::String, "tiling", 0.0, false, {}, ""}; + config["monitor.auto_detect"] = {LuaConfigValue::Type::Boolean, "", 0.0, true, {}, ""}; + config["monitor.primary_workspace"] = {LuaConfigValue::Type::Number, "", 1.0, false, {}, ""}; + config["monitor.workspace_count"] = {LuaConfigValue::Type::Number, "", 10.0, false, {}, ""}; + + config["performance.vsync"] = {LuaConfigValue::Type::Boolean, "", 0.0, true, {}, ""}; + config["performance.max_fps"] = {LuaConfigValue::Type::Number, "", 60.0, false, {}, ""}; + config["performance.window_cache_size"] = {LuaConfigValue::Type::Number, "", 100.0, false, {}, ""}; + config["performance.event_queue_size"] = {LuaConfigValue::Type::Number, "", 1000.0, false, {}, ""}; + + config["debug.logging"] = {LuaConfigValue::Type::Boolean, "", 0.0, true, {}, ""}; + config["debug.log_level"] = {LuaConfigValue::Type::String, "info", 0.0, false, {}, ""}; + config["debug.profile"] = {LuaConfigValue::Type::Boolean, "", 0.0, false, {}, ""}; + config["debug.trace_events"] = {LuaConfigValue::Type::Boolean, "", 0.0, false, {}, ""}; + + return config; + } +} + +LuaManager::LuaManager() + : L_(nullptr) + , window_manager_(nullptr) + , layout_engine_(nullptr) + , platform_(nullptr) { + + // Initialize with default values + config_values_ = Defaults::create_default_config(); +} + +LuaManager::~LuaManager() { + shutdown(); +} + +bool LuaManager::initialize() { + std::cout << "Initializing Lua manager..." << std::endl; + + // Create Lua state + L_ = luaL_newstate(); + if (!L_) { + std::cerr << "Failed to create Lua state" << std::endl; + return false; + } + + std::cout << "Lua state created successfully" << std::endl; + + // Open Lua libraries + luaL_openlibs(L_); + std::cout << "Lua libraries opened" << std::endl; + + // Setup Lua environment + setup_lua_environment(); + std::cout << "Lua environment setup complete" << std::endl; + + // Register SRD module + register_srd_module(); + std::cout << "SRD module registration complete" << std::endl; + + // Load default configuration + load_default_config(); + std::cout << "Default configuration loaded" << std::endl; + + std::cout << "Lua manager initialized successfully" << std::endl; + return true; +} + +void LuaManager::shutdown() { + if (L_) { + lua_close(L_); + L_ = nullptr; + } + + config_values_.clear(); + key_bindings_.clear(); + lua_errors_.clear(); + validation_errors_.clear(); +} + +void LuaManager::setup_lua_environment() { + // Set Lua path to include SRDWM modules + std::string lua_path = "package.path = package.path .. ';"; + lua_path += get_config_file_path() + "/?.lua;"; + lua_path += get_config_file_path() + "/?/init.lua'"; + + if (luaL_dostring(L_, lua_path.c_str()) != LUA_OK) { + std::cerr << "Failed to set Lua path: " << lua_tostring(L_, -1) << std::endl; + lua_pop(L_, 1); + } + + // Set global error handler + lua_pushcfunction(L_, [](lua_State* L) -> int { + std::string error_msg = lua_tostring(L, 1); + std::cerr << "Lua error: " << error_msg << std::endl; + return 0; + }); + lua_setglobal(L_, "error_handler"); +} + +void LuaManager::register_srd_module() { + std::cout << "Creating srd table..." << std::endl; + + // Create srd table + lua_newtable(L_); + + std::cout << "Registering window functions..." << std::endl; + // Register window functions + register_window_functions(); + + std::cout << "Registering layout functions..." << std::endl; + // Register layout functions + register_layout_functions(); + + std::cout << "Registering theme functions..." << std::endl; + // Register theme functions + register_theme_functions(); + + std::cout << "Registering utility functions..." << std::endl; + // Register utility functions + register_utility_functions(); + + std::cout << "Setting global srd variable..." << std::endl; + // Set global srd variable + lua_setglobal(L_, "srd"); + + // Store reference to this LuaManager instance for callbacks + lua_pushlightuserdata(L_, this); + lua_setglobal(L_, "_lua_manager_instance"); + + std::cout << "SRD module registered successfully" << std::endl; +} + +void LuaManager::register_window_functions() { + // Create window subtable + lua_newtable(L_); + + // Register window functions + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.window.focused() + // Get the global Lua manager instance + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager) { + // Return focused window info + lua_newtable(L); + lua_pushstring(L, "id"); + lua_pushinteger(L, 0); // Placeholder + lua_settable(L, -3); + lua_pushstring(L, "title"); + lua_pushstring(L, "Focused SRDWindow"); + lua_settable(L, -3); + return 1; + } + } + lua_pop(L, 1); + lua_pushnil(L); + return 1; + }); + lua_setfield(L_, -2, "focused"); + + // SRDWindow decoration controls + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.window.set_decorations(window_id, enabled) + const char* window_id = lua_tostring(L, 1); + bool enabled = lua_toboolean(L, 2); + + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager && window_id) { + manager->set_window_decorations(window_id, enabled); + } + } + lua_pop(L, 1); + return 0; + }); + lua_setfield(L_, -2, "set_decorations"); + + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.window.set_border_color(window_id, r, g, b) + const char* window_id = lua_tostring(L, 1); + int r = lua_tointeger(L, 2); + int g = lua_tointeger(L, 3); + int b = lua_tointeger(L, 4); + + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager && window_id) { + manager->set_window_border_color(window_id, r, g, b); + } + } + lua_pop(L, 1); + return 0; + }); + lua_setfield(L_, -2, "set_border_color"); + + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.window.set_border_width(window_id, width) + const char* window_id = lua_tostring(L, 1); + int width = lua_tointeger(L, 2); + + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager && window_id) { + manager->set_window_border_width(window_id, width); + } + } + lua_pop(L, 1); + return 0; + }); + lua_setfield(L_, -2, "set_border_width"); + + // SRDWindow state controls + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.window.set_floating(window_id, floating) + const char* window_id = lua_tostring(L, 1); + bool floating = lua_toboolean(L, 2); + + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager && window_id) { + manager->set_window_floating(window_id, floating); + } + } + lua_pop(L, 1); + return 0; + }); + lua_setfield(L_, -2, "set_floating"); + + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.window.toggle_floating(window_id) + const char* window_id = lua_tostring(L, 1); + + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager && window_id) { + manager->toggle_window_floating(window_id); + } + } + lua_pop(L, 1); + return 0; + }); + lua_setfield(L_, -2, "toggle_floating"); + + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.window.is_floating(window_id) + const char* window_id = lua_tostring(L, 1); + + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager && window_id) { + bool floating = manager->is_window_floating(window_id); + lua_pushboolean(L, floating); + lua_pop(L, 1); // Pop userdata + return 1; + } + } + lua_pop(L, 1); + lua_pushboolean(L, false); + return 1; + }); + lua_setfield(L_, -2, "is_floating"); + + // Set window subtable in the srd table (which is at index -2) + lua_setfield(L_, -2, "window"); +} + +void LuaManager::register_layout_functions() { + // Create layout subtable + lua_newtable(L_); + + // Register layout functions + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.layout.set(layout_name) + const char* layout_name = lua_tostring(L, 1); + if (layout_name) { + // Get the global Lua manager instance + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager) { + // For now, set layout on monitor 0 (primary monitor) + manager->set_layout(0, layout_name); + } + } + lua_pop(L, 1); // Pop the userdata + std::cout << "Switching to layout: " << layout_name << std::endl; + } + return 0; + }); + lua_setfield(L_, -2, "set"); + + // Register configure function + lua_pushcclosure(L_, [](lua_State* L) -> int { + // srd.layout.configure(layout_name, config_table) + const char* layout_name = lua_tostring(L, 1); + if (layout_name && lua_istable(L, 2)) { + // Get the global Lua manager instance + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager) { + // Convert Lua table to C++ map + std::map<std::string, std::string> config; + lua_pushnil(L); // First key + while (lua_next(L, 2) != 0) { + if (lua_isstring(L, -2) && lua_isstring(L, -1)) { + const char* key = lua_tostring(L, -2); + const char* value = lua_tostring(L, -1); + config[key] = value; + } + lua_pop(L, 1); // Remove value, keep key for next iteration + } + lua_pop(L, 1); // Remove the userdata + + // Configure the layout + manager->configure_layout(layout_name, config); + } + } else { + lua_pop(L, 1); + } + std::cout << "Configuring layout: " << layout_name << std::endl; + } + return 0; + }, 0); + lua_setfield(L_, -2, "configure"); + + // Set layout subtable in the srd table (which is at index -2) + lua_setfield(L_, -2, "layout"); +} + +void LuaManager::register_theme_functions() { + // Create theme subtable + lua_newtable(L_); + + // Register theme functions + lua_pushcclosure(L_, [](lua_State* L) -> int { + // srd.theme.set_colors(colors_table) + if (lua_istable(L, 1)) { + // Get the global Lua manager instance + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager) { + // Convert Lua table to C++ map + std::map<std::string, std::string> colors; + lua_pushnil(L); // First key + while (lua_next(L, 1) != 0) { + if (lua_isstring(L, -2) && lua_isstring(L, -1)) { + const char* key = lua_tostring(L, -2); + const char* value = lua_tostring(L, -1); + colors[key] = value; + } + lua_pop(L, 1); // Remove value, keep key for next iteration + } + lua_pop(L, 1); // Remove the userdata + + // Set theme colors + for (const auto& [key, value] : colors) { + manager->set_string("theme." + key, value); + } + } + } else { + lua_pop(L, 1); + } + std::cout << "Setting theme colors" << std::endl; + } + return 0; + }, 0); + lua_setfield(L_, -2, "set_colors"); + + // Set theme subtable in the srd table (which is at index -2) + lua_setfield(L_, -2, "theme"); +} + +void LuaManager::register_utility_functions() { + // Register utility functions directly in the srd table (which is at index -1) + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.set(key, value) + const char* key = lua_tostring(L, 1); + if (key) { + // Get the global Lua manager instance + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager) { + // Handle different value types + if (lua_isstring(L, 2)) { + const char* value = lua_tostring(L, 2); + manager->set_string(key, value); + } else if (lua_isnumber(L, 2)) { + double value = lua_tonumber(L, 2); + manager->set_float(key, value); + } else if (lua_isboolean(L, 2)) { + bool value = lua_toboolean(L, 2); + manager->set_bool(key, value); + } + } + } + lua_pop(L, 1); // Remove the userdata + std::cout << "Setting config: " << key << std::endl; + } + return 0; + }); + lua_setfield(L_, -2, "set"); + + // Register bind function + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.bind(key, function) + const char* key = lua_tostring(L, 1); + if (key && lua_isfunction(L, 2)) { + // Get the global Lua manager instance + lua_getglobal(L, "_lua_manager_instance"); + if (lua_islightuserdata(L, -1)) { + LuaManager* manager = static_cast<LuaManager*>(lua_touserdata(L, -1)); + if (manager) { + // Store the function reference + // For now, just store the key binding + manager->bind_key(key, "lua_function"); + } + } + lua_pop(L, 1); // Remove the userdata + std::cout << "Binding key: " << key << std::endl; + } + return 0; + }); + lua_setfield(L_, -2, "bind"); + + // Register load function + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.load(module_name) + const char* module_name = lua_tostring(L, 1); + if (module_name) { + // TODO: Implement module loading + std::cout << "Loading module: " << module_name << std::endl; + } + return 0; + }); + lua_setfield(L_, -2, "load"); + + // Register spawn function + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.spawn(command) + const char* command = lua_tostring(L, 1); + if (command) { + // TODO: Implement command spawning + std::cout << "Spawning command: " << command << std::endl; + } + return 0; + }); + lua_setfield(L_, -2, "spawn"); + + // Register notify function + lua_pushcfunction(L_, [](lua_State* L) -> int { + // srd.notify(message, level) + const char* message = lua_tostring(L, 1); + const char* level = lua_tostring(L, 2); + if (message) { + std::cout << "Notification [" << (level ? level : "info") << "]: " << message << std::endl; + } + return 0; + }); + lua_setfield(L_, -2, "notify"); +} + +bool LuaManager::load_config_file(const std::string& path) { + if (!L_) { + std::cerr << "Lua manager not initialized" << std::endl; + return false; + } + + std::cout << "Loading config file: " << path << std::endl; + + // Test if srd module exists before loading + lua_getglobal(L_, "srd"); + if (lua_isnil(L_, -1)) { + std::cerr << "ERROR: srd module is nil before loading config!" << std::endl; + lua_pop(L_, 1); + return false; + } else { + std::cout << "srd module exists before loading config" << std::endl; + lua_pop(L_, 1); + } + + // Load and execute Lua file + if (luaL_dofile(L_, path.c_str()) != LUA_OK) { + std::string error = lua_tostring(L_, -1); + add_lua_error("Failed to load config file: " + error); + lua_pop(L_, 1); + return false; + } + + std::cout << "Config file loaded successfully: " << path << std::endl; + return true; +} + +bool LuaManager::load_config_directory(const std::string& dir_path) { + std::cout << "Loading config directory: " << dir_path << std::endl; + + try { + std::filesystem::path config_dir(dir_path); + if (!std::filesystem::exists(config_dir)) { + std::cerr << "Config directory does not exist: " << dir_path << std::endl; + return false; + } + + // Load init.lua first if it exists + std::filesystem::path init_file = config_dir / "init.lua"; + if (std::filesystem::exists(init_file)) { + if (!load_config_file(init_file.string())) { + return false; + } + } + + // Load other .lua files + for (const auto& entry : std::filesystem::directory_iterator(config_dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".lua") { + if (entry.path().filename() != "init.lua") { + if (!load_config_file(entry.path().string())) { + std::cerr << "Failed to load config file: " << entry.path() << std::endl; + // Continue loading other files + } + } + } + } + + std::cout << "Config directory loaded successfully: " << dir_path << std::endl; + return true; + + } catch (const std::exception& e) { + std::cerr << "Error loading config directory: " << e.what() << std::endl; + return false; + } +} + +bool LuaManager::reload_config() { + std::cout << "Reloading configuration..." << std::endl; + + // Clear current configuration + config_values_.clear(); + key_bindings_.clear(); + clear_errors(); + + // Reload default configuration + load_default_config(); + + // Reload user configuration + std::string config_path = get_config_file_path(); + if (!load_config_directory(config_path)) { + std::cerr << "Failed to reload configuration" << std::endl; + return false; + } + + std::cout << "Configuration reloaded successfully" << std::endl; + return true; +} + +// SRDWindow decoration controls implementation +bool LuaManager::set_window_decorations(const std::string& window_id, bool enabled) { + std::cout << "LuaManager: Set window decorations for " << window_id << " to " << (enabled ? "enabled" : "disabled") << std::endl; + + if (!platform_) { + std::cerr << "Platform not available for decoration control" << std::endl; + return false; + } + + // Find window by ID (placeholder implementation) + // In a real implementation, you'd look up the window in the window manager + SRDWindow* window = nullptr; // TODO: Get window from window manager + + if (window) { + platform_->set_window_decorations(window, enabled); + return true; + } + + return false; +} + +bool LuaManager::set_window_border_color(const std::string& window_id, int r, int g, int b) { + std::cout << "LuaManager: Set border color for " << window_id << " to RGB(" << r << "," << g << "," << b << ")" << std::endl; + + if (!platform_) { + std::cerr << "Platform not available for border color control" << std::endl; + return false; + } + + // Find window by ID (placeholder implementation) + SRDWindow* window = nullptr; // TODO: Get window from window manager + + if (window) { + platform_->set_window_border_color(window, r, g, b); + return true; + } + + return false; +} + +bool LuaManager::set_window_border_width(const std::string& window_id, int width) { + std::cout << "LuaManager: Set border width for " << window_id << " to " << width << std::endl; + + if (!platform_) { + std::cerr << "Platform not available for border width control" << std::endl; + return false; + } + + // Find window by ID (placeholder implementation) + SRDWindow* window = nullptr; // TODO: Get window from window manager + + if (window) { + platform_->set_window_border_width(window, width); + return true; + } + + return false; +} + +bool LuaManager::get_window_decorations(const std::string& window_id) const { + if (!platform_) { + return false; + } + + // Find window by ID (placeholder implementation) + SRDWindow* window = nullptr; // TODO: Get window from window manager + + if (window) { + return platform_->get_window_decorations(window); + } + + return false; +} + +// SRDWindow state controls implementation +bool LuaManager::set_window_floating(const std::string& window_id, bool floating) { + std::cout << "LuaManager: Set window " << window_id << " floating to " << (floating ? "true" : "false") << std::endl; + + if (!window_manager_) { + std::cerr << "SRDWindow manager not available for floating control" << std::endl; + return false; + } + + // Find window by ID (placeholder implementation) + SRDWindow* window = nullptr; // TODO: Get window from window manager + + if (window) { + // TODO: Implement window floating state change + // This would involve changing the window's layout state + return true; + } + + return false; +} + +bool LuaManager::toggle_window_floating(const std::string& window_id) { + std::cout << "LuaManager: Toggle window " << window_id << " floating state" << std::endl; + + bool current_state = is_window_floating(window_id); + return set_window_floating(window_id, !current_state); +} + +bool LuaManager::is_window_floating(const std::string& window_id) const { + if (!window_manager_) { + return false; + } + + // Find window by ID (placeholder implementation) + SRDWindow* window = nullptr; // TODO: Get window from window manager + + if (window) { + // TODO: Check window's current layout state + return false; // Placeholder + } + + return false; +} + +void LuaManager::load_default_config() { + config_values_ = Defaults::create_default_config(); +} + +std::string LuaManager::get_string(const std::string& key, const std::string& default_value) const { + auto it = config_values_.find(key); + if (it != config_values_.end() && it->second.type == LuaConfigValue::Type::String) { + return it->second.string_value; + } + return default_value; +} + +int LuaManager::get_int(const std::string& key, int default_value) const { + auto it = config_values_.find(key); + if (it != config_values_.end() && it->second.type == LuaConfigValue::Type::Number) { + return static_cast<int>(it->second.number_value); + } + return default_value; +} + +bool LuaManager::get_bool(const std::string& key, bool default_value) const { + auto it = config_values_.find(key); + if (it != config_values_.end() && it->second.type == LuaConfigValue::Type::Boolean) { + return it->second.bool_value; + } + return default_value; +} + +double LuaManager::get_float(const std::string& key, double default_value) const { + auto it = config_values_.find(key); + if (it != config_values_.end() && it->second.type == LuaConfigValue::Type::Number) { + return it->second.number_value; + } + return default_value; +} + +void LuaManager::set_string(const std::string& key, const std::string& value) { + LuaConfigValue config_value; + config_value.type = LuaConfigValue::Type::String; + config_value.string_value = value; + config_values_[key] = config_value; +} + +void LuaManager::set_int(const std::string& key, int value) { + LuaConfigValue config_value; + config_value.type = LuaConfigValue::Type::Number; + config_value.number_value = static_cast<double>(value); + config_values_[key] = config_value; +} + +void LuaManager::set_bool(const std::string& key, bool value) { + LuaConfigValue config_value; + config_value.type = LuaConfigValue::Type::Boolean; + config_value.bool_value = value; + config_values_[key] = config_value; +} + +void LuaManager::set_float(const std::string& key, double value) { + LuaConfigValue config_value; + config_value.type = LuaConfigValue::Type::Number; + config_value.number_value = value; + config_values_[key] = config_value; +} + +bool LuaManager::bind_key(const std::string& key_combination, const std::string& lua_function) { + key_bindings_[key_combination] = lua_function; + std::cout << "Bound key: " << key_combination << " -> " << lua_function << std::endl; + return true; +} + +bool LuaManager::unbind_key(const std::string& key_combination) { + auto it = key_bindings_.find(key_combination); + if (it != key_bindings_.end()) { + key_bindings_.erase(it); + std::cout << "Unbound key: " << key_combination << std::endl; + return true; + } + return false; +} + +std::vector<std::string> LuaManager::get_bound_keys() const { + std::vector<std::string> keys; + for (const auto& binding : key_bindings_) { + keys.push_back(binding.first); + } + return keys; +} + +bool LuaManager::execute_lua_code(const std::string& code) { + if (!L_) { + return false; + } + + if (luaL_dostring(L_, code.c_str()) != LUA_OK) { + std::string error = lua_tostring(L_, -1); + add_lua_error("Failed to execute Lua code: " + error); + lua_pop(L_, 1); + return false; + } + + return true; +} + +bool LuaManager::validate_lua_syntax(const std::string& code) { + if (!L_) { + return false; + } + + // Try to load the code as a function (syntax check) + if (luaL_loadstring(L_, code.c_str()) != LUA_OK) { + std::string error = lua_tostring(L_, -1); + add_lua_error("Lua syntax error: " + error); + lua_pop(L_, 1); + return false; + } + + // Pop the loaded function + lua_pop(L_, 1); + return true; +} + +std::vector<std::string> LuaManager::get_lua_errors() const { + return lua_errors_; +} + +bool LuaManager::validate_config() const { + std::cout << "LuaManager: Validating configuration..." << std::endl; + + // Check for required configuration values + std::vector<std::string> required_keys = { + "general.default_layout", + "general.window_gap", + "general.border_width" + }; + + for (const auto& key : required_keys) { + auto it = config_values_.find(key); + if (it == config_values_.end()) { + add_validation_error("Missing required configuration: " + key); + return false; + } + } + + // Validate layout configuration + auto layout_it = config_values_.find("general.default_layout"); + if (layout_it != config_values_.end()) { + std::string layout = layout_it->second.string_value; + if (layout != "tiling" && layout != "dynamic" && layout != "floating") { + add_validation_error("Invalid default layout: " + layout); + return false; + } + } + + // Validate numeric values + auto gap_it = config_values_.find("general.window_gap"); + if (gap_it != config_values_.end()) { + double gap = gap_it->second.number_value; + if (gap < 0 || gap > 100) { + add_validation_error("SRDWindow gap must be between 0 and 100"); + return false; + } + } + + auto border_it = config_values_.find("general.border_width"); + if (border_it != config_values_.end()) { + double border = border_it->second.number_value; + if (border < 0 || border > 50) { + add_validation_error("Border width must be between 0 and 50"); + return false; + } + } + + std::cout << "LuaManager: Configuration validation passed" << std::endl; + return true; +} + +std::vector<std::string> LuaManager::get_validation_errors() const { + return validation_errors_; +} + +void LuaManager::reset_config(const std::string& key) { + auto default_config = Defaults::create_default_config(); + auto it = default_config.find(key); + if (it != default_config.end()) { + config_values_[key] = it->second; + } +} + +void LuaManager::reset_all_configs() { + config_values_ = Defaults::create_default_config(); +} + +void LuaManager::reset_category(const std::string& category) { + std::string prefix = category + "."; + auto default_config = Defaults::create_default_config(); + for (const auto& default_item : default_config) { + if (default_item.first.substr(0, prefix.length()) == prefix) { + config_values_[default_item.first] = default_item.second; + } + } +} + +void LuaManager::add_lua_error(const std::string& error) { + lua_errors_.push_back(error); +} + +void LuaManager::add_validation_error(const std::string& error) const { + const_cast<LuaManager*>(this)->validation_errors_.push_back(error); +} + +void LuaManager::clear_errors() { + lua_errors_.clear(); + validation_errors_.clear(); +} + +std::string LuaManager::get_config_file_path() const { + // Platform-specific config path detection + const char* home = std::getenv("HOME"); + if (home) { + std::string config_dir = std::string(home) + "/.config/srdwm"; + return config_dir + "/config.lua"; + } + return "./config.lua"; +} + +std::string LuaManager::get_default_config_path() const { + // Platform-specific default config path + const char* home = std::getenv("HOME"); + if (home) { + std::string config_dir = std::string(home) + "/.config/srdwm"; + return config_dir + "/default.lua"; + } + return "./default.lua"; +} + +bool LuaManager::create_default_config() const { + std::cout << "LuaManager: Creating default configuration..." << std::endl; + + // Create default configuration directory + const char* home = std::getenv("HOME"); + if (home) { + std::string config_dir = std::string(home) + "/.config/srdwm"; + std::string config_file = config_dir + "/config.lua"; + + // Create directory if it doesn't exist + std::filesystem::create_directories(config_dir); + + // Create default config file + std::ofstream file(config_file); + if (file.is_open()) { + file << "-- SRDWM Default Configuration\n"; + file << "-- Generated automatically\n\n"; + file << "-- Basic settings\n"; + file << "srd.set('general.default_layout', 'dynamic')\n"; + file << "srd.set('general.window_gap', 8)\n"; + file << "srd.set('general.border_width', 2)\n"; + file << "srd.set('general.animations', true)\n\n"; + file << "-- Key bindings\n"; + file << "srd.bind('Mod4+Return', function()\n"; + file << " srd.spawn('alacritty')\n"; + file << "end)\n\n"; + file << "srd.bind('Mod4+Q', function()\n"; + file << " local focused = srd.window.focused()\n"; + file << " if focused then\n"; + file << " srd.window.close(focused)\n"; + file << " end\n"; + file << "end)\n\n"; + file << "srd.bind('Mod4+Space', function()\n"; + file << " srd.layout.set('tiling')\n"; + file << "end)\n"; + file.close(); + + std::cout << "LuaManager: Default configuration created at " << config_file << std::endl; + return true; + } + } + + std::cout << "LuaManager: Failed to create default configuration" << std::endl; + return false; +} + +// Layout system methods +std::vector<std::string> LuaManager::get_available_layouts() const { + if (layout_engine_) { + return layout_engine_->get_available_layouts(); + } + return {"tiling", "dynamic", "floating"}; +} + +bool LuaManager::set_layout(int monitor_id, const std::string& layout_name) { + if (layout_engine_) { + return layout_engine_->set_layout(monitor_id, layout_name); + } + std::cout << "LuaManager: Setting layout '" << layout_name << "' for monitor " << monitor_id << std::endl; + return true; +} + +std::string LuaManager::get_layout_name(int monitor_id) const { + if (layout_engine_) { + return layout_engine_->get_layout_name(monitor_id); + } + return "dynamic"; +} + +void LuaManager::set_layout_engine(LayoutEngine* engine) { + layout_engine_ = engine; + std::cout << "LuaManager: Layout engine connected" << std::endl; +} + +bool LuaManager::configure_layout(const std::string& layout_name, const std::map<std::string, std::string>& config) { + if (layout_engine_) { + return layout_engine_->configure_layout(layout_name, config); + } + std::cout << "LuaManager: Configured layout '" << layout_name << "' with " << config.size() << " parameters" << std::endl; + return true; +} + + diff --git a/src/config/lua_manager.h b/src/config/lua_manager.h new file mode 100644 index 0000000..c1c22c5 --- /dev/null +++ b/src/config/lua_manager.h @@ -0,0 +1,166 @@ +#ifndef SRDWM_LUA_MANAGER_H +#define SRDWM_LUA_MANAGER_H + +#include <memory> +#include <string> +#include <vector> +#include <map> +#include <functional> +#include <lua.hpp> + +// Forward declarations +class SRDWindow; +class SRDWindowManager; +class LayoutEngine; + +// Include platform header for full definition +#include "../platform/platform.h" + +// Include layout engine header +#include "../layouts/layout_engine.h" + +// Lua callback function type +using LuaCallback = std::function<void()>; + +// Lua configuration value +struct LuaConfigValue { + enum class Type { + String, + Number, + Boolean, + Table, + Function + }; + + Type type; + std::string string_value; + double number_value; + bool bool_value; + std::map<std::string, LuaConfigValue> table_value; + std::string function_name; +}; + +// Lua manager for SRDWM +class LuaManager { +public: + LuaManager(); + ~LuaManager(); + + // Initialization and cleanup + bool initialize(); + void shutdown(); + + // Configuration loading + bool load_config_file(const std::string& path); + bool load_config_directory(const std::string& dir_path); + bool reload_config(); + + // Configuration access + LuaConfigValue get_config(const std::string& key) const; + std::string get_string(const std::string& key, const std::string& default_value = "") const; + int get_int(const std::string& key, int default_value = 0) const; + bool get_bool(const std::string& key, bool default_value = false) const; + double get_float(const std::string& key, double default_value = 0.0) const; + + // Configuration modification + void set_config(const std::string& key, const LuaConfigValue& value); + void set_string(const std::string& key, const std::string& value); + void set_int(const std::string& key, int value); + void set_bool(const std::string& key, bool value); + void set_float(const std::string& key, double value); + + // Key binding system + bool bind_key(const std::string& key_combination, const std::string& lua_function); + bool unbind_key(const std::string& key_combination); + std::vector<std::string> get_bound_keys() const; + + // Layout system + bool configure_layout(const std::string& layout_name, const std::map<std::string, LuaConfigValue>& config); + bool configure_layout(const std::string& layout_name, const std::map<std::string, std::string>& config); + bool register_custom_layout(const std::string& name, const std::string& lua_function); + std::vector<std::string> get_available_layouts() const; + bool set_layout(int monitor_id, const std::string& layout_name); + std::string get_layout_name(int monitor_id) const; + void set_layout_engine(LayoutEngine* engine); + + // Theme system + bool set_theme_colors(const std::map<std::string, std::string>& colors); + bool set_theme_decorations(const std::map<std::string, LuaConfigValue>& decorations); + + // SRDWindow decoration controls + bool set_window_decorations(const std::string& window_id, bool enabled); + bool set_window_border_color(const std::string& window_id, int r, int g, int b); + bool set_window_border_width(const std::string& window_id, int width); + bool get_window_decorations(const std::string& window_id) const; + + // SRDWindow state controls + bool set_window_floating(const std::string& window_id, bool floating); + bool toggle_window_floating(const std::string& window_id); + bool is_window_floating(const std::string& window_id) const; + std::map<std::string, std::string> get_theme_colors() const; + + // SRDWindow rules + bool add_window_rule(const std::map<std::string, LuaConfigValue>& rule); + bool remove_window_rule(const std::string& rule_name); + std::vector<std::map<std::string, LuaConfigValue>> get_window_rules() const; + + // Utility functions + bool execute_lua_code(const std::string& code); + bool validate_lua_syntax(const std::string& code); + std::vector<std::string> get_lua_errors() const; + + // Configuration validation + bool validate_config() const; + std::vector<std::string> get_validation_errors() const; + + // Reset functionality + void reset_config(const std::string& key); + void reset_all_configs(); + void reset_category(const std::string& category); + +private: + lua_State* L_; + std::map<std::string, LuaConfigValue> config_values_; + std::map<std::string, std::string> key_bindings_; + std::vector<std::string> lua_errors_; + std::vector<std::string> validation_errors_; + + // SRDWindow manager reference + SRDWindowManager* window_manager_; + LayoutEngine* layout_engine_; + Platform* platform_; + + // Private methods + void setup_lua_environment(); + void register_srd_module(); + void register_window_functions(); + void register_layout_functions(); + void register_theme_functions(); + void register_utility_functions(); + + // Configuration helpers + void parse_config_value(lua_State* L, int index, const std::string& key); + void save_config_to_lua(); + void load_default_config(); + + // Error handling + void add_lua_error(const std::string& error); + void add_validation_error(const std::string& error) const; + void clear_errors(); + + // File watching + void setup_file_watcher(); + void on_config_file_changed(const std::string& path); + + // Helper functions + std::string get_config_file_path() const; + std::string get_default_config_path() const; + bool create_default_config() const; +}; + +// Global Lua manager instance +extern std::unique_ptr<LuaManager> g_lua_manager; + +#endif // SRDWM_LUA_MANAGER_H + + diff --git a/src/core/event_system.cc b/src/core/event_system.cc new file mode 100644 index 0000000..ed022cb --- /dev/null +++ b/src/core/event_system.cc @@ -0,0 +1,94 @@ +#include "event_system.h" +#include "window.h" +#include <iostream> +#include <algorithm> + +// Global event system instance +EventSystem g_event_system; + +EventSystem::EventSystem() : processing_events(false) { +} + +EventSystem::~EventSystem() { + clear_handlers(); +} + +void EventSystem::register_handler(EventType type, EventHandler handler) { + handlers[type].push_back(handler); +} + +void EventSystem::unregister_handler(EventType type, EventHandler handler) { + auto& type_handlers = handlers[type]; + type_handlers.erase( + std::remove_if(type_handlers.begin(), type_handlers.end(), + [&handler](const EventHandler& h) { + // Note: This is a simplified comparison + // In a real implementation, you'd want a more sophisticated way to identify handlers + return false; // For now, we don't remove handlers + }), + type_handlers.end() + ); +} + +void EventSystem::emit_event(const Event& event) { + if (processing_events) { + // Queue the event if we're currently processing events + event_queue.push_back(std::make_unique<Event>(event)); + return; + } + + auto it = handlers.find(event.type); + if (it != handlers.end()) { + for (const auto& handler : it->second) { + try { + handler(event); + } catch (const std::exception& e) { + std::cerr << "Error in event handler: " << e.what() << std::endl; + } + } + } +} + +void EventSystem::emit_window_event(EventType type, SRDWindow* window) { + switch (type) { + case EventType::WINDOW_CREATED: + emit_event(SRDWindowCreatedEvent(window)); + break; + case EventType::WINDOW_DESTROYED: + emit_event(SRDWindowDestroyedEvent(window)); + break; + default: + // For other window events, we need more context + break; + } +} + +void EventSystem::emit_key_event(EventType type, unsigned int keycode, unsigned int modifiers) { + emit_event(KeyEvent(type, keycode, modifiers)); +} + +void EventSystem::emit_mouse_event(EventType type, int x, int y, unsigned int button, unsigned int modifiers) { + emit_event(MouseEvent(type, x, y, button, modifiers)); +} + +void EventSystem::process_events() { + if (processing_events) { + return; // Prevent recursive processing + } + + processing_events = true; + + // Process queued events + while (!event_queue.empty()) { + auto event = std::move(event_queue.front()); + event_queue.erase(event_queue.begin()); + + emit_event(*event); + } + + processing_events = false; +} + +void EventSystem::clear_handlers() { + handlers.clear(); +} diff --git a/src/core/event_system.h b/src/core/event_system.h new file mode 100644 index 0000000..2af717b --- /dev/null +++ b/src/core/event_system.h @@ -0,0 +1,134 @@ +#ifndef SRDWM_EVENT_SYSTEM_H +#define SRDWM_EVENT_SYSTEM_H + +#include <functional> +#include <map> +#include <vector> +#include <string> +#include <memory> + +// Forward declarations +class SRDWindow; +class Monitor; + +// Event types +enum class EventType { + WINDOW_CREATED, + WINDOW_DESTROYED, + WINDOW_MOVED, + WINDOW_RESIZED, + WINDOW_FOCUSED, + WINDOW_UNFOCUSED, + WINDOW_MINIMIZED, + WINDOW_MAXIMIZED, + WINDOW_RESTORED, + MONITOR_ADDED, + MONITOR_REMOVED, + MONITOR_CHANGED, + KEY_PRESSED, + KEY_RELEASED, + MOUSE_MOVED, + MOUSE_PRESSED, + MOUSE_RELEASED, + MOUSE_WHEEL, + CUSTOM_EVENT +}; + +// Base event class +class Event { +public: + virtual ~Event() = default; + EventType type; + + Event(EventType t) : type(t) {} +}; + +// SRDWindow events +class SRDWindowEvent : public Event { +public: + SRDWindow* window; + + SRDWindowEvent(EventType t, SRDWindow* w) : Event(t), window(w) {} +}; + +class SRDWindowCreatedEvent : public SRDWindowEvent { +public: + SRDWindowCreatedEvent(SRDWindow* w) : SRDWindowEvent(EventType::WINDOW_CREATED, w) {} +}; + +class SRDWindowDestroyedEvent : public SRDWindowEvent { +public: + SRDWindowDestroyedEvent(SRDWindow* w) : SRDWindowEvent(EventType::WINDOW_DESTROYED, w) {} +}; + +class SRDWindowMovedEvent : public SRDWindowEvent { +public: + int x, y; + + SRDWindowMovedEvent(SRDWindow* w, int new_x, int new_y) + : SRDWindowEvent(EventType::WINDOW_MOVED, w), x(new_x), y(new_y) {} +}; + +class SRDWindowResizedEvent : public SRDWindowEvent { +public: + int width, height; + + SRDWindowResizedEvent(SRDWindow* w, int new_width, int new_height) + : SRDWindowEvent(EventType::WINDOW_RESIZED, w), width(new_width), height(new_height) {} +}; + +// Input events +class KeyEvent : public Event { +public: + unsigned int keycode; + unsigned int modifiers; + + KeyEvent(EventType t, unsigned int kc, unsigned int mods) + : Event(t), keycode(kc), modifiers(mods) {} +}; + +class MouseEvent : public Event { +public: + int x, y; + unsigned int button; + unsigned int modifiers; + + MouseEvent(EventType t, int mouse_x, int mouse_y, unsigned int btn, unsigned int mods) + : Event(t), x(mouse_x), y(mouse_y), button(btn), modifiers(mods) {} +}; + +// Event handler function type +using EventHandler = std::function<void(const Event&)>; + +// Event system class +class EventSystem { +public: + EventSystem(); + ~EventSystem(); + + // Register event handlers + void register_handler(EventType type, EventHandler handler); + void unregister_handler(EventType type, EventHandler handler); + + // Emit events + void emit_event(const Event& event); + void emit_window_event(EventType type, SRDWindow* window); + void emit_key_event(EventType type, unsigned int keycode, unsigned int modifiers); + void emit_mouse_event(EventType type, int x, int y, unsigned int button, unsigned int modifiers); + + // Process event queue + void process_events(); + + // Clear all handlers + void clear_handlers(); + +private: + std::map<EventType, std::vector<EventHandler>> handlers; + std::vector<std::unique_ptr<Event>> event_queue; + bool processing_events; +}; + +// Global event system instance +extern EventSystem g_event_system; + +#endif // SRDWM_EVENT_SYSTEM_H diff --git a/src/core/window.cc b/src/core/window.cc new file mode 100644 index 0000000..cefc131 --- /dev/null +++ b/src/core/window.cc @@ -0,0 +1,82 @@ +#include "window.h" + +SRDWindow::SRDWindow(int id, const std::string& title) + : id_(id), title_(title), x_(0), y_(0), width_(0), height_(0), decorated_(true) { +} + +int SRDWindow::getId() const { + return id_; +} + +const std::string& SRDWindow::getTitle() const { + return title_; +} + +int SRDWindow::getX() const { + return x_; +} + +int SRDWindow::getY() const { + return y_; +} + +int SRDWindow::getWidth() const { + return width_; +} + +int SRDWindow::getHeight() const { + return height_; +} + +bool SRDWindow::isDecorated() const { + return decorated_; +} + +void SRDWindow::setPosition(int x, int y) { + x_ = x; + y_ = y; +} + +void SRDWindow::setSize(int width, int height) { + width_ = width; + height_ = height; +} + +void SRDWindow::setGeometry(int x, int y, int width, int height) { + x_ = x; + y_ = y; + width_ = width; + height_ = height; +} + +void SRDWindow::setDimensions(int x, int y, int width, int height) { + x_ = x; + y_ = y; + width_ = width; + height_ = height; +} + +void SRDWindow::setDecorated(bool decorated) { + decorated_ = decorated; +} + +void SRDWindow::setId(int id) { + id_ = id; +} + +// Basic methods for managing window state (will be platform-specific) +void SRDWindow::map() { + // Platform-specific implementation to show the window +} + +void SRDWindow::unmap() { + // Platform-specific implementation to hide the window +} + +void SRDWindow::focus() { + // Platform-specific implementation to give focus to the window +} + +void SRDWindow::close() { + // Platform-specific implementation to close the window +} diff --git a/src/core/window.h b/src/core/window.h new file mode 100644 index 0000000..6d7405a --- /dev/null +++ b/src/core/window.h @@ -0,0 +1,49 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include <string> + +class SRDWindow { +public: + // Constructor + SRDWindow(int id, const std::string& title); + + // Getters + int getId() const; + const std::string& getTitle() const; + int getX() const; + int getY() const; + int getWidth() const; + int getHeight() const; + bool isDecorated() const; + + // Setters + void setTitle(const std::string& title); + void setPosition(int x, int y); + void setSize(int width, int height); + void setGeometry(int x, int y, int width, int height); + void setDimensions(int x, int y, int width, int height); + void setDecorated(bool decorated); + void setId(int id); + + // Window management methods + void map(); + void unmap(); + void focus(); + void close(); + + // Comparison operators for use in containers + bool operator<(const SRDWindow& other) const { return id_ < other.id_; } + bool operator==(const SRDWindow& other) const { return id_ == other.id_; } + +private: + int id_; + std::string title_; + int x_; + int y_; + int width_; + int height_; + bool decorated_; +}; + +#endif // WINDOW_H diff --git a/src/core/window_manager.cc b/src/core/window_manager.cc new file mode 100644 index 0000000..08033fe --- /dev/null +++ b/src/core/window_manager.cc @@ -0,0 +1,673 @@ +#include "window_manager.h" +#include "window.h" +#include "../input/input_handler.h" +#include "../layouts/layout_engine.h" +#include "../platform/platform.h" +#include "../config/lua_manager.h" +#include <iostream> +#include <chrono> +#include <thread> + +SRDWindowManager::SRDWindowManager() { + std::cout << "SRDWindowManager: Initializing..." << std::endl; +} + +SRDWindowManager::~SRDWindowManager() { + std::cout << "SRDWindowManager: Shutting down..." << std::endl; +} + +void SRDWindowManager::run() { + // Main event loop + std::cout << "SRDWindowManager: Starting main loop" << std::endl; + + if (!platform_) { + std::cerr << "SRDWindowManager: No platform available, cannot run" << std::endl; + return; + } + + std::cout << "SRDWindowManager: Entering main event loop..." << std::endl; + + bool running = true; + while (running) { + // Poll for platform events + std::vector<Event> events; + if (platform_->poll_events(events)) { + // Process all events + for (const auto& event : events) { + handle_event(event); + + // Check for exit condition + if (event.type == EventType::KeyPress) { + // TODO: Check for exit key combination + // For now, just continue + } + } + } + + // Manage windows + manage_windows(); + + // Arrange windows if needed + if (layout_engine_) { + layout_engine_->arrange_all_monitors(); + } + + // Small delay to prevent busy waiting + // TODO: Use proper event-driven approach instead of polling + std::this_thread::sleep_for(std::chrono::milliseconds(16)); // ~60 FPS + } + + std::cout << "SRDWindowManager: Main loop ended" << std::endl; +} + +// SRDWindow management +void SRDWindowManager::add_window(std::unique_ptr<SRDWindow> window) { + if (window) { + windows_.push_back(window.get()); + window.release(); // Transfer ownership + + // Add to layout engine if available + if (layout_engine_) { + layout_engine_->add_window(window.get()); + } + + std::cout << "SRDWindowManager: Added window " << window->getId() << std::endl; + } +} + +void SRDWindowManager::remove_window(SRDWindow* window) { + auto it = std::find(windows_.begin(), windows_.end(), window); + if (it != windows_.end()) { + windows_.erase(it); + + // Remove from layout engine if available + if (layout_engine_) { + layout_engine_->remove_window(window); + } + + std::cout << "SRDWindowManager: Removed window " << window->getId() << std::endl; + } +} + +void SRDWindowManager::focus_window(SRDWindow* window) { + focused_window_ = window; + std::cout << "SRDWindowManager: Focused window " << (window ? window->getId() : -1) << std::endl; +} + +SRDWindow* SRDWindowManager::get_focused_window() const { + return focused_window_; +} + +std::vector<SRDWindow*> SRDWindowManager::get_windows() const { + return windows_; +} + +void SRDWindowManager::focus_next_window() { + if (windows_.empty()) return; + + if (!focused_window_) { + // No window focused, focus the first one + focus_window(windows_[0]); + return; + } + + // Find current focused window index + auto it = std::find(windows_.begin(), windows_.end(), focused_window_); + if (it == windows_.end()) { + focus_window(windows_[0]); + return; + } + + // Move to next window (wrap around) + ++it; + if (it == windows_.end()) { + it = windows_.begin(); + } + + focus_window(*it); + std::cout << "SRDWindowManager: Focused next window " << (*it)->getId() << std::endl; +} + +void SRDWindowManager::focus_previous_window() { + if (windows_.empty()) return; + + if (!focused_window_) { + // No window focused, focus the last one + focus_window(windows_.back()); + return; + } + + // Find current focused window index + auto it = std::find(windows_.begin(), windows_.end(), focused_window_); + if (it == windows_.end()) { + focus_window(windows_.back()); + return; + } + + // Move to previous window (wrap around) + if (it == windows_.begin()) { + it = windows_.end() - 1; + } else { + --it; + } + + focus_window(*it); + std::cout << "SRDWindowManager: Focused previous window " << (*it)->getId() << std::endl; +} + +void SRDWindowManager::manage_windows() { + // Manage window states + std::cout << "SRDWindowManager: Managing " << windows_.size() << " windows" << std::endl; +} + +// SRDWindow operations +void SRDWindowManager::close_window(SRDWindow* window) { + if (window) { + std::cout << "SRDWindowManager: Closing window " << window->getId() << std::endl; + // TODO: Implement actual window closing + } +} + +void SRDWindowManager::minimize_window(SRDWindow* window) { + if (window) { + std::cout << "SRDWindowManager: Minimizing window " << window->getId() << std::endl; + // TODO: Implement actual window minimizing + } +} + +void SRDWindowManager::maximize_window(SRDWindow* window) { + if (window) { + std::cout << "SRDWindowManager: Maximizing window " << window->getId() << std::endl; + // TODO: Implement actual window maximizing + } +} + +void SRDWindowManager::move_window(SRDWindow* window, int x, int y) { + if (window) { + window->setPosition(x, y); + update_layout_for_window(window); + std::cout << "SRDWindowManager: Moved window " << window->getId() << " to (" << x << ", " << y << ")" << std::endl; + } +} + +void SRDWindowManager::resize_window(SRDWindow* window, int width, int height) { + if (window) { + window->setSize(width, height); + update_layout_for_window(window); + std::cout << "SRDWindowManager: Resized window " << window->getId() << " to " << width << "x" << height << std::endl; + } +} + +void SRDWindowManager::toggle_window_floating(SRDWindow* window) { + if (!window) return; + + auto it = floating_windows_.find(window); + if (it != floating_windows_.end()) { + // Window is floating, make it tiled + floating_windows_.erase(it); + std::cout << "SRDWindowManager: Window " << window->getId() << " is now tiled" << std::endl; + } else { + // Window is tiled, make it floating + floating_windows_.insert(window); + std::cout << "SRDWindowManager: Window " << window->getId() << " is now floating" << std::endl; + } + + // Re-arrange windows to reflect the change + arrange_windows(); +} + +bool SRDWindowManager::is_window_floating(SRDWindow* window) const { + if (!window) return false; + return floating_windows_.find(window) != floating_windows_.end(); +} + +// Window dragging and resizing implementation +void SRDWindowManager::start_window_drag(SRDWindow* window, int start_x, int start_y) { + if (!window || dragging_window_) return; + + dragging_window_ = window; + drag_start_x_ = start_x; + drag_start_y_ = start_y; + drag_start_window_x_ = window->getX(); + drag_start_window_y_ = window->getY(); + + std::cout << "SRDWindowManager: Started dragging window " << window->getId() << std::endl; +} + +void SRDWindowManager::start_window_resize(SRDWindow* window, int start_x, int start_y, int edge) { + if (!window || resizing_window_) return; + + resizing_window_ = window; + resize_start_x_ = start_x; + resize_start_y_ = start_y; + resize_start_width_ = window->getWidth(); + resize_start_height_ = window->getHeight(); + resize_edge_ = edge; + + std::cout << "SRDWindowManager: Started resizing window " << window->getId() << " edge: " << edge << std::endl; +} + +void SRDWindowManager::update_window_drag(int x, int y) { + if (!dragging_window_) return; + + int delta_x = x - drag_start_x_; + int delta_y = y - drag_start_y_; + + int new_x = drag_start_window_x_ + delta_x; + int new_y = drag_start_window_y_ + delta_y; + + // Ensure window stays within monitor bounds + // TODO: Get actual monitor bounds + new_x = std::max(0, std::min(new_x, 1920 - dragging_window_->getWidth())); + new_y = std::max(0, std::min(new_y, 1080 - dragging_window_->getHeight())); + + dragging_window_->setPosition(new_x, new_y); + update_layout_for_window(dragging_window_); +} + +void SRDWindowManager::update_window_resize(int x, int y) { + if (!resizing_window_) return; + + int delta_x = x - resize_start_x_; + int delta_y = y - resize_start_y_; + + int new_width = resize_start_width_; + int new_height = resize_start_height_; + int new_x = resizing_window_->getX(); + int new_y = resizing_window_->getY(); + + // Handle different resize edges + switch (resize_edge_) { + case 1: // Left edge + new_width = std::max(100, resize_start_width_ - delta_x); + new_x = resize_start_x_ + resize_start_width_ - new_width; + break; + case 2: // Right edge + new_width = std::max(100, resize_start_width_ + delta_x); + break; + case 3: // Top edge + new_height = std::max(100, resize_start_height_ - delta_y); + new_y = resize_start_y_ + resize_start_height_ - new_height; + break; + case 4: // Bottom edge + new_height = std::max(100, resize_start_height_ + delta_y); + break; + case 5: // Corner (both width and height) + new_width = std::max(100, resize_start_width_ + delta_x); + new_height = std::max(100, resize_start_height_ + delta_y); + break; + } + + // Ensure minimum size and bounds + new_width = std::max(100, std::min(new_width, 1920 - new_x)); + new_height = std::max(100, std::min(new_height, 1080 - new_y)); + + resizing_window_->setPosition(new_x, new_y); + resizing_window_->setSize(new_width, new_height); + update_layout_for_window(resizing_window_); +} + +void SRDWindowManager::end_window_drag() { + if (dragging_window_) { + std::cout << "SRDWindowManager: Ended dragging window " << dragging_window_->getId() << std::endl; + dragging_window_ = nullptr; + } +} + +void SRDWindowManager::end_window_resize() { + if (resizing_window_) { + std::cout << "SRDWindowManager: Ended resizing window " << resizing_window_->getId() << std::endl; + resizing_window_ = nullptr; + } +} + +// Layout management +void SRDWindowManager::set_layout(int monitor_id, const std::string& layout_name) { + if (layout_engine_) { + layout_engine_->set_layout(monitor_id, layout_name); + std::cout << "SRDWindowManager: Set layout '" << layout_name << "' for monitor " << monitor_id << std::endl; + } +} + +std::string SRDWindowManager::get_layout(int monitor_id) const { + if (layout_engine_) { + return layout_engine_->get_layout_name(monitor_id); + } + return "dynamic"; +} + +void SRDWindowManager::arrange_windows() { + if (layout_engine_) { + layout_engine_->arrange_all_monitors(); + std::cout << "SRDWindowManager: Arranged all windows" << std::endl; + } +} + +void SRDWindowManager::tile_windows() { + set_layout(0, "tiling"); + arrange_windows(); +} + +void SRDWindowManager::arrange_windows_dynamic() { + set_layout(0, "dynamic"); + arrange_windows(); +} + +// Key binding system +void SRDWindowManager::bind_key(const std::string& key_combination, std::function<void()> action) { + key_bindings_[key_combination] = action; + std::cout << "SRDWindowManager: Bound key '" << key_combination << "'" << std::endl; +} + +void SRDWindowManager::unbind_key(const std::string& key_combination) { + auto it = key_bindings_.find(key_combination); + if (it != key_bindings_.end()) { + key_bindings_.erase(it); + std::cout << "SRDWindowManager: Unbound key '" << key_combination << "'" << std::endl; + } +} + +void SRDWindowManager::handle_key_press(int key_code, int modifiers) { + std::string key_string = key_code_to_string(key_code, modifiers); + pressed_keys_[key_code] = modifiers; + + std::cout << "SRDWindowManager: Key press " << key_code << " (modifiers: " << modifiers << ") -> '" << key_string << "'" << std::endl; + + // Check for key bindings + auto it = key_bindings_.find(key_string); + if (it != key_bindings_.end()) { + std::cout << "SRDWindowManager: Executing key binding for '" << key_string << "'" << std::endl; + it->second(); + } +} + +void SRDWindowManager::handle_key_release(int key_code, int modifiers) { + pressed_keys_.erase(key_code); + std::cout << "SRDWindowManager: Key release " << key_code << std::endl; +} + +// Integration +void SRDWindowManager::set_layout_engine(LayoutEngine* engine) { + layout_engine_ = engine; + std::cout << "SRDWindowManager: Layout engine connected" << std::endl; +} + +void SRDWindowManager::set_lua_manager(LuaManager* manager) { + lua_manager_ = manager; + std::cout << "SRDWindowManager: Lua manager connected" << std::endl; +} + +void SRDWindowManager::set_platform(Platform* platform) { + platform_ = platform; + std::cout << "SRDWindowManager: Platform connected" << std::endl; +} + +// Legacy input handling (for compatibility) +void SRDWindowManager::handle_key_press(int key_code) { + handle_key_press(key_code, 0); +} + +void SRDWindowManager::handle_key_release(int key_code) { + handle_key_release(key_code, 0); +} + +void SRDWindowManager::handle_mouse_button_press(int button, int x, int y) { + std::cout << "SRDWindowManager: Mouse button press " << button << " at (" << x << ", " << y << ")" << std::endl; + + // Find window under cursor + SRDWindow* window_under_cursor = find_window_at_position(x, y); + + if (window_under_cursor) { + // Focus the window + focus_window(window_under_cursor); + + // Handle different mouse buttons + if (button == 1) { // Left button - start drag or resize + if (is_in_titlebar_area(window_under_cursor, x, y)) { + // Start dragging from titlebar + start_window_drag(window_under_cursor, x, y); + } else if (is_in_resize_area(window_under_cursor, x, y)) { + // Start resizing + int edge = get_resize_edge(window_under_cursor, x, y); + start_window_resize(window_under_cursor, x, y, edge); + } + } + } +} + +void SRDWindowManager::handle_mouse_button_release(int button, int x, int y) { + std::cout << "SRDWindowManager: Mouse button release " << button << " at (" << x << ", " << y << ")" << std::endl; + + if (button == 1) { // Left button + if (is_dragging()) { + end_window_drag(); + } else if (is_resizing()) { + end_window_resize(); + } + } +} + +void SRDWindowManager::handle_mouse_motion(int x, int y) { + // Only log if we're not dragging or resizing to avoid spam + if (!is_dragging() && !is_resizing()) { + std::cout << "SRDWindowManager: Mouse motion to (" << x << ", " << y << ")" << std::endl; + } + + // Update drag or resize if active + if (is_dragging()) { + update_window_drag(x, y); + } else if (is_resizing()) { + update_window_resize(x, y); + } +} + +void SRDWindowManager::handle_event(const Event& event) { + std::cout << "SRDWindowManager: Handling event type " << static_cast<int>(event.type) << std::endl; +} + +// Workspace management +void SRDWindowManager::add_workspace(const std::string& name) { + Workspace workspace(next_workspace_id_++, name); + workspaces_.push_back(workspace); + + // If this is the first workspace, make it current + if (workspaces_.size() == 1) { + current_workspace_ = workspace.id; + workspace.visible = true; + } + + std::cout << "SRDWindowManager: Added workspace " << workspace.id << " (" << name << ")" << std::endl; +} + +void SRDWindowManager::remove_workspace(int workspace_id) { + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), + [workspace_id](const Workspace& w) { return w.id == workspace_id; }); + + if (it != workspaces_.end()) { + // Move windows to current workspace if removing current + if (workspace_id == current_workspace_) { + for (auto* window : it->windows) { + move_window_to_workspace(window, current_workspace_); + } + } + + workspaces_.erase(it); + std::cout << "SRDWindowManager: Removed workspace " << workspace_id << std::endl; + } +} + +void SRDWindowManager::switch_to_workspace(int workspace_id) { + auto* workspace = get_workspace(workspace_id); + if (workspace) { + // Hide current workspace + auto* current = get_workspace(current_workspace_); + if (current) { + current->visible = false; + } + + // Show new workspace + current_workspace_ = workspace_id; + workspace->visible = true; + + // Arrange windows on the new workspace + arrange_workspace_windows(workspace_id); + + std::cout << "SRDWindowManager: Switched to workspace " << workspace_id << std::endl; + } +} + +void SRDWindowManager::move_window_to_workspace(SRDWindow* window, int workspace_id) { + if (!window) return; + + auto* target_workspace = get_workspace(workspace_id); + if (!target_workspace) return; + + // Remove from current workspace + for (auto& workspace : workspaces_) { + auto it = std::find(workspace.windows.begin(), workspace.windows.end(), window); + if (it != workspace.windows.end()) { + workspace.windows.erase(it); + break; + } + } + + // Add to target workspace + target_workspace->windows.push_back(window); + + std::cout << "SRDWindowManager: Moved window " << window->getId() + << " to workspace " << workspace_id << std::endl; +} + +int SRDWindowManager::get_current_workspace() const { + return current_workspace_; +} + +std::vector<Workspace> SRDWindowManager::get_workspaces() const { + return workspaces_; +} + +Workspace* SRDWindowManager::get_workspace(int workspace_id) { + auto it = std::find_if(workspaces_.begin(), workspaces_.end(), + [workspace_id](const Workspace& w) { return w.id == workspace_id; }); + return it != workspaces_.end() ? &(*it) : nullptr; +} + +// Helper methods +std::string SRDWindowManager::key_code_to_string(int key_code, int modifiers) const { + std::string result; + + // Add modifiers + if (modifiers & 0x01) result += "Ctrl+"; // Control + if (modifiers & 0x02) result += "Shift+"; // Shift + if (modifiers & 0x04) result += "Alt+"; // Alt + if (modifiers & 0x08) result += "Mod4+"; // Super/SRDWindows + + // Add key + if (key_code >= 'A' && key_code <= 'Z') { + result += static_cast<char>(key_code); + } else if (key_code >= '0' && key_code <= '9') { + result += static_cast<char>(key_code); + } else { + result += "Key" + std::to_string(key_code); + } + + return result; +} + +void SRDWindowManager::execute_key_binding(const std::string& key_combination) { + auto it = key_bindings_.find(key_combination); + if (it != key_bindings_.end()) { + it->second(); + } +} + +void SRDWindowManager::update_layout_for_window(SRDWindow* window) { + if (layout_engine_) { + layout_engine_->update_window(window); + } +} + +void SRDWindowManager::arrange_workspace_windows(int workspace_id) { + auto* workspace = get_workspace(workspace_id); + if (!workspace || !layout_engine_) return; + + // Get monitor for current workspace (simplified - assuming single monitor for now) + if (!workspace->windows.empty() && !monitors_.empty()) { + // Arrange windows using the layout engine + // TODO: Get the actual monitor for this workspace + layout_engine_->arrange_on_monitor(monitors_[0]); + } +} + +void SRDWindowManager::update_workspace_visibility() { + for (auto& workspace : workspaces_) { + workspace.visible = (workspace.id == current_workspace_); + } +} + +// Window interaction helper methods +SRDWindow* SRDWindowManager::find_window_at_position(int x, int y) const { + // Find the topmost window at the given position + // For now, just check if point is within any window bounds + // TODO: Implement proper z-order checking + for (auto* window : windows_) { + if (x >= window->getX() && x < window->getX() + window->getWidth() && + y >= window->getY() && y < window->getY() + window->getHeight()) { + return window; + } + } + return nullptr; +} + +bool SRDWindowManager::is_in_titlebar_area(SRDWindow* window, int x, int y) const { + if (!window) return false; + + // Check if point is in the top area of the window (titlebar region) + // Titlebar is typically the top 20-30 pixels of the window + int titlebar_height = 30; + + return (x >= window->getX() && x < window->getX() + window->getWidth() && + y >= window->getY() && y < window->getY() + titlebar_height); +} + +bool SRDWindowManager::is_in_resize_area(SRDWindow* window, int x, int y) const { + if (!window) return false; + + // Check if point is near the edges of the window (resize handles) + int resize_margin = 5; + + int left = window->getX(); + int right = left + window->getWidth(); + int top = window->getY(); + int bottom = top + window->getHeight(); + + return (x <= left + resize_margin || x >= right - resize_margin || + y <= top + resize_margin || y >= bottom - resize_margin); +} + +int SRDWindowManager::get_resize_edge(SRDWindow* window, int x, int y) const { + if (!window) return 0; + + int resize_margin = 5; + int left = window->getX(); + int right = left + window->getWidth(); + int top = window->getY(); + int bottom = top + window->getHeight(); + + bool near_left = (x <= left + resize_margin); + bool near_right = (x >= right - resize_margin); + bool near_top = (y <= top + resize_margin); + bool near_bottom = (y >= bottom - resize_margin); + + // Determine which edge or corner + if (near_left && near_top) return 5; // Top-left corner + if (near_right && near_top) return 5; // Top-right corner + if (near_left && near_bottom) return 5; // Bottom-left corner + if (near_right && near_bottom) return 5; // Bottom-right corner + if (near_left) return 1; // Left edge + if (near_right) return 2; // Right edge + if (near_top) return 3; // Top edge + if (near_bottom) return 4; // Bottom edge + + return 0; // No resize edge +} diff --git a/src/core/window_manager.h b/src/core/window_manager.h new file mode 100644 index 0000000..2da999a --- /dev/null +++ b/src/core/window_manager.h @@ -0,0 +1,156 @@ +#ifndef SRDWM_WINDOW_MANAGER_H +#define SRDWM_WINDOW_MANAGER_H + +#include <vector> +#include <algorithm> // Required for std::remove_if +#include <memory> // Required for std::unique_ptr +#include <map> +#include <string> +#include <functional> +#include <set> // Required for std::set + +#include "../input/input_handler.h" +#include "../layouts/layout_engine.h" +#include "../platform/platform.h" // For Event type +#include "../layouts/layout.h" // For Monitor type + +class SRDWindow; // Forward declaration +class InputHandler; // Forward declaration +class LuaManager; // Forward declaration + +// Workspace structure +struct Workspace { + int id; + std::string name; + std::vector<SRDWindow*> windows; + std::string layout; + bool visible; + + Workspace(int id, const std::string& name = "") + : id(id), name(name), layout("tiling"), visible(false) {} +}; + +class SRDWindowManager { +public: + SRDWindowManager(); + ~SRDWindowManager(); + + void run(); // Main loop + + // SRDWindow management + void add_window(std::unique_ptr<SRDWindow> window); + void remove_window(SRDWindow* window); + void focus_window(SRDWindow* window); + SRDWindow* get_focused_window() const; + std::vector<SRDWindow*> get_windows() const; + void manage_windows(); // Added missing method + void focus_next_window(); + void focus_previous_window(); + + // SRDWindow operations + void close_window(SRDWindow* window); + void minimize_window(SRDWindow* window); + void maximize_window(SRDWindow* window); + void move_window(SRDWindow* window, int x, int y); + void resize_window(SRDWindow* window, int width, int height); + void toggle_window_floating(SRDWindow* window); + bool is_window_floating(SRDWindow* window) const; + + // Window dragging and resizing + void start_window_drag(SRDWindow* window, int start_x, int start_y); + void start_window_resize(SRDWindow* window, int start_x, int start_y, int edge); + void update_window_drag(int x, int y); + void update_window_resize(int x, int y); + void end_window_drag(); + void end_window_resize(); + bool is_dragging() const { return dragging_window_ != nullptr; } + bool is_resizing() const { return resizing_window_ != nullptr; } + + // Workspace management + void add_workspace(const std::string& name = ""); + void remove_workspace(int workspace_id); + void switch_to_workspace(int workspace_id); + void move_window_to_workspace(SRDWindow* window, int workspace_id); + int get_current_workspace() const; + std::vector<Workspace> get_workspaces() const; + Workspace* get_workspace(int workspace_id); + + // Layout management + void set_layout(int monitor_id, const std::string& layout_name); + std::string get_layout(int monitor_id) const; + void arrange_windows(); + void tile_windows(); + void arrange_windows_dynamic(); + + // Key binding system + void bind_key(const std::string& key_combination, std::function<void()> action); + void unbind_key(const std::string& key_combination); + void handle_key_press(int key_code, int modifiers); + void handle_key_release(int key_code, int modifiers); + + // Input event handling (called by InputHandler) + void handle_key_press(int key_code); + void handle_key_release(int key_code); + void handle_mouse_button_press(int button, int x, int y); + void handle_mouse_button_release(int button, int x, int y); + void handle_mouse_motion(int x, int y); + void handle_event(const Event& event); // Added missing method + + // Integration + void set_layout_engine(LayoutEngine* engine); + void set_lua_manager(LuaManager* manager); + void set_platform(Platform* platform); + +private: + // Window tracking + std::vector<SRDWindow*> windows_; // Added missing member variable + SRDWindow* focused_window_ = nullptr; + std::set<SRDWindow*> floating_windows_; // Track floating windows + InputHandler* input_handler_ = nullptr; + LayoutEngine* layout_engine_ = nullptr; + LuaManager* lua_manager_ = nullptr; + Platform* platform_ = nullptr; + + // Workspace management + std::vector<Workspace> workspaces_; + int current_workspace_ = 0; + int next_workspace_id_ = 1; + + // Window dragging and resizing state + SRDWindow* dragging_window_ = nullptr; + SRDWindow* resizing_window_ = nullptr; + int drag_start_x_ = 0; + int drag_start_y_ = 0; + int drag_start_window_x_ = 0; + int drag_start_window_y_ = 0; + int resize_start_x_ = 0; + int resize_start_y_ = 0; + int resize_start_width_ = 0; + int resize_start_height_ = 0; + int resize_edge_ = 0; // 0=none, 1=left, 2=right, 3=top, 4=bottom, 5=corner + + // Key binding system + std::map<std::string, std::function<void()>> key_bindings_; + std::map<int, int> pressed_keys_; // key_code -> modifiers + + // Platform-specific data (placeholder) + void* platform_data_ = nullptr; + + // Monitor information + std::vector<Monitor> monitors_; + + // Helper methods + std::string key_code_to_string(int key_code, int modifiers) const; + void execute_key_binding(const std::string& key_combination); + void update_layout_for_window(SRDWindow* window); + void arrange_workspace_windows(int workspace_id); + void update_workspace_visibility(); + + // Window interaction helpers + SRDWindow* find_window_at_position(int x, int y) const; + bool is_in_titlebar_area(SRDWindow* window, int x, int y) const; + bool is_in_resize_area(SRDWindow* window, int x, int y) const; + int get_resize_edge(SRDWindow* window, int x, int y) const; +}; + +#endif // SRDWM_WINDOW_MANAGER_H diff --git a/src/input/input_handler.h b/src/input/input_handler.h new file mode 100644 index 0000000..2f35608 --- /dev/null +++ b/src/input/input_handler.h @@ -0,0 +1,36 @@ +#ifndef SRDWM_INPUT_HANDLER_H +#define SRDWM_INPUT_HANDLER_H + +#include <memory> + +// Define basic event structures (these will need more detail later) +struct KeyboardEvent { + int key_code; + // Add modifiers, state (press/release) +}; + +struct MouseEvent { + enum class Type { Press, Release, Motion }; + Type type; + int button; // Valid for Press/Release + int x, y; + // Add modifiers +}; + +class InputHandler { +public: + virtual ~InputHandler() = default; + + // Pure virtual methods for handling events + virtual void handle_key_press(const KeyboardEvent& event) = 0; + virtual void handle_key_release(const KeyboardEvent& event) = 0; + virtual void handle_mouse_button_press(const MouseEvent& event) = 0; + virtual void handle_mouse_button_release(const MouseEvent& event) = 0; + virtual void handle_mouse_motion(const MouseEvent& event) = 0; + + // Other potential input-related methods + // virtual void initialize() = 0; + // virtual void shutdown() = 0; +}; + +#endif // SRDWM_INPUT_HANDLER_H diff --git a/src/layouts/dynamic_layout.cc b/src/layouts/dynamic_layout.cc new file mode 100644 index 0000000..7f10ac8 --- /dev/null +++ b/src/layouts/dynamic_layout.cc @@ -0,0 +1,31 @@ +#include "dynamic_layout.h" +#include "../core/window.h" +#include <iostream> + +DynamicLayout::DynamicLayout() { + // Constructor implementation if needed +} + +DynamicLayout::~DynamicLayout() { + // Destructor implementation if needed +} + +void DynamicLayout::arrange_windows(const std::vector<SRDWindow*>& windows, const Monitor& monitor) { + std::cout << "DynamicLayout::arrange_windows called for monitor (" << monitor.x << ", " << monitor.y << ", " << monitor.width << ", " << monitor.height << ")" << std::endl; + std::cout << "Arranging " << windows.size() << " windows dynamically." << std::endl; + + // Basic placeholder: In a real dynamic layout, you might not resize/reposition + // windows automatically here unless triggered by user interaction or specific rules. + // For now, just iterate through the windows and acknowledge them. + for (const auto& window : windows) { + std::cout << " - SRDWindow ID: " << window->getId() << ", Title: " << window->getTitle() << std::endl; + // In a real implementation, you might update window properties based on + // the dynamic layout logic, or simply leave their positions/sizes as they are + // unless a move/resize operation is in progress. + } + + // Future implementation would involve logic for: + // - Remembering window positions and sizes. + // - Handling user-initiated moves and resizes. + // - Potentially snapping windows to grid or other windows. +} diff --git a/src/layouts/dynamic_layout.h b/src/layouts/dynamic_layout.h new file mode 100644 index 0000000..3692c36 --- /dev/null +++ b/src/layouts/dynamic_layout.h @@ -0,0 +1,17 @@ +#ifndef SRDWM_DYNAMIC_LAYOUT_H +#define SRDWM_DYNAMIC_LAYOUT_H + +#include "layout.h" +#include "../core/window.h" +#include <vector> + +class DynamicLayout : public Layout { +public: + DynamicLayout(); + ~DynamicLayout(); + + // Implement the pure virtual method from the base class + void arrange_windows(const std::vector<SRDWindow*>& windows, const Monitor& monitor) override; +}; + +#endif // SRDWM_DYNAMIC_LAYOUT_H diff --git a/src/layouts/layout.h b/src/layouts/layout.h new file mode 100644 index 0000000..00c002f --- /dev/null +++ b/src/layouts/layout.h @@ -0,0 +1,33 @@ +#ifndef SRDWM_LAYOUT_H +#define SRDWM_LAYOUT_H + +#include <vector> +#include "../core/window.h" + +struct Monitor { + int id; + int x; + int y; + int width; + int height; + std::string name; + int refresh_rate; + + Monitor() : id(0), x(0), y(0), width(0), height(0), refresh_rate(60) {} + Monitor(int id, int x, int y, int width, int height, const std::string& name = "", int refresh = 60) + : id(id), x(x), y(y), width(width), height(height), name(name), refresh_rate(refresh) {} +}; + +class Layout { +public: + virtual ~Layout() = default; + + // Pure virtual method to arrange windows on a given monitor + virtual void arrange_windows(const std::vector<SRDWindow*>& windows, const Monitor& monitor) = 0; + + // You might add other common layout methods here later, e.g.: + // virtual void add_window(SRDWindow* window) = 0; + // virtual void remove_window(SRDWindow* window) = 0; +}; + +#endif // SRDWM_LAYOUT_H diff --git a/src/layouts/layout_engine.cc b/src/layouts/layout_engine.cc new file mode 100644 index 0000000..f6bc20f --- /dev/null +++ b/src/layouts/layout_engine.cc @@ -0,0 +1,188 @@ +#include "layout_engine.h" +#include <iostream> +#include <algorithm> + +LayoutEngine::LayoutEngine() { + std::cout << "LayoutEngine: Initializing..." << std::endl; +} + +LayoutEngine::~LayoutEngine() { + std::cout << "LayoutEngine: Shutting down..." << std::endl; +} + +// Layout management +bool LayoutEngine::set_layout(int monitor_id, LayoutType layout_type) { + active_layouts_[monitor_id] = layout_type; + std::cout << "LayoutEngine: Set layout " << layout_type_to_string(layout_type) + << " for monitor " << monitor_id << std::endl; + return true; +} + +bool LayoutEngine::set_layout(int monitor_id, const std::string& layout_name) { + LayoutType layout_type = string_to_layout_type(layout_name); + if (layout_type != LayoutType::TILING && layout_type != LayoutType::DYNAMIC && layout_type != LayoutType::FLOATING) { + std::cerr << "LayoutEngine: Unknown layout type: " << layout_name << std::endl; + return false; + } + return set_layout(monitor_id, layout_type); +} + +LayoutType LayoutEngine::get_layout(int monitor_id) const { + auto it = active_layouts_.find(monitor_id); + if (it != active_layouts_.end()) { + return it->second; + } + return LayoutType::DYNAMIC; // Default layout +} + +std::string LayoutEngine::get_layout_name(int monitor_id) const { + return layout_type_to_string(get_layout(monitor_id)); +} + +// Layout configuration +bool LayoutEngine::configure_layout(const std::string& layout_name, const std::map<std::string, std::string>& config) { + layout_configs_[layout_name] = config; + std::cout << "LayoutEngine: Configured layout '" << layout_name << "' with " + << config.size() << " parameters" << std::endl; + return true; +} + +bool LayoutEngine::register_custom_layout(const std::string& name, std::function<void(const std::vector<SRDWindow*>&, const Monitor&)> layout_func) { + custom_layouts_[name] = layout_func; + std::cout << "LayoutEngine: Registered custom layout '" << name << "'" << std::endl; + return true; +} + +// SRDWindow management +void LayoutEngine::add_window(SRDWindow* window) { + if (window && std::find(windows_.begin(), windows_.end(), window) == windows_.end()) { + windows_.push_back(window); + std::cout << "LayoutEngine: Added window " << window->getId() << std::endl; + } +} + +void LayoutEngine::remove_window(SRDWindow* window) { + auto it = std::find(windows_.begin(), windows_.end(), window); + if (it != windows_.end()) { + windows_.erase(it); + std::cout << "LayoutEngine: Removed window " << window->getId() << std::endl; + } +} + +void LayoutEngine::update_window(SRDWindow* window) { + // Trigger rearrangement for the monitor this window is on + // For now, just log the update + std::cout << "LayoutEngine: Updated window " << window->getId() << std::endl; +} + +// Monitor management +void LayoutEngine::add_monitor(const Monitor& monitor) { + // Check if monitor already exists + auto it = std::find_if(monitors_.begin(), monitors_.end(), + [&](const Monitor& m) { return m.id == monitor.id; }); + if (it == monitors_.end()) { + monitors_.push_back(monitor); + // Set default layout for new monitor + active_layouts_[monitor.id] = LayoutType::DYNAMIC; + std::cout << "LayoutEngine: Added monitor " << monitor.id << std::endl; + } +} + +void LayoutEngine::remove_monitor(int monitor_id) { + auto it = std::find_if(monitors_.begin(), monitors_.end(), + [monitor_id](const Monitor& m) { return m.id == monitor_id; }); + if (it != monitors_.end()) { + monitors_.erase(it); + active_layouts_.erase(monitor_id); + std::cout << "LayoutEngine: Removed monitor " << monitor_id << std::endl; + } +} + +void LayoutEngine::update_monitor(const Monitor& monitor) { + auto it = std::find_if(monitors_.begin(), monitors_.end(), + [&](const Monitor& m) { return m.id == monitor.id; }); + if (it != monitors_.end()) { + *it = monitor; + std::cout << "LayoutEngine: Updated monitor " << monitor.id << std::endl; + } +} + +// Arrangement +void LayoutEngine::arrange_on_monitor(const Monitor& monitor) { + if (active_layouts_.count(monitor.id)) { + LayoutType current_layout_type = active_layouts_[monitor.id]; + std::vector<SRDWindow*> windows_on_monitor = get_windows_on_monitor(monitor.id); + + std::cout << "LayoutEngine: Arranging " << windows_on_monitor.size() + << " windows on monitor " << monitor.id + << " with layout " << layout_type_to_string(current_layout_type) << std::endl; + + if (current_layout_type == LayoutType::TILING) { + tiling_layout_.arrange_windows(windows_on_monitor, monitor); + } else if (current_layout_type == LayoutType::DYNAMIC) { + dynamic_layout_.arrange_windows(windows_on_monitor, monitor); + } else if (current_layout_type == LayoutType::FLOATING) { + // Floating layout - windows keep their current positions + std::cout << "LayoutEngine: Floating layout - no arrangement needed" << std::endl; + } + } +} + +void LayoutEngine::arrange_all_monitors() { + for (const auto& monitor : monitors_) { + arrange_on_monitor(monitor); + } +} + +// Utility +std::vector<std::string> LayoutEngine::get_available_layouts() const { + return {"tiling", "dynamic", "floating"}; +} + +std::vector<SRDWindow*> LayoutEngine::get_windows_on_monitor(int monitor_id) const { + std::vector<SRDWindow*> windows_on_monitor; + + // Find the monitor + auto monitor_it = std::find_if(monitors_.begin(), monitors_.end(), + [monitor_id](const Monitor& m) { return m.id == monitor_id; }); + if (monitor_it == monitors_.end()) { + return windows_on_monitor; + } + + // Get windows that are on this monitor + for (SRDWindow* window : windows_) { + if (is_window_on_monitor(window, *monitor_it)) { + windows_on_monitor.push_back(window); + } + } + + return windows_on_monitor; +} + +// Helper methods +LayoutType LayoutEngine::string_to_layout_type(const std::string& name) const { + if (name == "tiling") return LayoutType::TILING; + if (name == "dynamic") return LayoutType::DYNAMIC; + if (name == "floating") return LayoutType::FLOATING; + return LayoutType::DYNAMIC; // Default +} + +std::string LayoutEngine::layout_type_to_string(LayoutType type) const { + switch (type) { + case LayoutType::TILING: return "tiling"; + case LayoutType::DYNAMIC: return "dynamic"; + case LayoutType::FLOATING: return "floating"; + default: return "dynamic"; + } +} + +bool LayoutEngine::is_window_on_monitor(const SRDWindow* window, const Monitor& monitor) const { + if (!window) return false; + + // Simple check: window center is within monitor bounds + int window_center_x = window->getX() + window->getWidth() / 2; + int window_center_y = window->getY() + window->getHeight() / 2; + + return window_center_x >= monitor.x && window_center_x < monitor.x + monitor.width && + window_center_y >= monitor.y && window_center_y < monitor.y + monitor.height; +} diff --git a/src/layouts/layout_engine.h b/src/layouts/layout_engine.h new file mode 100644 index 0000000..d23033c --- /dev/null +++ b/src/layouts/layout_engine.h @@ -0,0 +1,70 @@ +#ifndef SRDWM_LAYOUT_ENGINE_H +#define SRDWM_LAYOUT_ENGINE_H + +#include "layout.h" +#include "tiling_layout.h" +#include "dynamic_layout.h" +#include <vector> +#include <map> +#include <string> +#include <functional> + +class SRDWindow; // Forward declaration to avoid circular dependency + +enum class LayoutType { + TILING, + DYNAMIC, + FLOATING + // Add other layout types here later +}; + +class LayoutEngine { +public: + LayoutEngine(); + ~LayoutEngine(); + + // Layout management + bool set_layout(int monitor_id, LayoutType layout_type); + bool set_layout(int monitor_id, const std::string& layout_name); + LayoutType get_layout(int monitor_id) const; + std::string get_layout_name(int monitor_id) const; + + // Layout configuration + bool configure_layout(const std::string& layout_name, const std::map<std::string, std::string>& config); + bool register_custom_layout(const std::string& name, std::function<void(const std::vector<SRDWindow*>&, const Monitor&)> layout_func); + + // SRDWindow management + void add_window(SRDWindow* window); + void remove_window(SRDWindow* window); + void update_window(SRDWindow* window); + + // Monitor management + void add_monitor(const Monitor& monitor); + void remove_monitor(int monitor_id); + void update_monitor(const Monitor& monitor); + + // Arrangement + void arrange_on_monitor(const Monitor& monitor); + void arrange_all_monitors(); + + // Utility + std::vector<std::string> get_available_layouts() const; + std::vector<SRDWindow*> get_windows_on_monitor(int monitor_id) const; + +private: + // Member variables for layout state + std::vector<Monitor> monitors_; + std::vector<SRDWindow*> windows_; + TilingLayout tiling_layout_; + DynamicLayout dynamic_layout_; + std::map<int, LayoutType> active_layouts_; // Map monitor ID to active layout type + std::map<std::string, std::function<void(const std::vector<SRDWindow*>&, const Monitor&)>> custom_layouts_; + std::map<std::string, std::map<std::string, std::string>> layout_configs_; + + // Helper methods + LayoutType string_to_layout_type(const std::string& name) const; + std::string layout_type_to_string(LayoutType type) const; + bool is_window_on_monitor(const SRDWindow* window, const Monitor& monitor) const; +}; + +#endif // SRDWM_LAYOUT_ENGINE_H diff --git a/src/layouts/smart_placement.cc b/src/layouts/smart_placement.cc new file mode 100644 index 0000000..b23844c --- /dev/null +++ b/src/layouts/smart_placement.cc @@ -0,0 +1,278 @@ +#include "smart_placement.h" +#include <algorithm> +#include <cmath> +#include <iostream> + +// Constants +constexpr int SmartPlacement::MIN_WINDOW_WIDTH; +constexpr int SmartPlacement::MIN_WINDOW_HEIGHT; +constexpr int SmartPlacement::GRID_MARGIN; +constexpr int SmartPlacement::CASCADE_OFFSET; + +SmartPlacement::PlacementResult SmartPlacement::place_window( + const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows) { + + // Try grid placement first (SRDWindows 11 style) + auto grid_result = place_in_grid(window, monitor, existing_windows); + if (grid_result.success) { + return grid_result; + } + + // Fall back to cascade placement + return cascade_place(window, monitor, existing_windows); +} + +SmartPlacement::PlacementResult SmartPlacement::place_in_grid( + const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows) { + + PlacementResult result = {0, 0, 0, 0, false, "Grid placement failed"}; + + // Calculate optimal grid size based on monitor and window count + int window_count = existing_windows.size() + 1; + int grid_size = calculate_optimal_grid_size(monitor, window_count); + + if (grid_size <= 0) { + result.reason = "Invalid grid size"; + return result; + } + + // Calculate grid position for this window + auto [grid_x, grid_y] = calculate_grid_position(window, monitor); + + // Calculate cell dimensions + int cell_width = (monitor.width - (grid_size + 1) * GRID_MARGIN) / grid_size; + int cell_height = (monitor.height - (grid_size + 1) * GRID_MARGIN) / grid_size; + + // Ensure minimum cell size + cell_width = std::max(cell_width, MIN_WINDOW_WIDTH); + cell_height = std::max(cell_height, MIN_WINDOW_HEIGHT); + + // Calculate window position + int x = monitor.x + GRID_MARGIN + grid_x * (cell_width + GRID_MARGIN); + int y = monitor.y + GRID_MARGIN + grid_y * (cell_height + GRID_MARGIN); + + // Check if position is valid + if (is_position_valid(x, y, cell_width, cell_height, monitor)) { + result.x = x; + result.y = y; + result.width = cell_width; + result.height = cell_height; + result.success = true; + result.reason = "Grid placement successful"; + } + + return result; +} + +SmartPlacement::PlacementResult SmartPlacement::snap_to_edge( + const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows) { + + PlacementResult result = {0, 0, 0, 0, false, "Snap placement failed"}; + + // For now, implement simple edge snapping + // In a full implementation, this would detect when windows are dragged near edges + + int x = monitor.x + monitor.width / 4; + int y = monitor.y + monitor.height / 4; + int width = monitor.width / 2; + int height = monitor.height / 2; + + if (is_position_valid(x, y, width, height, monitor)) { + result.x = x; + result.y = y; + result.width = width; + result.height = height; + result.success = true; + result.reason = "Snap placement successful"; + } + + return result; +} + +SmartPlacement::PlacementResult SmartPlacement::cascade_place( + const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows) { + + PlacementResult result = {0, 0, 0, 0, false, "Cascade placement failed"}; + + // Find a free space for cascading + auto free_spaces = find_free_spaces(monitor, existing_windows); + + if (free_spaces.empty()) { + // No free spaces, use default position + int x = monitor.x + CASCADE_OFFSET; + int y = monitor.y + CASCADE_OFFSET; + int width = std::min(800, monitor.width - 2 * CASCADE_OFFSET); + int height = std::min(600, monitor.height - 2 * CASCADE_OFFSET); + + if (is_position_valid(x, y, width, height, monitor)) { + result.x = x; + result.y = y; + result.width = width; + result.height = height; + result.success = true; + result.reason = "Default cascade placement"; + } + } else { + // Use the first free space + auto [x, y] = free_spaces[0]; + int width = std::min(800, monitor.width - x - CASCADE_OFFSET); + int height = std::min(600, monitor.height - y - CASCADE_OFFSET); + + if (is_position_valid(x, y, width, height, monitor)) { + result.x = x; + result.y = y; + result.width = width; + result.height = height; + result.success = true; + result.reason = "Cascade placement in free space"; + } + } + + return result; +} + +SmartPlacement::PlacementResult SmartPlacement::smart_tile( + const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows) { + + PlacementResult result = {0, 0, 0, 0, false, "Smart tile placement failed"}; + + // Calculate overlap score to find best position + int best_score = -1; + int best_x = monitor.x; + int best_y = monitor.y; + int best_width = monitor.width / 2; + int best_height = monitor.height / 2; + + // Try different positions and find the one with least overlap + for (int x = monitor.x; x < monitor.x + monitor.width - MIN_WINDOW_WIDTH; x += 50) { + for (int y = monitor.y; y < monitor.y + monitor.height - MIN_WINDOW_HEIGHT; y += 50) { + int width = std::min(800, monitor.width - x); + int height = std::min(600, monitor.height - y); + + if (is_position_valid(x, y, width, height, monitor)) { + int score = calculate_overlap_score(window, monitor, existing_windows); + if (score > best_score) { + best_score = score; + best_x = x; + best_y = y; + best_width = width; + best_height = height; + } + } + } + } + + if (best_score >= 0) { + result.x = best_x; + result.y = best_y; + result.width = best_width; + result.height = best_height; + result.success = true; + result.reason = "Smart tile placement successful"; + } + + return result; +} + +bool SmartPlacement::windows_overlap(const SRDWindow* w1, const SRDWindow* w2) { + // Simple AABB overlap detection + int x1 = w1->getX(); + int y1 = w1->getY(); + int w1_width = w1->getWidth(); + int w1_height = w1->getHeight(); + + int x2 = w2->getX(); + int y2 = w2->getY(); + int w2_width = w2->getWidth(); + int w2_height = w2->getHeight(); + + return !(x1 + w1_width <= x2 || x2 + w2_width <= x1 || + y1 + w1_height <= y2 || y2 + w2_height <= y1); +} + +int SmartPlacement::calculate_overlap_score(const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows) { + int score = 0; + + // Calculate how much this position overlaps with existing windows + for (const auto* existing : existing_windows) { + if (windows_overlap(window, existing)) { + score -= 10; // Penalty for overlap + } else { + score += 1; // Bonus for no overlap + } + } + + return score; +} + +std::vector<std::pair<int, int>> SmartPlacement::find_free_spaces( + const Monitor& monitor, const std::vector<SRDWindow*>& existing_windows) { + + std::vector<std::pair<int, int>> free_spaces; + + // Simple algorithm: try positions in a grid pattern + for (int x = monitor.x; x < monitor.x + monitor.width - MIN_WINDOW_WIDTH; x += 100) { + for (int y = monitor.y; y < monitor.y + monitor.height - MIN_WINDOW_HEIGHT; y += 100) { + bool is_free = true; + + // Check if this position overlaps with any existing window + for (const auto* existing : existing_windows) { + int ex = existing->getX(); + int ey = existing->getY(); + int ew = existing->getWidth(); + int eh = existing->getHeight(); + + if (x < ex + ew && x + MIN_WINDOW_WIDTH > ex && + y < ey + eh && y + MIN_WINDOW_HEIGHT > ey) { + is_free = false; + break; + } + } + + if (is_free) { + free_spaces.emplace_back(x, y); + } + } + } + + return free_spaces; +} + +bool SmartPlacement::is_position_valid(int x, int y, int width, int height, const Monitor& monitor) { + return x >= monitor.x && y >= monitor.y && + x + width <= monitor.x + monitor.width && + y + height <= monitor.y + monitor.height && + width >= MIN_WINDOW_WIDTH && height >= MIN_WINDOW_HEIGHT; +} + +std::pair<int, int> SmartPlacement::calculate_grid_position(const SRDWindow* window, const Monitor& monitor) { + // Simple grid position calculation + // In a real implementation, this might consider window properties or user preferences + + // For now, use a simple pattern: first window top-left, second top-right, etc. + static int window_counter = 0; + int grid_x = window_counter % 2; + int grid_y = window_counter / 2; + window_counter++; + + return {grid_x, grid_y}; +} + +int SmartPlacement::calculate_optimal_grid_size(const Monitor& monitor, int window_count) { + // Calculate optimal grid size based on monitor dimensions and window count + if (window_count <= 0) return 1; + + // Simple heuristic: try to create a roughly square grid + int grid_size = static_cast<int>(std::ceil(std::sqrt(window_count))); + + // Ensure grid size is reasonable + grid_size = std::max(1, std::min(grid_size, 4)); + + return grid_size; +} diff --git a/src/layouts/smart_placement.h b/src/layouts/smart_placement.h new file mode 100644 index 0000000..69f4b94 --- /dev/null +++ b/src/layouts/smart_placement.h @@ -0,0 +1,62 @@ +#ifndef SRDWM_SMART_PLACEMENT_H +#define SRDWM_SMART_PLACEMENT_H + +#include "layout.h" +#include <vector> +#include <memory> + +// Forward declarations +class SRDWindow; +class Monitor; + +// Smart placement algorithm that mimics SRDWindows 11 behavior +class SmartPlacement { +public: + struct PlacementResult { + int x, y, width, height; + bool success; + std::string reason; + }; + + // Main placement function + static PlacementResult place_window(const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows); + + // Grid-based placement (SRDWindows 11 style) + static PlacementResult place_in_grid(const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows); + + // Snap-to-edge placement + static PlacementResult snap_to_edge(const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows); + + // Cascade placement for overlapping windows + static PlacementResult cascade_place(const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows); + + // Smart tiling placement + static PlacementResult smart_tile(const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows); + +private: + // Helper functions + static bool windows_overlap(const SRDWindow* w1, const SRDWindow* w2); + static int calculate_overlap_score(const SRDWindow* window, const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows); + static std::vector<std::pair<int, int>> find_free_spaces(const Monitor& monitor, + const std::vector<SRDWindow*>& existing_windows); + static bool is_position_valid(int x, int y, int width, int height, const Monitor& monitor); + + // Grid calculations + static std::pair<int, int> calculate_grid_position(const SRDWindow* window, const Monitor& monitor); + static int calculate_optimal_grid_size(const Monitor& monitor, int window_count); + + // Constants + static constexpr int MIN_WINDOW_WIDTH = 200; + static constexpr int MIN_WINDOW_HEIGHT = 150; + static constexpr int GRID_MARGIN = 10; + static constexpr int CASCADE_OFFSET = 30; +}; + +#endif // SRDWM_SMART_PLACEMENT_H + diff --git a/src/layouts/tiling_layout.cc b/src/layouts/tiling_layout.cc new file mode 100644 index 0000000..440c95a --- /dev/null +++ b/src/layouts/tiling_layout.cc @@ -0,0 +1,38 @@ +#include "tiling_layout.h" +#include <iostream> + +TilingLayout::TilingLayout() { + // Constructor implementation +} + +TilingLayout::~TilingLayout() { + // Destructor implementation +} + +void TilingLayout::arrange_windows(const std::vector<SRDWindow*>& windows, const Monitor& monitor) { + std::cout << "TilingLayout::arrange_windows called for monitor:" << std::endl; + std::cout << " Position: (" << monitor.x << ", " << monitor.y << "), Dimensions: (" << monitor.width << ", " << monitor.height << ")" << std::endl; + std::cout << " Number of windows: " << windows.size() << std::endl; + + // Basic placeholder tiling logic (e.g., splitting the screen vertically) + if (!windows.empty()) { + int window_width = monitor.width / windows.size(); + int current_x = monitor.x; + + for (size_t i = 0; i < windows.size(); ++i) { + SRDWindow* window = windows[i]; + // In a real implementation, you would calculate the desired + // position and size for the window based on the tiling algorithm + // and then call a method on the window object (which would + // internally use the platform backend) to apply these changes. + std::cout << " SRDWindow " << window->getId() << ": Placeholder position (" << current_x << ", " << monitor.y << "), size (" << window_width << ", " << monitor.height << ")" << std::endl; + + // Update the window's properties in the SRDWindow object + window->setPosition(current_x, monitor.y); + window->setDimensions(current_x, monitor.y, window_width, monitor.height); + + + current_x += window_width; + } + } +} diff --git a/src/layouts/tiling_layout.h b/src/layouts/tiling_layout.h new file mode 100644 index 0000000..333950a --- /dev/null +++ b/src/layouts/tiling_layout.h @@ -0,0 +1,17 @@ +#ifndef SRDWM_TILING_LAYOUT_H +#define SRDWM_TILING_LAYOUT_H + +#include "layout.h" +#include <iostream> +#include <vector> + +class TilingLayout : public Layout { +public: + TilingLayout(); + ~TilingLayout(); + + // Implement the pure virtual method from the base class + void arrange_windows(const std::vector<SRDWindow*>& windows, const Monitor& monitor) override; +}; + +#endif // SRDWM_TILING_LAYOUT_H diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..a1bc49c --- /dev/null +++ b/src/main.cc @@ -0,0 +1,321 @@ +#include <iostream> +#include <memory> +#include <string> + +// Include Lua manager +#include "config/lua_manager.h" + +// Include layout engine +#include "layouts/layout_engine.h" + +// Include window manager +#include "core/window_manager.h" + +// Include platform factory +#include "platform/platform_factory.h" + +int main(int argc, char* argv[]) { + std::cout << "SRDWM starting up..." << std::endl; + + // Print platform information + PlatformFactory::print_platform_info(); + + // Initialize layout engine + auto layout_engine = std::make_unique<LayoutEngine>(); + std::cout << "Layout engine created" << std::endl; + + // Add a default monitor + Monitor default_monitor{0, 0, 0, 1920, 1080, "Default", 60}; + layout_engine->add_monitor(default_monitor); + std::cout << "Default monitor added to layout engine" << std::endl; + + // Initialize Lua manager + g_lua_manager = std::make_unique<LuaManager>(); + if (!g_lua_manager->initialize()) { + std::cerr << "Failed to initialize Lua manager" << std::endl; + return 1; + } + + // Connect layout engine to Lua manager + g_lua_manager->set_layout_engine(layout_engine.get()); + std::cout << "Layout engine connected to Lua manager" << std::endl; + + // Initialize window manager + auto window_manager = std::make_unique<SRDWindowManager>(); + std::cout << "SRDWindow manager created" << std::endl; + + // Connect components + window_manager->set_layout_engine(layout_engine.get()); + window_manager->set_lua_manager(g_lua_manager.get()); + std::cout << "Components connected to window manager" << std::endl; + + // Initialize default workspaces + window_manager->add_workspace("Main"); + window_manager->add_workspace("Web"); + window_manager->add_workspace("Code"); + window_manager->add_workspace("Media"); + std::cout << "Default workspaces created" << std::endl; + + // Load configuration + std::string config_path = "./config/srdwm.lua"; + if (!g_lua_manager->load_config_file(config_path)) { + std::cout << "Failed to load configuration, using defaults" << std::endl; + // Set default configuration + g_lua_manager->set_string("general.default_layout", "tiling"); + g_lua_manager->set_bool("general.smart_placement", true); + g_lua_manager->set_int("general.window_gap", 8); + g_lua_manager->set_int("general.border_width", 2); + g_lua_manager->set_bool("general.animations", true); + g_lua_manager->set_int("general.animation_duration", 200); + } + + // Display current configuration + std::cout << "\nCurrent Configuration:" << std::endl; + std::cout << "Default Layout: " << g_lua_manager->get_string("general.default_layout", "tiling") << std::endl; + std::cout << "Smart Placement: " << (g_lua_manager->get_bool("general.smart_placement", true) ? "enabled" : "disabled") << std::endl; + std::cout << "Window Gap: " << g_lua_manager->get_int("general.window_gap", 8) << " pixels" << std::endl; + std::cout << "Border Width: " << g_lua_manager->get_int("general.border_width", 2) << " pixels" << std::endl; + std::cout << "Animations: " << (g_lua_manager->get_bool("general.animations", true) ? "enabled" : "disabled") << std::endl; + std::cout << "Animation Duration: " << g_lua_manager->get_int("general.animation_duration", 200) << " ms" << std::endl; + + // Platform initialization + std::cout << "\nInitializing platform..." << std::endl; + + // Create platform with automatic detection + auto platform = PlatformFactory::create_platform(); + if (!platform) { + std::cerr << "Failed to create platform" << std::endl; + return 1; + } + + std::cout << "Platform created: " << platform->get_platform_name() << std::endl; + + // Initialize platform + if (!platform->initialize()) { + std::cerr << "Failed to initialize platform" << std::endl; + return 1; + } + + std::cout << "Platform initialized successfully" << std::endl; + + // Connect platform to window manager + window_manager->set_platform(platform.get()); + + // Set up key bindings + std::cout << "\nSetting up key bindings..." << std::endl; + + // Workspace switching + window_manager->bind_key("Mod4+1", [&]() { window_manager->switch_to_workspace(0); }); + window_manager->bind_key("Mod4+2", [&]() { window_manager->switch_to_workspace(1); }); + window_manager->bind_key("Mod4+3", [&]() { window_manager->switch_to_workspace(2); }); + window_manager->bind_key("Mod4+4", [&]() { window_manager->switch_to_workspace(3); }); + + // Workspace management + window_manager->bind_key("Mod4+Shift+1", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) window_manager->move_window_to_workspace(focused, 0); + }); + window_manager->bind_key("Mod4+Shift+2", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) window_manager->move_window_to_workspace(focused, 1); + }); + window_manager->bind_key("Mod4+Shift+3", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) window_manager->move_window_to_workspace(focused, 2); + }); + window_manager->bind_key("Mod4+Shift+4", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) window_manager->move_window_to_workspace(focused, 3); + }); + + // Window focus cycling + window_manager->bind_key("Mod4+Tab", [&]() { + window_manager->focus_next_window(); + }); + window_manager->bind_key("Mod4+Shift+Tab", [&]() { + window_manager->focus_previous_window(); + }); + + // Layout switching + window_manager->bind_key("Mod4+t", [&]() { + layout_engine->set_layout(0, "tiling"); + window_manager->arrange_windows(); + }); + window_manager->bind_key("Mod4+d", [&]() { + layout_engine->set_layout(0, "dynamic"); + window_manager->arrange_windows(); + }); + window_manager->bind_key("Mod4+s", [&]() { + layout_engine->set_layout(0, "smart_placement"); + window_manager->arrange_windows(); + }); + + // Window management + window_manager->bind_key("Mod4+q", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) window_manager->close_window(focused); + }); + window_manager->bind_key("Mod4+m", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) window_manager->maximize_window(focused); + }); + + // Window floating and tiling + window_manager->bind_key("Mod4+f", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) { + window_manager->toggle_window_floating(focused); + } + }); + + // Window movement with arrow keys + window_manager->bind_key("Mod4+Shift+Left", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) { + int new_x = focused->getX() - 50; + window_manager->move_window(focused, new_x, focused->getY()); + } + }); + window_manager->bind_key("Mod4+Shift+Right", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) { + int new_x = focused->getX() + 50; + window_manager->move_window(focused, new_x, focused->getY()); + } + }); + window_manager->bind_key("Mod4+Shift+Up", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) { + int new_y = focused->getY() - 50; + window_manager->move_window(focused, focused->getX(), new_y); + } + }); + window_manager->bind_key("Mod4+Shift+Down", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) { + int new_y = focused->getY() + 50; + window_manager->move_window(focused, focused->getX(), new_y); + } + }); + + // Window resizing with arrow keys + window_manager->bind_key("Mod4+Ctrl+Left", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) { + int new_width = focused->getWidth() - 50; + if (new_width >= 100) { + window_manager->resize_window(focused, new_width, focused->getHeight()); + } + } + }); + window_manager->bind_key("Mod4+Ctrl+Right", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) { + int new_width = focused->getWidth() + 50; + window_manager->resize_window(focused, new_width, focused->getHeight()); + } + }); + window_manager->bind_key("Mod4+Ctrl+Up", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) { + int new_height = focused->getHeight() - 50; + if (new_height >= 100) { + window_manager->resize_window(focused, focused->getWidth(), new_height); + } + } + }); + window_manager->bind_key("Mod4+Ctrl+Down", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) { + int new_height = focused->getHeight() + 50; + window_manager->resize_window(focused, focused->getWidth(), new_height); + } + }); + + // Additional window operations + window_manager->bind_key("Mod4+space", [&]() { + auto* focused = window_manager->get_focused_window(); + if (focused) window_manager->minimize_window(focused); + }); + + window_manager->bind_key("Mod4+Return", [&]() { + // TODO: Launch terminal + std::cout << "Launch terminal" << std::endl; + }); + + window_manager->bind_key("Mod4+d", [&]() { + // TODO: Launch application launcher + std::cout << "Launch application launcher" << std::endl; + }); + + // Quick layout presets + window_manager->bind_key("Mod4+Shift+t", [&]() { + layout_engine->set_layout(0, "tiling"); + window_manager->arrange_windows(); + }); + window_manager->bind_key("Mod4+Shift+d", [&]() { + layout_engine->set_layout(0, "dynamic"); + window_manager->arrange_windows(); + }); + window_manager->bind_key("Mod4+Shift+s", [&]() { + layout_engine->set_layout(0, "smart_placement"); + window_manager->arrange_windows(); + }); + + // Exit + window_manager->bind_key("Mod4+Shift+q", [&]() { + std::cout << "Exit key combination pressed" << std::endl; + // TODO: Implement proper cleanup and exit + }); + + std::cout << "Key bindings configured" << std::endl; + + // Set initial layout + std::string default_layout = g_lua_manager->get_string("general.default_layout", "tiling"); + layout_engine->set_layout(0, default_layout); + + // Arrange initial windows + window_manager->arrange_windows(); + + std::cout << "\nSRDWM initialization complete!" << std::endl; + std::cout << "\nAvailable Key Bindings:" << std::endl; + std::cout << " Mod4+1-4 - Switch to workspace 1-4" << std::endl; + std::cout << " Mod4+Shift+1-4 - Move focused window to workspace 1-4" << std::endl; + std::cout << " Mod4+t/d/s - Switch to tiling/dynamic/smart placement layout" << std::endl; + std::cout << " Mod4+Shift+t/d/s - Quick layout presets" << std::endl; + std::cout << " Mod4+Tab - Focus next window" << std::endl; + std::cout << " Mod4+Shift+Tab - Focus previous window" << std::endl; + std::cout << " Mod4+f - Toggle window floating" << std::endl; + std::cout << " Mod4+q - Close focused window" << std::endl; + std::cout << " Mod4+m - Maximize focused window" << std::endl; + std::cout << " Mod4+space - Minimize focused window" << std::endl; + std::cout << " Mod4+Shift+Arrows - Move focused window" << std::endl; + std::cout << " Mod4+Ctrl+Arrows - Resize focused window" << std::endl; + std::cout << " Mod4+Return - Launch terminal" << std::endl; + std::cout << " Mod4+d - Launch application launcher" << std::endl; + std::cout << " Mod4+Shift+q - Exit SRDWM" << std::endl; + std::cout << "\nMouse Controls:" << std::endl; + std::cout << " Left click + drag on titlebar - Move window" << std::endl; + std::cout << " Left click + drag on edges - Resize window" << std::endl; + + // Main event loop + try { + window_manager->run(); + } catch (const std::exception& e) { + std::cerr << "Error in main loop: " << e.what() << std::endl; + } + + std::cout << "SRDWM shutting down." << std::endl; + + // Clean up platform + platform->shutdown(); + platform.reset(); + + // Clean up Lua manager + g_lua_manager->shutdown(); + g_lua_manager.reset(); + + std::cout << "Cleanup completed." << std::endl; + + return 0; +} diff --git a/src/platform/linux_platform.h b/src/platform/linux_platform.h new file mode 100644 index 0000000..1237c85 --- /dev/null +++ b/src/platform/linux_platform.h @@ -0,0 +1,105 @@ +#ifndef SRDWM_LINUX_PLATFORM_H +#define SRDWM_LINUX_PLATFORM_H + +#include "platform.h" +#include <memory> +#include <string> + +// Forward declarations for X11 +#ifdef __linux__ +struct _XDisplay; +typedef struct _XDisplay Display; +typedef unsigned long SRDWindow; +typedef unsigned long Atom; + +// Forward declarations for Wayland +struct wl_display; +struct wl_registry; +struct wl_compositor; +struct wl_shell; +struct wl_seat; +struct wl_keyboard; +struct wl_pointer; +#endif + +class LinuxPlatform : public Platform { +public: + enum class Backend { + Auto, + X11, + Wayland + }; + + explicit LinuxPlatform(Backend backend = Backend::Auto); + ~LinuxPlatform() override; + + // Platform interface implementation + bool initialize() override; + void shutdown() override; + + bool poll_events(std::vector<Event>& events) override; + void process_event(const Event& event) override; + + std::unique_ptr<SRDWindow> create_window(const std::string& title, int x, int y, int width, int height) override; + void destroy_window(SRDWindow* window) override; + void set_window_position(SRDWindow* window, int x, int y) override; + void set_window_size(SRDWindow* window, int width, int height) override; + void set_window_title(SRDWindow* window, const std::string& title) override; + void focus_window(SRDWindow* window) override; + void minimize_window(SRDWindow* window) override; + void maximize_window(SRDWindow* window) override; + void close_window(SRDWindow* window) override; + + std::vector<Monitor> get_monitors() override; + Monitor get_primary_monitor() override; + + void grab_keyboard() override; + void ungrab_keyboard() override; + void grab_pointer() override; + void ungrab_pointer() override; + + std::string get_platform_name() const override; + bool is_wayland() const override; + bool is_x11() const override; + bool is_windows() const override; + bool is_macos() const override; + +private: + Backend backend_; + bool initialized_; + + // X11 specific members + Display* x11_display_; + SRDWindow x11_root_; + Atom x11_wm_delete_window_; + Atom x11_wm_protocols_; + + // Wayland specific members + wl_display* wayland_display_; + wl_registry* wayland_registry_; + wl_compositor* wayland_compositor_; + wl_shell* wayland_shell_; + wl_seat* wayland_seat_; + wl_keyboard* wayland_keyboard_; + wl_pointer* wayland_pointer_; + + // Common members + std::vector<Monitor> monitors_; + + // Private methods + bool initialize_x11(); + bool initialize_wayland(); + void shutdown_x11(); + void shutdown_wayland(); + + bool detect_backend(); + void setup_x11_atoms(); + void setup_wayland_registry(); + + // Event processing + void process_x11_event(XEvent& event, std::vector<Event>& events); + void process_wayland_event(wl_display* display, std::vector<Event>& events); +}; + +#endif // SRDWM_LINUX_PLATFORM_H + diff --git a/src/platform/macos_platform.cc b/src/platform/macos_platform.cc new file mode 100644 index 0000000..d489cb7 --- /dev/null +++ b/src/platform/macos_platform.cc @@ -0,0 +1,480 @@ +#include "macos_platform.h" +#include <iostream> +#include <CoreGraphics/CoreGraphics.h> +#include <ApplicationServices/ApplicationServices.h> +#include <Carbon/Carbon.h> + +// Static member initialization +MacOSPlatform* MacOSPlatform::instance_ = nullptr; + +MacOSPlatform::MacOSPlatform() + : event_tap_(nullptr) { + + instance_ = this; +} + +MacOSPlatform::~MacOSPlatform() { + shutdown(); + if (instance_ == this) { + instance_ = nullptr; + } +} + +bool MacOSPlatform::initialize() { + std::cout << "Initializing macOS platform..." << std::endl; + + // Request accessibility permissions + if (!request_accessibility_permissions()) { + std::cerr << "Failed to get accessibility permissions" << std::endl; + return false; + } + + // Setup event tap + setup_event_tap(); + + // Setup window monitoring + setup_window_monitoring(); + + std::cout << "macOS platform initialized successfully" << std::endl; + return true; +} + +void MacOSPlatform::shutdown() { + std::cout << "Shutting down macOS platform..." << std::endl; + + // Clean up event tap + if (event_tap_) { + CGEventTapEnable(event_tap_, false); + CFRelease(event_tap_); + event_tap_ = nullptr; + } + + // Clean up windows + for (auto& pair : window_map_) { + if (pair.second) { + delete pair.second; + } + } + window_map_.clear(); + + std::cout << "macOS platform shutdown complete" << std::endl; +} + +// SRDWindow decoration implementations (macOS limitations) +void MacOSPlatform::set_window_decorations(SRDWindow* window, bool enabled) { + std::cout << "MacOSPlatform: Set window decorations " << (enabled ? "enabled" : "disabled") << std::endl; + + if (!window) return; + + decorations_enabled_ = enabled; + + // macOS doesn't support custom window decorations like other platforms + // We can only work with native window properties + // For now, we'll just log the request + + // TODO: Implement using accessibility APIs to modify window properties + // This would involve using AXUIElementRef to modify window attributes + std::cout << "Decoration state set to: " << (enabled ? "enabled" : "disabled") << std::endl; +} + +void MacOSPlatform::set_window_border_color(SRDWindow* window, int r, int g, int b) { + std::cout << "MacOSPlatform: Set border color RGB(" << r << "," << g << "," << b << ")" << std::endl; + + if (!window) return; + + // macOS doesn't support custom border colors through public APIs + // This would require private APIs or overlay windows + // For now, we'll just log the request + + // TODO: Implement using private APIs or overlay windows + // This could involve: + // 1. Creating transparent overlay windows around the target window + // 2. Using private Core Graphics APIs (not recommended for production) + // 3. Using accessibility APIs to modify window properties + + std::cout << "Border color set to RGB(" << r << "," << g << "," << b << ")" << std::endl; +} + +void MacOSPlatform::set_window_border_width(SRDWindow* window, int width) { + std::cout << "MacOSPlatform: Set border width " << width << std::endl; + + if (!window) return; + + // macOS doesn't support custom border widths through public APIs + // This would require private APIs or overlay windows + // For now, we'll just log the request + + // TODO: Implement using private APIs or overlay windows + // This could involve: + // 1. Creating transparent overlay windows around the target window + // 2. Using private Core Graphics APIs (not recommended for production) + // 3. Using accessibility APIs to modify window properties + + std::cout << "Border width set to " << width << std::endl; +} + +bool MacOSPlatform::get_window_decorations(SRDWindow* window) const { + if (!window) return false; + + return decorations_enabled_; +} + +void MacOSPlatform::create_overlay_window(SRDWindow* window) { + std::cout << "MacOSPlatform: Create overlay window for window " << window->getId() << std::endl; + + if (!window) return; + + // TODO: Implement overlay window creation for custom decorations + // This would involve: + // 1. Creating a transparent window using Core Graphics + // 2. Positioning it over the target window + // 3. Drawing custom borders/titlebar on it + // 4. Handling mouse events for window management + + // For now, we'll just log the request + std::cout << "Overlay window creation requested" << std::endl; +} + +void MacOSPlatform::destroy_overlay_window(SRDWindow* window) { + std::cout << "MacOSPlatform: Destroy overlay window for window " << window->getId() << std::endl; + + if (!window) return; + + // TODO: Implement overlay window destruction + // This would involve: + // 1. Finding the overlay window for this window + // 2. Destroying the overlay window + // 3. Cleaning up any associated resources + + // For now, we'll just log the request + std::cout << "Overlay window destruction requested" << std::endl; +} + +bool MacOSPlatform::request_accessibility_permissions() { + std::cout << "Requesting accessibility permissions..." << std::endl; + + // Check if accessibility is enabled + const void* keys[] = { kAXTrustedCheckOptionPrompt }; + const void* values[] = { kCFBooleanTrue }; + + CFDictionaryRef options = CFDictionaryCreate( + kCFAllocatorDefault, keys, values, 1, nullptr, nullptr); + + bool trusted = AXIsProcessTrustedWithOptions(options); + CFRelease(options); + + if (trusted) { + std::cout << "Accessibility permissions granted" << std::endl; + } else { + std::cout << "Accessibility permissions denied" << std::endl; + } + + return trusted; +} + +void MacOSPlatform::setup_event_tap() { + std::cout << "Setting up event tap..." << std::endl; + + // Create event tap for global events + event_tap_ = CGEventTapCreate( + kCGSessionEventTap, + kCGHeadInsertEventTap, + kCGEventTapOptionDefault, + CGEventMaskBit(kCGEventKeyDown) | + CGEventMaskBit(kCGEventKeyUp) | + CGEventMaskBit(kCGEventLeftMouseDown) | + CGEventMaskBit(kCGEventLeftMouseUp) | + CGEventMaskBit(kCGEventRightMouseDown) | + CGEventMaskBit(kCGEventRightMouseUp) | + CGEventMaskBit(kCGEventMouseMoved), + event_tap_callback, + this); + + if (event_tap_) { + CFRunLoopSourceRef run_loop_source = + CFMachPortCreateRunLoopSource(kCFAllocatorDefault, event_tap_, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source, kCFRunLoopCommonModes); + CGEventTapEnable(event_tap_, true); + + std::cout << "Event tap setup complete" << std::endl; + } else { + std::cerr << "Failed to create event tap" << std::endl; + } +} + +void MacOSPlatform::setup_window_monitoring() { + std::cout << "Setting up window monitoring..." << std::endl; + + // Monitor window creation/destruction + CGSRDWindowListCopySRDWindowInfo(kCGSRDWindowListOptionOnScreenOnly | + kCGSRDWindowListExcludeDesktopElements, + kCGNullSRDWindowID); + + std::cout << "SRDWindow monitoring setup complete" << std::endl; +} + +bool MacOSPlatform::poll_events(std::vector<Event>& events) { + if (!initialized_ || !event_tap_) return false; + + events.clear(); + + // macOS events are handled through the event tap callback + // The event tap callback handles event conversion + // For now, we'll just return any pending events + + // TODO: Implement proper event queue processing + // This would involve processing events from the event tap callback + + return false; +} + +void MacOSPlatform::process_event(const Event& event) { + // TODO: Implement event processing +} + +std::unique_ptr<SRDWindow> MacOSPlatform::create_window(const std::string& title, int x, int y, int width, int height) { + std::cout << "Creating macOS window: " << title << std::endl; + + // TODO: Implement actual window creation using Core Graphics/AppKit + // For now, create a placeholder window object + + auto window = std::make_unique<SRDWindow>(); + // TODO: Set window properties + + std::cout << "macOS window creation requested" << std::endl; + return window; +} + +void MacOSPlatform::destroy_window(SRDWindow* window) { + std::cout << "Destroying macOS window" << std::endl; + + // TODO: Implement window destruction +} + +void MacOSPlatform::set_window_position(SRDWindow* window, int x, int y) { + // TODO: Implement window positioning using accessibility APIs +} + +void MacOSPlatform::set_window_size(SRDWindow* window, int width, int height) { + // TODO: Implement window resizing using accessibility APIs +} + +void MacOSPlatform::set_window_title(SRDWindow* window, const std::string& title) { + // TODO: Implement title setting +} + +void MacOSPlatform::focus_window(SRDWindow* window) { + // TODO: Implement window focusing +} + +void MacOSPlatform::minimize_window(SRDWindow* window) { + // TODO: Implement window minimization +} + +void MacOSPlatform::maximize_window(SRDWindow* window) { + // TODO: Implement window maximization +} + +void MacOSPlatform::close_window(SRDWindow* window) { + // TODO: Implement window closing +} + +std::vector<Monitor> MacOSPlatform::get_monitors() { + std::vector<Monitor> monitors; + + // Get display information using Core Graphics + uint32_t display_count = 0; + CGGetActiveDisplayList(0, nullptr, &display_count); + + if (display_count > 0) { + std::vector<CGDirectDisplayID> display_ids(display_count); + CGGetActiveDisplayList(display_count, display_ids.data(), &display_count); + + for (uint32_t i = 0; i < display_count; ++i) { + CGDirectDisplayID display_id = display_ids[i]; + + Monitor monitor; + monitor.id = static_cast<int>(display_id); + monitor.name = "Display " + std::to_string(i + 1); + + // Get display bounds + CGRect bounds = CGDisplayBounds(display_id); + monitor.x = static_cast<int>(bounds.origin.x); + monitor.y = static_cast<int>(bounds.origin.y); + monitor.width = static_cast<int>(bounds.size.width); + monitor.height = static_cast<int>(bounds.size.height); + + // Get refresh rate + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display_id); + if (mode) { + monitor.refresh_rate = static_cast<int>(CGDisplayModeGetRefreshRate(mode)); + CGDisplayModeRelease(mode); + } else { + monitor.refresh_rate = 60; // Default + } + + monitors.push_back(monitor); + + std::cout << "Monitor " << i << ": " << monitor.width << "x" << monitor.height + << " @ " << monitor.refresh_rate << "Hz" << std::endl; + } + } + + return monitors; +} + +Monitor MacOSPlatform::get_primary_monitor() { + auto monitors = get_monitors(); + if (!monitors.empty()) { + return monitors[0]; + } + + // Fallback to main display + CGDirectDisplayID main_display = CGMainDisplayID(); + CGRect bounds = CGDisplayBounds(main_display); + + Monitor monitor; + monitor.id = static_cast<int>(main_display); + monitor.name = "Main Display"; + monitor.x = static_cast<int>(bounds.origin.x); + monitor.y = static_cast<int>(bounds.origin.y); + monitor.width = static_cast<int>(bounds.size.width); + monitor.height = static_cast<int>(bounds.size.height); + monitor.refresh_rate = 60; // Default + + return monitor; +} + +void MacOSPlatform::grab_keyboard() { + // TODO: Implement keyboard grabbing + std::cout << "Keyboard grabbing setup" << std::endl; +} + +void MacOSPlatform::ungrab_keyboard() { + // TODO: Implement keyboard ungrab + std::cout << "Keyboard ungrab" << std::endl; +} + +void MacOSPlatform::grab_pointer() { + // TODO: Implement pointer grabbing + std::cout << "Pointer grabbing setup" << std::endl; +} + +void MacOSPlatform::ungrab_pointer() { + // TODO: Implement pointer ungrab + std::cout << "Pointer ungrab" << std::endl; +} + +// Static callback functions +CGEventRef MacOSPlatform::event_tap_callback(CGEventTapProxy proxy, CGEventType type, + CGEventRef event, void* user_info) { + MacOSPlatform* platform = static_cast<MacOSPlatform*>(user_info); + return platform->handle_event_tap(proxy, type, event); +} + +CGEventRef MacOSPlatform::handle_event_tap(CGEventTapProxy proxy, CGEventType type, CGEventRef event) { + switch (type) { + case kCGEventKeyDown: + handle_key_event(event, true); + break; + + case kCGEventKeyUp: + handle_key_event(event, false); + break; + + case kCGEventLeftMouseDown: + handle_mouse_event(event, true, 1); + break; + + case kCGEventLeftMouseUp: + handle_mouse_event(event, false, 1); + break; + + case kCGEventRightMouseDown: + handle_mouse_event(event, true, 2); + break; + + case kCGEventRightMouseUp: + handle_mouse_event(event, false, 2); + break; + + case kCGEventMouseMoved: + handle_mouse_motion(event); + break; + } + + return event; +} + +void MacOSPlatform::handle_key_event(CGEventRef event, bool pressed) { + // Get key code + CGKeyCode key_code = static_cast<CGKeyCode>(CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode)); + + std::cout << "Key " << (pressed ? "press" : "release") << ": " << key_code << std::endl; + + // TODO: Convert to SRDWM key event +} + +void MacOSPlatform::handle_mouse_event(CGEventRef event, bool pressed, int button) { + // Get mouse position + CGPoint location = CGEventGetLocation(event); + + std::cout << "Mouse button " << button << " " << (pressed ? "down" : "up") + << " at (" << location.x << ", " << location.y << ")" << std::endl; + + // TODO: Convert to SRDWM button event +} + +void MacOSPlatform::handle_mouse_motion(CGEventRef event) { + // Get mouse position + CGPoint location = CGEventGetLocation(event); + + // TODO: Convert to SRDWM motion event +} + +// Utility methods +CGSRDWindowID MacOSPlatform::get_macos_window_id(SRDWindow* window) { + // TODO: Implement window ID retrieval + return 0; +} + +pid_t MacOSPlatform::get_macos_pid(SRDWindow* window) { + // TODO: Implement PID retrieval + return 0; +} + +void MacOSPlatform::update_window_monitoring() { + // TODO: Implement window monitoring update +} + +void MacOSPlatform::handle_window_created(CGSRDWindowID window_id) { + std::cout << "SRDWindow created: " << window_id << std::endl; + + // TODO: Create SRDWM window object and manage it +} + +void MacOSPlatform::handle_window_destroyed(CGSRDWindowID window_id) { + std::cout << "SRDWindow destroyed: " << window_id << std::endl; + + // TODO: Clean up SRDWM window object +} + +void MacOSPlatform::handle_window_focused(CGSRDWindowID window_id) { + std::cout << "SRDWindow focused: " << window_id << std::endl; + + // TODO: Handle window focus +} + +void MacOSPlatform::handle_window_moved(CGSRDWindowID window_id, int x, int y) { + std::cout << "SRDWindow " << window_id << " moved to (" << x << ", " << y << ")" << std::endl; + + // TODO: Handle window movement +} + +void MacOSPlatform::handle_window_resized(CGSRDWindowID window_id, int width, int height) { + std::cout << "SRDWindow " << window_id << " resized to " << width << "x" << height << std::endl; + + // TODO: Handle window resizing +} + + diff --git a/src/platform/macos_platform.h b/src/platform/macos_platform.h new file mode 100644 index 0000000..39dc3db --- /dev/null +++ b/src/platform/macos_platform.h @@ -0,0 +1,118 @@ +#ifndef SRDWM_MACOS_PLATFORM_H +#define SRDWM_MACOS_PLATFORM_H + +#include "platform.h" +#include <memory> +#include <string> +#include <map> + +// Forward declarations for macOS +#ifdef __APPLE__ +typedef struct CGSRDWindow* CGSRDWindowRef; +typedef struct CGEvent* CGEventRef; +typedef struct CGDisplay* CGDirectDisplayID; +typedef struct CGMenu* CGMenuRef; +typedef struct CGMenuBar* CGMenuBarRef; +#endif + +class MacOSPlatform : public Platform { +public: + MacOSPlatform(); + ~MacOSPlatform() override; + + // Platform interface implementation + bool initialize() override; + void shutdown() override; + + bool poll_events(std::vector<Event>& events) override; + void process_event(const Event& event) override; + + std::unique_ptr<SRDWindow> create_window(const std::string& title, int x, int y, int width, int height) override; + void destroy_window(SRDWindow* window) override; + void set_window_position(SRDWindow* window, int x, int y) override; + void set_window_size(SRDWindow* window, int width, int height) override; + void set_window_title(SRDWindow* window, const std::string& title) override; + void focus_window(SRDWindow* window) override; + void minimize_window(SRDWindow* window) override; + void maximize_window(SRDWindow* window) override; + void close_window(SRDWindow* window) override; + + std::vector<Monitor> get_monitors() override; + Monitor get_primary_monitor() override; + + void grab_keyboard() override; + void ungrab_keyboard() override; + void grab_pointer() override; + void ungrab_pointer() override; + + // SRDWindow decorations (macOS implementation) + void set_window_decorations(SRDWindow* window, bool enabled) override; + void set_window_border_color(SRDWindow* window, int r, int g, int b) override; + void set_window_border_width(SRDWindow* window, int width) override; + bool get_window_decorations(SRDWindow* window) const override; + + // macOS-specific features + void setup_global_menu(); + void update_global_menu(const std::string& app_name); + void set_menu_bar_visible(bool visible); + void set_dock_visible(bool visible); + void set_spaces_enabled(bool enabled); + void switch_to_space(int space_id); + int get_current_space() const; + std::vector<int> get_available_spaces() const; + + // Mission Control and Spaces + void show_mission_control(); + void show_app_expose(); + void show_desktop(); + + // Window management enhancements + void set_window_level(SRDWindow* window, int level); + void set_window_shadow(SRDWindow* window, bool enabled); + void set_window_blur(SRDWindow* window, bool enabled); + void set_window_alpha(SRDWindow* window, float alpha); + + std::string get_platform_name() const override; + bool is_wayland() const override; + bool is_x11() const override; + bool is_windows() const override; + bool is_macos() const override; + +private: + bool initialized_; + std::map<CGSRDWindowRef, SRDWindow*> window_map_; + std::vector<Monitor> monitors_; + + // Decoration state (macOS limitations) + bool decorations_enabled_; + std::map<CGSRDWindowRef, CGSRDWindowRef> overlay_window_map_; // window -> overlay + + // macOS-specific state + bool global_menu_enabled_; + bool dock_visible_; + bool spaces_enabled_; + CGMenuBarRef menu_bar_; + std::map<std::string, CGMenuRef> app_menus_; + + // Event handling + void setup_event_tap(); + void remove_event_tap(); + + // Monitor management + void update_monitors(); + + // Utility methods + SRDWindow* find_window_by_cgwindow(CGSRDWindowRef cgwindow); + + // Decoration methods (macOS limitations) + void create_overlay_window(SRDWindow* window); + void destroy_overlay_window(SRDWindow* window); + + // Global menu methods + void create_app_menu(const std::string& app_name); + void update_menu_bar(); + void handle_menu_event(CGEventRef event); +}; + +#endif // SRDWM_MACOS_PLATFORM_H + diff --git a/src/platform/platform.h b/src/platform/platform.h new file mode 100644 index 0000000..41cc6bf --- /dev/null +++ b/src/platform/platform.h @@ -0,0 +1,91 @@ +#ifndef SRDWM_PLATFORM_H +#define SRDWM_PLATFORM_H + +#include <memory> +#include <vector> +#include <string> +#include <functional> + +// Forward declarations +class SRDWindow; + +// Include Monitor struct definition from layouts +#include "../layouts/layout.h" + +// Platform-independent event types +enum class EventType { + WindowCreated, + WindowDestroyed, + WindowMoved, + WindowResized, + WindowFocused, + WindowUnfocused, + KeyPress, + KeyRelease, + MouseButtonPress, + MouseButtonRelease, + MouseMotion, + MonitorAdded, + MonitorRemoved +}; + +// Event structure +struct Event { + EventType type; + void* data; + size_t data_size; +}; + +// Platform abstraction interface +class Platform { +public: + virtual ~Platform() = default; + + // Initialization and cleanup + virtual bool initialize() = 0; + virtual void shutdown() = 0; + + // Event handling + virtual bool poll_events(std::vector<Event>& events) = 0; + virtual void process_event(const Event& event) = 0; + + // Window management + virtual std::unique_ptr<SRDWindow> create_window(const std::string& title, int x, int y, int width, int height) = 0; + virtual void destroy_window(SRDWindow* window) = 0; + virtual void set_window_position(SRDWindow* window, int x, int y) = 0; + virtual void set_window_size(SRDWindow* window, int width, int height) = 0; + virtual void set_window_title(SRDWindow* window, const std::string& title) = 0; + virtual void focus_window(SRDWindow* window) = 0; + virtual void minimize_window(SRDWindow* window) = 0; + virtual void maximize_window(SRDWindow* window) = 0; + virtual void close_window(SRDWindow* window) = 0; + + // Window decorations (cross-platform) + virtual void set_window_decorations(SRDWindow* window, bool enabled) = 0; + virtual void set_window_border_color(SRDWindow* window, int r, int g, int b) = 0; + virtual void set_window_border_width(SRDWindow* window, int width) = 0; + virtual bool get_window_decorations(SRDWindow* window) const = 0; + + // Monitor management + virtual std::vector<Monitor> get_monitors() = 0; + virtual Monitor get_primary_monitor() = 0; + + // Input handling + virtual void grab_keyboard() = 0; + virtual void ungrab_keyboard() = 0; + virtual void grab_pointer() = 0; + virtual void ungrab_pointer() = 0; + + // Utility + virtual std::string get_platform_name() const = 0; + virtual bool is_wayland() const = 0; + virtual bool is_x11() const = 0; + virtual bool is_windows() const = 0; + virtual bool is_macos() const = 0; +}; + +// Forward declaration +class PlatformFactory; + +#endif // SRDWM_PLATFORM_H + diff --git a/src/platform/platform_factory.cc b/src/platform/platform_factory.cc new file mode 100644 index 0000000..ef1981a --- /dev/null +++ b/src/platform/platform_factory.cc @@ -0,0 +1,214 @@ +#include "platform_factory.h" +#include <iostream> +#include <cstring> +#include <cstdlib> +#include <algorithm> + +// Platform-specific includes +#ifdef LINUX_PLATFORM + #include "x11_platform.h" + #ifdef WAYLAND_ENABLED + #include "wayland_platform.h" + #endif +#elif defined(WIN32_PLATFORM) + #include "windows_platform.h" +#elif defined(MACOS_PLATFORM) + #include "macos_platform.h" +#endif + +std::unique_ptr<Platform> PlatformFactory::create_platform() { + #ifdef _WIN32 + std::cout << "Creating SRDWindows platform..." << std::endl; + return std::make_unique<SRDWindowsPlatform>(); + + #elif defined(__APPLE__) + std::cout << "Creating macOS platform..." << std::endl; + return std::make_unique<MacOSPlatform>(); + + #else + // Linux: detect X11 vs Wayland + return detect_linux_platform(); + #endif +} + +std::unique_ptr<Platform> PlatformFactory::create_platform(const std::string& platform_name) { + std::cout << "Creating platform: " << platform_name << std::endl; + + if (platform_name == "x11" || platform_name == "X11") { + #ifdef LINUX_PLATFORM + return std::make_unique<X11Platform>(); + #else + std::cerr << "X11 platform not available on this system" << std::endl; + return nullptr; + #endif + + } else if (platform_name == "wayland" || platform_name == "Wayland") { + #ifdef WAYLAND_ENABLED + return std::make_unique<WaylandPlatform>(); + #else + std::cerr << "Wayland platform not available on this system" << std::endl; + return nullptr; + #endif + + } else if (platform_name == "windows" || platform_name == "SRDWindows") { + #ifdef WIN32_PLATFORM + return std::make_unique<SRDWindowsPlatform>(); + #else + std::cerr << "SRDWindows platform not available on this system" << std::endl; + return nullptr; + #endif + + } else if (platform_name == "macos" || platform_name == "macOS") { + #ifdef MACOS_PLATFORM + return std::make_unique<MacOSPlatform>(); + #else + std::cerr << "macOS platform not available on this system" << std::endl; + return nullptr; + #endif + + } else { + std::cerr << "Unknown platform: " << platform_name << std::endl; + std::cerr << "Available platforms: x11, wayland, windows, macos" << std::endl; + return nullptr; + } +} + +std::unique_ptr<Platform> PlatformFactory::detect_linux_platform() { + std::cout << "Detecting Linux platform..." << std::endl; + + // Check environment variables for Wayland + const char* wayland_display = std::getenv("WAYLAND_DISPLAY"); + const char* xdg_session_type = std::getenv("XDG_SESSION_TYPE"); + const char* display = std::getenv("DISPLAY"); + + std::cout << "Environment variables:" << std::endl; + std::cout << " WAYLAND_DISPLAY: " << (wayland_display ? wayland_display : "not set") << std::endl; + std::cout << " XDG_SESSION_TYPE: " << (xdg_session_type ? xdg_session_type : "not set") << std::endl; + std::cout << " DISPLAY: " << (display ? display : "not set") << std::endl; + + // Try Wayland first if environment suggests it + if (wayland_display || (xdg_session_type && strcmp(xdg_session_type, "wayland") == 0)) { + std::cout << "Wayland environment detected, attempting Wayland initialization..." << std::endl; + + #ifdef WAYLAND_ENABLED + auto wayland_platform = std::make_unique<WaylandPlatform>(); + if (wayland_platform->initialize()) { + std::cout << "✓ Wayland platform initialized successfully" << std::endl; + return wayland_platform; + } else { + std::cout << "✗ Wayland initialization failed, falling back to X11" << std::endl; + } + #else + std::cout << "Wayland support not compiled in, falling back to X11" << std::endl; + #endif + } + + // Fall back to X11 + std::cout << "Attempting X11 initialization..." << std::endl; + + #ifdef LINUX_PLATFORM + auto x11_platform = std::make_unique<X11Platform>(); + if (x11_platform->initialize()) { + std::cout << "✓ X11 platform initialized successfully" << std::endl; + return x11_platform; + } else { + std::cout << "✗ X11 initialization failed" << std::endl; + } + #else + std::cout << "X11 support not compiled in" << std::endl; + #endif + + std::cerr << "Failed to initialize any platform backend" << std::endl; + return nullptr; +} + +std::vector<std::string> PlatformFactory::get_available_platforms() { + std::vector<std::string> platforms; + + #ifdef LINUX_PLATFORM + platforms.push_back("x11"); + #ifdef WAYLAND_ENABLED + platforms.push_back("wayland"); + #endif + #endif + + #ifdef WIN32_PLATFORM + platforms.push_back("windows"); + #endif + + #ifdef MACOS_PLATFORM + platforms.push_back("macos"); + #endif + + return platforms; +} + +std::string PlatformFactory::get_current_platform() { + #ifdef _WIN32 + return "windows"; + #elif defined(__APPLE__) + return "macos"; + #else + // Check if we're running on Wayland + const char* wayland_display = std::getenv("WAYLAND_DISPLAY"); + const char* xdg_session_type = std::getenv("XDG_SESSION_TYPE"); + + if (wayland_display || (xdg_session_type && strcmp(xdg_session_type, "wayland") == 0)) { + return "wayland"; + } else { + return "x11"; + } + #endif +} + +bool PlatformFactory::is_platform_available(const std::string& platform_name) { + auto available = get_available_platforms(); + return std::find(available.begin(), available.end(), platform_name) != available.end(); +} + +void PlatformFactory::print_platform_info() { + std::cout << "\n=== Platform Information ===" << std::endl; + std::cout << "Current platform: " << get_current_platform() << std::endl; + + auto available = get_available_platforms(); + std::cout << "Available platforms: "; + for (size_t i = 0; i < available.size(); ++i) { + if (i > 0) std::cout << ", "; + std::cout << available[i]; + } + std::cout << std::endl; + + std::cout << "Environment variables:" << std::endl; + const char* wayland_display = std::getenv("WAYLAND_DISPLAY"); + const char* xdg_session_type = std::getenv("XDG_SESSION_TYPE"); + const char* display = std::getenv("DISPLAY"); + + std::cout << " WAYLAND_DISPLAY: " << (wayland_display ? wayland_display : "not set") << std::endl; + std::cout << " XDG_SESSION_TYPE: " << (xdg_session_type ? xdg_session_type : "not set") << std::endl; + std::cout << " DISPLAY: " << (display ? display : "not set") << std::endl; + + #ifdef LINUX_PLATFORM + std::cout << "Linux platform support: Enabled" << std::endl; + #ifdef WAYLAND_ENABLED + std::cout << "Wayland support: Enabled" << std::endl; + #else + std::cout << "Wayland support: Disabled" << std::endl; + #endif + #else + std::cout << "Linux platform support: Disabled" << std::endl; + #endif + + #ifdef WIN32_PLATFORM + std::cout << "SRDWindows platform support: Enabled" << std::endl; + #else + std::cout << "SRDWindows platform support: Disabled" << std::endl; + #endif + + #ifdef MACOS_PLATFORM + std::cout << "macOS platform support: Enabled" << std::endl; + #else + std::cout << "macOS platform support: Disabled" << std::endl; + #endif + + std::cout << "=============================" << std::endl; +} diff --git a/src/platform/platform_factory.h b/src/platform/platform_factory.h new file mode 100644 index 0000000..24f7586 --- /dev/null +++ b/src/platform/platform_factory.h @@ -0,0 +1,37 @@ +#ifndef SRDWM_PLATFORM_FACTORY_H +#define SRDWM_PLATFORM_FACTORY_H + +#include "platform.h" +#include <memory> +#include <string> +#include <vector> + +// Platform factory for creating platform-specific implementations +class PlatformFactory { +public: + // Create platform with automatic detection + static std::unique_ptr<Platform> create_platform(); + + // Create specific platform by name + static std::unique_ptr<Platform> create_platform(const std::string& platform_name); + + // Get list of available platforms + static std::vector<std::string> get_available_platforms(); + + // Get current platform name + static std::string get_current_platform(); + + // Check if platform is available + static bool is_platform_available(const std::string& platform_name); + + // Print platform information + static void print_platform_info(); + +private: + // Linux platform detection + static std::unique_ptr<Platform> detect_linux_platform(); +}; + +#endif // SRDWM_PLATFORM_FACTORY_H + + diff --git a/src/platform/wayland_platform.cc b/src/platform/wayland_platform.cc new file mode 100644 index 0000000..2e4a591 --- /dev/null +++ b/src/platform/wayland_platform.cc @@ -0,0 +1,507 @@ +#include "wayland_platform.h" +#include <iostream> +#include <cstring> +#include <cstdint> + +#ifndef USE_WAYLAND_STUB +#include <wayland-server-core.h> +extern "C" { + // Minimal wlroots declarations to avoid pulling C99-only headers into C++ + struct wlr_backend; + struct wlr_renderer; + struct wlr_compositor; + struct wlr_seat; + struct wlr_xdg_shell; + + // Logging + int wlr_log_init(int verbosity, void* callback); + + // Backend + struct wlr_backend* wlr_backend_autocreate(struct wl_display* display, void* session); + bool wlr_backend_start(struct wlr_backend* backend); + void wlr_backend_destroy(struct wlr_backend* backend); + struct wlr_renderer* wlr_backend_get_renderer(struct wlr_backend* backend); + + // Compositor and seat + struct wlr_compositor* wlr_compositor_create(struct wl_display* display, uint32_t version, struct wlr_renderer* renderer); + struct wlr_seat* wlr_seat_create(struct wl_display* display, const char* name); + + // xdg-shell + struct wlr_xdg_shell* wlr_xdg_shell_create(struct wl_display* display, uint32_t version); + struct wlr_renderer* wlr_renderer_autocreate(struct wlr_backend* backend); + void wlr_renderer_init_wl_display(struct wlr_renderer* renderer, struct wl_display* display); + + // Wayland display helpers (for C++ compilation) + struct wl_display* wl_display_create(void); + void wl_display_destroy(struct wl_display* display); + int wl_display_dispatch_pending(struct wl_display* display); + void wl_display_flush_clients(struct wl_display* display); +} +#endif + +// Static member initialization +WaylandPlatform* WaylandPlatform::instance_ = nullptr; + +WaylandPlatform::WaylandPlatform() + : display_(nullptr) + , registry_(nullptr) + , compositor_(nullptr) + , shm_(nullptr) + , seat_(nullptr) + , output_(nullptr) + , shell_(nullptr) + , backend_(nullptr) + , renderer_(nullptr) + , wlr_compositor_(nullptr) + , output_layout_(nullptr) + , cursor_(nullptr) + , xcursor_manager_(nullptr) + , wlr_seat_(nullptr) + , xdg_shell_(nullptr) + // , layer_shell_(nullptr) + , event_loop_running_(false) { + + instance_ = this; +} + +WaylandPlatform::~WaylandPlatform() { + shutdown(); + if (instance_ == this) { + instance_ = nullptr; + } +} + +bool WaylandPlatform::initialize() { +#ifndef USE_WAYLAND_STUB + std::cout << "Initializing Wayland platform (wlroots real backend)..." << std::endl; + // Create Wayland display + display_ = wl_display_create(); + if (!display_) { + std::cerr << "Failed to create wl_display" << std::endl; + return false; + } + + // Initialize wlroots logging (optional) - 2 corresponds to INFO + wlr_log_init(2, nullptr); + + // Create backend (auto-detect e.g., DRM, Wayland, X11) + backend_ = wlr_backend_autocreate(display_, nullptr); + if (!backend_) { + std::cerr << "Failed to create wlr_backend" << std::endl; + return false; + } + + // Create renderer and hook to display + renderer_ = wlr_renderer_autocreate(backend_); + if (!renderer_) { + std::cerr << "Failed to create wlr_renderer" << std::endl; + return false; + } + wlr_renderer_init_wl_display(renderer_, display_); + + // Create compositor (wlroots 0.17 requires protocol version). Use version 6. + wlr_compositor_ = wlr_compositor_create(display_, 6, renderer_); + if (!wlr_compositor_) { + std::cerr << "Failed to create wlr_compositor" << std::endl; + return false; + } + + // Create seat (input) + wlr_seat_ = wlr_seat_create(display_, "seat0"); + if (!wlr_seat_) { + std::cerr << "Failed to create wlr_seat" << std::endl; + return false; + } + + // Create xdg-shell (requires protocol version) + xdg_shell_ = wlr_xdg_shell_create(display_, 6); + if (!xdg_shell_) { + std::cerr << "Failed to create wlr_xdg_shell" << std::endl; + return false; + } + + // Minimal bring-up: listeners will be wired in a follow-up + + // Start backend + if (!wlr_backend_start(backend_)) { + std::cerr << "Failed to start wlr_backend" << std::endl; + return false; + } + + decorations_enabled_ = true; + std::cout << "Wayland (wlroots) backend started." << std::endl; + return true; +#else + // Minimal Wayland backend initialization (no wlroots API calls). + std::cout << "Initializing Wayland platform (minimal stub)..." << std::endl; + decorations_enabled_ = true; + std::cout << "Wayland platform initialized (stub)." << std::endl; + return true; +#endif +} + +void WaylandPlatform::shutdown() { +#ifndef USE_WAYLAND_STUB + std::cout << "Shutting down Wayland platform (wlroots)..." << std::endl; + if (xdg_shell_) { xdg_shell_ = nullptr; } + // Seat is tied to display lifecycle; skip explicit destroy to avoid ABI issues + wlr_seat_ = nullptr; + if (wlr_compositor_) { /* wlr_compositor is owned by display, no explicit destroy */ wlr_compositor_ = nullptr; } + // Renderer is owned by backend in this path; no explicit destroy + renderer_ = nullptr; + if (backend_) { wlr_backend_destroy(backend_); backend_ = nullptr; } + if (display_) { wl_display_destroy(display_); display_ = nullptr; } +#else + std::cout << "Shutting down Wayland platform (stub)..." << std::endl; +#endif +} + +bool WaylandPlatform::setup_wlroots_backend() { std::cout << "wlroots backend (stub)" << std::endl; return true; } + +bool WaylandPlatform::setup_compositor() { std::cout << "compositor (stub)" << std::endl; return true; } + +// (removed unused stub-only setup_output/setup_input in real backend path) + +bool WaylandPlatform::setup_shell_protocols() { std::cout << "shell protocols (stub)" << std::endl; return true; } + +// bool WaylandPlatform::setup_xwayland() { return true; } + +bool WaylandPlatform::poll_events(std::vector<Event>& events) { +#ifndef USE_WAYLAND_STUB + if (!display_ || !backend_) return false; + events.clear(); + // Dispatch Wayland events; non-blocking to integrate with app loop + wl_display_dispatch_pending(display_); + wl_display_flush_clients(display_); + return true; +#else + events.clear(); + return true; +#endif +} + +void WaylandPlatform::process_event(const Event& event) { + // TODO: Implement event processing +} + +std::unique_ptr<SRDWindow> WaylandPlatform::create_window(const std::string& title, int x, int y, int width, int height) { + // TODO: Implement window creation + std::cout << "Creating Wayland window: " << title << std::endl; + return nullptr; +} + +void WaylandPlatform::destroy_window(SRDWindow* window) { + // TODO: Implement window destruction + std::cout << "Destroying Wayland window" << std::endl; +} + +void WaylandPlatform::set_window_position(SRDWindow* window, int x, int y) { + // TODO: Implement window positioning +} + +void WaylandPlatform::set_window_size(SRDWindow* window, int width, int height) { + // TODO: Implement window resizing +} + +void WaylandPlatform::set_window_title(SRDWindow* window, const std::string& title) { + // TODO: Implement title setting +} + +void WaylandPlatform::focus_window(SRDWindow* window) { + // TODO: Implement window focusing +} + +void WaylandPlatform::minimize_window(SRDWindow* window) { + // TODO: Implement window minimization +} + +void WaylandPlatform::maximize_window(SRDWindow* window) { + // TODO: Implement window maximization +} + +void WaylandPlatform::close_window(SRDWindow* window) { + // TODO: Implement window closing +} + +std::vector<Monitor> WaylandPlatform::get_monitors() { + // TODO: Implement monitor detection + return {}; +} + +Monitor WaylandPlatform::get_primary_monitor() { + // TODO: Implement primary monitor detection + return Monitor{}; +} + +void WaylandPlatform::grab_keyboard() { + // TODO: Implement keyboard grabbing +} + +void WaylandPlatform::ungrab_keyboard() { + // TODO: Implement keyboard ungrab +} + +void WaylandPlatform::grab_pointer() { + // TODO: Implement pointer grabbing +} + +void WaylandPlatform::ungrab_pointer() { + // TODO: Implement pointer ungrab +} + +// Static callback functions +void WaylandPlatform::registry_global_handler(void* data, struct wl_registry* registry, + uint32_t name, const char* interface, uint32_t version) { + (void)registry; (void)name; (void)interface; (void)version; + WaylandPlatform* platform = static_cast<WaylandPlatform*>(data); + platform->handle_registry_global(nullptr, 0, interface ? interface : "", 0); +} + +void WaylandPlatform::registry_global_remove_handler(void* data, struct wl_registry* registry, uint32_t name) { + (void)registry; (void)name; + WaylandPlatform* platform = static_cast<WaylandPlatform*>(data); + platform->handle_registry_global_remove(nullptr, 0); +} + +void WaylandPlatform::xdg_surface_new_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_xdg_surface_new(static_cast<struct wlr_xdg_surface*>(data)); + } +} + +void WaylandPlatform::xdg_surface_destroy_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_xdg_surface_destroy(static_cast<struct wlr_xdg_surface*>(data)); + } +} + +void WaylandPlatform::xdg_toplevel_new_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_xdg_toplevel_new(static_cast<struct wlr_xdg_toplevel*>(data)); + } +} + +void WaylandPlatform::xdg_toplevel_destroy_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_xdg_toplevel_destroy(static_cast<struct wlr_xdg_toplevel*>(data)); + } +} + +// (removed xwayland handlers; not declared while XWayland is disabled) + +void WaylandPlatform::output_new_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_output_new(static_cast<struct wlr_output*>(data)); + } +} + +void WaylandPlatform::output_destroy_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_output_destroy(static_cast<struct wlr_output*>(data)); + } +} + +void WaylandPlatform::output_frame_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_output_frame(static_cast<struct wlr_output*>(data)); + } +} + +void WaylandPlatform::pointer_motion_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_pointer_motion(static_cast<struct wlr_pointer_motion_event*>(data)); + } +} + +void WaylandPlatform::pointer_button_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_pointer_button(static_cast<struct wlr_pointer_button_event*>(data)); + } +} + +void WaylandPlatform::pointer_axis_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_pointer_axis(static_cast<struct wlr_pointer_axis_event*>(data)); + } +} + +void WaylandPlatform::keyboard_key_handler(struct wl_listener* listener, void* data) { + (void)listener; + if (WaylandPlatform::instance_) { + WaylandPlatform::instance_->handle_keyboard_key(static_cast<struct wlr_keyboard_key_event*>(data)); + } +} + +// Event handling methods +void WaylandPlatform::handle_registry_global(struct wl_registry* registry, uint32_t name, + const char* interface, uint32_t version) { + (void)registry; (void)name; (void)version; + std::cout << "Registry global (stub): " << (interface ? interface : "?") << std::endl; +} + +void WaylandPlatform::handle_registry_global_remove(struct wl_registry* registry, uint32_t name) { + (void)registry; (void)name; +} + +void WaylandPlatform::handle_xdg_surface_new(struct wlr_xdg_surface* surface) { + std::cout << "New XDG surface" << std::endl; + manage_xdg_window(surface); +} + +void WaylandPlatform::handle_xdg_surface_destroy(struct wlr_xdg_surface* surface) { + std::cout << "XDG surface destroyed" << std::endl; +} + +void WaylandPlatform::handle_xdg_toplevel_new(struct wlr_xdg_toplevel* toplevel) { + std::cout << "New XDG toplevel" << std::endl; +} + +void WaylandPlatform::handle_xdg_toplevel_destroy(struct wlr_xdg_toplevel* toplevel) { + std::cout << "XDG toplevel destroyed" << std::endl; +} + +// void WaylandPlatform::handle_xwayland_surface_new(struct wlr_xwayland_surface* surface) {} + +// void WaylandPlatform::handle_xwayland_surface_destroy(struct wlr_xwayland_surface* surface) {} + +void WaylandPlatform::handle_output_new(struct wlr_output* output) { + std::cout << "New output" << std::endl; + handle_output_mode(output); +} + +void WaylandPlatform::handle_output_destroy(struct wlr_output* output) { + std::cout << "Output destroyed" << std::endl; +} + +void WaylandPlatform::handle_output_frame(struct wlr_output* output) { + // Handle output frame event +} + +void WaylandPlatform::handle_pointer_motion(struct wlr_pointer_motion_event* event) { + // Handle pointer motion +} + +void WaylandPlatform::handle_pointer_button(struct wlr_pointer_button_event* event) { + // Handle pointer button +} + +void WaylandPlatform::handle_pointer_axis(struct wlr_pointer_axis_event* event) { + // Handle pointer axis +} + +void WaylandPlatform::handle_keyboard_key(struct wlr_keyboard_key_event* event) { + // Handle keyboard key +} + +void WaylandPlatform::manage_xdg_window(struct wlr_xdg_surface* surface) { + // TODO: Implement XDG window management +} + +// void WaylandPlatform::manage_xwayland_window(struct wlr_xwayland_surface* surface) {} + +void WaylandPlatform::unmanage_window(SRDWindow* window) { + // TODO: Implement window unmanagement +} + +void WaylandPlatform::handle_output_mode(struct wlr_output* output) { + // TODO: Implement output mode handling +} + +void WaylandPlatform::handle_output_scale(struct wlr_output* output) { + // TODO: Implement output scale handling +} + +void WaylandPlatform::handle_key_event(uint32_t key, bool pressed) { + // TODO: Implement key event handling +} + +void WaylandPlatform::handle_button_event(uint32_t button, bool pressed) { + // TODO: Implement button event handling +} + +void WaylandPlatform::create_surface_window(struct wlr_surface* surface) { + // TODO: Implement surface window creation +} + +void WaylandPlatform::destroy_surface_window(struct wlr_surface* surface) { + // TODO: Implement surface window destruction +} + +void WaylandPlatform::update_surface_window(struct wlr_surface* surface) { + // TODO: Implement surface window update +} + +void WaylandPlatform::convert_wlroots_event_to_srdwm_event(void* event_data, EventType type) { + // TODO: Implement event conversion +} + +void WaylandPlatform::handle_wlroots_error(const std::string& error) { + std::cerr << "wlroots error: " << error << std::endl; +} + +// SRDWindow decoration implementations +void WaylandPlatform::set_window_decorations(SRDWindow* window, bool enabled) { + // Temporary stub: wlroots xdg-decoration v1 helpers differ across versions. + // Keep internal state and log, skip calling decoration APIs. + std::cout << "WaylandPlatform: Set window decorations " << (enabled ? "enabled" : "disabled") << std::endl; + (void)window; // unused for now + decorations_enabled_ = enabled; +} + +void WaylandPlatform::set_window_border_color(SRDWindow* window, int r, int g, int b) { + std::cout << "WaylandPlatform: Set border color RGB(" << r << "," << g << "," << b << ")" << std::endl; + + if (!window || !renderer_) return; + + // Store border color for this window + // In a full implementation, this would be stored in a window-specific data structure + // and applied during rendering + + // For now, we'll just log the request + // TODO: Implement custom border rendering via wlroots rendering pipeline + std::cout << "Border color set for window " << window->getId() + << ": RGB(" << r << "," << g << "," << b << ")" << std::endl; +} + +void WaylandPlatform::set_window_border_width(SRDWindow* window, int width) { + std::cout << "WaylandPlatform: Set border width " << width << std::endl; + + if (!window) return; + + // TODO: Implement when wlroots rendering is set up + // This would involve drawing custom borders on surfaces +} + +bool WaylandPlatform::get_window_decorations(SRDWindow* window) const { + if (!window) return false; + + return decorations_enabled_; +} + +void WaylandPlatform::setup_decoration_manager() { + // Temporary stub: skip creating xdg-decoration manager to avoid + // wlroots API/ABI differences across distro versions. + std::cout << "Decoration manager (stub) initialized" << std::endl; +} + +void WaylandPlatform::handle_decoration_request(struct wlr_xdg_surface* surface, uint32_t mode) { + std::cout << "Handling decoration request, mode: " << mode << std::endl; + + // TODO: Implement when zxdg-decoration protocol is available + // This would involve: + // 1. Checking if server-side decorations are enabled + // 2. Setting the decoration mode for the surface + // 3. Drawing decorations if needed +} + + diff --git a/src/platform/wayland_platform.h b/src/platform/wayland_platform.h new file mode 100644 index 0000000..3df5867 --- /dev/null +++ b/src/platform/wayland_platform.h @@ -0,0 +1,227 @@ +#ifndef SRDWM_WAYLAND_PLATFORM_H +#define SRDWM_WAYLAND_PLATFORM_H + +#include "platform.h" +#ifndef USE_WAYLAND_STUB +#include <wayland-server-core.h> +#endif +// Forward-declare Wayland and wlroots types to avoid version-specific headers. +struct wl_display; struct wl_registry; struct wl_compositor; struct wl_shm; struct wl_seat; struct wl_output; struct wl_shell; struct wl_listener; struct wl_surface; +struct wlr_backend; struct wlr_renderer; struct wlr_compositor; struct wlr_output_layout; struct wlr_cursor; struct wlr_xcursor_manager; struct wlr_seat; +struct wlr_xdg_shell; struct wlr_xdg_surface; struct wlr_xdg_toplevel; +struct wlr_output; struct wlr_pointer_motion_event; struct wlr_pointer_button_event; struct wlr_pointer_axis_event; struct wlr_keyboard_key_event; +struct wlr_xdg_decoration_manager_v1; +#include <map> +#include <memory> +#include <vector> + +// Forward declarations +class SRDWindow; +class Monitor; + +// Wayland-specific platform implementation +class WaylandPlatform : public Platform { +public: + WaylandPlatform(); + ~WaylandPlatform(); + + // Platform interface implementation + bool initialize() override; + void shutdown() override; + + // Event handling + bool poll_events(std::vector<Event>& events) override; + void process_event(const Event& event) override; + + // SRDWindow management + std::unique_ptr<SRDWindow> create_window(const std::string& title, int x, int y, int width, int height) override; + void destroy_window(SRDWindow* window) override; + void set_window_position(SRDWindow* window, int x, int y) override; + void set_window_size(SRDWindow* window, int width, int height) override; + void set_window_title(SRDWindow* window, const std::string& title) override; + void focus_window(SRDWindow* window) override; + void minimize_window(SRDWindow* window) override; + void maximize_window(SRDWindow* window) override; + void close_window(SRDWindow* window) override; + + // Monitor management + std::vector<Monitor> get_monitors() override; + Monitor get_primary_monitor() override; + + // Input handling + void grab_keyboard() override; + void ungrab_keyboard() override; + void grab_pointer() override; + void ungrab_pointer() override; + + // Utility + // SRDWindow decorations (Wayland implementation) + void set_window_decorations(SRDWindow* window, bool enabled) override; + void set_window_border_color(SRDWindow* window, int r, int g, int b) override; + void set_window_border_width(SRDWindow* window, int width) override; + bool get_window_decorations(SRDWindow* window) const override; + + std::string get_platform_name() const override { return "Wayland"; } + bool is_wayland() const override { return true; } + bool is_x11() const override { return false; } + bool is_windows() const override { return false; } + bool is_macos() const override { return false; } + +private: + static WaylandPlatform* instance_; + // Wayland-specific members + struct wl_display* display_; + struct wl_registry* registry_; + struct wl_compositor* compositor_; + struct wl_shm* shm_; + struct wl_seat* seat_; + struct wl_output* output_; + struct wl_shell* shell_; + + // wlroots backend + struct wlr_backend* backend_; + struct wlr_renderer* renderer_; + struct wlr_compositor* wlr_compositor_; + struct wlr_output_layout* output_layout_; + struct wlr_cursor* cursor_; + struct wlr_xcursor_manager* xcursor_manager_; + struct wlr_seat* wlr_seat_; + + // Shell protocols + struct wlr_xdg_shell* xdg_shell_; + // struct wlr_layer_shell_v1* layer_shell_; + + // XWayland support (temporarily disabled) + // struct wlr_xwayland* xwayland_; + // struct wlr_xwayland_server* xwayland_server_; + + // SRDWindow tracking + std::map<struct wlr_surface*, SRDWindow*> surface_window_map_; + std::map<struct wlr_xdg_surface*, SRDWindow*> xdg_window_map_; + // std::map<struct wlr_xwayland_surface*, SRDWindow*> xwayland_window_map_; + + // Monitor information + std::vector<Monitor> monitors_; + Monitor primary_monitor_; + + // Event handling + bool event_loop_running_; + std::vector<Event> pending_events_; + + // Private methods + bool setup_wlroots_backend(); + bool setup_compositor(); + bool setup_shell_protocols(); + // bool setup_xwayland(); + + // Event handling + void handle_registry_global(struct wl_registry* registry, uint32_t name, + const char* interface, uint32_t version); + void handle_registry_global_remove(struct wl_registry* registry, uint32_t name); + + // Shell protocol handlers + void handle_xdg_surface_new(struct wlr_xdg_surface* surface); + void handle_xdg_surface_destroy(struct wlr_xdg_surface* surface); + void handle_xdg_toplevel_new(struct wlr_xdg_toplevel* toplevel); + void handle_xdg_toplevel_destroy(struct wlr_xdg_toplevel* toplevel); + + // XWayland handlers (temporarily disabled) + // void handle_xwayland_surface_new(struct wlr_xwayland_surface* surface); + // void handle_xwayland_surface_destroy(struct wlr_xwayland_surface* surface); + + // Output handlers + void handle_output_new(struct wlr_output* output); + void handle_output_destroy(struct wlr_output* output); + void handle_output_frame(struct wlr_output* output); + + // Input handlers + void handle_pointer_motion(struct wlr_pointer_motion_event* event); + void handle_pointer_button(struct wlr_pointer_button_event* event); + void handle_pointer_axis(struct wlr_pointer_axis_event* event); + void handle_keyboard_key(struct wlr_keyboard_key_event* event); + + // SRDWindow management helpers + SRDWindow* find_window_by_surface(struct wlr_surface* surface); + SRDWindow* find_window_by_xdg_surface(struct wlr_xdg_surface* surface); + // SRDWindow* find_window_by_xwayland_surface(struct wlr_xwayland_surface* surface); + void manage_xdg_window(struct wlr_xdg_surface* surface); + // void manage_xwayland_window(struct wlr_xwayland_surface* surface); + void unmanage_window(SRDWindow* window); + + // Monitor helpers + void update_monitor_info(); + void handle_output_mode(struct wlr_output* output); + void handle_output_scale(struct wlr_output* output); + + // Input helpers + void setup_keyboard_grab(); + void setup_pointer_grab(); + void handle_key_event(uint32_t key, bool pressed); + void handle_button_event(uint32_t button, bool pressed); + + // Utility helpers + void create_surface_window(struct wlr_surface* surface); + void destroy_surface_window(struct wlr_surface* surface); + void update_surface_window(struct wlr_surface* surface); + + // Event conversion + void convert_wlroots_event_to_srdwm_event(void* event_data, EventType type); + + // Error handling + void handle_wlroots_error(const std::string& error); + + // Decoration methods + void setup_decoration_manager(); + void handle_decoration_request(struct wlr_xdg_surface* surface, uint32_t mode); + + // Static callback functions + static void registry_global_handler(void* data, struct wl_registry* registry, + uint32_t name, const char* interface, uint32_t version); + static void registry_global_remove_handler(void* data, struct wl_registry* registry, uint32_t name); + + static void xdg_surface_new_handler(struct wl_listener* listener, void* data); + static void xdg_surface_destroy_handler(struct wl_listener* listener, void* data); + static void xdg_toplevel_new_handler(struct wl_listener* listener, void* data); + static void xdg_toplevel_destroy_handler(struct wl_listener* listener, void* data); + + // static void xwayland_surface_new_handler(struct wl_listener* listener, void* data); + // static void xwayland_surface_destroy_handler(struct wl_listener* listener, void* data); + + static void output_new_handler(struct wl_listener* listener, void* data); + static void output_destroy_handler(struct wl_listener* listener, void* data); + static void output_frame_handler(struct wl_listener* listener, void* data); + + static void pointer_motion_handler(struct wl_listener* listener, void* data); + static void pointer_button_handler(struct wl_listener* listener, void* data); + static void pointer_axis_handler(struct wl_listener* listener, void* data); + static void keyboard_key_handler(struct wl_listener* listener, void* data); + + // Event listeners removed in stubbed build to avoid wl_listener dependency + // wlroots event listeners (instantiated only in real Wayland path) + #ifndef USE_WAYLAND_STUB + struct wl_listener xdg_surface_new_listener_; + struct wl_listener xdg_surface_destroy_listener_; + struct wl_listener xdg_toplevel_new_listener_; + struct wl_listener xdg_toplevel_destroy_listener_; + struct wl_listener output_new_listener_; + struct wl_listener output_destroy_listener_; + struct wl_listener output_frame_listener_; + struct wl_listener pointer_motion_listener_; + struct wl_listener pointer_button_listener_; + struct wl_listener pointer_axis_listener_; + struct wl_listener keyboard_key_listener_; + #endif + + // Decoration state + bool decorations_enabled_; + struct wlr_xdg_decoration_manager_v1* decoration_manager_; + + // struct wl_listener xwayland_new_surface; + // struct wl_listener xwayland_destroy; + + // Additional listeners removed in stubbed build +}; + +#endif // SRDWM_WAYLAND_PLATFORM_H + + diff --git a/src/platform/wayland_platform_stub.cc b/src/platform/wayland_platform_stub.cc new file mode 100644 index 0000000..bdf6ee9 --- /dev/null +++ b/src/platform/wayland_platform_stub.cc @@ -0,0 +1,114 @@ +#include "wayland_platform.h" +#include <iostream> +#include <cstdint> + +WaylandPlatform* WaylandPlatform::instance_ = nullptr; + +WaylandPlatform::WaylandPlatform() + : display_(nullptr) + , registry_(nullptr) + , compositor_(nullptr) + , shm_(nullptr) + , seat_(nullptr) + , output_(nullptr) + , shell_(nullptr) + , backend_(nullptr) + , renderer_(nullptr) + , wlr_compositor_(nullptr) + , output_layout_(nullptr) + , cursor_(nullptr) + , xcursor_manager_(nullptr) + , wlr_seat_(nullptr) + , xdg_shell_(nullptr) + , event_loop_running_(false) + , decorations_enabled_(true) + , decoration_manager_(nullptr) { + instance_ = this; +} + +WaylandPlatform::~WaylandPlatform() { + shutdown(); + if (instance_ == this) instance_ = nullptr; +} + +bool WaylandPlatform::initialize() { + std::cout << "Wayland (stub): initialize" << std::endl; + decorations_enabled_ = true; + return true; +} + +void WaylandPlatform::shutdown() { + std::cout << "Wayland (stub): shutdown" << std::endl; +} + +bool WaylandPlatform::poll_events(std::vector<Event>& events) { + events.clear(); + return true; +} + +void WaylandPlatform::process_event(const Event& /*event*/) {} + +std::unique_ptr<SRDWindow> WaylandPlatform::create_window(const std::string& title, int, int, int, int) { + std::cout << "Wayland (stub): create_window '" << title << "'" << std::endl; + return nullptr; +} + +void WaylandPlatform::destroy_window(SRDWindow*) {} +void WaylandPlatform::set_window_position(SRDWindow*, int, int) {} +void WaylandPlatform::set_window_size(SRDWindow*, int, int) {} +void WaylandPlatform::set_window_title(SRDWindow*, const std::string&) {} +void WaylandPlatform::focus_window(SRDWindow*) {} +void WaylandPlatform::minimize_window(SRDWindow*) {} +void WaylandPlatform::maximize_window(SRDWindow*) {} +void WaylandPlatform::close_window(SRDWindow*) {} +std::vector<Monitor> WaylandPlatform::get_monitors() { return {}; } +Monitor WaylandPlatform::get_primary_monitor() { return Monitor{}; } +void WaylandPlatform::grab_keyboard() {} +void WaylandPlatform::ungrab_keyboard() {} +void WaylandPlatform::grab_pointer() {} +void WaylandPlatform::ungrab_pointer() {} +void WaylandPlatform::set_window_decorations(SRDWindow*, bool enabled) { decorations_enabled_ = enabled; } +void WaylandPlatform::set_window_border_color(SRDWindow*, int, int, int) {} +void WaylandPlatform::set_window_border_width(SRDWindow*, int) {} +bool WaylandPlatform::get_window_decorations(SRDWindow*) const { return decorations_enabled_; } + +// Private helpers (stubs) +bool WaylandPlatform::setup_wlroots_backend() { return true; } +bool WaylandPlatform::setup_compositor() { return true; } +bool WaylandPlatform::setup_shell_protocols() { return true; } +void WaylandPlatform::handle_registry_global(struct wl_registry*, uint32_t, const char*, uint32_t) {} +void WaylandPlatform::handle_registry_global_remove(struct wl_registry*, uint32_t) {} +void WaylandPlatform::handle_xdg_surface_new(struct wlr_xdg_surface*) {} +void WaylandPlatform::handle_xdg_surface_destroy(struct wlr_xdg_surface*) {} +void WaylandPlatform::handle_xdg_toplevel_new(struct wlr_xdg_toplevel*) {} +void WaylandPlatform::handle_xdg_toplevel_destroy(struct wlr_xdg_toplevel*) {} +void WaylandPlatform::handle_output_new(struct wlr_output*) {} +void WaylandPlatform::handle_output_destroy(struct wlr_output*) {} +void WaylandPlatform::handle_output_frame(struct wlr_output*) {} +void WaylandPlatform::handle_pointer_motion(struct wlr_pointer_motion_event*) {} +void WaylandPlatform::handle_pointer_button(struct wlr_pointer_button_event*) {} +void WaylandPlatform::handle_pointer_axis(struct wlr_pointer_axis_event*) {} +void WaylandPlatform::keyboard_key_handler(struct wl_listener*, void*) {} +void WaylandPlatform::pointer_motion_handler(struct wl_listener*, void*) {} +void WaylandPlatform::pointer_button_handler(struct wl_listener*, void*) {} +void WaylandPlatform::pointer_axis_handler(struct wl_listener*, void*) {} +void WaylandPlatform::output_new_handler(struct wl_listener*, void*) {} +void WaylandPlatform::output_destroy_handler(struct wl_listener*, void*) {} +void WaylandPlatform::output_frame_handler(struct wl_listener*, void*) {} +void WaylandPlatform::xdg_surface_new_handler(struct wl_listener*, void*) {} +void WaylandPlatform::xdg_surface_destroy_handler(struct wl_listener*, void*) {} +void WaylandPlatform::xdg_toplevel_new_handler(struct wl_listener*, void*) {} +void WaylandPlatform::xdg_toplevel_destroy_handler(struct wl_listener*, void*) {} +void WaylandPlatform::manage_xdg_window(struct wlr_xdg_surface*) {} +void WaylandPlatform::unmanage_window(SRDWindow*) {} +void WaylandPlatform::handle_output_mode(struct wlr_output*) {} +void WaylandPlatform::handle_output_scale(struct wlr_output*) {} +void WaylandPlatform::handle_key_event(uint32_t, bool) {} +void WaylandPlatform::handle_button_event(uint32_t, bool) {} +void WaylandPlatform::create_surface_window(struct wlr_surface*) {} +void WaylandPlatform::destroy_surface_window(struct wlr_surface*) {} +void WaylandPlatform::update_surface_window(struct wlr_surface*) {} +void WaylandPlatform::convert_wlroots_event_to_srdwm_event(void*, EventType) {} +void WaylandPlatform::handle_wlroots_error(const std::string&) {} +void WaylandPlatform::setup_decoration_manager() {} +void WaylandPlatform::handle_decoration_request(struct wlr_xdg_surface*, uint32_t) {} diff --git a/src/platform/windows_platform.cc b/src/platform/windows_platform.cc new file mode 100644 index 0000000..11cadca --- /dev/null +++ b/src/platform/windows_platform.cc @@ -0,0 +1,585 @@ +#include "windows_platform.h" +#include <iostream> +#include <windows.h> +#include <dwmapi.h> + +// Static member initialization +SRDWindowsPlatform* SRDWindowsPlatform::instance_ = nullptr; + +SRDWindowsPlatform::SRDWindowsPlatform() + : h_instance_(nullptr) + , keyboard_hook_(nullptr) + , mouse_hook_(nullptr) { + + instance_ = this; +} + +SRDWindowsPlatform::~SRDWindowsPlatform() { + shutdown(); + if (instance_ == this) { + instance_ = nullptr; + } +} + +bool SRDWindowsPlatform::initialize() { + std::cout << "Initializing SRDWindows platform..." << std::endl; + + // Get module handle + h_instance_ = GetModuleHandle(nullptr); + if (!h_instance_) { + std::cerr << "Failed to get module handle" << std::endl; + return false; + } + + // Register window class + if (!register_window_class()) { + std::cerr << "Failed to register window class" << std::endl; + return false; + } + + // Setup global hooks + setup_global_hooks(); + + std::cout << "SRDWindows platform initialized successfully" << std::endl; + return true; +} + +void SRDWindowsPlatform::shutdown() { + std::cout << "Shutting down SRDWindows platform..." << std::endl; + + // Unhook global hooks + if (keyboard_hook_) { + UnhookSRDWindowsHookEx(keyboard_hook_); + keyboard_hook_ = nullptr; + } + + if (mouse_hook_) { + UnhookSRDWindowsHookEx(mouse_hook_); + mouse_hook_ = nullptr; + } + + // Clean up windows + for (auto& pair : window_map_) { + if (pair.second) { + delete pair.second; + } + } + window_map_.clear(); + + std::cout << "SRDWindows platform shutdown complete" << std::endl; +} + +// SRDWindow decoration implementations +void SRDWindowsPlatform::set_window_decorations(SRDWindow* window, bool enabled) { + std::cout << "SRDWindowsPlatform: Set window decorations " << (enabled ? "enabled" : "disabled") << std::endl; + + if (!window) return; + + // Find the HWND for this window + HWND hwnd = nullptr; + for (const auto& pair : window_map_) { + if (pair.second == window) { + hwnd = pair.first; + break; + } + } + + if (!hwnd) return; + + decorations_enabled_ = enabled; + + if (enabled) { + // Enable native window decorations + LONG style = GetSRDWindowLong(hwnd, GWL_STYLE); + style |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + SetSRDWindowLong(hwnd, GWL_STYLE, style); + } else { + // Disable native window decorations + LONG style = GetSRDWindowLong(hwnd, GWL_STYLE); + style &= ~(WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX); + SetSRDWindowLong(hwnd, GWL_STYLE, style); + } + + // Force window redraw + SetSRDWindowPos(hwnd, nullptr, 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); +} + +void SRDWindowsPlatform::set_window_border_color(SRDWindow* window, int r, int g, int b) { + std::cout << "SRDWindowsPlatform: Set border color RGB(" << r << "," << g << "," << b << ")" << std::endl; + + if (!window) return; + + // Find the HWND for this window + HWND hwnd = nullptr; + for (const auto& pair : window_map_) { + if (pair.second == window) { + hwnd = pair.first; + break; + } + } + + if (!hwnd) return; + + COLORREF color = RGB(r, g, b); + apply_dwm_border_color(hwnd, r, g, b); +} + +void SRDWindowsPlatform::set_window_border_width(SRDWindow* window, int width) { + std::cout << "SRDWindowsPlatform: Set border width " << width << std::endl; + + if (!window) return; + + border_width_ = width; + + // SRDWindows doesn't support custom border widths via DWM + // This would need to be implemented with custom window frames +} + +bool SRDWindowsPlatform::get_window_decorations(SRDWindow* window) const { + if (!window) return false; + + return decorations_enabled_; +} + +void SRDWindowsPlatform::apply_dwm_border_color(HWND hwnd, int r, int g, int b) { + // Use DWM API to set border color (SRDWindows 11 feature) + COLORREF color = RGB(r, g, b); + + HRESULT result = DwmSetSRDWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &color, sizeof(color)); + if (FAILED(result)) { + std::cerr << "Failed to set DWM border color" << std::endl; + } +} + +void SRDWindowsPlatform::remove_dwm_border_color(HWND hwnd) { + // Remove custom border color + COLORREF color = DWMWA_COLOR_DEFAULT; + + HRESULT result = DwmSetSRDWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &color, sizeof(color)); + if (FAILED(result)) { + std::cerr << "Failed to remove DWM border color" << std::endl; + } +} + +bool SRDWindowsPlatform::register_window_class() { + WNDCLASSEX wc = {}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = window_proc; + wc.hInstance = h_instance_; + wc.lpszClassName = L"SRDWM_SRDWindow"; + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wc.style = CS_HREDRAW | CS_VREDRAW; + + return RegisterClassEx(&wc) != 0; +} + +void SRDWindowsPlatform::setup_global_hooks() { + std::cout << "Setting up global hooks..." << std::endl; + + // Global keyboard hook + keyboard_hook_ = SetSRDWindowsHookEx(WH_KEYBOARD_LL, + keyboard_proc, h_instance_, 0); + if (!keyboard_hook_) { + std::cerr << "Failed to set keyboard hook" << std::endl; + } + + // Global mouse hook + mouse_hook_ = SetSRDWindowsHookEx(WH_MOUSE_LL, + mouse_proc, h_instance_, 0); + if (!mouse_hook_) { + std::cerr << "Failed to set mouse hook" << std::endl; + } + + std::cout << "Global hooks setup complete" << std::endl; +} + +LRESULT CALLBACK SRDWindowsPlatform::window_proc(HWND hwnd, UINT msg, + WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_CREATE: + std::cout << "SRDWindow created: " << hwnd << std::endl; + break; + + case WM_DESTROY: + std::cout << "SRDWindow destroyed: " << hwnd << std::endl; + break; + + case WM_SIZE: + // Handle window resizing + break; + + case WM_MOVE: + // Handle window moving + break; + + case WM_SETFOCUS: + // Handle window focus + break; + + case WM_KILLFOCUS: + // Handle window unfocus + break; + + case WM_CLOSE: + // Handle window close request + break; + + default: + return DefSRDWindowProc(hwnd, msg, wparam, lparam); + } + return 0; +} + +LRESULT CALLBACK SRDWindowsPlatform::keyboard_proc(int nCode, WPARAM wparam, LPARAM lparam) { + if (nCode >= 0) { + KBDLLHOOKSTRUCT* kbhs = (KBDLLHOOKSTRUCT*)lparam; + + if (wparam == WM_KEYDOWN || wparam == WM_SYSKEYDOWN) { + std::cout << "Key pressed: " << kbhs->vkCode << std::endl; + } else if (wparam == WM_KEYUP || wparam == WM_SYSKEYUP) { + std::cout << "Key released: " << kbhs->vkCode << std::endl; + } + } + return CallNextHookEx(nullptr, nCode, wparam, lparam); +} + +LRESULT CALLBACK SRDWindowsPlatform::mouse_proc(int nCode, WPARAM wparam, LPARAM lparam) { + if (nCode >= 0) { + MSLLHOOKSTRUCT* mhs = (MSLLHOOKSTRUCT*)lparam; + + switch (wparam) { + case WM_LBUTTONDOWN: + std::cout << "Left mouse button down at (" << mhs->pt.x << ", " << mhs->pt.y << ")" << std::endl; + break; + + case WM_LBUTTONUP: + std::cout << "Left mouse button up at (" << mhs->pt.x << ", " << mhs->pt.y << ")" << std::endl; + break; + + case WM_RBUTTONDOWN: + std::cout << "Right mouse button down at (" << mhs->pt.x << ", " << mhs->pt.y << ")" << std::endl; + break; + + case WM_RBUTTONUP: + std::cout << "Right mouse button up at (" << mhs->pt.x << ", " << mhs->pt.y << ")" << std::endl; + break; + + case WM_MOUSEMOVE: + // Handle mouse movement + break; + } + } + return CallNextHookEx(nullptr, nCode, wparam, lparam); +} + +void SRDWindowsPlatform::convert_win32_message(UINT msg, WPARAM wparam, LPARAM lparam, std::vector<Event>& events) { + Event event; + + switch (msg) { + case WM_CREATE: + event.type = EventType::SRDWindowCreated; + break; + case WM_DESTROY: + event.type = EventType::SRDWindowDestroyed; + break; + case WM_MOVE: + event.type = EventType::SRDWindowMoved; + break; + case WM_SIZE: + event.type = EventType::SRDWindowResized; + break; + case WM_SETFOCUS: + event.type = EventType::SRDWindowFocused; + break; + case WM_KILLFOCUS: + event.type = EventType::SRDWindowUnfocused; + break; + case WM_KEYDOWN: + event.type = EventType::KeyPress; + break; + case WM_KEYUP: + event.type = EventType::KeyRelease; + break; + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + event.type = EventType::MouseButtonPress; + break; + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + event.type = EventType::MouseButtonRelease; + break; + case WM_MOUSEMOVE: + event.type = EventType::MouseMotion; + break; + default: + return; // Skip unknown messages + } + + event.data = nullptr; + event.data_size = 0; + events.push_back(event); +} + +bool SRDWindowsPlatform::poll_events(std::vector<Event>& events) { + if (!initialized_) return false; + + events.clear(); + + // Process SRDWindows messages + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + + // Convert SRDWindows message to SRDWM event + convert_win32_message(msg.message, msg.wParam, msg.lParam, events); + } + + return !events.empty(); +} + +void SRDWindowsPlatform::process_event(const Event& event) { + // TODO: Implement event processing +} + +std::unique_ptr<SRDWindow> SRDWindowsPlatform::create_window(const std::string& title, int x, int y, int width, int height) { + // Convert title to wide string + int title_len = MultiByteToWideChar(CP_UTF8, 0, title.c_str(), -1, nullptr, 0); + std::wstring wtitle(title_len, 0); + MultiByteToWideChar(CP_UTF8, 0, title.c_str(), -1, &wtitle[0], title_len); + + // Create window + HWND hwnd = CreateSRDWindowEx( + WS_EX_OVERLAPPEDWINDOW, + L"SRDWM_SRDWindow", + wtitle.c_str(), + WS_OVERLAPPEDWINDOW, + x, y, width, height, + nullptr, nullptr, h_instance_, nullptr + ); + + if (!hwnd) { + std::cerr << "Failed to create SRDWindows window" << std::endl; + return nullptr; + } + + // Create SRDWM window object + auto window = std::make_unique<SRDWindow>(); + // TODO: Set window properties + + // Store window mapping + window_map_[hwnd] = window.get(); + + std::cout << "Created SRDWindows window: " << hwnd << " (" << title << ")" << std::endl; + return window; +} + +void SRDWindowsPlatform::destroy_window(SRDWindow* window) { + // Find window handle + for (auto& pair : window_map_) { + if (pair.second == window) { + DestroySRDWindow(pair.first); + window_map_.erase(pair.first); + break; + } + } +} + +void SRDWindowsPlatform::set_window_position(SRDWindow* window, int x, int y) { + // Find window handle + for (auto& pair : window_map_) { + if (pair.second == window) { + SetSRDWindowPos(pair.first, nullptr, x, y, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + break; + } + } +} + +void SRDWindowsPlatform::set_window_size(SRDWindow* window, int width, int height) { + // Find window handle + for (auto& pair : window_map_) { + if (pair.second == window) { + SetSRDWindowPos(pair.first, nullptr, 0, 0, width, height, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + break; + } + } +} + +void SRDWindowsPlatform::set_window_title(SRDWindow* window, const std::string& title) { + // Find window handle + for (auto& pair : window_map_) { + if (pair.second == window) { + // Convert title to wide string + int title_len = MultiByteToWideChar(CP_UTF8, 0, title.c_str(), -1, nullptr, 0); + std::wstring wtitle(title_len, 0); + MultiByteToWideChar(CP_UTF8, 0, title.c_str(), -1, &wtitle[0], title_len); + + SetSRDWindowText(pair.first, wtitle.c_str()); + break; + } + } +} + +void SRDWindowsPlatform::focus_window(SRDWindow* window) { + // Find window handle + for (auto& pair : window_map_) { + if (pair.second == window) { + SetForegroundSRDWindow(pair.first); + SetFocus(pair.first); + break; + } + } +} + +void SRDWindowsPlatform::minimize_window(SRDWindow* window) { + // Find window handle + for (auto& pair : window_map_) { + if (pair.second == window) { + ShowSRDWindow(pair.first, SW_MINIMIZE); + break; + } + } +} + +void SRDWindowsPlatform::maximize_window(SRDWindow* window) { + // Find window handle + for (auto& pair : window_map_) { + if (pair.second == window) { + ShowSRDWindow(pair.first, SW_MAXIMIZE); + break; + } + } +} + +void SRDWindowsPlatform::close_window(SRDWindow* window) { + // Find window handle + for (auto& pair : window_map_) { + if (pair.second == window) { + PostMessage(pair.first, WM_CLOSE, 0, 0); + break; + } + } +} + +std::vector<Monitor> SRDWindowsPlatform::get_monitors() { + std::vector<Monitor> monitors; + + // Enumerate monitors using EnumDisplayMonitors + EnumDisplayMonitors(nullptr, nullptr, + [](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL { + auto* monitors = reinterpret_cast<std::vector<Monitor>*>(dwData); + + MONITORINFOEX monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfo(hMonitor, &monitorInfo); + + Monitor monitor; + monitor.id = reinterpret_cast<int>(hMonitor); + monitor.name = std::string(monitorInfo.szDevice); + monitor.x = monitorInfo.rcMonitor.left; + monitor.y = monitorInfo.rcMonitor.top; + monitor.width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; + monitor.height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top; + monitor.refresh_rate = 60; // TODO: Get actual refresh rate + + monitors->push_back(monitor); + + return TRUE; + }, reinterpret_cast<LPARAM>(&monitors)); + + return monitors; +} + +Monitor SRDWindowsPlatform::get_primary_monitor() { + auto monitors = get_monitors(); + if (!monitors.empty()) { + return monitors[0]; + } + + // Fallback to primary display + Monitor monitor; + monitor.id = 0; + monitor.name = "Primary Display"; + monitor.x = 0; + monitor.y = 0; + monitor.width = GetSystemMetrics(SM_CXSCREEN); + monitor.height = GetSystemMetrics(SM_CYSCREEN); + monitor.refresh_rate = 60; + + return monitor; +} + +void SRDWindowsPlatform::grab_keyboard() { + // TODO: Implement keyboard grabbing + std::cout << "Keyboard grabbing setup" << std::endl; +} + +void SRDWindowsPlatform::ungrab_keyboard() { + // TODO: Implement keyboard ungrab + std::cout << "Keyboard ungrab" << std::endl; +} + +void SRDWindowsPlatform::grab_pointer() { + // TODO: Implement pointer grabbing + std::cout << "Pointer grabbing setup" << std::endl; +} + +void SRDWindowsPlatform::ungrab_pointer() { + // TODO: Implement pointer ungrab + std::cout << "Pointer ungrab" << std::endl; +} + +void SRDWindowsPlatform::convert_windows_message_to_event(const MSG& msg, std::vector<Event>& events) { + // TODO: Convert SRDWindows messages to SRDWM events + switch (msg.message) { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + // Convert to key press event + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + // Convert to key release event + break; + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + // Convert to button press event + break; + + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + // Convert to button release event + break; + + case WM_MOUSEMOVE: + // Convert to mouse motion event + break; + + case WM_SIZE: + // Convert to window resize event + break; + + case WM_MOVE: + // Convert to window move event + break; + } +} + +void SRDWindowsPlatform::handle_global_keyboard(WPARAM wparam, KBDLLHOOKSTRUCT* kbhs) { + // TODO: Implement global keyboard handling +} + +void SRDWindowsPlatform::handle_global_mouse(WPARAM wparam, MSLLHOOKSTRUCT* mhs) { + // TODO: Implement global mouse handling +} + + diff --git a/src/platform/windows_platform.h b/src/platform/windows_platform.h new file mode 100644 index 0000000..70b7d54 --- /dev/null +++ b/src/platform/windows_platform.h @@ -0,0 +1,140 @@ +#ifndef SRDWM_WINDOWS_PLATFORM_H +#define SRDWM_WINDOWS_PLATFORM_H + +#include "platform.h" +#include <windows.h> +#include <memory> +#include <string> +#include <map> + +class SRDWindowsPlatform : public Platform { +public: + SRDWindowsPlatform(); + ~SRDWindowsPlatform() override; + + // Platform interface implementation + bool initialize() override; + void shutdown() override; + + bool poll_events(std::vector<Event>& events) override; + void process_event(const Event& event) override; + + std::unique_ptr<SRDWindow> create_window(const std::string& title, int x, int y, int width, int height) override; + void destroy_window(SRDWindow* window) override; + void set_window_position(SRDWindow* window, int x, int y) override; + void set_window_size(SRDWindow* window, int width, int height) override; + void set_window_title(SRDWindow* window, const std::string& title) override; + void focus_window(SRDWindow* window) override; + void minimize_window(SRDWindow* window) override; + void maximize_window(SRDWindow* window) override; + void close_window(SRDWindow* window) override; + + std::vector<Monitor> get_monitors() override; + Monitor get_primary_monitor() override; + + void grab_keyboard() override; + void ungrab_keyboard() override; + void grab_pointer() override; + void ungrab_pointer() override; + + // SRDWindow decorations (SRDWindows implementation) + void set_window_decorations(SRDWindow* window, bool enabled) override; + void set_window_border_color(SRDWindow* window, int r, int g, int b) override; + void set_window_border_width(SRDWindow* window, int width) override; + bool get_window_decorations(SRDWindow* window) const override; + + // Windows-specific features + void enable_dwm_composition(bool enabled); + void set_dwm_window_attribute(HWND hwnd, DWORD attribute, DWORD value); + void set_dwm_window_thumbnail(HWND hwnd, HWND thumbnail_hwnd); + void set_dwm_window_peek(HWND hwnd, bool enabled); + + // Virtual Desktop support (Windows 10+) + void create_virtual_desktop(); + void remove_virtual_desktop(int desktop_id); + void switch_to_virtual_desktop(int desktop_id); + int get_current_virtual_desktop() const; + std::vector<int> get_virtual_desktops() const; + void move_window_to_desktop(SRDWindow* window, int desktop_id); + + // Taskbar integration + void set_taskbar_visible(bool visible); + void set_taskbar_position(int position); // 0=bottom, 1=top, 2=left, 3=right + void set_taskbar_auto_hide(bool enabled); + void update_taskbar_preview(HWND hwnd, const std::string& title); + + // Aero effects + void enable_aero_effects(bool enabled); + void set_window_transparency(HWND hwnd, BYTE alpha); + void set_window_blur(HWND hwnd, bool enabled); + void set_window_shadow(HWND hwnd, bool enabled); + + // System tray integration + void add_system_tray_icon(const std::string& tooltip, HICON icon); + void remove_system_tray_icon(); + void show_system_tray_menu(HMENU menu); + + std::string get_platform_name() const override; + bool is_wayland() const override; + bool is_x11() const override; + bool is_windows() const override; + bool is_macos() const override; + +private: + bool initialized_; + HINSTANCE h_instance_; + std::map<HWND, SRDWindow*> window_map_; + std::vector<Monitor> monitors_; + + // Decoration state + bool decorations_enabled_; + int border_width_; + COLORREF border_color_; + COLORREF focused_border_color_; + + // Windows-specific state + bool dwm_enabled_; + bool aero_enabled_; + bool taskbar_visible_; + bool taskbar_auto_hide_; + int taskbar_position_; + int current_virtual_desktop_; + std::vector<int> virtual_desktops_; + NOTIFYICONDATA system_tray_icon_; + + // SRDWindow class registration + bool register_window_class(); + void unregister_window_class(); + + // SRDWindow procedure + static LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + + // Event conversion + void convert_win32_message(UINT msg, WPARAM wparam, LPARAM lparam, std::vector<Event>& events); + + // Monitor enumeration + static BOOL CALLBACK enum_monitor_proc(HMONITOR hmonitor, HDC hdc_monitor, LPRECT lprc_monitor, LPARAM dw_data); + + // Utility methods + void update_monitors(); + SRDWindow* find_window_by_hwnd(HWND hwnd); + + // Decoration methods + void apply_dwm_border_color(HWND hwnd, int r, int g, int b); + void remove_dwm_border_color(HWND hwnd); + + // Virtual Desktop methods + void initialize_virtual_desktops(); + void cleanup_virtual_desktops(); + + // DWM methods + bool initialize_dwm(); + void cleanup_dwm(); + + // Taskbar methods + void initialize_taskbar(); + void update_taskbar(); +}; + +#endif // SRDWM_WINDOWS_PLATFORM_H + diff --git a/src/platform/x11_platform.cc b/src/platform/x11_platform.cc new file mode 100644 index 0000000..0d25369 --- /dev/null +++ b/src/platform/x11_platform.cc @@ -0,0 +1,1005 @@ +#include "x11_platform.h" +#include <iostream> +#include <cstring> + +X11Platform::X11Platform() { + std::cout << "X11Platform: Constructor called" << std::endl; +} + +X11Platform::~X11Platform() { + std::cout << "X11Platform: Destructor called" << std::endl; +} + +bool X11Platform::initialize() { + std::cout << "X11Platform: Initializing X11 backend..." << std::endl; + + // Open X11 display + display_ = XOpenDisplay(nullptr); + if (!display_) { + std::cerr << "Failed to open X11 display" << std::endl; + return false; + } + + // Get root window + root_ = DefaultRootWindow(display_); + + // Check for other window manager + if (!check_for_other_wm()) { + std::cerr << "Another window manager is already running" << std::endl; + return false; + } + + // Setup X11 environment + if (!setup_x11_environment()) { + std::cerr << "Failed to setup X11 environment" << std::endl; + return false; + } + + // Setup event masks + if (!setup_event_masks()) { + std::cerr << "Failed to setup event masks" << std::endl; + return false; + } + + // Setup atoms + setup_atoms(); + + // Setup extensions + setup_extensions(); + + // Initialize decoration state + decorations_enabled_ = true; + border_width_ = 2; + border_color_ = 0x2e3440; // Default border color + focused_border_color_ = 0x88c0d0; // Default focused border color + + std::cout << "X11Platform: Initialized successfully" << std::endl; + return true; +} + +void X11Platform::shutdown() { + std::cout << "X11Platform: Shutting down..." << std::endl; + + // Clean up windows + for (auto& pair : window_map_) { + if (pair.second) { + destroy_window(pair.second); + } + } + window_map_.clear(); + frame_window_map_.clear(); + + // Close X11 display + if (display_) { + XCloseDisplay(display_); + display_ = nullptr; + } + + std::cout << "X11Platform: Shutdown complete" << std::endl; +} + +bool X11Platform::poll_events(std::vector<Event>& events) { + if (!display_) return false; + + events.clear(); + + // Check for pending events + if (XPending(display_) > 0) { + XEvent xevent; + XNextEvent(display_, &xevent); + + // Handle the X11 event + handle_x11_event(xevent); + + // Convert to SRDWM events (simplified for now) + Event event; + event.type = EventType::WindowCreated; // Placeholder + event.data = nullptr; + event.data_size = 0; + events.push_back(event); + + return true; + } + + return false; +} + +void X11Platform::process_event(const Event& event) { + std::cout << "X11Platform: Process event called" << std::endl; +} + +std::unique_ptr<SRDWindow> X11Platform::create_window(const std::string& title, int x, int y, int width, int height) { + std::cout << "X11Platform: Create window called" << std::endl; + // TODO: Implement actual window creation when headers are available + return nullptr; +} + +void X11Platform::destroy_window(SRDWindow* window) { + std::cout << "X11Platform: Destroy window called" << std::endl; +} + +void X11Platform::set_window_position(SRDWindow* window, int x, int y) { + std::cout << "X11Platform: Set window position to (" << x << "," << y << ")" << std::endl; + if (!window || !display_) return; + X11Window x11_window = static_cast<X11Window>(window->getId()); + XMoveWindow(display_, to_x11_window(x11_window), x, y); + XFlush(display_); +} + +void X11Platform::set_window_size(SRDWindow* window, int width, int height) { + std::cout << "X11Platform: Set window size to (" << width << "x" << height << ")" << std::endl; + if (!window || !display_) return; + X11Window x11_window = static_cast<X11Window>(window->getId()); + XResizeWindow(display_, to_x11_window(x11_window), static_cast<unsigned int>(width), static_cast<unsigned int>(height)); + XFlush(display_); +} + +void X11Platform::set_window_title(SRDWindow* window, const std::string& title) { + std::cout << "X11Platform: Set window title to '" << title << "'" << std::endl; + if (!window || !display_) return; + X11Window x11_window = static_cast<X11Window>(window->getId()); + XStoreName(display_, to_x11_window(x11_window), title.c_str()); + XFlush(display_); +} + +void X11Platform::focus_window(SRDWindow* window) { + std::cout << "X11Platform: Focus window" << std::endl; + if (!window || !display_) return; + X11Window x11_window = static_cast<X11Window>(window->getId()); + XSetInputFocus(display_, to_x11_window(x11_window), RevertToParent, CurrentTime); + XFlush(display_); +} + +void X11Platform::minimize_window(SRDWindow* window) { + std::cout << "X11Platform: Minimize window called" << std::endl; +} + +void X11Platform::maximize_window(SRDWindow* window) { + std::cout << "X11Platform: Maximize window called" << std::endl; +} + +void X11Platform::close_window(SRDWindow* window) { + std::cout << "X11Platform: Close window" << std::endl; + if (!window || !display_) return; + X11Window x11_window = static_cast<X11Window>(window->getId()); + Atom wm_delete = XInternAtom(display_, "WM_DELETE_WINDOW", False); + if (wm_delete != None) { + XEvent ev{}; + ev.xclient.type = ClientMessage; + ev.xclient.message_type = XInternAtom(display_, "WM_PROTOCOLS", False); + ev.xclient.display = display_; + ev.xclient.window = to_x11_window(x11_window); + ev.xclient.format = 32; + ev.xclient.data.l[0] = wm_delete; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(display_, to_x11_window(x11_window), False, NoEventMask, &ev); + XFlush(display_); + } else { + XDestroyWindow(display_, to_x11_window(x11_window)); + XFlush(display_); + } +} + +// EWMH (Extended Window Manager Hints) support implementation +void X11Platform::set_ewmh_supported(bool supported) { + ewmh_supported_ = supported; + if (supported) { + setup_ewmh(); + } + std::cout << "X11Platform: EWMH support " << (supported ? "enabled" : "disabled") << std::endl; +} + +void X11Platform::set_window_type(SRDWindow* window, const std::string& type) { + if (!ewmh_supported_ || !window) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + Atom window_type_atom = None; + + if (type == "desktop") window_type_atom = _NET_WM_WINDOW_TYPE_DESKTOP_; + else if (type == "dock") window_type_atom = _NET_WM_WINDOW_TYPE_DOCK_; + else if (type == "toolbar") window_type_atom = _NET_WM_WINDOW_TYPE_TOOLBAR_; + else if (type == "menu") window_type_atom = _NET_WM_WINDOW_TYPE_MENU_; + else if (type == "utility") window_type_atom = _NET_WM_WINDOW_TYPE_UTILITY_; + else if (type == "splash") window_type_atom = _NET_WM_WINDOW_TYPE_SPLASH_; + else if (type == "dialog") window_type_atom = _NET_WM_WINDOW_TYPE_DIALOG_; + else if (type == "dropdown_menu") window_type_atom = _NET_WM_WINDOW_TYPE_DROPDOWN_MENU_; + else if (type == "popup_menu") window_type_atom = _NET_WM_WINDOW_TYPE_POPUP_MENU_; + else if (type == "tooltip") window_type_atom = _NET_WM_WINDOW_TYPE_TOOLTIP_; + else if (type == "notification") window_type_atom = _NET_WM_WINDOW_TYPE_NOTIFICATION_; + else if (type == "combo") window_type_atom = _NET_WM_WINDOW_TYPE_COMBO_; + else if (type == "dnd") window_type_atom = _NET_WM_WINDOW_TYPE_DND_; + else window_type_atom = _NET_WM_WINDOW_TYPE_NORMAL_; + + if (window_type_atom != None) { + XChangeProperty(display_, x11_window, _NET_WM_WINDOW_TYPE_, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&window_type_atom, 1); + } +} + +void X11Platform::set_window_state(SRDWindow* window, const std::vector<std::string>& states) { + if (!ewmh_supported_ || !window) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + std::vector<Atom> state_atoms; + + for (const auto& state : states) { + Atom state_atom = None; + if (state == "maximized_vert") state_atom = _NET_WM_STATE_MAXIMIZED_VERT_; + else if (state == "maximized_horz") state_atom = _NET_WM_STATE_MAXIMIZED_HORZ_; + else if (state == "fullscreen") state_atom = _NET_WM_STATE_FULLSCREEN_; + else if (state == "above") state_atom = _NET_WM_STATE_ABOVE_; + else if (state == "below") state_atom = _NET_WM_STATE_BELOW_; + + if (state_atom != None) { + state_atoms.push_back(state_atom); + } + } + + if (!state_atoms.empty()) { + XChangeProperty(display_, x11_window, _NET_WM_STATE_, XA_ATOM, 32, + PropModeReplace, (unsigned char*)state_atoms.data(), state_atoms.size()); + } +} + +void X11Platform::set_window_strut(SRDWindow* window, int left, int right, int top, int bottom) { + if (!ewmh_supported_ || !window) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + long strut[12] = {left, right, top, bottom, 0, 0, 0, 0, 0, 0, 0, 0}; + + XChangeProperty(display_, x11_window, XInternAtom(display_, "_NET_WM_STRUT_PARTIAL", False), + XA_CARDINAL, 32, PropModeReplace, (unsigned char*)strut, 12); +} + +// Virtual Desktop support implementation +void X11Platform::create_virtual_desktop(const std::string& name) { + if (!ewmh_supported_) return; + + int desktop_id = virtual_desktops_.size(); + virtual_desktops_.push_back(desktop_id); + + // Update EWMH desktop info + update_ewmh_desktop_info(); + + std::cout << "X11Platform: Created virtual desktop " << desktop_id << " (" << name << ")" << std::endl; +} + +void X11Platform::remove_virtual_desktop(int desktop_id) { + if (!ewmh_supported_) return; + + auto it = std::find(virtual_desktops_.begin(), virtual_desktops_.end(), desktop_id); + if (it != virtual_desktops_.end()) { + virtual_desktops_.erase(it); + + // If removing current desktop, switch to first available + if (desktop_id == current_virtual_desktop_ && !virtual_desktops_.empty()) { + switch_to_virtual_desktop(virtual_desktops_[0]); + } + + update_ewmh_desktop_info(); + std::cout << "X11Platform: Removed virtual desktop " << desktop_id << std::endl; + } +} + +void X11Platform::switch_to_virtual_desktop(int desktop_id) { + if (!ewmh_supported_) return; + + auto it = std::find(virtual_desktops_.begin(), virtual_desktops_.end(), desktop_id); + if (it != virtual_desktops_.end()) { + current_virtual_desktop_ = desktop_id; + + // Update EWMH current desktop + XChangeProperty(display_, root_, _NET_CURRENT_DESKTOP_, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)¤t_virtual_desktop_, 1); + + std::cout << "X11Platform: Switched to virtual desktop " << desktop_id << std::endl; + } +} + +int X11Platform::get_current_virtual_desktop() const { + return current_virtual_desktop_; +} + +std::vector<int> X11Platform::get_virtual_desktops() const { + return virtual_desktops_; +} + +void X11Platform::move_window_to_desktop(SRDWindow* window, int desktop_id) { + if (!ewmh_supported_ || !window) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + XChangeProperty(display_, x11_window, _NET_WM_DESKTOP_, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&desktop_id, 1); + + std::cout << "X11Platform: Moved window " << window->getId() << " to desktop " << desktop_id << std::endl; +} + +std::vector<Monitor> X11Platform::get_monitors() { + std::cout << "X11Platform: Get monitors called" << std::endl; + // Return a default monitor for now + return {Monitor{0, 0, 0, 1920, 1080}}; +} + +Monitor X11Platform::get_primary_monitor() { + std::cout << "X11Platform: Get primary monitor called" << std::endl; + return Monitor{0, 0, 0, 1920, 1080}; +} + +void X11Platform::grab_keyboard() { + std::cout << "X11Platform: Grab keyboard called" << std::endl; +} + +void X11Platform::ungrab_keyboard() { + std::cout << "X11Platform: Ungrab keyboard called" << std::endl; +} + +void X11Platform::grab_pointer() { + std::cout << "X11Platform: Grab pointer called" << std::endl; +} + +void X11Platform::ungrab_pointer() { + std::cout << "X11Platform: Ungrab pointer called" << std::endl; +} + +// Private method stubs removed - actual implementations exist below + +void X11Platform::setup_extensions() { + std::cout << "X11Platform: Setup extensions called" << std::endl; +} + +bool X11Platform::check_for_other_wm() { + // Try to select SubstructureRedirectMask on root window + // If another WM is running, this will fail + XSelectInput(display_, root_, SubstructureRedirectMask); + XSync(display_, False); + + // Check if the selection was successful + XErrorHandler old_handler = XSetErrorHandler([](Display*, XErrorEvent*) -> int { + return 0; // Ignore errors + }); + + XSync(display_, False); + XSetErrorHandler(old_handler); + + return true; // Simplified check +} + +bool X11Platform::setup_x11_environment() { + std::cout << "X11Platform: Setting up X11 environment..." << std::endl; + + // Set up error handling + XSetErrorHandler([](Display*, XErrorEvent* e) -> int { + if (e->error_code == BadWindow || + e->error_code == BadMatch || + e->error_code == BadAccess) { + return 0; // Ignore common errors + } + std::cerr << "X11 error: " << e->error_code << std::endl; + return 0; + }); + + return true; +} + +bool X11Platform::setup_event_masks() { + std::cout << "X11Platform: Setting up event masks..." << std::endl; + + // Set up root window event mask + long event_mask = SubstructureRedirectMask | SubstructureNotifyMask | + StructureNotifyMask | PropertyChangeMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + PointerMotionMask | EnterWindowMask | LeaveWindowMask; + + XSelectInput(display_, root_, event_mask); + + return true; +} + +void X11Platform::setup_atoms() { + std::cout << "X11Platform: Setting up atoms..." << std::endl; + + // TODO: Implement atom setup for EWMH/ICCCM + // This would involve creating atoms for window manager protocols +} + +void X11Platform::handle_x11_event(XEvent& event) { + std::cout << "X11Platform: Handle X11 event called" << std::endl; + + switch (event.type) { + case MapRequest: + handle_map_request(event.xmaprequest); + break; + case ConfigureRequest: + handle_configure_request(event.xconfigurerequest); + break; + case DestroyNotify: + handle_destroy_notify(event.xdestroywindow); + break; + case UnmapNotify: + handle_unmap_notify(event.xunmap); + break; + case KeyPress: + handle_key_press(event.xkey); + break; + case ButtonPress: + handle_button_press(event.xbutton); + break; + case MotionNotify: + handle_motion_notify(event.xmotion); + break; + default: + break; + } +} + +void X11Platform::handle_map_request(XMapRequestEvent& event) { + std::cout << "X11Platform: Map request for window " << static_cast<unsigned long>(event.window) << std::endl; + + // Create a SRDWindow object for this X11 window + auto window = std::make_unique<SRDWindow>(static_cast<int>(static_cast<unsigned long>(event.window)), "X11 Window"); + + // Add to window map + window_map_[from_x11_window(event.window)] = window.get(); + window.release(); + + // Map the window + XMapWindow(display_, event.window); + + // Apply decorations if enabled + if (decorations_enabled_) { + create_frame_window(window_map_[from_x11_window(event.window)]); + } +} + +void X11Platform::handle_configure_request(XConfigureRequestEvent& event) { + std::cout << "X11Platform: Configure request for window " << static_cast<unsigned long>(event.window) << std::endl; + + XWindowChanges changes; + changes.x = event.x; + changes.y = event.y; + changes.width = event.width; + changes.height = event.height; + changes.border_width = event.border_width; + changes.sibling = static_cast<::Window>(event.above); + changes.stack_mode = event.detail; + + XConfigureWindow(display_, event.window, event.value_mask, &changes); +} + +void X11Platform::handle_destroy_notify(XDestroyWindowEvent& event) { + std::cout << "X11Platform: Destroy notify for window " << static_cast<unsigned long>(event.window) << std::endl; + + auto it = window_map_.find(from_x11_window(event.window)); + if (it != window_map_.end()) { + destroy_window(it->second); + window_map_.erase(it); + } +} + +void X11Platform::handle_unmap_notify(XUnmapEvent& event) { + std::cout << "X11Platform: Unmap notify for window " << static_cast<unsigned long>(event.window) << std::endl; + + // Handle window unmapping +} + +void X11Platform::handle_key_press(XKeyEvent& event) { + std::cout << "X11Platform: Key press event" << std::endl; + + // Convert X11 key event to SRDWM event + // TODO: Implement key event conversion +} + +void X11Platform::handle_button_press(XButtonEvent& event) { + std::cout << "X11Platform: Button press event" << std::endl; + + // Convert X11 button event to SRDWM event + // TODO: Implement button event conversion +} + +void X11Platform::handle_motion_notify(XMotionEvent& event) { + std::cout << "X11Platform: Motion notify event" << std::endl; + + // Convert X11 motion event to SRDWM event + // TODO: Implement motion event conversion +} + +// Window decoration implementations +void X11Platform::set_window_decorations(SRDWindow* window, bool enabled) { + std::cout << "X11Platform: Set window decorations " << (enabled ? "enabled" : "disabled") << std::endl; + + if (!window || !display_) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + + if (enabled) { + create_frame_window(window); + } else { + destroy_frame_window(window); + } + + decorations_enabled_ = enabled; +} + +void X11Platform::set_window_border_color(SRDWindow* window, int r, int g, int b) { + std::cout << "X11Platform: Set border color RGB(" << r << "," << g << "," << b << ")" << std::endl; + + if (!window || !display_) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + unsigned long color = (r << 16) | (g << 8) | b; + + XSetWindowBorder(display_, to_x11_window(x11_window), color); + + // Update decoration state + if (window == get_focused_window()) { + focused_border_color_ = color; + } else { + border_color_ = color; + } +} + +void X11Platform::set_window_border_width(SRDWindow* window, int width) { + std::cout << "X11Platform: Set border width " << width << std::endl; + + if (!window || !display_) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + XSetWindowBorderWidth(display_, to_x11_window(x11_window), width); + + border_width_ = width; +} + +bool X11Platform::get_window_decorations(SRDWindow* window) const { + if (!window) return false; + + // Check if window has a frame window + X11Window x11_window = static_cast<X11Window>(window->getId()); + auto it = frame_window_map_.find(x11_window); + + return it != frame_window_map_.end(); +} + +void X11Platform::create_frame_window(SRDWindow* window) { + std::cout << "X11Platform: Create frame window for window " << window->getId() << std::endl; + + if (!window || !display_) return; + + X11Window client_window = static_cast<X11Window>(window->getId()); + + // Check if frame already exists + if (frame_window_map_.find(client_window) != frame_window_map_.end()) { + return; + } + + // Get client window attributes + XWindowAttributes attr; + attr.x = 0; + attr.y = 0; + attr.width = 0; + attr.height = 0; + attr.border_width = 0; + attr.depth = 0; + attr.visual = nullptr; + attr.root = 0; + attr.c_class = 0; + attr.bit_gravity = 0; + attr.win_gravity = 0; + attr.backing_store = 0; + attr.backing_planes = 0; + attr.backing_pixel = 0; + attr.save_under = 0; + attr.colormap = 0; + attr.map_installed = 0; + attr.map_state = 0; + attr.all_event_masks = 0; + attr.your_event_mask = 0; + attr.do_not_propagate_mask = 0; + attr.override_redirect = 0; + if (XGetWindowAttributes(display_, to_x11_window(client_window), &attr) == 0) { + std::cerr << "Failed to get window attributes" << std::endl; + return; + } + + // Create frame window + X11Window frame_window = from_x11_window(XCreateSimpleWindow( + display_, to_x11_window(root_), + attr.x, attr.y, + attr.width + border_width_ * 2, + attr.height + border_width_ + 30, // Add titlebar height + border_width_, + border_color_, + 0x000000 // Background color + )); + + // Set frame window properties + XSetWindowAttributes frame_attr; + frame_attr.event_mask = ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | ExposureMask; + XChangeWindowAttributes(display_, to_x11_window(frame_window), CWEventMask, &frame_attr); + + // Reparent client window into frame + XReparentWindow(display_, to_x11_window(client_window), to_x11_window(frame_window), border_width_, 30); + + // Map frame window + XMapWindow(display_, to_x11_window(frame_window)); + + // Store frame window mapping + frame_window_map_[client_window] = frame_window; + + // Draw titlebar + draw_titlebar(window); + + std::cout << "X11Platform: Frame window created successfully" << std::endl; +} + +void X11Platform::destroy_frame_window(SRDWindow* window) { + std::cout << "X11Platform: Destroy frame window for window " << window->getId() << std::endl; + + if (!window || !display_) return; + + X11Window client_window = static_cast<X11Window>(window->getId()); + + // Find frame window + auto it = frame_window_map_.find(client_window); + if (it == frame_window_map_.end()) { + return; + } + + X11Window frame_window = it->second; + + // Reparent client window back to root + XReparentWindow(display_, to_x11_window(client_window), to_x11_window(root_), 0, 0); + + // Destroy frame window + XDestroyWindow(display_, to_x11_window(frame_window)); + + // Remove from mapping + frame_window_map_.erase(it); + + std::cout << "X11Platform: Frame window destroyed successfully" << std::endl; +} + +void X11Platform::draw_titlebar(SRDWindow* window) { + std::cout << "X11Platform: Draw titlebar for window " << window->getId() << std::endl; + + if (!window || !display_) return; + + X11Window client_window = static_cast<X11Window>(window->getId()); + + // Find frame window + auto it = frame_window_map_.find(client_window); + if (it == frame_window_map_.end()) { + return; + } + + X11Window frame_window = it->second; + + // Get window title + char* window_name = nullptr; + if (XFetchName(display_, to_x11_window(client_window), &window_name) && window_name) { + // Create GC for drawing + XGCValues gc_values; + gc_values.foreground = 0xFFFFFF; // White text + gc_values.background = 0x2E3440; // Dark background + gc_values.font = XLoadFont(display_, "fixed"); + + GC gc = XCreateGC(display_, to_x11_window(frame_window), + GCForeground | GCBackground | GCFont, &gc_values); + + // Draw titlebar background + XSetForeground(display_, gc, 0x2E3440); + XFillRectangle(display_, to_x11_window(frame_window), gc, 0, 0, 800, 30); // Placeholder size + + // Draw title text + XSetForeground(display_, gc, 0xFFFFFF); + XDrawString(display_, to_x11_window(frame_window), gc, 10, 20, window_name, strlen(window_name)); + + // Clean up + XFreeGC(display_, gc); + XFree(window_name); + } +} + +void X11Platform::update_frame_geometry(SRDWindow* window) { + std::cout << "X11Platform: Update frame geometry" << std::endl; + + // TODO: Implement when X11 headers are available + // Update frame window geometry when client window changes +} + +SRDWindow* X11Platform::get_focused_window() const { + if (!display_) return nullptr; + + typedef ::Window X11WindowType; + X11WindowType focused_window; + int revert_to; + XGetInputFocus(display_, &focused_window, &revert_to); + + auto it = window_map_.find(from_x11_window(focused_window)); + if (it != window_map_.end()) { + return it->second; + } + + return nullptr; +} + +// EWMH helper methods +void X11Platform::setup_ewmh() { + if (!display_ || !ewmh_supported_) return; + + // Set EWMH supported atoms + Atom supported[] = { + _NET_WM_STATE_, + _NET_WM_STATE_MAXIMIZED_VERT_, + _NET_WM_STATE_MAXIMIZED_HORZ_, + _NET_WM_STATE_FULLSCREEN_, + _NET_WM_STATE_ABOVE_, + _NET_WM_STATE_BELOW_, + _NET_WM_WINDOW_TYPE_, + _NET_WM_WINDOW_TYPE_DESKTOP_, + _NET_WM_WINDOW_TYPE_DOCK_, + _NET_WM_WINDOW_TYPE_TOOLBAR_, + _NET_WM_WINDOW_TYPE_MENU_, + _NET_WM_WINDOW_TYPE_UTILITY_, + _NET_WM_WINDOW_TYPE_SPLASH_, + _NET_WM_WINDOW_TYPE_DIALOG_, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU_, + _NET_WM_WINDOW_TYPE_POPUP_MENU_, + _NET_WM_WINDOW_TYPE_TOOLTIP_, + _NET_WM_WINDOW_TYPE_NOTIFICATION_, + _NET_WM_WINDOW_TYPE_COMBO_, + _NET_WM_WINDOW_TYPE_DND_, + _NET_WM_WINDOW_TYPE_NORMAL_, + _NET_WM_DESKTOP_, + _NET_NUMBER_OF_DESKTOPS_, + _NET_CURRENT_DESKTOP_, + _NET_DESKTOP_NAMES_ + }; + + XChangeProperty(display_, root_, XInternAtom(display_, "_NET_SUPPORTED", False), + XA_ATOM, 32, PropModeReplace, (unsigned char*)supported, + sizeof(supported) / sizeof(supported[0])); + + // Set initial desktop info + update_ewmh_desktop_info(); + + std::cout << "X11Platform: EWMH setup completed" << std::endl; +} + +void X11Platform::update_ewmh_desktop_info() { + if (!display_ || !ewmh_supported_) return; + + // Set number of desktops + int num_desktops = virtual_desktops_.size(); + XChangeProperty(display_, root_, _NET_NUMBER_OF_DESKTOPS_, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&num_desktops, 1); + + // Set current desktop + XChangeProperty(display_, root_, _NET_CURRENT_DESKTOP_, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)¤t_virtual_desktop_, 1); + + // Set desktop names (simplified - just use numbers for now) + std::vector<std::string> desktop_names; + for (int i = 0; i < num_desktops; ++i) { + desktop_names.push_back("Desktop " + std::to_string(i + 1)); + } + + // Convert to X11 string format + std::string names_str; + for (const auto& name : desktop_names) { + names_str += name + '\0'; + } + + XChangeProperty(display_, root_, _NET_DESKTOP_NAMES_, XA_STRING, 8, + PropModeReplace, (unsigned char*)names_str.c_str(), names_str.length()); +} + +// Linux/X11-specific features implementation +void X11Platform::enable_compositor(bool enabled) { + compositor_enabled_ = enabled; + + if (enabled) { + // Try to enable compositor effects + // This is a simplified implementation - real compositors have more complex setup + std::cout << "X11Platform: Compositor effects enabled" << std::endl; + } else { + std::cout << "X11Platform: Compositor effects disabled" << std::endl; + } +} + +void X11Platform::set_window_opacity(SRDWindow* window, unsigned char opacity) { + if (!window) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + + // Set window opacity using _NET_WM_WINDOW_OPACITY atom + Atom opacity_atom = XInternAtom(display_, "_NET_WM_WINDOW_OPACITY", False); + if (opacity_atom != None) { + unsigned long opacity_value = (opacity << 24) | (opacity << 16) | (opacity << 8) | opacity; + XChangeProperty(display_, x11_window, opacity_atom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&opacity_value, 1); + } + + std::cout << "X11Platform: Set window " << window->getId() << " opacity to " << (int)opacity << std::endl; +} + +void X11Platform::set_window_blur(SRDWindow* window, bool enabled) { + if (!window) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + + // Set window blur using _NET_WM_WINDOW_BLUR atom (if supported) + Atom blur_atom = XInternAtom(display_, "_NET_WM_WINDOW_BLUR", False); + if (blur_atom != None) { + unsigned long blur_value = enabled ? 1 : 0; + XChangeProperty(display_, x11_window, blur_atom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&blur_value, 1); + } + + std::cout << "X11Platform: Set window " << window->getId() << " blur " << (enabled ? "enabled" : "disabled") << std::endl; +} + +void X11Platform::set_window_shadow(SRDWindow* window, bool enabled) { + if (!window) return; + + X11Window x11_window = static_cast<X11Window>(window->getId()); + + // Set window shadow using _NET_WM_WINDOW_SHADOW atom (if supported) + Atom shadow_atom = XInternAtom(display_, "_NET_WM_WINDOW_SHADOW", False); + if (shadow_atom != None) { + unsigned long shadow_value = enabled ? 1 : 0; + XChangeProperty(display_, x11_window, shadow_atom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&shadow_value, 1); + } + + std::cout << "X11Platform: Set window " << window->getId() << " shadow " << (enabled ? "enabled" : "disabled") << std::endl; +} + +// RandR (Resize and Rotate) support implementation +void X11Platform::enable_randr(bool enabled) { + randr_enabled_ = enabled; + + if (enabled) { + initialize_randr(); + } else { + cleanup_randr(); + } + + std::cout << "X11Platform: RandR support " << (enabled ? "enabled" : "disabled") << std::endl; +} + +void X11Platform::set_monitor_rotation(int monitor_id, int rotation) { + if (!randr_enabled_) return; + + // Rotation values: 0=0°, 1=90°, 2=180°, 3=270° + if (rotation < 0 || rotation > 3) return; + + // TODO: Implement actual RandR rotation + std::cout << "X11Platform: Set monitor " << monitor_id << " rotation to " << (rotation * 90) << "°" << std::endl; +} + +void X11Platform::set_monitor_refresh_rate(int monitor_id, int refresh_rate) { + if (!randr_enabled_) return; + + if (refresh_rate < 30 || refresh_rate > 240) return; + + // TODO: Implement actual RandR refresh rate setting + std::cout << "X11Platform: Set monitor " << monitor_id << " refresh rate to " << refresh_rate << " Hz" << std::endl; +} + +void X11Platform::set_monitor_scale(int monitor_id, float scale) { + if (!randr_enabled_) return; + + if (scale < 0.5f || scale > 3.0f) return; + + // TODO: Implement actual RandR scaling + std::cout << "X11Platform: Set monitor " << monitor_id << " scale to " << scale << "x" << std::endl; +} + +void X11Platform::initialize_randr() { + if (!display_) return; + + // Check if RandR extension is available + int event_base, error_base; + if (XRRQueryExtension(display_, &event_base, &error_base)) { + std::cout << "X11Platform: RandR extension available" << std::endl; + + // Get screen resources + Window root = DefaultRootWindow(display_); + int screen = DefaultScreen(display_); + XRRScreenResources* resources = XRRGetScreenResources(display_, root); + + if (resources) { + // Process monitor information + for (int i = 0; i < resources->noutput; ++i) { + XRROutputInfo* output_info = XRRGetOutputInfo(display_, resources, resources->outputs[i]); + if (output_info && output_info->connection == RR_Connected) { + // Add monitor to our list + Monitor monitor; + monitor.id = i; + monitor.x = 0; // TODO: Get actual position + monitor.y = 0; + monitor.width = output_info->mm_width; // Convert mm to pixels + monitor.height = output_info->mm_height; + monitor.name = output_info->name; + monitor.refresh_rate = 60; // Default + + monitors_.push_back(monitor); + } + if (output_info) XRRFreeOutputInfo(output_info); + } + XRRFreeScreenResources(resources); + } + } else { + std::cout << "X11Platform: RandR extension not available" << std::endl; + } +} + +void X11Platform::cleanup_randr() { + // Clean up RandR resources if needed + std::cout << "X11Platform: RandR cleanup completed" << std::endl; +} + +// Panel/Dock integration implementation +void X11Platform::set_panel_visible(bool visible) { + panel_visible_ = visible; + + // TODO: Implement actual panel visibility control + std::cout << "X11Platform: Panel visibility " << (visible ? "enabled" : "disabled") << std::endl; +} + +void X11Platform::set_panel_position(int position) { + if (position < 0 || position > 3) return; + + panel_position_ = position; + + // Position values: 0=bottom, 1=top, 2=left, 3=right + std::string position_str; + switch (position) { + case 0: position_str = "bottom"; break; + case 1: position_str = "top"; break; + case 2: position_str = "left"; break; + case 3: position_str = "right"; break; + default: position_str = "unknown"; break; + } + + std::cout << "X11Platform: Panel position set to " << position_str << std::endl; +} + +void X11Platform::set_panel_auto_hide(bool enabled) { + panel_auto_hide_ = enabled; + + // TODO: Implement actual panel auto-hide behavior + std::cout << "X11Platform: Panel auto-hide " << (enabled ? "enabled" : "disabled") << std::endl; +} + +void X11Platform::update_panel_workspace_list() { + if (!ewmh_supported_) return; + + // Update panel with current workspace information + // This would typically involve communicating with the panel application + std::cout << "X11Platform: Updated panel workspace list" << std::endl; +} + +// System tray integration implementation +void X11Platform::add_system_tray_icon(const std::string& tooltip, Pixmap icon) { + // TODO: Implement actual system tray icon addition + // This would involve using the _NET_SYSTEM_TRAY_S0 atom and related protocols + + system_tray_icon_ = 1; // Placeholder + std::cout << "X11Platform: Added system tray icon with tooltip: " << tooltip << std::endl; +} + +void X11Platform::remove_system_tray_icon() { + if (system_tray_icon_) { + // TODO: Implement actual system tray icon removal + system_tray_icon_ = 0; + std::cout << "X11Platform: Removed system tray icon" << std::endl; + } +} + +void X11Platform::show_system_tray_menu(Window menu) { + // TODO: Implement system tray menu display + std::cout << "X11Platform: Show system tray menu" << std::endl; +} diff --git a/src/platform/x11_platform.h b/src/platform/x11_platform.h new file mode 100644 index 0000000..095081e --- /dev/null +++ b/src/platform/x11_platform.h @@ -0,0 +1,210 @@ +#ifndef SRDWM_X11_PLATFORM_H +#define SRDWM_X11_PLATFORM_H + +#include "platform.h" +#include <string> +#include <vector> +#include <map> +#include <algorithm> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xrandr.h> +#include <X11/extensions/Xinerama.h> + +// X11 types are now properly included +// Use X11Window typedef to avoid collision with our SRDWindow class +typedef unsigned long X11Window; + +// Helper functions to convert between X11Window and X11's Window type +inline ::Window to_x11_window(X11Window w) { return static_cast<::Window>(w); } +inline X11Window from_x11_window(::Window w) { return static_cast<X11Window>(w); } + +class X11Platform : public Platform { +public: + X11Platform(); + ~X11Platform() override; + + // Platform interface implementation + bool initialize() override; + void shutdown() override; + bool poll_events(std::vector<Event>& events) override; + void process_event(const Event& event) override; + + // Window management + std::unique_ptr<SRDWindow> create_window(const std::string& title, int x, int y, int width, int height) override; + void destroy_window(SRDWindow* window) override; + void set_window_position(SRDWindow* window, int x, int y) override; + void set_window_size(SRDWindow* window, int width, int height) override; + void set_window_title(SRDWindow* window, const std::string& title) override; + void focus_window(SRDWindow* window) override; + void minimize_window(SRDWindow* window) override; + void maximize_window(SRDWindow* window) override; + void close_window(SRDWindow* window) override; + + // Monitor management + std::vector<Monitor> get_monitors() override; + Monitor get_primary_monitor() override; + + // Input handling + void grab_keyboard() override; + void ungrab_keyboard() override; + void grab_pointer() override; + void ungrab_pointer() override; + + // Window decorations (X11 implementation) + void set_window_decorations(SRDWindow* window, bool enabled) override; + void set_window_border_color(SRDWindow* window, int r, int g, int b) override; + void set_window_border_width(SRDWindow* window, int width) override; + bool get_window_decorations(SRDWindow* window) const override; + + // Linux/X11-specific features + void enable_compositor(bool enabled); + void set_window_opacity(SRDWindow* window, unsigned char opacity); + void set_window_blur(SRDWindow* window, bool enabled); + void set_window_shadow(SRDWindow* window, bool enabled); + + // EWMH (Extended Window Manager Hints) support + void set_ewmh_supported(bool supported); + void set_window_type(SRDWindow* window, const std::string& type); + void set_window_state(SRDWindow* window, const std::vector<std::string>& states); + void set_window_strut(SRDWindow* window, int left, int right, int top, int bottom); + + // Virtual Desktop support (X11 workspaces) + void create_virtual_desktop(const std::string& name); + void remove_virtual_desktop(int desktop_id); + void switch_to_virtual_desktop(int desktop_id); + int get_current_virtual_desktop() const; + std::vector<int> get_virtual_desktops() const; + void move_window_to_desktop(SRDWindow* window, int desktop_id); + + // Panel/Dock integration + void set_panel_visible(bool visible); + void set_panel_position(int position); // 0=bottom, 1=top, 2=left, 3=right + void set_panel_auto_hide(bool enabled); + void update_panel_workspace_list(); + + // System tray integration + void add_system_tray_icon(const std::string& tooltip, Pixmap icon); + void remove_system_tray_icon(); + void show_system_tray_menu(Window menu); + + // RandR (Resize and Rotate) support + void enable_randr(bool enabled); + void set_monitor_rotation(int monitor_id, int rotation); + void set_monitor_refresh_rate(int monitor_id, int refresh_rate); + void set_monitor_scale(int monitor_id, float scale); + + // Utility + std::string get_platform_name() const override { return "X11"; } + bool is_wayland() const override { return false; } + bool is_x11() const override { return true; } + bool is_windows() const override { return false; } + bool is_macos() const override { return false; } + +private: + // X11-specific members + Display* display_ = nullptr; + X11Window root_ = 0; + + // Window tracking + std::map<X11Window, ::SRDWindow*> window_map_; + std::map<X11Window, X11Window> frame_window_map_; // client -> frame + + // Monitor information + std::vector<Monitor> monitors_; + + // Decoration state + bool decorations_enabled_; + int border_width_; + unsigned long border_color_; + unsigned long focused_border_color_; + + // Linux/X11-specific state + bool compositor_enabled_; + bool ewmh_supported_; + bool randr_enabled_; + int current_virtual_desktop_; + std::vector<int> virtual_desktops_; + bool panel_visible_; + bool panel_auto_hide_; + int panel_position_; + Window system_tray_icon_; + + // EWMH atoms + Atom _NET_WM_STATE_; + Atom _NET_WM_STATE_MAXIMIZED_VERT_; + Atom _NET_WM_STATE_MAXIMIZED_HORZ_; + Atom _NET_WM_STATE_FULLSCREEN_; + Atom _NET_WM_STATE_ABOVE_; + Atom _NET_WM_STATE_BELOW_; + Atom _NET_WM_WINDOW_TYPE_; + Atom _NET_WM_WINDOW_TYPE_DESKTOP_; + Atom _NET_WM_WINDOW_TYPE_DOCK_; + Atom _NET_WM_WINDOW_TYPE_TOOLBAR_; + Atom _NET_WM_WINDOW_TYPE_MENU_; + Atom _NET_WM_WINDOW_TYPE_UTILITY_; + Atom _NET_WM_WINDOW_TYPE_SPLASH_; + Atom _NET_WM_WINDOW_TYPE_DIALOG_; + Atom _NET_WM_WINDOW_TYPE_DROPDOWN_MENU_; + Atom _NET_WM_WINDOW_TYPE_POPUP_MENU_; + Atom _NET_WM_WINDOW_TYPE_TOOLTIP_; + Atom _NET_WM_WINDOW_TYPE_NOTIFICATION_; + Atom _NET_WM_WINDOW_TYPE_COMBO_; + Atom _NET_WM_WINDOW_TYPE_DND_; + Atom _NET_WM_WINDOW_TYPE_NORMAL_; + Atom _NET_WM_DESKTOP_; + Atom _NET_NUMBER_OF_DESKTOPS_; + Atom _NET_CURRENT_DESKTOP_; + Atom _NET_DESKTOP_NAMES_; + Atom _NET_WM_STRUT_; + Atom _NET_WM_STRUT_PARTIAL_; + Atom _NET_WM_OPACITY_; + + // Helper methods + SRDWindow* get_focused_window() const; + + // Private methods + bool setup_x11_environment(); + bool setup_event_masks(); + void handle_x11_event(XEvent& event); + void setup_atoms(); + void setup_extensions(); + bool check_for_other_wm(); + + // Event handlers + void handle_map_request(XMapRequestEvent& event); + void handle_configure_request(XConfigureRequestEvent& event); + void handle_destroy_notify(XDestroyWindowEvent& event); + void handle_unmap_notify(XUnmapEvent& event); + void handle_key_press(XKeyEvent& event); + void handle_button_press(XButtonEvent& event); + void handle_motion_notify(XMotionEvent& event); + + // Decoration methods + void create_frame_window(SRDWindow* window); + void destroy_frame_window(SRDWindow* window); + void draw_titlebar(SRDWindow* window); + void update_frame_geometry(SRDWindow* window); + + // EWMH methods + void setup_ewmh(); + void update_ewmh_desktop_info(); + void handle_ewmh_message(XClientMessageEvent& event); + + // Virtual Desktop methods + void initialize_virtual_desktops(); + void cleanup_virtual_desktops(); + + // RandR methods + void initialize_randr(); + void cleanup_randr(); + + // Panel methods + void initialize_panel(); + void update_panel(); +}; + +#endif // SRDWM_X11_PLATFORM_H + diff --git a/src/utils/logger.cc b/src/utils/logger.cc new file mode 100644 index 0000000..92d26d9 --- /dev/null +++ b/src/utils/logger.cc @@ -0,0 +1,126 @@ +#include "logger.h" +#include <iostream> +#include <fstream> +#include <chrono> +#include <iomanip> +#include <ctime> + +// Global logger instance +Logger g_logger; + +Logger::Logger() + : current_level(LogLevel::INFO) + , console_enabled(true) + , file_enabled(false) { +} + +Logger::~Logger() { + if (file_stream) { + file_stream->flush(); + } +} + +void Logger::set_level(LogLevel level) { + current_level = level; +} + +void Logger::debug(const std::string& message) { + log(LogLevel::DEBUG, message); +} + +void Logger::info(const std::string& message) { + log(LogLevel::INFO, message); +} + +void Logger::warning(const std::string& message) { + log(LogLevel::WARNING, message); +} + +void Logger::error(const std::string& message) { + log(LogLevel::ERROR, message); +} + +void Logger::fatal(const std::string& message) { + log(LogLevel::FATAL, message); +} + +void Logger::log(LogLevel level, const std::string& message) { + if (level >= current_level) { + write_log(level, message); + } +} + +void Logger::set_output_file(const std::string& filename) { + output_filename = filename; + if (file_enabled && !filename.empty()) { + file_stream = std::make_unique<std::ofstream>(filename, std::ios::app); + if (!file_stream->good()) { + std::cerr << "Failed to open log file: " << filename << std::endl; + file_stream.reset(); + } + } +} + +void Logger::enable_console(bool enable) { + console_enabled = enable; +} + +void Logger::enable_file(bool enable) { + file_enabled = enable; + if (enable && !output_filename.empty()) { + set_output_file(output_filename); + } else if (!enable) { + file_stream.reset(); + } +} + +std::string Logger::level_to_string(LogLevel level) { + switch (level) { + case LogLevel::DEBUG: return "DEBUG"; + case LogLevel::INFO: return "INFO"; + case LogLevel::WARNING: return "WARNING"; + case LogLevel::ERROR: return "ERROR"; + case LogLevel::FATAL: return "FATAL"; + default: return "UNKNOWN"; + } +} + +std::string Logger::get_timestamp() { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( + now.time_since_epoch()) % 1000; + + std::stringstream ss; + ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S"); + ss << '.' << std::setfill('0') << std::setw(3) << ms.count(); + return ss.str(); +} + +void Logger::write_log(LogLevel level, const std::string& message) { + std::string timestamp = get_timestamp(); + std::string level_str = level_to_string(level); + std::string log_entry = "[" + timestamp + "] [" + level_str + "] " + message + "\n"; + + if (console_enabled) { + if (level == LogLevel::ERROR || level == LogLevel::FATAL) { + std::cerr << log_entry; + } else { + std::cout << log_entry; + } + } + + if (file_enabled && file_stream && file_stream->good()) { + *file_stream << log_entry; + file_stream->flush(); + } +} + +// LogStream implementation +LogStream::LogStream(Logger& logger, LogLevel level) + : logger(logger), level(level) { +} + +LogStream::~LogStream() { + logger.log(level, stream.str()); +} diff --git a/src/utils/logger.h b/src/utils/logger.h new file mode 100644 index 0000000..2896c7e --- /dev/null +++ b/src/utils/logger.h @@ -0,0 +1,92 @@ +#ifndef SRDWM_LOGGER_H +#define SRDWM_LOGGER_H + +#include <string> +#include <sstream> +#include <memory> + +// Log levels +enum class LogLevel { + DEBUG, + INFO, + WARNING, + ERROR, + FATAL +}; + +// Logger class +class Logger { +public: + Logger(); + ~Logger(); + + // Set log level + void set_level(LogLevel level); + + // Logging methods + void debug(const std::string& message); + void info(const std::string& message); + void warning(const std::string& message); + void error(const std::string& message); + void fatal(const std::string& message); + + // Log with level + void log(LogLevel level, const std::string& message); + + // Set output file + void set_output_file(const std::string& filename); + + // Enable/disable console output + void enable_console(bool enable); + + // Enable/disable file output + void enable_file(bool enable); + +private: + LogLevel current_level; + bool console_enabled; + bool file_enabled; + std::string output_filename; + std::unique_ptr<std::ostream> file_stream; + + std::string level_to_string(LogLevel level); + std::string get_timestamp(); + void write_log(LogLevel level, const std::string& message); +}; + +// Global logger instance +extern Logger g_logger; + +// Convenience macros +#define LOG_DEBUG(msg) g_logger.debug(msg) +#define LOG_INFO(msg) g_logger.info(msg) +#define LOG_WARNING(msg) g_logger.warning(msg) +#define LOG_ERROR(msg) g_logger.error(msg) +#define LOG_FATAL(msg) g_logger.fatal(msg) + +// Stream-based logging +class LogStream { +public: + LogStream(Logger& logger, LogLevel level); + ~LogStream(); + + template<typename T> + LogStream& operator<<(const T& value) { + stream << value; + return *this; + } + +private: + Logger& logger; + LogLevel level; + std::ostringstream stream; +}; + +// Stream logging macros +#define LOG_STREAM_DEBUG LogStream(g_logger, LogLevel::DEBUG) +#define LOG_STREAM_INFO LogStream(g_logger, LogLevel::INFO) +#define LOG_STREAM_WARNING LogStream(g_logger, LogLevel::WARNING) +#define LOG_STREAM_ERROR LogStream(g_logger, LogLevel::ERROR) +#define LOG_STREAM_FATAL LogStream(g_logger, LogLevel::FATAL) + +#endif // SRDWM_LOGGER_H |
