RSATom / WebChimera

[DEPRECATED] Web browser plugin based on Vlc + Firebreath + Qt Quick 2/Qml
GNU Lesser General Public License v2.1
82 stars 22 forks source link

Option to move or show player on another screen #105

Closed kevkha closed 9 years ago

kevkha commented 9 years ago

Auto detect multiple screens and show option to move player to another screen. Even better have option to automatically move player once screen 2 detected and move back to screen 1 when screen 2 is disconnected. Can this be achieved in QML or NW.js?

Some info for Qt5 I found http://www.ics.com/blog/whats-new-qt-5-qscreen-class

RSATom commented 9 years ago

I'll look what I can do. At the first look it should be possible, just don't sure what exact logic will be best.

jaruba commented 9 years ago

I think this can be done with just NW.js, try something like:

var gui = require('nw.gui');
gui.Screen.Init();
var win = gui.Window.get();
var lastScreens = 1;

function checkScreens() {
    win = gui.Window.get();
    if (gui.Screen.screens.length != lastScreens) {
        if (gui.Screen.screens.length == 2 && lastScreens == 1) {
            lastScreens = gui.Screen.screens.length;
            if (win.x < gui.Screen.screens[lastScreens -1].work_area.x) win.x = gui.Screen.screens[lastScreens -1].work_area.x + win.x;
        } else if (gui.Screen.screens.length == 1 && lastScreens == 2) lastScreens = gui.Screen.screens.length;
    }
}

setInterval(function() { checkScreens(); },2000);

checkScreens();

This should (hopefully) move the nw window to the second monitor when it is detected.. it does not move the window back to the first monitor when the second monitor is disconnected because the OS does that anyway (why would it leave it on a non-existing screen?). I did not test this to much though.. tell me if it worked for you. :)

kevkha commented 9 years ago

Thanks for a quick turnaround. The above code works great as this observation

  1. Start off with two screens
  2. Launch NW app and player appears on screen 2
  3. Disconnect screen 2, player goes back to screen 1 as expected
  4. Reconnect screen 2, player does not go back to screen 2

Can you check on # 4 again? Thanks.

jaruba commented 9 years ago

I think I over-complicated things in that code, try this code instead:

var gui = require('nw.gui');
gui.Screen.Init();
win = gui.Window.get();
if (win.x < gui.Screen.screens[gui.Screen.screens.length -1].work_area.x) win.x = gui.Screen.screens[gui.Screen.screens.length -1].work_area.x + win.x;

var screenCB = {
    onDisplayAdded : function(screen) {
        win = gui.Window.get();
        if (win.x < screen.work_area.x) win.x = screen.work_area.x + win.x;
    }
};

gui.Screen.on('displayAdded', screenCB.onDisplayAdded);

It worked for me in all the situations you mentioned.

kevkha commented 9 years ago

You are a super genius, latter code works both cases. Thanks so much. Now, suppose I create a second window ie

// Create a new window
var new_win = gui.Window.open('player.html', {
  position: 'center',
  width: 800,
  height: 640
});

and want to move the second window instead and leave default 'win' on screen 1 at all time. Can I simply replace 'win' with 'new_win'?

jaruba commented 9 years ago

why not just put that code only in player.html directly?

kevkha commented 9 years ago

Good point. I was thinking a hard way again and focused on default index.html. Will keep you posted shortly. Thanks.

kevkha commented 9 years ago

Sweet! Everything works the way I wanted. Thanks so much again jaruba! Please consider NW.js side is resolved.

jaruba commented 9 years ago

Glad I could help! :)

kevkha commented 9 years ago

Is there a donate button on your plugin page? Gotta buy you coffees :-)

jaruba commented 9 years ago

The button from http://www.webchimera.org/ is the entire project's donate button.

You already donated. :)

kevkha commented 9 years ago

Ok cool. Good to know. More to come in a few days ;p

RSATom commented 9 years ago

I think we still need do something similar for fullscreen window?

kevkha commented 9 years ago

Agreed.

