Open alexwhitman opened 5 years ago
No. Windows are processed as they appear, with no relationship information determined.
That said I suspect you can work around this for firefox. IF you run this when you have several firefox windows open, and a popup:
kpie --single ./samples/dump.lua
You'll probably see what I see, which is a main-window:
( window_class() == "Firefox" ) and
( window_type() == "WINDOW_NORMAL" ) and
( window_role() == "browser" ) and
( window_application() == "Firefox" ) ) then
xy(0,26 )
size(1920,1030 )
workspace(1)
maximize()
end
vs the window you get from Help | About
:
if ( ( window_title() == "About Mozilla Firefox" ) and
( window_class() == "Firefox" ) and
( window_type() == "WINDOW_NORMAL" ) and
( window_role() == "About" ) and
( window_application() == "Firefox" ) ) then
xy(579,352 )
size(771,383 )
workspace(1)
end
Notice that window_role()
is Firefox
, or not? I suspect you can use that to handle "browser" vs. "popup".
Test to see if window_role()
is browser
is of course what I meant :)
In this case the window_role()
is browser
in both cases. An example of what is happening can be seen by testing with https://codepen.io/anon/pen/KJYqQV
Ahh you do mean a literal popup. Maybe that should have been obvious, but it wasn't.
Sadly I think you're out of luck here. We literally get called when a new window appears, and we get given that window - and nothing else. I can imagine iterating anew each time, to look to see if it were related to the same process (PID) as an existing idea, but even then it wouldn't necessarily help unless the ordering was stable, and I suspect it wouldn't be.
If you know the popup is created at a "small" size then you could catch it that way, but that would be fragile and annoying too :/
Could anything be done by checking for a parent window? https://stackoverflow.com/questions/25951656/get-parent-window-of-newly-created-window
This would of course be a new feature for kpie and I don't know enough about X to know if every window would have the "root" window as a parent.
Although thinking about it, in Firefox's case the pop-up window might not be a child of the window that opened it.
Yeah I've been experimenting with this and there seems to be little bearing between the window ID and the parent IDs I'm finding.
Here's the popup:
if ( ( window_title() == "Google - Mozilla Firefox" ) and
( window_class() == "Firefox" ) and
( window_type() == "WINDOW_NORMAL" ) and
( window_role() == "browser" ) and
( window_application() == "Firefox" ) ) then
xy(754,330 )
id(46153216)
size(412,421 )
parent(8527323)
workspace(1)
end
Here's the parent:
if ( ( window_title() == "An Anonymous Pen on CodePen - Mozilla Firefox" ) and
( window_class() == "Firefox" ) and
( window_type() == "WINDOW_NORMAL" ) and
( window_role() == "browser" ) and
( window_application() == "Firefox" ) ) then
xy(0,26 )
id(46137361)
size(1920,1030 )
parent(8388965)
workspace(1)
maximize()
end
The IDs don't match .. Though they are stable between runs; I wonder if I'm missing something?
I added an id()
function and a parent()
function to investigage:
int lua_id(lua_State * L)
{
debug("id");
Window w = wnck_window_get_xid(g_window);
lua_pushinteger(L, w & INT_MAX );
return 1;
}
then
int lua_parent(lua_State * L)
{
debug("parent");
Window root, parent, *children = NULL;
unsigned int num_children;
Display *d = gdk_x11_get_default_xdisplay();
Window w = wnck_window_get_xid(g_window);
if(!XQueryTree(d, w,
&root,
&parent,
&children,
&num_children)) {
printf("Failed to query tree\n");
return 0;
}
if (children)
XFree((char *)children);
lua_pushinteger(L, parent & INT_MAX);
return 1;
}
}
I'll sleep on things, but I'm not horribly optimistic ..
Running xwininfo -tree -root
shows that Firefox has a lot of windows but there doesn't appear to be any parent/child relationship between the windows I'm interested in.
I think the conclusion here was that there wasn't a simple/obvious way of giving you what you want.
I did think of keeping state - since the way the program works is that we get a function invoked every time a window is created. I could record :
windows[application][class] += 1
Then allow that to be tested, but it doesn't really help you because we still can't tell what kind of window it is. Maybe it's a popup-dialog, maybe it's a new window. Regardless the problem is that there's no notification of when a window is closed, so we'd end up with bogus counts anyway. (I guess I could retest each known window .. but that gets horrible quickly.)
I think the best I can do is close this, or tag it "can't fix".
Sorry!
For my case recording the window count would work as I tend to only want to resize the first window but I don't know how useful the feature might be to anyone else to warrant adding it.
I don't know if it's possible, but can the concept of global variables/state be made available in lua scripts? That way I could add a window counter in lua without the need to have it added to kpie directly.
I don't know if it's possible, but can the concept of global variables/state be made available in lua scripts?
That's an interesting question! Although the script is run again each time a window is created you can preserve state, as I just tested:
--
-- See if we have a global state hash present
--
function exists(var)
for k, _ in pairs(_G) do
if k == var then
return true
end
end
end
-- keep track of windows here...
if not exists("global_state") then
global_state = {}
end
-- get the current class
c = window_class()
cur = global_state[c] or 0
-- increase and store
cur = cur + 1
global_state[c] = cur
-- Show output.
print( "Count of windows with class " .. c .. " is " .. global_state[c] .. "\n" )
The problem here is that the state will increment each time you open a new window, but never decrease.
Again the problem is that the callback is invoked when a window is created, but nothing happens for windows being closed. Not sure how to handle that without reworking the API singificantly.
The global state is a good starting point.
Would the API require significant rework, or does it just need an extra signal to be connected?
g_signal_connect(screen, "window-closed", G_CALLBACK(on_window_closed), NULL);
In theory the callback could also invoke_lua()
as the window-closed
signal also provides a WnckWindow window
parameter.
I can't see a wnck_window_
function which would check the window status (open or closed) so it might just be an extra var to assign within the callbacks g_window_status = "open";
with a lua binding around it to access that status.
I'm speculating a lot, just going by the existing code so the above might not be sensible or workable.
Right now the script is free-form, and executed from the top every time, when a window is created.
So I was kinda thinking if it we invoked a lua-callback on two different events (window open and window close) it would be best if it were changed:
function window_opened()
-- All code that used to live in the script moved here.
end
function window_closed()
-- extra code added here, if the user needs.
end
In short all users would need to add the function window_opened()
/ end
around their code. That would make it 100% explicit when things were run, and would ensure that the window_title()
, etc, functions were not called by the user when a window were closed/destroyed. (Not sure when the window-closed event would fire, just before/just after the window destruction. But if after then any calls that try to interrogate the window would fail.)
Of course I could just parse the code and invoke window_closed()
if it exists when a window is closed. But then you see how things don't match.
Does that clarify my thinking?
Makes sense, I was thinking more along the lines of
if (window_closed) then
-- do something
end
but didn't take into account the backwards incompatibilities.
Let me update the code to monitor window close-events. If I can make that work, I could add a window_closed()
method to allow control-flow.
If that all works out I suspect I'll break compatibility in the future - but I think if you could get the window-closed to decrement the count in the global state that'll allow you to differentiate between "first" and "subsequent" windows.
Will test this evening anyway.
This whole thing is horrid! I've added logic to trigger a callback on window-close, but when it is fired the window passed to it is null.
i.e. I can call lua when the window close event happens, but all the calls fail. For example:
if window_closed() then
print("This is a window close event" )
print("Class " .. window_class())
print("App " .. window_application())
return
end
The output is:
This is a window close event
(kpie:6063): Wnck-CRITICAL **: wnck_class_group_get_res_class: assertion 'class_group != NULL' failed
ERROR: ./tmp.lua:22: attempt to concatenate a nil value
:(
Diff attached of code:
Sample program that demonstrates how this fails:
The obvious solution is to have a cache:
Then:
That would allow a destroyed window, which has an ID, to return values. The downside is that it would be a huge change, and it would lead to stale data - for the cases of browsers the title changes on tab-change, etc, not to mention X,Y coords change for other windows.
I think we're back to "can't fix" and another hour or two of our lives wasted. Computers are hard!
Bizarre that the window-closed
callback has a window parameter if it's going to be null. Oh well. Thanks for looking.
What I did to solve a problem similar to this one is add to functions to the bindings.{h,c}
and then write a script like this:
-- Change geometry and position only for the first window of
-- Firefox
if (window_class() == "firefox") then
local num = window_class_list_length()
if (num == 1) then
xy(1315, 29)
size(1245, 1435)
end
end
My changes, which if you want I can do a pull request for:
diff --git a/bindings.h b/bindings.h
index a60d355..7ae327b 100644
--- a/bindings.h
+++ b/bindings.h
@@ -97,6 +97,8 @@ int lua_unpin(lua_State * L);
int lua_unshade(lua_State * L);
int lua_window_application(lua_State * L);
int lua_window_class(lua_State * L);
+int lua_window_class_list_length(lua_State * L);
+int lua_window_class_window_index(lua_State * L);
int lua_window_decoration(lua_State * L);
int lua_window_id(lua_State * L);
int lua_window_xid(lua_State * L);
diff --git a/bindings.c b/bindings.c
index 327c956..90aee1b 100644
--- a/bindings.c
+++ b/bindings.c
@@ -167,6 +167,8 @@ void init_lua(int _debug, const char *config_file)
lua_register(g_L, "unshade", lua_unshade);
lua_register(g_L, "window_application", lua_window_application);
lua_register(g_L, "window_class", lua_window_class);
+ lua_register(g_L, "window_class_list_length", lua_window_class_list_length);
+ lua_register(g_L, "window_class_window_index", lua_window_class_window_index);
lua_register(g_L, "window_id", lua_window_id);
lua_register(g_L, "window_xid", lua_window_xid);
lua_register(g_L, "window_pid", lua_window_pid);
@@ -759,6 +761,39 @@ int lua_window_class(lua_State * L)
}
+/**
+ * Return the number of windows in the class of this window
+ */
+int lua_window_class_list_length(lua_State * L)
+{
+ WnckClassGroup *x = wnck_window_get_class_group(g_window);
+ GList *list = wnck_class_group_get_windows(x);
+ lua_pushinteger(L, g_list_length(list));
+ return 1;
+}
+
+
+/**
+ * Return the index of this window within its class
+ */
+int lua_window_class_window_index(lua_State * L)
+{
+ WnckClassGroup *x = wnck_window_get_class_group(g_window);
+ GList *list = wnck_class_group_get_windows(x);
+ int idx = 0;
+ for (GList *l = list; l != NULL; l = l->next, ++idx) {
+ if (l->data == g_window) {
+ lua_pushinteger(L, idx);
+ return 1;
+ }
+ }
+ /* Should never be here. A window should belong to its own group */
+ * Return the index of this window within its class
+ */
+int lua_window_class_window_index(lua_State * L)
+{
+ WnckClassGroup *x = wnck_window_get_class_group(g_window);
+ GList *list = wnck_class_group_get_windows(x);
+ int idx = 0;
+ for (GList *l = list; l != NULL; l = l->next, ++idx) {
+ if (l->data == g_window) {
+ lua_pushinteger(L, idx);
+ return 1;
+ }
+ }
+ /* Should never be here. A window should belong to its own group */
+ lua_pushinteger(L, -1);
+ return 1;
+}
+
+
+
/**
* Set the decorations for the current window.
*/
This is just an idea. I am sure skx
can do a much better job with these functions. I also use Gnome so it adds a dependency that in the optimal case shouldn't be needed.
Is there any way to perform logic based on window count? I currently use kpie to position and size Firefox which works fine, except it also positions and sizes any pop-up windows. What I would like is to only position and size the first "main" window and ignore anything else. The window types and classes are the same for both the main and pop-up windows so I can't use that to distinguish which window is which.