dannyedel / dspdfviewer

Dual-Screen PDF Viewer for latex-beamer
http://dspdfviewer.danny-edel.de
GNU General Public License v2.0
219 stars 29 forks source link

Moving to a monitor does not work on i3 window manager #3

Closed dannyedel closed 9 years ago

dannyedel commented 11 years ago

When running dspdfviewer under i3, it cannot move to a specific monitor (both windows spawn on top of each other). I dont know if this is a bug in dspdfviewer, qt or i3, a bit of debugging will be necessary.

Temporary workaround:

Start dspdfviewer, and move the windows manually to the correct workspaces ($mod+shift+[1-9]) so that the external monitor's workspace contains the presentation.

dannyedel commented 11 years ago

I have tried and cannot fix this "bug". Its the whole purpose of i3 (and other tiling window managers) to force the positioning of windows and ignore the requests of the application drawing them.

mmonaco commented 10 years ago

I agree that you probably can't fix this (without i3-specific quirks at least), but having WM_CLASS or WM_NAME be a little different for each window would help us automatically place the windows. Right now, as far as I see, there's no way to differentiate.

dannyedel commented 10 years ago

Ticket reopen: Thank you @mmonaco for your input, WM_NAME and WM_CLASS being the same for both windows does sound like a Bad Thing to do in the first place. I'll fix that.

On the topic of i3-specific quirks: I would not mind putting some code in to help i3 users (I use it myself on my laptop). I've found documentation on how to send commands to i3, and I think something along the lines of the firefox example may suite my needs. Can you give me pointers to the question "How do I reliably detect that I'm running under i3?" - I have an envvar called DESKTOP_SESSION, but that might have been set by my login manager and not the WM itself, so it wouldn't work for users preferring startx.

dannyedel commented 10 years ago

WM_CLASS seems to get auto-set by Qt, resulting in something™ dependent on the value of argv[0] and the string Dspdfviewer (git grep says I've put it nowhere in myself with the first letter capitalized, so expect this to be auto-generated by the framework aswell). Note that you can define the first part of WM_CLASS yourself by calling it with dspdfviewer -name whateverYouWant [more options] <your-file> - also done by the Qt Framework.

But. I've set WM_NAME (with Qt's setWindowTitle) and WM_WINDOW_ROLE accordingly, so you should be able to try out i3wm commands with the window_role matcher. Please let me know if you think this is a good starting point, then I'll try to work some i3 IPC magic into the code.

mmonaco commented 10 years ago

I don't see anything directly in the WM_ stuff, however if we looked in the source code for $(i3 --get-socketpath) there might be clue.

As an aside, this project is exactly what I was looking for. Unfortunately it's only within a few days of a conference presentation. I plan on using it, but I don't have the time just yet to make enhancements and send pull requests. In the meantime, I'll submit some feature reqs for things I'd like to do.

mmonaco commented 10 years ago

Ah, I see. The root window has I3_SOCKET_PATH, so if that is set, then we're running under i3. This is nice because we'd be looking for the socket_path anyway for communicating with i3. There's some info on how to use it here:

http://i3-wm.org/docs/ipc.html

The constants are defined in /usr/include/i3/ipc.h

dannyedel commented 10 years ago

Thank you very much for your feedback, you don't believe how good it makes me feel to know my work is appreciated.

Bug reports and Feature requests are always welcome!


Back on-topic: Thanks for the root window information, I should be able to parse that with Qt and then only load i3-specific routines if it's actually active, thereby avoiding a link-time dependency.

mmonaco commented 10 years ago

It's only a header. So you can do one of two things:

1) copy the header into your source tree 2) add a cmake flag to build with/without i3 support, this would just guard #include <i3/ipc.h> in case it's not available on the builder's system

Run time doesn't matter, a packager could build with i3 support, but then distribute to a user without i3 installed. The latter seems cleaner to me, but I don't know how to do it. I only ever use make, not cmake.

dannyedel commented 10 years ago

Ah of course, if there is no library, then there will be no link dependence.

Number 2 is pretty easy with CMake, there is a command named option. Along that line, if you're used to writing bare-metal Makefiles, you might want to read up on CMake - for "standard" problems like a project made up of C++ files it can be a good helper.

mmonaco commented 10 years ago