RSATom commented 9 years ago

@kevkha, btw, how do you attach/detach additional displays? hdmi? And why do you need it to do "on the fly"?

kevkha commented 9 years ago

Yes, attach and detach the HDMI cable. I need this solution when there is no mouse available to drag player to another screen.

RSATom commented 9 years ago

so it will be enough for fullscreen window if it just will follow movements of main application window across displays (i.e. without need save active screen history inside fullscreen window)?

RSATom commented 9 years ago

or it's another solution exists - if you can detect screen attach/detach inside NW.js - you could just:

It should work, since fullscreen window shown on screen where plugin area is (inside browser). Now it will work at least on Windows, on Mac I still need implement fullcreen on second monitor.

kevkha commented 9 years ago

For my project I need main app window be on screen 1 to display other elements from back end server. When there is only one screen the player (second) window should move to screen 1 and can be on top of main app window. I haven't tested in fullscreen mode movement yet. Do you suggest that I should add fullscreen in onLoad or do it in QML?

RSATom commented 9 years ago

Hm... it's more complex configuration than I thought. Did you implement this 2 windows already? I just don't sure if it possible communicate between 2 top level windows in NW.js?

RSATom commented 9 years ago

And will you have WebChimera on both windows or only one of them?

kevkha commented 9 years ago

Yes, I have implemented two windows. I only have WebChimera on second window. So it will be active to accept commands from IR. I will test to see if I can do some basic controls using mouse from main window like toggleFullscreen() to second window.

jaruba commented 9 years ago

Do you guys mean that if a second monitor is connected, and the player is already fullscreen on the first window, it should move with fullscreen already set to the second monitor?

var wasFullscreen = 0;
var gui = require('nw.gui');
gui.Screen.Init();

function onDisplayAdded(screen) {
    if (wjs("#webchimera").plugin.fullscreen) {
        wasFullscreen = 1;
        wjs("#webchimera").plugin.toggleFullscreen();
    } else wasFullscreen = 0;
    if (wjs("#webchimera").plugin.fullscreen) wjs("#webchimera").plugin.toggleFullscreen();
    win = gui.Window.get();
    if (win.x < screen.work_area.x) win.x = screen.work_area.x + win.x;
    if (wasFullscreen == 1) setTimeout(function() { wjs("#webchimera").plugin.toggleFullscreen(); }, 3000);
}

function onDisplayRemoved(screen) {
    if (wjs("#webchimera").plugin.fullscreen) {
        wjs("#webchimera").plugin.toggleFullscreen();
        setTimeout(function() { wjs("#webchimera").plugin.toggleFullscreen(); }, 1000);
    }
}

if (gui.Screen.screens.length > 1) onDisplayAdded(gui.Screen.screens[gui.Screen.screens.length -1]);

gui.Screen.on('displayAdded', onDisplayAdded);
gui.Screen.on('displayRemoved', onDisplayRemoved);

Or do you want the player to auto-fullscreen on the second monitor every time it is connected? (as you mentioned you had no mouse anyway)

var gui = require('nw.gui');
gui.Screen.Init();

function onDisplayAdded(screen) {
    if (wjs("#webchimera").plugin.fullscreen) wjs("#webchimera").plugin.toggleFullscreen();
    win = gui.Window.get();
    if (win.x < screen.work_area.x) win.x = screen.work_area.x + win.x;
    setTimeout(function() { wjs("#webchimera").plugin.toggleFullscreen(); }, 3000);
}

function onDisplayRemoved(screen) {
    if (wjs("#webchimera").plugin.fullscreen) {
        wjs("#webchimera").plugin.toggleFullscreen();
        setTimeout(function() { wjs("#webchimera").plugin.toggleFullscreen(); }, 1000);
    }
}

if (gui.Screen.screens.length > 1) onDisplayAdded(gui.Screen.screens[gui.Screen.screens.length -1]);

gui.Screen.on('displayAdded', onDisplayAdded);
gui.Screen.on('displayRemoved', onDisplayRemoved);

