1 -- For honest workspaces, the initial outputs information, which determines the
2 -- physical screen that a workspace wants to be on, is part of the C class
3 -- WGroupWS. For "full screen workspaces" and scratchpads, we only keep this
4 -- information in a temporary list.
7 function getInitialOutputs(ws
)
8 if obj_is(ws
, "WGroupCW") or is_scratchpad(ws
) then
9 return InitialOutputs
[ws
:name()]
10 elseif obj_is(ws
, "WGroupWS") then
11 return WGroupWS
.get_initial_outputs(ws
)
17 function setInitialOutputs(ws
, outputs
)
18 if obj_is(ws
, "WGroupCW") or is_scratchpad(ws
) then
19 InitialOutputs
[ws
:name()] = outputs
20 elseif obj_is(ws
, "WGroupWS") then
21 WGroupWS
.set_initial_outputs(ws
, outputs
)
25 function nilOrEmpty(t
)
26 return not t
or empty(t
)
29 function mod_xrandr
.workspace_added(ws
)
30 if nilOrEmpty(getInitialOutputs(ws
)) then
31 outputs
= mod_xrandr
.get_outputs(ws
:screen_of(ws
))
33 for k
,v
in pairs(outputs
) do
34 table.insert(outputKeys
, k
)
36 setInitialOutputs(ws
, outputKeys
)
41 function for_all_workspaces_do(fn
)
43 notioncore
.region_i(function(scr
)
44 scr
:managed_i(function(ws
)
45 table.insert(workspaces
, ws
)
50 for _
,ws
in ipairs(workspaces
) do
55 function mod_xrandr
.workspaces_added()
56 for_all_workspaces_do(mod_xrandr
.workspace_added
)
59 function mod_xrandr
.screenmanagedchanged(tab
)
60 if tab
.mode
== 'add' then
61 mod_xrandr
.workspace_added(tab
.sub
);
65 screen_managed_changed_hook
= notioncore
.get_hook('screen_managed_changed_hook')
66 if screen_managed_changed_hook
then
67 screen_managed_changed_hook
:add(mod_xrandr
.screenmanagedchanged
)
70 post_layout_setup_hook
= notioncore
.get_hook('ioncore_post_layout_setup_hook')
71 post_layout_setup_hook
:add(mod_xrandr
.workspaces_added
)
73 function add_safe(t
, key
, value
)
78 table.insert(t
[key
], value
)
81 -- parameter: list of output names
82 -- returns: map from screen name to screen
83 function candidate_screens_for_output(max_screen_id
, all_outputs
, outputname
)
86 function addIfContainsOutput(screen
)
87 local outputs_within_screen
= mod_xrandr
.get_outputs_within(all_outputs
, screen
)
88 if screen
:id() <= max_screen_id
and outputs_within_screen
[outputname
] ~= nil then
89 retval
[screen
:name()] = screen
93 notioncore
.region_i(addIfContainsOutput
, "WScreen")
98 -- parameter: maximum screen id, list of all output names, list of output names for which we want the screens
99 -- returns: map from screen name to screen
100 function candidate_screens_for_outputs(max_screen_id
, all_outputs
, outputnames
)
103 if outputnames
== nil then return result
end
105 for i
,outputname
in pairs(outputnames
) do
106 local screens
= candidate_screens_for_output(max_screen_id
, all_outputs
, outputname
)
107 for k
,screen
in pairs(screens
) do
114 function firstValue(t
)
115 local key
, value
= next(t
)
120 local key
, value
= next(t
)
128 function singleton(t
)
129 local first
= next(t
)
130 return first
and not next(t
, first
)
133 function is_scratchpad(ws
)
134 return package
.loaded
["mod_sp"] and mod_sp
.is_scratchpad(ws
)
137 function find_scratchpad(screen
)
139 screen
:managed_i(function(ws
)
140 if is_scratchpad(ws
) then
150 function move_if_needed(workspace
, screen_id
)
151 local screen
= notioncore
.find_screen_id(screen_id
)
153 if workspace
:screen_of() ~= screen
then
154 if is_scratchpad(workspace
) then
155 -- Moving a scratchpad to another screen is not meaningful, so instead we move
158 workspace
:bottom():managed_i(function(reg
)
159 table.insert(content
, reg
)
162 local sp
=find_scratchpad(screen
)
163 for _
,reg
in ipairs(content
) do
164 sp
:bottom():attach(reg
)
169 screen
:attach(workspace
)
173 -- Arrange the workspaces over the first number_of_screens screens
174 function mod_xrandr
.rearrangeworkspaces(max_screen_id
)
175 -- for each screen id, which workspaces should be on that screen
177 -- workspaces that want to be on an output that's currently not on any screen
179 -- workspaces that want to be on multiple available outputs
182 local all_outputs
= mod_xrandr
.get_all_outputs()
184 -- When moving a "full screen workspace" to another screen, we seem to lose
185 -- its placeholder and thereby the possibility to return it from full
186 -- screen later. Let's therefore try to close any full screen workspace
187 -- before rearranging.
188 full_screen_workspaces
={}
189 for_all_workspaces_do(function(ws
)
190 if obj_is(ws
, "WGroupCW") then table.insert(full_screen_workspaces
, ws
)
194 for _
,ws
in ipairs(full_screen_workspaces
) do
195 ws
:set_fullscreen("false")
198 -- round one: divide workspaces in directly assignable,
199 -- orphans and wanderers
200 function roundone(workspace
)
201 local screens
= candidate_screens_for_outputs(max_screen_id
, all_outputs
, getInitialOutputs(workspace
))
202 if nilOrEmpty(screens
) then
203 table.insert(orphans
, workspace
)
204 elseif singleton(screens
) then
205 add_safe(new_mapping
, firstValue(screens
):id(), workspace
)
207 wanderers
[workspace
] = screens
211 for_all_workspaces_do(roundone
)
213 for workspace
,screens
in pairs(wanderers
) do
214 -- TODO add to screen with least # of workspaces instead of just the
215 -- first one that applies
216 if screens
[workspace
:screen_of():name()] then
217 add_safe(new_mapping
, workspace
:screen_of():id(), workspace
)
219 add_safe(new_mapping
, firstValue(screens
):id(), workspace
)
222 for i
,workspace
in pairs(orphans
) do
223 -- TODO add to screen with least # of workspaces instead of just the first one
224 add_safe(new_mapping
, 0, workspace
)
227 for screen_id
,workspaces
in pairs(new_mapping
) do
228 -- move workspace to that
229 for i
,workspace
in pairs(workspaces
) do
230 move_if_needed(workspace
, screen_id
)
235 -- refresh xinerama and rearrange workspaces on screen layout updates
236 function mod_xrandr
.screenlayoutupdated()
237 notioncore
.profiling_start('notion_xrandrrefresh.prof')
239 local screens
= mod_xinerama
.query_screens()
241 local merged_screens
= mod_xinerama
.merge_overlapping_screens(screens
)
242 mod_xinerama
.setup_screens(merged_screens
)
245 local max_screen_id
= mod_xinerama
.find_max_screen_id(screens
);
246 mod_xrandr
.rearrangeworkspaces(max_screen_id
)
249 mod_xinerama
.close_invisible_screens(max_screen_id
)
252 mod_xinerama
.populate_empty_screens()
254 notioncore
.screens_updated(notioncore
.rootwin())
255 notioncore
.profiling_stop()
258 randr_screen_change_notify_hook
= notioncore
.get_hook('randr_screen_change_notify')
260 if randr_screen_change_notify_hook
then
261 randr_screen_change_notify_hook
:add(mod_xrandr
.screenlayoutupdated
)