Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Horrible Flickering #944

Closed
1 task done
Gorgeous-Goose opened this issue Jul 6, 2024 · 12 comments · Fixed by #959
Closed
1 task done

Horrible Flickering #944

Gorgeous-Goose opened this issue Jul 6, 2024 · 12 comments · Fixed by #959
Labels
bug Something isn't working triage Untriaged issues

Comments

@Gorgeous-Goose
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Behavior

Buggy since an update or two ago (I think .20).

  • Sometimes red border flickers continually
  • Sometimes the whole window flickers and refreshes every couple seconds or so, making it almost unusable.

I keep having to quit out of whim because it's so bad.

Unrelated or older issues:

  • the update widget thingie always tries to get me to update even when I click 'skip this update' or whatever it says, and also even after I'm already on the newest version.

  • Sometimes when switching to a workspace, it doesn't show the window content until I drag the window a bit to reset its position (this has always been the case since starting to use Whim). Some programs have this issue more than others - seems to happen quite often with TickTick for example.

Config

#nullable enable
#r "C:\Users\compa\AppData\Local\Programs\Whim\whim.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.Bar\Whim.Bar.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.CommandPalette\Whim.CommandPalette.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.FloatingLayout\Whim.FloatingLayout.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.FocusIndicator\Whim.FocusIndicator.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.Gaps\Whim.Gaps.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.LayoutPreview\Whim.LayoutPreview.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.SliceLayout\Whim.SliceLayout.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.TreeLayout\Whim.TreeLayout.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.TreeLayout.Bar\Whim.TreeLayout.Bar.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.TreeLayout.CommandPalette\Whim.TreeLayout.CommandPalette.dll"
#r "C:\Users\compa\AppData\Local\Programs\Whim\plugins\Whim.Updater\Whim.Updater.dll"

using System;
using System.Collections.Generic;
using Microsoft.UI;
using Microsoft.UI.Xaml.Media;
using Whim;
using Whim.Bar;
using Whim.CommandPalette;
using Whim.FloatingLayout;
using Whim.FocusIndicator;
using Whim.Gaps;
using Whim.LayoutPreview;
using Whim.SliceLayout;
using Whim.TreeLayout;
using Whim.TreeLayout.Bar;
using Whim.TreeLayout.CommandPalette;
using Whim.Updater;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using System.Linq;

