How can I reliably resize a window via `metaWindow.move_resize_frame(boolean, x, y, width, height);` right after an app launched by `Shell.App.launch()`

Hi,

Question again …

After an application was launched by shell.App.launch(), I connect the signal of windows-changed, and call metaWindow.move_resize_frame(false, x, y, width, height);

But the problem is that it can’t move and resize the window. Looks the window manager just ignore this operation. Bu if a window has existed for a long time (say maybe more than two seconds after showing in the workspace), calling metaWindow.move_resize_frame(false, x, y, width, height); can move and resize the the window.

What’s wrong here? How can I know when an app’s window can be moved and resized by calling metaWindow.move_resize_frame(false, x, y, width, height); after it was launched? Any solution for this issue?


Updated:

Code is at https://github.com/nlpsuge/gnome-shell-extension-another-window-session-manager/pull/18 in case someone want to see the code. All relevant code is here: https://github.com/nlpsuge/gnome-shell-extension-another-window-session-manager/blob/bb0fbd0e4671809a8700a8ffb017932e01861773/moveSession.js#L73-L153.

Still need help.

Thank you.

GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
        metaWindow.move_resize_frame(false, x, y, width, height);
        return GLib.SOURCE_REMOVE;
});

The above code looks work on X11, but not work on Wayland.

On Wayland I see an error:
meta_window_set_stack_position_no_sync: assertion 'window->stack_position >= 0' failed

Any suggestion?

Just a thought, but maybe try a lower priority like GLib.PRIORITY_LOW + 1? It might be mutter is using it’s own idle source somewhere that’s being dispatched after yours.

Thanks for your reply.

After some search, especially these comments above PaperWM/tiling.js at 10215f57e8b34a044e10b7407cac8fac4b93bbbc · paperwm/PaperWM · GitHub, I connect first-frame and in this signal I still use below code to resize the window:

GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
        metaWindow.move_resize_frame(false, x, y, width, height);
        return GLib.SOURCE_REMOVE;
});

I also found that by using meta_window.change_workspace_by_index(desktop_number, false); cannot move a window to another workspace right after a window opened on Wayland, it’s OK on X11. So I also have to put meta_window.change_workspace_by_index(desktop_number, false); in to the signal first-frame. The whole pseudocode is like

// Do below code only on Wayland

let metaWindowActor = meta_window.get_compositor_private();
const firstFrameId = metaWindowActor.connect('first-frame', () => {
	GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
			let frameRect = open_window.get_frame_rect();
			let current_x = frameRect.x;
			let current_y = frameRect.y;
			let current_width = frameRect.width;
			let current_height = frameRect.height;
			// All zero, may indicates this window is not rendered completely
			if (current_x === 0 &&
				current_y === 0 &&
				current_width === 0 &&
				current_height === 0) 
			{
				return GLib.SOURCE_CONTINUE;
			}

			// Resize and move
			metaWindow.move_resize_frame(false, x, y, width, height);
			
			// Change workspace if necessary
			if (not-in-desktop_number) {
			    open_window.change_workspace_by_index(desktop_number, false);
			}
			
			metaWindowActor.disconnect(firstFrameId);
			return GLib.SOURCE_REMOVE;
	});
});

But I’m not sure how reliable it is. The return value of meta_window.get_compositor_private(); can be null somehow?

More code than on X11. And metaWindow.move_resize_frame(false, x, y, width, height); is more stable and reliable on X11.


Just a thought, but maybe try a lower priority like GLib.PRIORITY_LOW + 1?

I tried this just now, don’t work.

The code looks like this:

GObject *
meta_window_get_compositor_private (MetaWindow *window)
{
  if (!window)
    return NULL;
  return window->compositor_private;
}

Since you’re holding a (toggle) reference in JavaScript and calling the method on it, I think it can not return null. It’s possible the window could be destroyed with you still holding a reference though.

You could do something like this, to be extra sure that doesn’t happen:

let windowReadyId = 0;
const firstFrameId = metaWindowActor.connect('first-frame', () => {
	windowReadyId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
        // stuff

        // before returning GLib.SOURCE_REMOVE, zero out the id
        windowReadyId = 0
        return GLib.SOURCE_REMOVE;
    });
});
meta_window.connect('unmanaging', () => {
    if (windowReadyId)
        GLib.Source.remove(windowReadyId);
});

OK, thanks, I’ll try it.

@andyholmes

Hi,

Is it possible that I can miss the first-frame signal? because there is a situation that before I connect first-frame, first-frame has been send out by metaWindowActor, is this possible?

If so, how can I make sure not miss a signal?

The documentation says you should connect to this when Meta.Display::window-created is emitted, so I guess you might want to track all windows so you can be sure of their current state.

Indeed. I don’t quite understand what the doc said until you told me this. Thanks for this.


And during recently tests, I found that I cannot disconnect signals from metaWindowActor in a destroy(), if I do this many errors occur:

gnome-shell[27918]: Object .MetaWindowActorWayland (0x5646e9bae460), has been already disposed — impossible to access it. This might be caused by the object having been destroyed from C code using something such as destroy(), dispose(), or remove() vfuncs.
gnome-shell[27918]: == Stack trace for context 0x5646e58be190 ==
gnome-shell[27918]: #0   5646ebb25cb8 i   resource:///org/gnome/shell/ui/environment.js:360 (10e78dfafdd0 @ 22)
gnome-shell[27918]: #1   7ffd1fe2f930 b   resource:///org/gnome/shell/ui/environment.js:299 (10e78dfbf100 @ 39)

But if I disconnect signals from metaWindowActor within the signal unmanaging on meta_window like this and no errors reported:

meta_window.connect('unmanaging', () => {
	if (firstFrameId) {
		obj.disconnect(firstFrameId);
	}
});

I don’t understand. Why I cannot disconnect signals from metaWindowActor, but can do this on meta_window? metaWindowActor is attached to meta_window, is part of meta_window? Aren’t they existing and disappearing together?

Do you mean you’re overriding the destroy() method? If so, you should definitely not do that in GJS. This should be safe:

// This is a Meta.WindowActor, which is a subclass of Clutter.Actor and has a ::destroy signal
const destroyId = metaWindowActor.connect('destroy', (actor) => {
    actor.disconnect(destroyId);
});

This is safe, because the object must not be finalized if it is emitting a signal. Note that Meta.Window is just a GObject and has no ::destroy() signal. Meta.Window::unmanaged seems to be the closest that object has.

Also note that if an object is destroyed any signal handlers will be removed as part of the finalization. Since a finalized object can not emit signals, it’s not necessary to disconnect signals if you know the object is being destroyed.

I added a destroy() method in the indicator, which is the subclass of PanelMenu.Button. And I call super.destroy() at the end of this destroy().

Yes, you can’t do that. Never override the destroy() method of a C class (Clutter.Actor in this case). Instead you should connect to the signal, otherwise your override may be called after JavaScript is permitted to run.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.