These are untested though, but they should work.

As @RSATom mentioned, fullscreen on second monitor will only work on Windows, on Mac it was not implemented yet.

Although we could partially hack it on Mac too.. as HTML5 fullscreen does not require any user interaction in NW. (but the player will act by the rules of a non-fullscreen environment, as with html5 fullscreen, it doesn't know when it's fullscreen or not in QML)

RSATom commented 9 years ago

@jaruba, yes, something like this. I just think (but didn't try, so not sure) current fullscreen implementation has bug with disconnecting second monitor on-the-fly: If fullscreen mode activated on second screen, and detach this screen, I think fullscreen window will not go to first screen without help from JS.

jaruba commented 9 years ago

I edited the code above to include a scenario in which the display is removed. (although I didn't test this either) I also delayed the second .toggleFullscreen() for safety. I tried to test it but didn't manage to test it to well... I think it's because I don't test it with a hdmi cable, I'm testing with WiDi on a TV and connecting/disconnecting take a lot longer, in my last tests fullscreen didn't work on the tv at all.. but again, this could be from WiDi.

kevkha commented 9 years ago

@jaruba I tested both of your new codes but none works. Player does not follow screen 2 at all.

Since I can't move webchimera player when in fullscreen mode I manged to move NW fullscreen window back and forth with following code

// moving player to secondary screen when available
var gui = require('nw.gui');
gui.Screen.Init();
win = gui.Window.get();
if (win.x < gui.Screen.screens[gui.Screen.screens.length -1].work_area.x) win.x = gui.Screen.screens[gui.Screen.screens.length -1].work_area.x + win.x;

// start with fullscreen and focus
win.enterFullscreen();
win.focus();

var screenCB = {
    onDisplayBoundsChanged : function(screen) {
        win = gui.Window.get();
        win.leaveFullscreen();
        win.enterFullscreen();
    },
    onDisplayAdded : function(screen) {
        win = gui.Window.get();
        if (win.x < screen.work_area.x) win.x = screen.work_area.x + win.x;
        win.leaveFullscreen();
        win.enterFullscreen();
    },
    onDisplayRemoved : function(screen) {
        win = gui.Window.get();
        win.leaveFullscreen();
        win.enterFullscreen();
    }
};

// listen to screen events
gui.Screen.on('displayBoundsChanged', screenCB.onDisplayBoundsChanged);
gui.Screen.on('displayAdded', screenCB.onDisplayAdded);
gui.Screen.on('displayRemoved', screenCB.onDisplayRemoved);

And in css I have

html, body { margin: 0; padding: 0; overflow: hidden}
#player_wrapper { width: 100%; height: 100%; position: absolute; top: 0; bottom: 0}

With this solution I need to disable fullscreen on double-click to prevent window movement issue.

jaruba commented 9 years ago

But that will make the player act like it is not fullscreen.. for example, the toolbar doesn't fade out, it slides down and the progress bar remains visible..

What happens when you try my codes?

kevkha commented 9 years ago

Yes, that's the only last thing I need to hide.

jaruba commented 9 years ago

And about your code.. I think this code:

// moving player to secondary screen when available
var gui = require('nw.gui');
gui.Screen.Init();
win = gui.Window.get();
if (win.x < gui.Screen.screens[gui.Screen.screens.length -1].work_area.x) win.x = gui.Screen.screens[gui.Screen.screens.length -1].work_area.x + win.x;

// start with fullscreen and focus
win.enterFullscreen();
win.focus();

function onDisplayAdded(screen) {
    win = gui.Window.get();
    if (win.x < screen.work_area.x) win.x = screen.work_area.x + win.x;
    win.leaveFullscreen();
    win.enterFullscreen();
}

// listen to screen events
gui.Screen.on('displayAdded', onDisplayAdded);

Does the exact same thing, I don't think you need displayBoundsChanged and displayRemoved at all in this particular case. :)

kevkha commented 9 years ago

You're absolutely right. I use your code now to save some memory :-). Any hint to hide the Toolbar in this situation? I observe the entire tool bar is visible on screen 1 and on screen 2 just the single progress bar which I can live at the moment but hide the whole thing would be a nice hack.

jaruba commented 9 years ago

That is a bit tricky... the toolbar itself is not made of one element..

In /player/themes/sleek/components/Toolbar.qml change:

anchors.bottomMargin: fullscreen ? 0 : parent.containsMouse ? 0 : -height
...
opacity: fullscreen ? settings.ismoving > 5 ? 0 : 1 : 1

to this:

anchors.bottomMargin: 0
...
opacity: settings.ismoving > 5 ? 0 : 1

Then in /player/themes/sleek/components/ProgressBar.qml change:

anchors.bottomMargin: settings.multiscreen == 1 ? fullscreen ? 32 : -8 : fullscreen ? 32 : mousesurface.containsMouse ? 30 : 0 // Multiscreen - Edit
opacity: settings.multiscreen == 1 ? fullscreen ? fullscreen ? settings.ismoving > 5 ? 0 : 1 : 1 : 0 : fullscreen ? settings.ismoving > 5 ? 0 : 1 : 1 // Multiscreen - Edit

to:

anchors.bottomMargin: 30
opacity: settings.ismoving > 5 ? 0 : 1

I can't be sure of any other implications node-webkit's internal fullscreen window might have on the player though..

jaruba commented 9 years ago

Mouse Cursor might now disappear either..

kevkha commented 9 years ago

Above works well except for the circular/square progress position indicator. Which line is it?

jaruba commented 9 years ago

Try going to /player/themes/sleek/components/ProgressBar.qml and looking for id: movecur then changing:

anchors.bottomMargin: settings.multiscreen == 1 ? fullscreen ? toolbar.height : -16 : fullscreen ? toolbar.height : mousesurface.containsMouse ? toolbar.height : 0
opacity: settings.multiscreen == 1 ? fullscreen ? settings.ismoving > 5 ? 0 : 1 : 0 : fullscreen ? settings.ismoving > 5 ? 0 : 1 : 1

to this:

anchors.bottomMargin: toolbar.height
opacity: settings.ismoving > 5 ? 0 : 1
kevkha commented 9 years ago

You're super! Everything is working. Thanks! As for your NW complication concern I don't it matter with webchimer player not being fullscreen inside NW fullscreen window. If we have both NW and Webchimera in fullscreen mode there would be 2 or more instances of windows to deal with. If I wish to exit fullscreen of NW I can do so with win.toggleFullscreen().

jaruba commented 9 years ago

I don't see it as a complication, quite the contrary, I understand why your doing it. Even more so, as fullscreen does not work on second monitor on Mac and doesn't work at all on Linux, using a full-window player with Node-Webkit's Window API - Fullscreen actually fixes all of them in Node-Webkit.

My concern was with the player itself.. it checks "fullscreen" in a lot of places in the logic. So more things might not work as expected.. and I can never be sure what works and what doesn't in this scenario as the player is really big these days. :)

Also, for a more general scenario, it might even be better to use HTML5 Fullscreen instead of NW's Window API - Fullscreen, as HTML5 Fullscreen can be called on html elements, so the player doesn't even need to be the full size of the window for it to work.

kevkha commented 9 years ago

I hear you. Currently, I have disabled Key_F and double-click for toggleFullscreen in QML so that should prevent it I think. I will run more tests on Windows before moving onto Linux and OSX later. If you don't hear back from me which means things are going well. :-)

My next challenge is figure out how to use global variables in NW so that I can pass and and make calls to webchimera player commands using Javascript API across windows. If you have any ideas I'm all ears. Thanks mate!

jaruba commented 9 years ago

I'm afraid I never tried communication between 2 windows in NW.

I know there is an object in NW, called localStorage that is not erased even between app close/reopen, so it might also be possible to use it between windows, I can't be sure though, I never tried. I just keep user settings in localStorage.

mitselek commented 8 years ago

Consider using something like socket.io for communication