/// <summary>
/// This is what's called when Whim is loaded.
/// </summary>
/// <param name="context"></param>
void DoConfig(IContext context)
{
	context.Logger.Config = new LoggerConfig();

	// Bar plugin.
	List<BarComponent> leftComponents = new() { WorkspaceWidget.CreateComponent() };
	List<BarComponent> centerComponents = new() { FocusedWindowWidget.CreateComponent() };
	List<BarComponent> rightComponents =
		new()
		{
			DateTimeWidget.CreateComponent()
		};

	BarConfig barConfig = new(leftComponents, centerComponents, rightComponents);
	BarPlugin barPlugin = new(context, barConfig);
	context.PluginManager.AddPlugin(barPlugin);

	// Gap plugin.
	GapsConfig gapsConfig = new() { OuterGap = 1, InnerGap = 0 };
	GapsPlugin gapsPlugin = new(context, gapsConfig);
	context.PluginManager.AddPlugin(gapsPlugin);

	// Floating window plugin.
	FloatingLayoutPlugin floatingLayoutPlugin = new(context);
	context.PluginManager.AddPlugin(floatingLayoutPlugin);

	// Focus indicator.
	FocusIndicatorConfig focusIndicatorConfig = new() { Color = new SolidColorBrush(Colors.Red), FadeEnabled = false, BorderSize = 1 };
	FocusIndicatorPlugin focusIndicatorPlugin = new(context, focusIndicatorConfig);

	context.PluginManager.AddPlugin(focusIndicatorPlugin);

	// Command palette.
	CommandPaletteConfig commandPaletteConfig = new(context);
	CommandPalettePlugin commandPalettePlugin = new(context, commandPaletteConfig);
	context.PluginManager.AddPlugin(commandPalettePlugin);

	// Slice layout.
	SliceLayoutPlugin sliceLayoutPlugin = new(context);
	context.PluginManager.AddPlugin(sliceLayoutPlugin);

	// Tree layout.
	TreeLayoutPlugin treeLayoutPlugin = new(context);
	context.PluginManager.AddPlugin(treeLayoutPlugin);

	// Tree layout bar.
	TreeLayoutBarPlugin treeLayoutBarPlugin = new(treeLayoutPlugin);
	context.PluginManager.AddPlugin(treeLayoutBarPlugin);
	rightComponents.Add(treeLayoutBarPlugin.CreateComponent());

	// Tree layout command palette.
	TreeLayoutCommandPalettePlugin treeLayoutCommandPalettePlugin =
		new(context, treeLayoutPlugin, commandPalettePlugin);
	context.PluginManager.AddPlugin(treeLayoutCommandPalettePlugin);

	// Layout preview.
	LayoutPreviewPlugin layoutPreviewPlugin = new(context);
	context.PluginManager.AddPlugin(layoutPreviewPlugin);

	// Updater.
	UpdaterConfig updaterConfig = new() { ReleaseChannel = ReleaseChannel.Alpha };
	UpdaterPlugin updaterPlugin = new(context, updaterConfig);
	context.PluginManager.AddPlugin(updaterPlugin);

	// Set up workspaces.
	context.WorkspaceManager.Add("1");
	context.WorkspaceManager.Add("2");
	context.WorkspaceManager.Add("3");
	context.WorkspaceManager.Add("Q");
	context.WorkspaceManager.Add("W");
	context.WorkspaceManager.Add("E");
	context.WorkspaceManager.Add("A");
	context.WorkspaceManager.Add("S");
	context.WorkspaceManager.Add("D");
	
	context.RouterManager.AddProcessFileNameRoute("Rider64.exe", "1"); 
	context.RouterManager.AddProcessFileNameRoute("Unity Hub.exe", "2");
	context.RouterManager.AddProcessFileNameRoute("Unity.exe", "2");
	context.RouterManager.AddProcessFileNameRoute("brave.exe", "Q");
	context.RouterManager.AddProcessFileNameRoute("gimp.exe", "W");
	context.RouterManager.AddProcessFileNameRoute("StabilityMatrix.exe", "E");
	context.RouterManager.AddProcessFileNameRoute("TickTick.exe", "A"); 
	context.RouterManager.AddProcessFileNameRoute("OneCommander.exe", "S"); 
	context.RouterManager.AddProcessFileNameRoute("Obsidian.exe", "D"); 
	
	context.FilterManager.AddProcessFileNameFilter("Flow.Launcher.exe");


	// Set up layout engines.
	context.WorkspaceManager.CreateLayoutEngines = () =>
		new CreateLeafLayoutEngine[]
		{
			(id) => SliceLayouts.CreateMultiColumnLayout(context, sliceLayoutPlugin, id, 1, 2, 0),
			(id) => SliceLayouts.CreatePrimaryStackLayout(context, sliceLayoutPlugin, id),
			(id) => SliceLayouts.CreateSecondaryPrimaryLayout(context, sliceLayoutPlugin, id),
			(id) => new FocusLayoutEngine(id),
			(id) => new TreeLayoutEngine(context, treeLayoutPlugin, id)
		};
		
		
	KeyModifiers AltShift = KeyModifiers.LAlt | KeyModifiers.LShift;
	
	context.KeybindManager.SetKeybind("whim.core.activate_workspace_1", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_1));
	context.KeybindManager.SetKeybind("whim.core.activate_workspace_2", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_2));
	context.KeybindManager.SetKeybind("whim.core.activate_workspace_3", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_3));
	context.KeybindManager.SetKeybind("whim.core.activate_workspace_4", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_Q));
	context.KeybindManager.SetKeybind("whim.core.activate_workspace_5", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_W));
	context.KeybindManager.SetKeybind("whim.core.activate_workspace_6", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_E));
	context.KeybindManager.SetKeybind("whim.core.activate_workspace_7", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_A));
	context.KeybindManager.SetKeybind("whim.core.activate_workspace_8", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_S));
	context.KeybindManager.SetKeybind("whim.core.activate_workspace_9", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_D));
	
	context.KeybindManager.SetKeybind("whim.core.restart_whim", new Keybind(AltShift, VIRTUAL_KEY.VK_V));
	
	//context.KeybindManager.SetKeybind("whim.core.close_current_workspace", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_X));
	
	context.KeybindManager.SetKeybind("whim.core.focus_next_monitor", new Keybind(KeyModifiers.LAlt, VIRTUAL_KEY.VK_C));
	
	
	//all this crap is just to be able to move windows to workstations