I wouldn't know how to set this with Qt, but it looks like _NET_WM_DESKTOP might tell i3 which screen to place a window on. A problem is that the ids don't necessarily seem to correspond to the screen numbers. For example, I see when hacking with Qt that my primary is always 0, and secondary is 1, but _NET_CURRENT_DESKTOP shows my primary as 2 and secondary as 1.

dannyedel commented 10 years ago

Oh my, this can get intresting. Reading up on the i3 command language, I see that I have to add a JSON parser to understand its response. The _NET_WM_DESKTOP seems to be no easy thing either, if I have to guess the values.

Before I spend too much time on that, @mmonaco can you check if b1c6968 at least made it possible to define a custom keybind in i3config that does "Move dspdfviewer/AudienceWindow to projector screen and dspdfviewer/SecondaryWindow to my notebook's screen"?

dannyedel commented 9 years ago

Yes, I know this bug is almost two years old.

But.

Having swapped to debian sid, I have finally been able to reproduce this myself, and I think I have also found a workaround. I still believe that no real "solution" purely inside dspdfviewer's code can be possible, since it is the whole purpose of i3 to have complete authority over window placement.


I don't know if there's a reliable way to activate fullscreen in i3's command language, but there is at the very least a toggle. Since construction order is deterministic and "second fullscreen hides first fullscreen" I believe it will always be "no-fullscreen".


Try the following script i3dspdfviewer for the basic idea:

#!/bin/bash

# Spawn dspdfviewer in the background
dspdfviewer "$@" &

#FIXME: Wait for the window to appear
sleep 2
i3-msg '[class="Dspdfviewer" window_role="Audience_Window"] move to output right, fullscreen'

# join with background process
wait

(be warned that, at least on my system, i3 crashes if the selector can't find anything. Probably an if exists required somewhere in that i3-msg command.)


If you're still reading this, please give me a feedback whether we can mark the i3 issue as solved with this.

btw: With $mod+F I can un-fullscreen my notes window and do $stuff, while the other screen stays on audience mode in fullscreen. Pressing S into dspdfviewer at that state reliably fullscreens both windows (without swapping them) once they're on their respective monitors.

adnidor commented 9 years ago

Thanks for addressing this bug, your workaround works for me, I think it would be great to have something like this build in to dspdfviewer, maybe with an flag to activate it

dannyedel commented 9 years ago

If this exact shellcode (i3-msg '[class="Dspdfviewer" window_role="Audience_Window"] move to output right, fullscreen') works reliable, I could embed it as a std::system call that gets executed after the audience window has constructed, provided some --enable-i3-workaround flag has been given on commandline or configuration file.


It may be ugly, but as long as one doesn't pass unescaped user input to system() its just as safe as anything else that calls external programs.

@adnidor If you're willing to try this, I will commit a to a branch and you could give it a shot.

adnidor commented 9 years ago

Sure

dannyedel commented 9 years ago

Well then, please try the i3-workaround branch. On my system, it works when started with or without -sync parameter, so I think I've got just the right timing for the flush() call.

dannyedel commented 9 years ago

One moment, already spotted a bug. I've done it on the wrong screen, as usual. It's most likely pure change that it worked anyway.

adnidor commented 9 years ago

Tested, works for me Thanks!

dannyedel commented 9 years ago

Please re-test with the c87d3c8fe77ec463fdda54d5d47901dfe1d46732 commit included. It should eliminate timing-issues, since now I wait for a spontaneous resize event (such as created by i3's moving of the window) to fire the shellcode.

imo, this is the right place to do it. But it will need a bit of testing.

adnidor commented 9 years ago

Just tested, still works

dannyedel commented 9 years ago

@mmonaco Other than this workaround, I've looked into your suggestion with _NET_WM_DESKTOP.

i3 does not seem to ever query a client for that property so setting it would be most likely not do anything, and in a discussion back in 2014 it was made clear by the author that we should use the IPC interface to communicate with i3. There's a question again but I would not get my hopes up.

If I get into using i3 more by myself, I might program this myself, otherwise I'll have to rely on someone™ figuring out a good replacemeht for the generic window-positioning code


For now, please give the workaround a shot, and/or send pull requests : ) Since I'm commiting the workaround to the mainline, feel free to create a fresh bugreport if stuff doesn't work, since this ticket is already getting very long : )