diff --git a/api/lua/example_config.lua b/api/lua/example_config.lua index 1dfe7b7..b6352af 100644 --- a/api/lua/example_config.lua +++ b/api/lua/example_config.lua @@ -66,14 +66,14 @@ require("pinnacle").setup(function(pinnacle) -- Just testing stuff input.keybind({ mod_key }, keys.h, function() - -- local win = window.get_focused() - -- if win ~= nil then - -- print("loc: " .. (win:loc() and win:loc().x or "nil") .. ", " .. (win:loc() and win:loc().y or "nil")) - -- print("size: " .. (win:size() and win:size().w or "nil") .. ", " .. (win:size() and win:size().h or "nil")) - -- print("class: " .. (win:class() or "nil")) - -- print("title: " .. (win:title() or "nil")) - -- print("float: " .. tostring(win:floating())) - -- end + local wins = window.get_all() + for _, win in pairs(wins) do + print("loc: " .. (win:loc() and win:loc().x or "nil") .. ", " .. (win:loc() and win:loc().y or "nil")) + print("size: " .. (win:size() and win:size().w or "nil") .. ", " .. (win:size() and win:size().h or "nil")) + print("class: " .. (win:class() or "nil")) + print("title: " .. (win:title() or "nil")) + print("float: " .. tostring(win:floating())) + end local op = output.get_focused() --[[@as Output]] print("res: " .. (op:res() and (op:res().w .. ", " .. op:res().h) or "nil")) diff --git a/api/lua/msg.lua b/api/lua/msg.lua index f1d9737..3fc3f8e 100644 --- a/api/lua/msg.lua +++ b/api/lua/msg.lua @@ -33,13 +33,7 @@ ---@class _Request --Windows ----@field GetWindowByAppId { app_id: string } ----@field GetWindowByTitle { title: string } ----@field GetWindowSize { window_id: WindowId } ----@field GetWindowLocation { window_id: WindowId } ----@field GetWindowFLoating { window_id: WindowId } ----@field GetWindowClass { window_id: WindowId } ----@field GetWindowTitle { window_id: WindowId } +---@field GetWindowProps { window_id: WindowId } --Outputs ---@field GetOutputProps { output_name: string } --Tags @@ -49,7 +43,7 @@ ---@field GetTagActive { tag_id: TagId } ---@field GetTagName { tag_id: TagId } ----@alias Request _Request | "GetWindowByFocus" | "GetAllWindows" | "GetOutputs" +---@alias Request _Request | "GetWindows" | "GetOutputs" ---@class IncomingMsg ---@field CallCallback { callback_id: integer, args: Args } @@ -67,11 +61,7 @@ --Windows ---@field Window { window_id: WindowId|nil } ---@field Windows { window_ids: WindowId[] } ----@field WindowSize { size: integer[]? } ----@field WindowLocation { loc: integer[]? } ----@field WindowClass { class: string? } ----@field WindowTitle { title: string? } ----@field WindowFloating { floating: boolean? } +---@field WindowProps { size: integer[]?, loc: integer[]?, class: string?, title: string?, floating: boolean?, focused: boolean? } --Outputs ---@field Output { output_name: OutputName? } ---@field Outputs { output_names: OutputName[] } diff --git a/api/lua/window.lua b/api/lua/window.lua index bec9b3c..6c1ba46 100644 --- a/api/lua/window.lua +++ b/api/lua/window.lua @@ -118,13 +118,13 @@ end ---@return { w: integer, h: integer }|nil size The size of the window, or nil if it doesn't exist. function win:size() SendRequest({ - GetWindowSize = { + GetWindowProps = { window_id = self.id, }, }) local response = ReadMsg() - local size = response.RequestResponse.response.WindowSize.size + local size = response.RequestResponse.response.WindowProps.size if size == nil then return nil else @@ -151,13 +151,13 @@ end ---@return { x: integer, y: integer }|nil loc The location of the window, or nil if it's not on-screen or alive. function win:loc() SendRequest({ - GetWindowLocation = { + GetWindowProps = { window_id = self.id, }, }) local response = ReadMsg() - local loc = response.RequestResponse.response.WindowLocation.loc + local loc = response.RequestResponse.response.WindowProps.loc if loc == nil then return nil else @@ -179,13 +179,13 @@ end ---@return string|nil class This window's class, or nil if it doesn't exist. function win:class() SendRequest({ - GetWindowClass = { + GetWindowProps = { window_id = self.id, }, }) local response = ReadMsg() - local class = response.RequestResponse.response.WindowClass.class + local class = response.RequestResponse.response.WindowProps.class return class end @@ -200,13 +200,13 @@ end ---@return string|nil title This window's title, or nil if it doesn't exist. function win:title() SendRequest({ - GetWindowTitle = { + GetWindowProps = { window_id = self.id, }, }) local response = ReadMsg() - local title = response.RequestResponse.response.WindowTitle.title + local title = response.RequestResponse.response.WindowProps.title return title end @@ -214,114 +214,120 @@ end --- ---### Example ---```lua ------ With Alacritty focused and floating... ----print(tostring(window.get_focused():floating())) ------ ...should print "true". +----- With the focused window floating... +---print(window.get_focused():floating()) +----- ...should print `true`. ---``` ---@return boolean|nil floating `true` if it's floating, `false` if it's tiled, or nil if it doesn't exist. function win:floating() SendRequest({ - GetWindowFloating = { + GetWindowProps = { window_id = self.id, }, }) local response = ReadMsg() - local floating = response.RequestResponse.response.WindowFloating.floating + local floating = response.RequestResponse.response.WindowProps.floating return floating end +---Get whether or not this window is focused. +--- +---### Example +---```lua +---print(window.get_focused():focused()) -- should print `true`. +---``` +---@return boolean|nil floating `true` if it's floating, `false` if it's tiled, or nil if it doesn't exist. +function win:focused() + SendRequest({ + GetWindowProps = { + window_id = self.id, + }, + }) + + local response = ReadMsg() + local focused = response.RequestResponse.response.WindowProps.focused + return focused +end + ------------------------------------------------------------------- +---@class WindowGlobal local window = {} ----TODO: This function is not implemented yet. ---- ----Get a window by its app id (aka its X11 class). ----@param app_id string The window's app id. For example, Alacritty's app id is "Alacritty". ----@return Window|nil -function window.get_by_app_id(app_id) - SendRequest({ - GetWindowByAppId = { - app_id = app_id, - }, - }) +---Get all windows with the specified class (usually the name of the application). +---@param class string The class. For example, Alacritty's class is "Alacritty". +---@return Window[] +function window.get_by_class(class) + SendRequest("GetWindows") local response = ReadMsg() - local window_id = response.RequestResponse.response.Window.window_id + local window_ids = response.RequestResponse.response.Windows.window_ids - if window_id == nil then - return nil + ---@type Window[] + local windows = {} + for _, window_id in pairs(window_ids) do + local w = new_window({ id = window_id }) + if w:class() == class then + table.insert(windows, w) + end end - ---@type Window - local wind = { - id = window_id, - } - - return new_window(wind) + return windows end ----TODO: This function is not implemented yet. ---- ----Get a window by its title. ----@param title string The window's title. ----@return Window|nil +---Get all windows with the specified title. +---@param title string The title. +---@return Window[] function window.get_by_title(title) - SendRequest({ - GetWindowByTitle = { - title = title, - }, - }) + SendRequest("GetWindows") local response = ReadMsg() - local window_id = response.RequestResponse.response.Window.window_id + local window_ids = response.RequestResponse.response.Windows.window_ids - if window_id == nil then - return nil + ---@type Window[] + local windows = {} + for _, window_id in pairs(window_ids) do + local w = new_window({ id = window_id }) + if w:title() == title then + table.insert(windows, w) + end end - ---@type Window - local wind = { - id = window_id, - } - - return new_window(wind) + return windows end ---Get the currently focused window. ---@return Window|nil function window.get_focused() - SendRequest("GetWindowByFocus") + SendRequest("GetWindows") local response = ReadMsg() - local window_id = response.RequestResponse.response.Window.window_id + local window_ids = response.RequestResponse.response.Windows.window_ids - if window_id == nil then - return nil + for _, window_id in pairs(window_ids) do + local w = new_window({ id = window_id }) + if w:focused() then + return w + end end - ---@type Window - local wind = { - id = window_id, - } - - return new_window(wind) + return nil end ---Get all windows. ---@return Window[] function window.get_all() - SendRequest("GetAllWindows") + SendRequest("GetWindows") local window_ids = ReadMsg().RequestResponse.response.Windows.window_ids ---@type Window[] local windows = {} - for i, window_id in ipairs(window_ids) do - windows[i] = new_window({ id = window_id }) + for _, window_id in pairs(window_ids) do + table.insert(windows, new_window({ id = window_id })) end return windows end diff --git a/src/api/msg.rs b/src/api/msg.rs index d8bbefa..45e0b4a 100644 --- a/src/api/msg.rs +++ b/src/api/msg.rs @@ -100,20 +100,13 @@ pub struct RequestId(pub u32); /// Messages that require a server response, usually to provide some data. pub enum Request { // Windows - GetWindowByAppId { app_id: String }, - GetWindowByTitle { title: String }, - GetWindowByFocus, - GetAllWindows, - GetWindowSize { window_id: WindowId }, - GetWindowLocation { window_id: WindowId }, - GetWindowFloating { window_id: WindowId }, - GetWindowClass { window_id: WindowId }, - GetWindowTitle { window_id: WindowId }, + GetWindows, + GetWindowProps { window_id: WindowId }, // Outputs GetOutputs, GetOutputProps { output_name: String }, // Tags - GetTagsByOutput { output_name: String }, + GetTagsByOutput { output_name: String }, // TODO: move into props GetTagsByName { tag_name: String }, GetTagOutput { tag_id: TagId }, GetTagActive { tag_id: TagId }, @@ -202,20 +195,13 @@ pub enum RequestResponse { Windows { window_ids: Vec, }, - WindowSize { + WindowProps { size: Option<(i32, i32)>, - }, - WindowLocation { loc: Option<(i32, i32)>, - }, - WindowClass { class: Option, - }, - WindowTitle { title: Option, - }, - WindowFloating { floating: Option, + focused: Option, }, Output { output_name: Option, diff --git a/src/state.rs b/src/state.rs index d748204..68ab8e4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -353,32 +353,7 @@ impl State { .expect("Stream doesn't exist"); let mut stream = stream.lock().expect("Couldn't lock stream"); match request { - Request::GetWindowByAppId { app_id: _ } => todo!(), - Request::GetWindowByTitle { title: _ } => todo!(), - Request::GetWindowByFocus => match self.focus_state.current_focus() { - Some(current_focus) => { - let window_id = current_focus.with_state(|state| state.id); - crate::api::send_to_client( - &mut stream, - &OutgoingMsg::RequestResponse { - response: RequestResponse::Window { - window_id: Some(window_id), - }, - }, - ) - .expect("Send to client failed"); - } - None => { - crate::api::send_to_client( - &mut stream, - &OutgoingMsg::RequestResponse { - response: RequestResponse::Window { window_id: None }, - }, - ) - .expect("Send to client failed"); - } - }, - Request::GetAllWindows => { + Request::GetWindows => { let window_ids = self .windows .iter() @@ -394,33 +369,16 @@ impl State { ) .expect("Couldn't send to client"); } - Request::GetWindowSize { window_id } => { - let size = window_id - .window(self) + Request::GetWindowProps { window_id } => { + let window = window_id.window(self); + let size = window + .as_ref() .map(|win| (win.geometry().size.w, win.geometry().size.h)); - crate::api::send_to_client( - &mut stream, - &OutgoingMsg::RequestResponse { - response: RequestResponse::WindowSize { size }, - }, - ) - .expect("failed to send to client"); - } - Request::GetWindowLocation { window_id } => { - let loc = window_id - .window(self) - .and_then(|win| self.space.element_location(&win)) + let loc = window + .as_ref() + .and_then(|win| self.space.element_location(win)) .map(|loc| (loc.x, loc.y)); - crate::api::send_to_client( - &mut stream, - &OutgoingMsg::RequestResponse { - response: RequestResponse::WindowLocation { loc }, - }, - ) - .expect("failed to send to client"); - } - Request::GetWindowClass { window_id } => { - let class = window_id.window(self).and_then(|win| { + let (class, title) = window.as_ref().map_or((None, None), |win| { compositor::with_states(win.toplevel().wl_surface(), |states| { let lock = states .data_map @@ -428,45 +386,28 @@ impl State { .expect("XdgToplevelSurfaceData wasn't in surface's data map") .lock() .expect("failed to acquire lock"); - lock.app_id.clone() + (lock.app_id.clone(), lock.title.clone()) }) }); - crate::api::send_to_client( - &mut stream, - &OutgoingMsg::RequestResponse { - response: RequestResponse::WindowClass { class }, - }, - ) - .expect("failed to send to client"); - } - Request::GetWindowTitle { window_id } => { - let title = window_id.window(self).and_then(|win| { - compositor::with_states(win.toplevel().wl_surface(), |states| { - let lock = states - .data_map - .get::() - .expect("XdgToplevelSurfaceData wasn't in surface's data map") - .lock() - .expect("failed to acquire lock"); - lock.title.clone() - }) - }); - crate::api::send_to_client( - &mut stream, - &OutgoingMsg::RequestResponse { - response: RequestResponse::WindowTitle { title }, - }, - ) - .expect("failed to send to client"); - } - Request::GetWindowFloating { window_id } => { - let floating = window_id - .window(self) + let floating = window + .as_ref() .map(|win| win.with_state(|state| state.floating.is_floating())); + let focused = window.as_ref().and_then(|win| { + self.focus_state + .current_focus() // TODO: actual focus + .map(|foc_win| win == &foc_win) + }); crate::api::send_to_client( &mut stream, &OutgoingMsg::RequestResponse { - response: RequestResponse::WindowFloating { floating }, + response: RequestResponse::WindowProps { + size, + loc, + class, + title, + floating, + focused, + }, }, ) .expect("failed to send to client");