context.CommandManager.Add(
    identifier: "move_window_to_workspace_1",
    title: "Move window to workspace 1",
    callback: () =>
    {
        int Index = 1;  // use a fixed index as proof of concept
        IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
        
        if (Index <= workspaces.Length)
        {
            context.Butler.MoveWindowToWorkspace(workspaces[Index - 1]);
            context.Butler.Activate(workspaces[Index - 1]);
        }
    }
);

context.CommandManager.Add(
    identifier: "move_window_to_workspace_2",
    title: "Move window to workspace 2",
    callback: () =>
    {
        int Index = 2;  // use a fixed index as proof of concept
        IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
        
        if (Index <= workspaces.Length)
        {
            context.Butler.MoveWindowToWorkspace(workspaces[Index - 1]);
            context.Butler.Activate(workspaces[Index - 1]);
        }
    }
);

context.CommandManager.Add(
    identifier: "move_window_to_workspace_3",
    title: "Move window to workspace 3",
    callback: () =>
    {
        int Index = 3;  // use a fixed index as proof of concept
        IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
        
        if (Index <= workspaces.Length)
        {
            context.Butler.MoveWindowToWorkspace(workspaces[Index - 1]);
            context.Butler.Activate(workspaces[Index - 1]);
        }
    }
);

context.CommandManager.Add(
    identifier: "move_window_to_workspace_4",
    title: "Move window to workspace 4",
    callback: () =>
    {
        int Index = 4;  // use a fixed index as proof of concept
        IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
        
        if (Index <= workspaces.Length)
        {
            context.Butler.MoveWindowToWorkspace(workspaces[Index - 1]);
            context.Butler.Activate(workspaces[Index - 1]);
        }
    }
);


context.CommandManager.Add(
    identifier: "move_window_to_workspace_5",
    title: "Move window to workspace 5",
    callback: () =>
    {
        int Index = 5;  // use a fixed index as proof of concept
        IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
        
        if (Index <= workspaces.Length)
        {
            context.Butler.MoveWindowToWorkspace(workspaces[Index - 1]);
            context.Butler.Activate(workspaces[Index - 1]);
        }
    }
);


context.CommandManager.Add(
    identifier: "move_window_to_workspace_6",
    title: "Move window to workspace 6",
    callback: () =>
    {
        int Index = 6;  // use a fixed index as proof of concept
        IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
        
        if (Index <= workspaces.Length)
        {
            context.Butler.MoveWindowToWorkspace(workspaces[Index - 1]);
            context.Butler.Activate(workspaces[Index - 1]);
        }
    }
);

context.CommandManager.Add(
    identifier: "move_window_to_workspace_7",
    title: "Move window to workspace 7",
    callback: () =>
    {
        int Index = 7;  // use a fixed index as proof of concept
        IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
        
        if (Index <= workspaces.Length)
        {
            context.Butler.MoveWindowToWorkspace(workspaces[Index - 1]);
            context.Butler.Activate(workspaces[Index - 1]);
        }
    }
);

context.CommandManager.Add(
    identifier: "move_window_to_workspace_8",
    title: "Move window to workspace 8",
    callback: () =>
    {
        int Index = 8;  // use a fixed index as proof of concept
        IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
        
        if (Index <= workspaces.Length)
        {
            context.Butler.MoveWindowToWorkspace(workspaces[Index - 1]);
            context.Butler.Activate(workspaces[Index - 1]);
        }
    }
);

