Update 2022-12-04 23:38 OpenBSD/amd64
This commit is contained in:
262
.notion/cfg_xrandr.lua
Normal file
262
.notion/cfg_xrandr.lua
Normal file
@@ -0,0 +1,262 @@
|
||||
-- For honest workspaces, the initial outputs information, which determines the
|
||||
-- physical screen that a workspace wants to be on, is part of the C class
|
||||
-- WGroupWS. For "full screen workspaces" and scratchpads, we only keep this
|
||||
-- information in a temporary list.
|
||||
InitialOutputs={}
|
||||
|
||||
function getInitialOutputs(ws)
|
||||
if obj_is(ws, "WGroupCW") or is_scratchpad(ws) then
|
||||
return InitialOutputs[ws:name()]
|
||||
elseif obj_is(ws, "WGroupWS") then
|
||||
return WGroupWS.get_initial_outputs(ws)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function setInitialOutputs(ws, outputs)
|
||||
if obj_is(ws, "WGroupCW") or is_scratchpad(ws) then
|
||||
InitialOutputs[ws:name()] = outputs
|
||||
elseif obj_is(ws, "WGroupWS") then
|
||||
WGroupWS.set_initial_outputs(ws, outputs)
|
||||
end
|
||||
end
|
||||
|
||||
function nilOrEmpty(t)
|
||||
return not t or empty(t)
|
||||
end
|
||||
|
||||
function mod_xrandr.workspace_added(ws)
|
||||
if nilOrEmpty(getInitialOutputs(ws)) then
|
||||
outputs = mod_xrandr.get_outputs(ws:screen_of(ws))
|
||||
outputKeys = {}
|
||||
for k,v in pairs(outputs) do
|
||||
table.insert(outputKeys, k)
|
||||
end
|
||||
setInitialOutputs(ws, outputKeys)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function for_all_workspaces_do(fn)
|
||||
local workspaces={}
|
||||
notioncore.region_i(function(scr)
|
||||
scr:managed_i(function(ws)
|
||||
table.insert(workspaces, ws)
|
||||
return true
|
||||
end)
|
||||
return true
|
||||
end, "WScreen")
|
||||
for _,ws in ipairs(workspaces) do
|
||||
fn(ws)
|
||||
end
|
||||
end
|
||||
|
||||
function mod_xrandr.workspaces_added()
|
||||
for_all_workspaces_do(mod_xrandr.workspace_added)
|
||||
end
|
||||
|
||||
function mod_xrandr.screenmanagedchanged(tab)
|
||||
if tab.mode == 'add' then
|
||||
mod_xrandr.workspace_added(tab.sub);
|
||||
end
|
||||
end
|
||||
|
||||
screen_managed_changed_hook = notioncore.get_hook('screen_managed_changed_hook')
|
||||
if screen_managed_changed_hook then
|
||||
screen_managed_changed_hook:add(mod_xrandr.screenmanagedchanged)
|
||||
end
|
||||
|
||||
post_layout_setup_hook = notioncore.get_hook('ioncore_post_layout_setup_hook')
|
||||
post_layout_setup_hook:add(mod_xrandr.workspaces_added)
|
||||
|
||||
function add_safe(t, key, value)
|
||||
if t[key] == nil then
|
||||
t[key] = {}
|
||||
end
|
||||
|
||||
table.insert(t[key], value)
|
||||
end
|
||||
|
||||
-- parameter: list of output names
|
||||
-- returns: map from screen name to screen
|
||||
function candidate_screens_for_output(max_screen_id, all_outputs, outputname)
|
||||
local retval = {}
|
||||
|
||||
function addIfContainsOutput(screen)
|
||||
local outputs_within_screen = mod_xrandr.get_outputs_within(all_outputs, screen)
|
||||
if screen:id() <= max_screen_id and outputs_within_screen[outputname] ~= nil then
|
||||
retval[screen:name()] = screen
|
||||
end
|
||||
return true
|
||||
end
|
||||
notioncore.region_i(addIfContainsOutput, "WScreen")
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
-- parameter: maximum screen id, list of all output names, list of output names for which we want the screens
|
||||
-- returns: map from screen name to screen
|
||||
function candidate_screens_for_outputs(max_screen_id, all_outputs, outputnames)
|
||||
local result = {}
|
||||
|
||||
if outputnames == nil then return result end
|
||||
|
||||
for i,outputname in pairs(outputnames) do
|
||||
local screens = candidate_screens_for_output(max_screen_id, all_outputs, outputname)
|
||||
for k,screen in pairs(screens) do
|
||||
result[k] = screen;
|
||||
end
|
||||
end
|
||||
return result;
|
||||
end
|
||||
|
||||
function firstValue(t)
|
||||
local key, value = next(t)
|
||||
return value
|
||||
end
|
||||
|
||||
function firstKey(t)
|
||||
local key, value = next(t)
|
||||
return key
|
||||
end
|
||||
|
||||
function empty(t)
|
||||
return not next(t)
|
||||
end
|
||||
|
||||
function singleton(t)
|
||||
local first = next(t)
|
||||
return first and not next(t, first)
|
||||
end
|
||||
|
||||
function is_scratchpad(ws)
|
||||
return package.loaded["mod_sp"] and mod_sp.is_scratchpad(ws)
|
||||
end
|
||||
|
||||
function find_scratchpad(screen)
|
||||
local sp
|
||||
screen:managed_i(function(ws)
|
||||
if is_scratchpad(ws) then
|
||||
sp=ws
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end)
|
||||
return sp
|
||||
end
|
||||
|
||||
function move_if_needed(workspace, screen_id)
|
||||
local screen = notioncore.find_screen_id(screen_id)
|
||||
|
||||
if workspace:screen_of() ~= screen then
|
||||
if is_scratchpad(workspace) then
|
||||
-- Moving a scratchpad to another screen is not meaningful, so instead we move
|
||||
-- its content
|
||||
local content={}
|
||||
workspace:bottom():managed_i(function(reg)
|
||||
table.insert(content, reg)
|
||||
return true
|
||||
end)
|
||||
local sp=find_scratchpad(screen)
|
||||
for _,reg in ipairs(content) do
|
||||
sp:bottom():attach(reg)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
screen:attach(workspace)
|
||||
end
|
||||
end
|
||||
|
||||
-- Arrange the workspaces over the first number_of_screens screens
|
||||
function mod_xrandr.rearrangeworkspaces(max_screen_id)
|
||||
-- for each screen id, which workspaces should be on that screen
|
||||
new_mapping = {}
|
||||
-- workspaces that want to be on an output that's currently not on any screen
|
||||
orphans = {}
|
||||
-- workspaces that want to be on multiple available outputs
|
||||
wanderers = {}
|
||||
|
||||
local all_outputs = mod_xrandr.get_all_outputs()
|
||||
|
||||
-- When moving a "full screen workspace" to another screen, we seem to lose
|
||||
-- its placeholder and thereby the possibility to return it from full
|
||||
-- screen later. Let's therefore try to close any full screen workspace
|
||||
-- before rearranging.
|
||||
full_screen_workspaces={}
|
||||
for_all_workspaces_do(function(ws)
|
||||
if obj_is(ws, "WGroupCW") then table.insert(full_screen_workspaces, ws)
|
||||
end
|
||||
return true
|
||||
end)
|
||||
for _,ws in ipairs(full_screen_workspaces) do
|
||||
ws:set_fullscreen("false")
|
||||
end
|
||||
|
||||
-- round one: divide workspaces in directly assignable,
|
||||
-- orphans and wanderers
|
||||
function roundone(workspace)
|
||||
local screens = candidate_screens_for_outputs(max_screen_id, all_outputs, getInitialOutputs(workspace))
|
||||
if nilOrEmpty(screens) then
|
||||
table.insert(orphans, workspace)
|
||||
elseif singleton(screens) then
|
||||
add_safe(new_mapping, firstValue(screens):id(), workspace)
|
||||
else
|
||||
wanderers[workspace] = screens
|
||||
end
|
||||
return true
|
||||
end
|
||||
for_all_workspaces_do(roundone)
|
||||
|
||||
for workspace,screens in pairs(wanderers) do
|
||||
-- TODO add to screen with least # of workspaces instead of just the
|
||||
-- first one that applies
|
||||
if screens[workspace:screen_of():name()] then
|
||||
add_safe(new_mapping, workspace:screen_of():id(), workspace)
|
||||
else
|
||||
add_safe(new_mapping, firstValue(screens):id(), workspace)
|
||||
end
|
||||
end
|
||||
for i,workspace in pairs(orphans) do
|
||||
-- TODO add to screen with least # of workspaces instead of just the first one
|
||||
add_safe(new_mapping, 0, workspace)
|
||||
end
|
||||
|
||||
for screen_id,workspaces in pairs(new_mapping) do
|
||||
-- move workspace to that
|
||||
for i,workspace in pairs(workspaces) do
|
||||
move_if_needed(workspace, screen_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- refresh xinerama and rearrange workspaces on screen layout updates
|
||||
function mod_xrandr.screenlayoutupdated()
|
||||
notioncore.profiling_start('notion_xrandrrefresh.prof')
|
||||
|
||||
local screens = mod_xinerama.query_screens()
|
||||
if screens then
|
||||
local merged_screens = mod_xinerama.merge_overlapping_screens(screens)
|
||||
mod_xinerama.setup_screens(merged_screens)
|
||||
end
|
||||
|
||||
local max_screen_id = mod_xinerama.find_max_screen_id(screens);
|
||||
mod_xrandr.rearrangeworkspaces(max_screen_id)
|
||||
|
||||
if screens then
|
||||
mod_xinerama.close_invisible_screens(max_screen_id)
|
||||
end
|
||||
|
||||
mod_xinerama.populate_empty_screens()
|
||||
|
||||
notioncore.screens_updated(notioncore.rootwin())
|
||||
notioncore.profiling_stop()
|
||||
end
|
||||
|
||||
randr_screen_change_notify_hook = notioncore.get_hook('randr_screen_change_notify')
|
||||
|
||||
if randr_screen_change_notify_hook then
|
||||
randr_screen_change_notify_hook:add(mod_xrandr.screenlayoutupdated)
|
||||
end
|
||||
Reference in New Issue
Block a user