@@ -216,6 +216,40 @@ SDispatchResult splitChangeMonitor(const std::string& value)
216
216
return changeMonitor (false , value);
217
217
}
218
218
219
+ SDispatchResult grabRogueWindows (const std::string& /* unused*/ )
220
+ {
221
+ // implementation loosely based on shezdy's hyprsplit: https://github.com/shezdy/hyprsplit
222
+ Debug::log (INFO, " [split-monitor-workspaces] Grabbing rogue windows" );
223
+ const auto currentMonitor = getCurrentMonitor ();
224
+ if (currentMonitor == nullptr ) {
225
+ Debug::log (ERR, " [split-monitor-workspaces] No active monitor found" );
226
+ return {.success = false , .error = " No active monitor found" };
227
+ }
228
+ const auto currentWorkspace = currentMonitor->activeWorkspace ;
229
+ if (currentWorkspace == nullptr ) {
230
+ Debug::log (ERR, " [split-monitor-workspaces] No active workspace found" );
231
+ return {.success = false , .error = " No active workspace found" };
232
+ }
233
+
234
+ for (const auto & window : g_pCompositor->m_vWindows ) {
235
+ // ignore unmapped and special windows
236
+ if (!window->m_bIsMapped && !window->onSpecialWorkspace ())
237
+ continue ;
238
+
239
+ auto const workspaceName = window->m_pWorkspace ->m_szName ;
240
+ auto const monitorID = window->m_pMonitor ->ID ;
241
+
242
+ bool isInRogueWorkspace = !g_vMonitorWorkspaceMap.contains (monitorID) || // if the monitor is not mapped, the window is rogue
243
+ !std::ranges::any_of (g_vMonitorWorkspaceMap[monitorID], [&workspaceName](const auto & mappedWorkspaceName) { return workspaceName == mappedWorkspaceName; });
244
+ if (isInRogueWorkspace) {
245
+ Debug::log (INFO, " [split-monitor-workspaces] Moving rogue window {} from workspace {} to workspace {}" , window->m_szTitle .c_str (), workspaceName.c_str (),
246
+ currentWorkspace->m_szName .c_str ());
247
+ g_pCompositor->moveWindowToWorkspaceSafe (window, currentWorkspace);
248
+ }
249
+ }
250
+ return {.success = true , .error = " " };
251
+ }
252
+
219
253
void mapMonitor (const PHLMONITOR& monitor) // NOLINT(readability-convert-member-functions-to-static)
220
254
{
221
255
if (monitor->activeMonitorRule .disabled ) {
@@ -228,7 +262,7 @@ void mapMonitor(const PHLMONITOR& monitor) // NOLINT(readability-convert-member-
228
262
return ;
229
263
}
230
264
231
- int workspaceIndex = monitor->ID * g_workspaceCount + 1 ;
265
+ int workspaceIndex = ( monitor->ID * g_workspaceCount) + 1 ;
232
266
233
267
Debug::log (INFO, " {}" ,
234
268
" [split-monitor-workspaces] Mapping workspaces " + std::to_string (workspaceIndex) + " -" + std::to_string (workspaceIndex + g_workspaceCount - 1 ) + " to monitor " + monitor->szName );
@@ -262,7 +296,7 @@ void mapMonitor(const PHLMONITOR& monitor) // NOLINT(readability-convert-member-
262
296
263
297
void unmapMonitor (const PHLMONITOR& monitor)
264
298
{
265
- int workspaceIndex = monitor->ID * g_workspaceCount + 1 ;
299
+ int workspaceIndex = ( monitor->ID * g_workspaceCount) + 1 ;
266
300
267
301
Debug::log (INFO, " {}" ,
268
302
" [split-monitor-workspaces] Unmapping workspaces " + std::to_string (workspaceIndex) + " -" + std::to_string (workspaceIndex + g_workspaceCount - 1 ) + " from monitor " + monitor->szName );
@@ -367,6 +401,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle)
367
401
HyprlandAPI::addDispatcherV2 (PHANDLE, " split-movetoworkspacesilent" , splitMoveToWorkspaceSilent);
368
402
HyprlandAPI::addDispatcherV2 (PHANDLE, " split-changemonitor" , splitChangeMonitor);
369
403
HyprlandAPI::addDispatcherV2 (PHANDLE, " split-changemonitorsilent" , splitChangeMonitorSilent);
404
+ HyprlandAPI::addDispatcherV2 (PHANDLE, " split-grabroguewindows" , grabRogueWindows);
370
405
371
406
// reload the config before adding the callback, so we can already use the config's values we defined above
372
407
HyprlandAPI::reloadConfig ();
0 commit comments