context.CommandManager.Add(
    identifier: "move_window_to_workspace_9",
    title: "Move window to workspace 9",
    callback: () =>
    {
        int Index = 9;  // use a fixed index as proof of concept
        IWorkspace[] workspaces = context.WorkspaceManager.ToArray();
        
        if (Index <= workspaces.Length)
        {
            context.Butler.MoveWindowToWorkspace(workspaces[Index - 1]);
            context.Butler.Activate(workspaces[Index - 1]);
        }
    }
);
// Create an associated keybind for Win + Shift + 1
context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_1", new Keybind(AltShift, VIRTUAL_KEY.VK_1));	
context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_2", new Keybind(AltShift, VIRTUAL_KEY.VK_2));	
context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_3", new Keybind(AltShift, VIRTUAL_KEY.VK_3));	
context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_4", new Keybind(AltShift, VIRTUAL_KEY.VK_Q));	
context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_5", new Keybind(AltShift, VIRTUAL_KEY.VK_W));	
context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_6", new Keybind(AltShift, VIRTUAL_KEY.VK_E));	
context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_7", new Keybind(AltShift, VIRTUAL_KEY.VK_A));	
context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_8", new Keybind(AltShift, VIRTUAL_KEY.VK_S));	
context.KeybindManager.SetKeybind("whim.custom.move_window_to_workspace_9", new Keybind(AltShift, VIRTUAL_KEY.VK_D));	
}

// We return doConfig here so that Whim can call it when it loads.
return DoConfig;

Environment

  • Windows: 11
  • Architecture: x86
  • Whim: newest (I think .22)

Steps To Reproduce

The flickering seems to occur most often when I have discord open.
The non flickering issues just happen throughout normal usage with programs which were redirected to certain workspaces. Maybe worth nothing that some of the programs, like TickTick, I have a shell script open on startup. Could have something to do with it.

Anything else?

No response

@Gorgeous-Goose Gorgeous-Goose added bug Something isn't working triage Untriaged issues labels Jul 6, 2024
@urob
Copy link
Contributor

urob commented Jul 6, 2024

Same for me. Unfortunately, #922 didn't fix #918 for me either. I hadn't had time yet to collect logs and downgraded for now.

The two times I ran post #922, things seemed similar to as described in #918. I.e., the flicker got progressively worse over time until Whim eventually freezes (possibly due to #941?).

I also noted some issues with the auto-updater ever since it was added that sound similar to the ones documented here. But I usually have it disabled so I hadn't paid much attention.

@dalyIsaac
Copy link
Owner

dalyIsaac commented Jul 6, 2024

  • Sometimes red border flickers continually

Same for me. Unfortunately, #922 didn't fix #918 for me either. I hadn't had time yet to collect logs and downgraded for now.

I really thought I fixed that - maybe I don't see it as much because I've got FadeEnabled = true.
I checked your config @urob and it looks like you've also got FadeEnabled = false.


Sometimes the whole window flickers and refreshes every couple seconds or so, making it almost unusable.

Do you mean that the window you're using, or the focus indicator window? If it is the window you're using, what sort of flickering do you mean?


Unrelated or older issues:

  • the update widget thingie always tries to get me to update even when I click 'skip this update' or whatever it says, and also even after I'm already on the newest version.

Sorry yeah that sucks. I've only got Whim running on my dev machine so I haven't actually had the updater running recently. I agree with @urob, you're probably better off just disabling it until I get around to fixing it #728.


  • Sometimes when switching to a workspace, it doesn't show the window content until I drag the window a bit to reset its position (this has always been the case since starting to use Whim). Some programs have this issue more than others - seems to happen quite often with TickTick for example.

I do want to fix this - it's annoying.


The two times I ran post #922, things seemed similar to as described in #918. I.e., the flicker got progressively worse over time until Whim eventually freezes (possibly due to #941?).

This bug is infuriating - there's a call to a Windows API which is just not behaving as it should. Might be a little bit of a rabbit hole. It'll be hard to check if it's fixed because I haven't yet identified a reliable way to reproduce it.

I'm also aware that the logs have become too noisy and unhelpful - I'll trim those down soon too.

@urob
Copy link
Contributor

urob commented Jul 6, 2024

Re: logs, what's the most helpful verbosity level? I have them set to DEBUG, which contributes to the noise I imagine.

Re: flickering, I'll try running on the latest version again sometime this weekend to get some logs and see if I can spot any pattern.

@dalyIsaac
Copy link
Owner

I just merged a change which means that Debug should be less noisy compared to before.

My guess for the cause of this flickering is that Whim's core now queues up events on to be triggered to the main thread. This queueing can get backed up (probably the case of #941). As a result, the FloatingLayoutPlugin receives a large batch of events, the last of which is only needed.

I intend to fix #941 first and then see if there's an elegant way of getting the focus indicator to only take the last event. I also intend to move up the change of making the focus indicator a real border (#669) because the way it's implemented as another window has sufficiently annoyed me.

@urob
Copy link
Contributor

urob commented Jul 9, 2024

I have some logs: whim20240709.log running on v0.6.27 (the latest release as of today).

This is right after a restart of Windows. There are only two open windows:

  • WindowsTerminal on Workspace 1 (on primary monitor)
  • Firefox on Workspace 3 (on secondary monitor)

Other than starting Whim, there is no keyboard or mouse interaction for the first 30 or so seconds. The only interaction is in the last 5 or so second to stop Whim.

The flickering was notable almost immediately and grew progressively stronger over the 30 seconds. I haven't tried in this session so as to not pollute the logs, but in earlier trials Whim was unresponsive at similar points.

@urob
Copy link
Contributor

urob commented Jul 9, 2024

Adding a screen cast showing the flickering and freezing of Whim. Both the Statusbar and hotkeys didn't seem to do anything while Whim remained frozen.

Recording.2024-07-09.162143.mp4

Interestingly, both the freezing and flickering stopped as soon as I closed Firefox. I have confirmed this in multiple repeats and every time the flickering starts once I open and focus Firefox and stops once I close Firefox again. I haven't explored long enough yet to see if there are other programs that trigger it (but OP mentions that Discord is causing the flickering on their end).

Timeline in Video

  • 4s: Starting Whim
  • 11s: Whim is fully started
  • 23s: Flickering becomes noticable
  • 29s: Trying to switch the Workspace via StatusBar, which doesn't do anything for now
  • 38s: Closing Firefox
  • 42s: Whim belatedly switches Workspace (requested at 29s)
  • 46s: Manually switch back to Workspace 3, which executes immediately. No more flickering

@urob
Copy link
Contributor

urob commented Jul 10, 2024

I had a go at analyzing the logs posted earlier. Ignoring DispatchEvents and Pick events, there are two common event chains that always occur in sequence:

Chain 1:

[DBG] Store:77                       [Dispatch]                     Entering task, executing transform DoWorkspaceLayoutTransform { WorkspaceId = 32592244-1c41-4c23-9670-ac15b68b32b1 }
[DBG] WorkspaceSector:86             [DoLayout]                     Doing layout
[DBG] WorkspaceSector:146            [DoLayout]                     Layout 3
[DBG] WorkspaceSector:155            [DoLayout]                     Starting layout for workspace 3
[DBG] WorkspaceSector:173            [SetWindowPositions]           Setting window positions for workspace 3
[DBG] DeferWindowPosHandle:47        [.ctor]                        Creating new WindowDeferPosHandle
[DBG] SliceLayoutEngine:145          [DoLayout]                     Doing layout on (X: 3847, Y: 49, Width: 3826, Height: 2104) on Monitor[Bounds=(X: 3840, Y: 0, Width: 3840, Height: 2160) WorkingArea=(X: 3840, Y: 0, Width: 3840, Height: 2160) Name=\\.\DISPLAY1 ScaleFactor=150 IsPrimary=False], with 1 windows and 0 minimized windows
[DBG] DeferWindowPosHandle:88        [DeferWindowPos]               Adding window 0x0001073C after 0x00000001 with flags 
[DBG] WorkspaceSector:187            [SetWindowPositions]           Window New Tab — Mozilla Firefox (firefox.exe) [4380] <MozillaWindowClass> {0x0001073C} has rectangle (X: 3854, Y: 56, Width: 3812, Height: 2090)
[DBG] DeferWindowPosHandle:112       [Dispose]                      Disposing WindowDeferPosHandle
[DBG] DeferWindowPosHandle:132       [Dispose]                      Setting window position 2 times for 1 windows
[DBG] NativeManager:80               [ShowWindowNoActivate]         Showing window HWND 0x0001073C no activate
[DBG] NativeManager:80               [ShowWindowNoActivate]         Showing window HWND 0x0001073C no activate
[DBG] DeferWindowPosHandle:168       [Dispose]                      Finished setting window position
[DBG] WindowEventListener:135        [WinEventProc]                 Windows event 0x800B for New Tab — Mozilla Firefox (firefox.exe) [4380] <MozillaWindowClass> {0x0001073C}
[DBG] Store:77                       [Dispatch]                     Entering task, executing transform WindowMovedTransform { Window = New Tab — Mozilla Firefox (firefox.exe) [4380] <MozillaWindowClass> {0x0001073C} }
[DBG] WindowUtils:15                 [GetMovedEdges]                Trying to move window edges in direction of mouse movement
[DBG] WorkspaceSector:86             [DoLayout]                     Doing layout
[DBG] WindowEventListener:135        [WinEventProc]                 Windows event 0x800B for New Tab — Mozilla Firefox (firefox.exe) [4380] <MozillaWindowClass> {0x0001073C}
[DBG] Store:77                       [Dispatch]                     Entering task, executing transform WindowMovedTransform { Window = New Tab — Mozilla Firefox (firefox.exe) [4380] <MozillaWindowClass> {0x0001073C} }
[DBG] WindowUtils:15                 [GetMovedEdges]                Trying to move window edges in direction of mouse movement
[DBG] WorkspaceSector:86             [DoLayout]                     Doing layout

Chain 2

This chain comes in two variations, differing in whether the first event selects the primary monitor (DISPLAY) or secondary monitor (DISPLAY1).

[DBG] WorkspaceManager:35            [ActiveWorkspace]              Getting active workspace for monitor Monitor[Bounds=(X: 3840, Y: 0, Width: 3840, Height: 2160) WorkingArea=(X: 3840, Y: 0, Width: 3840, Height: 2160) Name=\\.\DISPLAY1 ScaleFactor=150 IsPrimary=False]
[DBG] FocusIndicatorWindow.xaml:33   [Activate]                     Activating focus indicator window
[DBG] DeferWindowPosHandle:47        [.ctor]                        Creating new WindowDeferPosHandle
[DBG] DeferWindowPosHandle:88        [DeferWindowPos]               Adding window 0x000203D6 after 0x0001073C with flags SWP_NOREDRAW, SWP_NOACTIVATE
[DBG] DeferWindowPosHandle:112       [Dispose]                      Disposing WindowDeferPosHandle
[DBG] DeferWindowPosHandle:132       [Dispose]                      Setting window position 2 times for 1 windows
[DBG] NativeManager:80               [ShowWindowNoActivate]         Showing window HWND 0x000203D6 no activate
[DBG] NativeManager:80               [ShowWindowNoActivate]         Showing window HWND 0x000203D6 no activate
[DBG] DeferWindowPosHandle:168       [Dispose]                      Finished setting window position

The Flickering

Both chains are often followed and/or interrupted by two types of WhimException:15. The precise handles differ, but here is an an example for either exception:

[DBG] WhimException:15               [.ctor]                        Window with handle 0x000203D6 not found.
[DBG] WhimException:15               [.ctor]                        Window was ignored by filter

Ignoring all such exceptions, the logs consist of alternating events of Show and Hide of the focus indicator, interspersed by the two types of chains. Interestingly, as the session continues the Hide and Show nest longer and longer stretches of the two chains, presumably causing the flickering.

Here's an excerpt of the cleaned-up log right after Whim completed the initialization. Note how most Hide events are immediately followed by a Show event, explaining why the flicker is barely noticeable at first.

(To keep the logs clean, I omit the actual HideWindow event which is triggered by the Hide event.)

-- Chain 2
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] MutableRootSector:65           [DispatchEventsFn]             Dispatching events
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] MutableRootSector:65           [DispatchEventsFn]             Dispatching events
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] MutableRootSector:65           [DispatchEventsFn]             Dispatching events
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] MutableRootSector:65           [DispatchEventsFn]             Dispatching events
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator

Contrast this with an excerpt after about 30s. Here Hide is often followed by Chain 1 and Chain 2, before the indicator is shown again. My guess is that this explains the flickering.

[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
-- Chain 1
-- Chain 1
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator
-- Chain 2
[DBG] FocusIndicatorPlugin:137       [Hide]                         Hiding focus indicator
-- Chain 1
-- Chain 1
[DBG] FocusIndicatorPlugin:93        [Show]                         Showing focus indicator

Here's the full, clean-up log: debug.log (link to original log: whim20240709.log)

The Freezing

Just guessing here but it seems that the two event chains somehow seem to be circular, keeping triggering new of the same event chains that eventually overload the system. I tried disabling the FocusIndicator plugin and indeed would still eventually encounter the freezing when Firefox was opened (though it tends to take longer until it happens).

@dalyIsaac
Copy link
Owner

I've been hunting down why Firefox behaves atypically compared to other windows. There are a few issues I've found (and corroborated with Firefox's source code):

  1. Firefox will move a window after an interdeterminate timeout on startup [Source]
  2. Firefox can perform actions 500ms after an event is received - e.g., after WindowMoved [Source]
  3. Firefox will cloak the window when showing for the first time [Source]

The prior approach was for Whim to retry laying out the window after a timeout. This was not a good solution. My current plan is to add a way to ignore window events for different applications. That would work away some of the atypical events which can occur during operation:

2024-07-25 21:55:26.324 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x800B (EVENT_OBJECT_LOCATIONCHANGE) for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:26.427 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x0003 (EVENT_SYSTEM_FOREGROUND)     for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:26.605 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x800B (EVENT_OBJECT_LOCATIONCHANGE) for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:26.646 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x800B (EVENT_OBJECT_LOCATIONCHANGE) for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:27.317 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x800B (EVENT_OBJECT_LOCATIONCHANGE) for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:27.399 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x8018 (EVENT_OBJECT_UNCLOAKED)      for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:28.395 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x800B (EVENT_OBJECT_LOCATIONCHANGE) for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:29.473 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x0003 (EVENT_SYSTEM_FOREGROUND)     for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:30.884 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x800B (EVENT_OBJECT_LOCATIONCHANGE) for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:31.054 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x8002 (EVENT_OBJECT_SHOW)           for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:31.610 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x800B (EVENT_OBJECT_LOCATIONCHANGE) for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}
2024-07-25 21:55:31.805 +10:00 [DBG] WindowEventListener:135 [WinEventProc] Windows event 0x8017 (EVENT_OBJECT_CLOAKED)        for Firefox Developer Edition (firefox.exe) [24884] <MozillaWindowClass> {0x00A407C0}

Just guessing here but it seems that the two event chains somehow seem to be circular, keeping triggering new of the same event chains that eventually overload the system.

I agree. There was an earlier case where enqueueing lots of callbacks onto the main thread caused a crash. This will be similar.

dalyIsaac added a commit that referenced this issue Jul 28, 2024
Previously, Whim would enqueue a `DoLayout` whenever a window moved. This was done to account for windows doing atypical things, like trying to restore their own position.

This worked prior to Whim becoming multi-threaded. Now that it is, this is no longer a viable approach. The `WindowMovedTransform` is triggered many times by Whim. Accordingly, the enqueueing of `DoLayout` similarly is done many times, sometimes overloading the main thread (related issues: #941, #944).

This PR allows custom logic for handling events for specific types of windows. `IWindowProcessor` s specify whether to ignore the current Windows event. A `FirefoxWindowProcessor` has been implemented to account for atypical behavior by Firefox during startup.
@urob
Copy link
Contributor

urob commented Jul 29, 2024

#957 resolved the flickering for me and I'm finally able to run Whim in multithreaded mode without major issues. 🎉

Tiny observation: While the FocusIndicator now generally works with Firefox, it is only correctly activated when Firefox is focused via Whim. If it is focused via other methods (e.g., via mouse or Alt+Tab), the FocusIndicator continuous to be displayed around the previously focused window. Not a big deal, but perhaps worth tracking as a separate (low-priority) issue?

@dalyIsaac
Copy link
Owner

While the FocusIndicator now generally works with Firefox, it is only correctly activated when Firefox is focused via Whim.

From my investigation, it seemed that this occurred when Firefox was already started before Whim started. I've pushed a fix in #958 so that the custom startup processing logic is ignored if the Firefox window is already open when Whim starts.

@dalyIsaac
Copy link
Owner

There are still some flickering that I've noticed when Firefox (and I'm assuming other windows) start up for the first time. I've got some changes planned to reduce the amount of events emitted to the focus indicator which should ameliorate the flickering.

@urob
Copy link
Contributor

urob commented Jul 30, 2024

I've pushed a fix in #958 so that the custom startup processing logic is ignored if the Firefox window is already open when Whim starts.

Thanks! That fixed it for me, too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working triage Untriaged issues
Projects
Status: Done
3 participants